Move lib up one level
This commit is contained in:
21
src/tools/AndByDiscriminatingKey.ts
Normal file
21
src/tools/AndByDiscriminatingKey.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export type AndByDiscriminatingKey<
|
||||
DiscriminatingKey extends string,
|
||||
U1 extends Record<DiscriminatingKey, string>,
|
||||
U2 extends Record<DiscriminatingKey, string>
|
||||
> = AndByDiscriminatingKey.Tf1<DiscriminatingKey, U1, U1, U2>;
|
||||
|
||||
export declare namespace AndByDiscriminatingKey {
|
||||
export type Tf1<
|
||||
DiscriminatingKey extends string,
|
||||
U1,
|
||||
U1Again extends Record<DiscriminatingKey, string>,
|
||||
U2 extends Record<DiscriminatingKey, string>
|
||||
> = U1 extends Pick<U2, DiscriminatingKey> ? Tf2<DiscriminatingKey, U1, U2, U1Again> : U1;
|
||||
|
||||
export type Tf2<
|
||||
DiscriminatingKey extends string,
|
||||
SingletonU1 extends Record<DiscriminatingKey, string>,
|
||||
U2,
|
||||
U1 extends Record<DiscriminatingKey, string>
|
||||
> = U2 extends Pick<SingletonU1, DiscriminatingKey> ? U2 & SingletonU1 : U2 extends Pick<U1, DiscriminatingKey> ? never : U2;
|
||||
}
|
64
src/tools/Array.prototype.every.ts
Normal file
64
src/tools/Array.prototype.every.ts
Normal file
@ -0,0 +1,64 @@
|
||||
if (!Array.prototype.every) {
|
||||
Array.prototype.every = function (callbackfn: any, thisArg: any) {
|
||||
"use strict";
|
||||
var T, k;
|
||||
|
||||
if (this == null) {
|
||||
throw new TypeError("this is null or not defined");
|
||||
}
|
||||
|
||||
// 1. Let O be the result of calling ToObject passing the this
|
||||
// value as the argument.
|
||||
var O = Object(this);
|
||||
|
||||
// 2. Let lenValue be the result of calling the Get internal method
|
||||
// of O with the argument "length".
|
||||
// 3. Let len be ToUint32(lenValue).
|
||||
var len = O.length >>> 0;
|
||||
|
||||
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
|
||||
if (typeof callbackfn !== "function" && Object.prototype.toString.call(callbackfn) !== "[object Function]") {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
if (arguments.length > 1) {
|
||||
T = thisArg;
|
||||
}
|
||||
|
||||
// 6. Let k be 0.
|
||||
k = 0;
|
||||
|
||||
// 7. Repeat, while k < len
|
||||
while (k < len) {
|
||||
var kValue;
|
||||
|
||||
// a. Let Pk be ToString(k).
|
||||
// This is implicit for LHS operands of the in operator
|
||||
// b. Let kPresent be the result of calling the HasProperty internal
|
||||
// method of O with argument Pk.
|
||||
// This step can be combined with c
|
||||
// c. If kPresent is true, then
|
||||
if (k in O) {
|
||||
var testResult;
|
||||
// i. Let kValue be the result of calling the Get internal method
|
||||
// of O with argument Pk.
|
||||
kValue = O[k];
|
||||
|
||||
// ii. Let testResult be the result of calling the Call internal method
|
||||
// of callbackfn with T as the this value if T is not undefined
|
||||
// else is the result of calling callbackfn
|
||||
// and argument list containing kValue, k, and O.
|
||||
if (T) testResult = callbackfn.call(T, kValue, k, O);
|
||||
else testResult = callbackfn(kValue, k, O);
|
||||
|
||||
// iii. If ToBoolean(testResult) is false, return false.
|
||||
if (!testResult) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
k++;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
3
src/tools/DeepPartial.ts
Normal file
3
src/tools/DeepPartial.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export type DeepPartial<T> = {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
};
|
9
src/tools/HTMLElement.prototype.prepend.ts
Normal file
9
src/tools/HTMLElement.prototype.prepend.ts
Normal file
@ -0,0 +1,9 @@
|
||||
if (!HTMLElement.prototype.prepend) {
|
||||
HTMLElement.prototype.prepend = function (childNode) {
|
||||
if (typeof childNode === "string") {
|
||||
throw new Error("Error with HTMLElement.prototype.appendFirst polyfill");
|
||||
}
|
||||
|
||||
this.insertBefore(childNode, this.firstChild);
|
||||
};
|
||||
}
|
3
src/tools/Markdown.ts
Normal file
3
src/tools/Markdown.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import Markdown from "react-markdown";
|
||||
|
||||
export { Markdown };
|
1
src/tools/SetOptional.ts
Normal file
1
src/tools/SetOptional.ts
Normal file
@ -0,0 +1 @@
|
||||
export type SetOptional<T extends Record<string, unknown>, K extends keyof T> = Omit<T, K> & Partial<Record<K, T[K]>>;
|
5
src/tools/allPropertiesValuesToUndefined.ts
Normal file
5
src/tools/allPropertiesValuesToUndefined.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import "minimal-polyfills/Object.fromEntries";
|
||||
|
||||
export function allPropertiesValuesToUndefined<T extends Record<string, unknown>>(obj: T): Record<keyof T, undefined> {
|
||||
return Object.fromEntries(Object.entries(obj).map(([key]) => [key, undefined])) as any;
|
||||
}
|
1
src/tools/assert.ts
Normal file
1
src/tools/assert.ts
Normal file
@ -0,0 +1 @@
|
||||
export { assert } from "tsafe/assert";
|
44
src/tools/clsx.ts
Normal file
44
src/tools/clsx.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { assert } from "tsafe/assert";
|
||||
import { typeGuard } from "tsafe/typeGuard";
|
||||
|
||||
export type CxArg = undefined | null | string | boolean | Partial<Record<string, boolean | null | undefined>> | readonly CxArg[];
|
||||
|
||||
export const clsx = (...args: CxArg[]): string => {
|
||||
const len = args.length;
|
||||
let i = 0;
|
||||
let cls = "";
|
||||
for (; i < len; i++) {
|
||||
const arg = args[i];
|
||||
if (arg == null) continue;
|
||||
|
||||
let toAdd;
|
||||
switch (typeof arg) {
|
||||
case "boolean":
|
||||
break;
|
||||
case "object": {
|
||||
if (Array.isArray(arg)) {
|
||||
toAdd = clsx(...arg);
|
||||
} else {
|
||||
assert(!typeGuard<{ length: number }>(arg, false));
|
||||
|
||||
toAdd = "";
|
||||
for (const k in arg) {
|
||||
if (arg[k as string] && k) {
|
||||
toAdd && (toAdd += " ");
|
||||
toAdd += k;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
toAdd = arg;
|
||||
}
|
||||
}
|
||||
if (toAdd) {
|
||||
cls && (cls += " ");
|
||||
cls += toAdd;
|
||||
}
|
||||
}
|
||||
return cls;
|
||||
};
|
44
src/tools/deepAssign.ts
Normal file
44
src/tools/deepAssign.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { assert } from "tsafe/assert";
|
||||
import { is } from "tsafe/is";
|
||||
import { deepClone } from "./deepClone";
|
||||
|
||||
//Warning: Be mindful that because of array this is not idempotent.
|
||||
export function deepAssign(params: { target: Record<string, unknown>; source: Record<string, unknown> }) {
|
||||
const { target } = params;
|
||||
|
||||
const source = deepClone(params.source);
|
||||
|
||||
Object.keys(source).forEach(key => {
|
||||
var dereferencedSource = source[key];
|
||||
|
||||
if (target[key] === undefined || dereferencedSource instanceof Function || !(dereferencedSource instanceof Object)) {
|
||||
Object.defineProperty(target, key, {
|
||||
"enumerable": true,
|
||||
"writable": true,
|
||||
"configurable": true,
|
||||
"value": dereferencedSource
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const dereferencedTarget = target[key];
|
||||
|
||||
if (dereferencedSource instanceof Array) {
|
||||
assert(is<unknown[]>(dereferencedTarget));
|
||||
assert(is<unknown[]>(dereferencedSource));
|
||||
|
||||
dereferencedSource.forEach(entry => dereferencedTarget.push(entry));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
assert(is<Record<string, unknown>>(dereferencedTarget));
|
||||
assert(is<Record<string, unknown>>(dereferencedSource));
|
||||
|
||||
deepAssign({
|
||||
"target": dereferencedTarget,
|
||||
"source": dereferencedSource
|
||||
});
|
||||
});
|
||||
}
|
17
src/tools/deepClone.ts
Normal file
17
src/tools/deepClone.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import "minimal-polyfills/Object.fromEntries";
|
||||
|
||||
export function deepClone<T>(o: T): T {
|
||||
if (!(o instanceof Object)) {
|
||||
return o;
|
||||
}
|
||||
|
||||
if (typeof o === "function") {
|
||||
return o;
|
||||
}
|
||||
|
||||
if (o instanceof Array) {
|
||||
return o.map(deepClone) as any;
|
||||
}
|
||||
|
||||
return Object.fromEntries(Object.entries(o).map(([key, value]) => [key, deepClone(value)])) as any;
|
||||
}
|
2
src/tools/emailRegExp.ts
Normal file
2
src/tools/emailRegExp.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const emailRegexp =
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
70
src/tools/headInsert.ts
Normal file
70
src/tools/headInsert.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import "./HTMLElement.prototype.prepend";
|
||||
import { Deferred } from "evt/tools/Deferred";
|
||||
|
||||
export function headInsert(
|
||||
params:
|
||||
| {
|
||||
type: "css";
|
||||
href: string;
|
||||
position: "append" | "prepend";
|
||||
}
|
||||
| {
|
||||
type: "javascript";
|
||||
src: string;
|
||||
}
|
||||
) {
|
||||
const htmlElement = document.createElement(
|
||||
(() => {
|
||||
switch (params.type) {
|
||||
case "css":
|
||||
return "link";
|
||||
case "javascript":
|
||||
return "script";
|
||||
}
|
||||
})()
|
||||
);
|
||||
|
||||
const dLoaded = new Deferred<void>();
|
||||
|
||||
htmlElement.addEventListener("load", () => dLoaded.resolve());
|
||||
|
||||
Object.assign(
|
||||
htmlElement,
|
||||
(() => {
|
||||
switch (params.type) {
|
||||
case "css":
|
||||
return {
|
||||
"href": params.href,
|
||||
"type": "text/css",
|
||||
"rel": "stylesheet",
|
||||
"media": "screen,print"
|
||||
};
|
||||
case "javascript":
|
||||
return {
|
||||
"src": params.src,
|
||||
"type": "text/javascript"
|
||||
};
|
||||
}
|
||||
})()
|
||||
);
|
||||
|
||||
document.getElementsByTagName("head")[0][
|
||||
(() => {
|
||||
switch (params.type) {
|
||||
case "javascript":
|
||||
return "appendChild";
|
||||
case "css":
|
||||
return (() => {
|
||||
switch (params.position) {
|
||||
case "append":
|
||||
return "appendChild";
|
||||
case "prepend":
|
||||
return "prepend";
|
||||
}
|
||||
})();
|
||||
}
|
||||
})()
|
||||
](htmlElement);
|
||||
|
||||
return dLoaded.pr;
|
||||
}
|
55
src/tools/memoize.ts
Normal file
55
src/tools/memoize.ts
Normal file
@ -0,0 +1,55 @@
|
||||
type SimpleType = number | string | boolean | null | undefined;
|
||||
type FuncWithSimpleParams<T extends SimpleType[], R> = (...args: T) => R;
|
||||
|
||||
export function memoize<T extends SimpleType[], R>(
|
||||
fn: FuncWithSimpleParams<T, R>,
|
||||
options?: {
|
||||
argsLength?: number;
|
||||
max?: number;
|
||||
}
|
||||
): FuncWithSimpleParams<T, R> {
|
||||
const cache = new Map<string, ReturnType<FuncWithSimpleParams<T, R>>>();
|
||||
|
||||
const { argsLength = fn.length, max = Infinity } = options ?? {};
|
||||
|
||||
return ((...args: Parameters<FuncWithSimpleParams<T, R>>) => {
|
||||
const key = JSON.stringify(
|
||||
args
|
||||
.slice(0, argsLength)
|
||||
.map(v => {
|
||||
if (v === null) {
|
||||
return "null";
|
||||
}
|
||||
if (v === undefined) {
|
||||
return "undefined";
|
||||
}
|
||||
switch (typeof v) {
|
||||
case "number":
|
||||
return `number-${v}`;
|
||||
case "string":
|
||||
return `string-${v}`;
|
||||
case "boolean":
|
||||
return `boolean-${v ? "true" : "false"}`;
|
||||
}
|
||||
})
|
||||
.join("-sIs9sAslOdeWlEdIos3-")
|
||||
);
|
||||
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key);
|
||||
}
|
||||
|
||||
if (max === cache.size) {
|
||||
for (const key of cache.keys()) {
|
||||
cache.delete(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const value = fn(...args);
|
||||
|
||||
cache.set(key, value);
|
||||
|
||||
return value;
|
||||
}) as any;
|
||||
}
|
3
src/tools/pathBasename.ts
Normal file
3
src/tools/pathBasename.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function pathBasename(path: string) {
|
||||
return path.split("/").reverse()[0];
|
||||
}
|
45
src/tools/useCallbackFactory.ts
Normal file
45
src/tools/useCallbackFactory.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { id } from "tsafe/id";
|
||||
import { memoize } from "./memoize";
|
||||
|
||||
export type CallbackFactory<FactoryArgs extends unknown[], Args extends unknown[], R> = (...factoryArgs: FactoryArgs) => (...args: Args) => R;
|
||||
|
||||
/**
|
||||
* https://docs.powerhooks.dev/api-reference/usecallbackfactory
|
||||
*
|
||||
* const callbackFactory= useCallbackFactory(
|
||||
* ([key]: [string], [params]: [{ foo: number; }]) => {
|
||||
* ...
|
||||
* },
|
||||
* []
|
||||
* );
|
||||
*
|
||||
* WARNING: Factory args should not be of variable length.
|
||||
*
|
||||
*/
|
||||
export function useCallbackFactory<FactoryArgs extends (string | number | boolean)[], Args extends unknown[], R = void>(
|
||||
callback: (...callbackArgs: [FactoryArgs, Args]) => R
|
||||
): CallbackFactory<FactoryArgs, Args, R> {
|
||||
type Out = CallbackFactory<FactoryArgs, Args, R>;
|
||||
|
||||
const callbackRef = useRef<typeof callback>(callback);
|
||||
|
||||
callbackRef.current = callback;
|
||||
|
||||
const memoizedRef = useRef<Out | undefined>(undefined);
|
||||
|
||||
return useState(() =>
|
||||
id<Out>((...factoryArgs) => {
|
||||
if (memoizedRef.current === undefined) {
|
||||
memoizedRef.current = memoize(
|
||||
(...factoryArgs: FactoryArgs) =>
|
||||
(...args: Args) =>
|
||||
callbackRef.current(factoryArgs, args),
|
||||
{ "argsLength": factoryArgs.length }
|
||||
);
|
||||
}
|
||||
|
||||
return memoizedRef.current(...factoryArgs);
|
||||
})
|
||||
)[0];
|
||||
}
|
10
src/tools/useConst.ts
Normal file
10
src/tools/useConst.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { useState } from "react";
|
||||
|
||||
/**
|
||||
* Compute a value on first render and never again,
|
||||
* Equivalent of const [x] = useState(()=> ...)
|
||||
*/
|
||||
export function useConst<T>(getValue: () => T): T {
|
||||
const [value] = useState(getValue);
|
||||
return value;
|
||||
}
|
15
src/tools/useConstCallback.ts
Normal file
15
src/tools/useConstCallback.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { Parameters } from "tsafe/Parameters";
|
||||
|
||||
/** https://stackoverflow.com/questions/65890278/why-cant-usecallback-always-return-the-same-ref */
|
||||
export function useConstCallback<T extends ((...args: any[]) => unknown) | undefined | null>(callback: NonNullable<T>): T {
|
||||
const callbackRef = useRef<typeof callback>(null as any);
|
||||
|
||||
callbackRef.current = callback;
|
||||
|
||||
return useState(
|
||||
() =>
|
||||
(...args: Parameters<T>) =>
|
||||
callbackRef.current(...args)
|
||||
)[0] as T;
|
||||
}
|
12
src/tools/useCssAndCx.ts
Normal file
12
src/tools/useCssAndCx.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { clsx as cx } from "./clsx";
|
||||
|
||||
/**
|
||||
* @deprecated: Use clsx instead.
|
||||
* import { clsx } from "keycloakify/lib/tools/clsx";
|
||||
* You can use clsx as cx.
|
||||
* If you where using the css() function you can import
|
||||
* it from @emotion/css: https://emotion.sh/docs/@emotion/css
|
||||
*/
|
||||
export function useCssAndCx() {
|
||||
return { cx };
|
||||
}
|
Reference in New Issue
Block a user