@aweebit/react-essentials 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +290 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useEventListener.d.ts +69 -10
- package/dist/hooks/useEventListener.d.ts.map +1 -1
- package/dist/hooks/useEventListener.js +16 -12
- package/dist/hooks/useEventListener.js.map +1 -1
- package/dist/hooks/useForceUpdate.d.ts +3 -4
- package/dist/hooks/useForceUpdate.d.ts.map +1 -1
- package/dist/hooks/useForceUpdate.js +4 -4
- package/dist/hooks/useForceUpdate.js.map +1 -1
- package/dist/hooks/useReducerWithDeps.d.ts +13 -10
- package/dist/hooks/useReducerWithDeps.d.ts.map +1 -1
- package/dist/hooks/useReducerWithDeps.js +10 -11
- package/dist/hooks/useReducerWithDeps.js.map +1 -1
- package/dist/hooks/useStateWithDeps.d.ts +6 -7
- package/dist/hooks/useStateWithDeps.d.ts.map +1 -1
- package/dist/hooks/useStateWithDeps.js +8 -9
- package/dist/hooks/useStateWithDeps.js.map +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/misc/createSafeContext.d.ts +52 -0
- package/dist/misc/createSafeContext.d.ts.map +1 -0
- package/dist/misc/createSafeContext.js +46 -0
- package/dist/misc/createSafeContext.js.map +1 -0
- package/dist/misc/index.d.ts +2 -0
- package/dist/misc/index.d.ts.map +1 -0
- package/dist/misc/index.js +2 -0
- package/dist/misc/index.js.map +1 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/{utils/index.js → utils.js} +2 -2
- package/dist/utils.js.map +1 -0
- package/package.json +25 -15
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useEventListener.ts +170 -0
- package/{lib → src}/hooks/useForceUpdate.ts +8 -6
- package/{lib → src}/hooks/useReducerWithDeps.ts +23 -17
- package/{lib → src}/hooks/useStateWithDeps.ts +8 -9
- package/src/index.ts +2 -0
- package/src/misc/createSafeContext.ts +83 -0
- package/src/misc/index.ts +1 -0
- package/src/utils.ts +25 -0
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/lib/hooks/useEventListener.ts +0 -95
- package/lib/index.ts +0 -4
- package/lib/utils/index.ts +0 -16
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type Context, type Provider, createContext, useContext } from 'react';
|
|
2
|
+
import type { ArgumentFallback } from '../utils.js';
|
|
3
|
+
|
|
4
|
+
const moValueSymbol = Symbol('noValue');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A React context with a required `displayName` and the obsolete `Consumer`
|
|
8
|
+
* property purposefully omitted so that it is impossible to pass the context
|
|
9
|
+
* as an argument to `useContext` or `use` (the hook produced with
|
|
10
|
+
* {@linkcode createSafeContext} should be used instead)
|
|
11
|
+
*
|
|
12
|
+
* @see {@linkcode createSafeContext}
|
|
13
|
+
*/
|
|
14
|
+
// The type is conditional so that both React 18 and 19 are correctly supported.
|
|
15
|
+
// The code duplication is necessary for the type to be displayed correctly by
|
|
16
|
+
// TypeDoc.
|
|
17
|
+
export type RestrictedContext<T> =
|
|
18
|
+
Context<T> extends Provider<T>
|
|
19
|
+
? { Provider: Provider<T>; displayName: string } & Provider<T>
|
|
20
|
+
: { Provider: Provider<T>; displayName: string };
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @see {@linkcode createSafeContext}
|
|
24
|
+
*/
|
|
25
|
+
export type SafeContext<DisplayName extends string, T> = {
|
|
26
|
+
[K in `${DisplayName}Context`]: RestrictedContext<T>;
|
|
27
|
+
} & {
|
|
28
|
+
[K in `use${DisplayName}`]: () => T;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* For a given type `T`, returns a function that produces both a context of that
|
|
33
|
+
* type and a hook that returns the current context value if one was provided,
|
|
34
|
+
* or throws an error otherwise
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* const { ItemsContext, useItems } = createSafeContext<string[]>()('Items');
|
|
39
|
+
*
|
|
40
|
+
* const Parent = () => (
|
|
41
|
+
* <ItemsContext value={['compass', 'newspaper', 'banana']}>
|
|
42
|
+
* <Child />
|
|
43
|
+
* </ItemsContext>
|
|
44
|
+
* );
|
|
45
|
+
*
|
|
46
|
+
* const Child = () => useItems().join(', ');
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @returns
|
|
50
|
+
* A function that accepts a single string argument `displayName` (e.g.
|
|
51
|
+
* `"Items"`) and returns an object with the following properties:
|
|
52
|
+
* - ``` `${displayName}Context` ``` (e.g. `ItemsContext`): the context
|
|
53
|
+
* - ``` `use${displayName}` ``` (e.g. `useItems`): a hook that returns the
|
|
54
|
+
* current context value if one was provided, or throws an error otherwise
|
|
55
|
+
*/
|
|
56
|
+
export function createSafeContext<T = never>() {
|
|
57
|
+
return <DisplayName extends string>(
|
|
58
|
+
displayName: [T] extends [never]
|
|
59
|
+
? never
|
|
60
|
+
: ArgumentFallback<DisplayName, never, string>,
|
|
61
|
+
): SafeContext<DisplayName, T> => {
|
|
62
|
+
const contextName = `${displayName as DisplayName}Context` as const;
|
|
63
|
+
const hookName = `use${displayName as DisplayName}` as const;
|
|
64
|
+
|
|
65
|
+
const Context = createContext<T | typeof moValueSymbol>(moValueSymbol);
|
|
66
|
+
Context.displayName = contextName;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
[contextName]: Context as RestrictedContext<T>,
|
|
70
|
+
[hookName]: () => {
|
|
71
|
+
const value = useContext(Context);
|
|
72
|
+
if (value === moValueSymbol) {
|
|
73
|
+
throw new Error(`No ${contextName} value was provided`);
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
},
|
|
77
|
+
} as {
|
|
78
|
+
[K in typeof contextName]: RestrictedContext<T>;
|
|
79
|
+
} & {
|
|
80
|
+
[K in typeof hookName]: () => T;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './createSafeContext.js';
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DependencyList } from 'react';
|
|
2
|
+
|
|
3
|
+
export type Callable = (...args: never) => unknown;
|
|
4
|
+
|
|
5
|
+
export type ArgumentFallback<
|
|
6
|
+
T extends Base,
|
|
7
|
+
Default extends Base,
|
|
8
|
+
Base = unknown,
|
|
9
|
+
> = [T] extends [never] ? Default : [Base] extends [T] ? Default : T;
|
|
10
|
+
|
|
11
|
+
export function noop() {}
|
|
12
|
+
|
|
13
|
+
export function isFunction(input: unknown): input is Callable {
|
|
14
|
+
return typeof input === 'function';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function depsAreEqual(
|
|
18
|
+
prevDeps: DependencyList,
|
|
19
|
+
deps: DependencyList,
|
|
20
|
+
): boolean {
|
|
21
|
+
return (
|
|
22
|
+
prevDeps.length === deps.length &&
|
|
23
|
+
deps.every((dep, index) => Object.is(dep, prevDeps[index]))
|
|
24
|
+
);
|
|
25
|
+
}
|
package/dist/utils/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAG5C,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,cAAc,GACnB,OAAO,CAKT"}
|
package/dist/utils/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAEA,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAwB,EACxB,IAAoB;IAEpB,OAAO,CACL,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAC/B,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5D,CAAC;AACJ,CAAC"}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Based on {@link https://github.com/juliencrn/usehooks-ts}
|
|
3
|
-
*
|
|
4
|
-
* @license MIT
|
|
5
|
-
* @copyright 2020 Julien CARON
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { useEffect, useLayoutEffect, useRef } from 'react';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Adds `handler` as a listener for the event `eventName` of `element`
|
|
12
|
-
* (or `window` by default) with the provided `options` applied
|
|
13
|
-
*
|
|
14
|
-
* It is the user's responsibility to make sure `element` and `options` values
|
|
15
|
-
* are correctly memoized!
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
// SVGElement Event based useEventListener interface
|
|
19
|
-
function useEventListener<
|
|
20
|
-
K extends keyof SVGElementEventMap,
|
|
21
|
-
T extends SVGElement,
|
|
22
|
-
>(
|
|
23
|
-
eventName: K,
|
|
24
|
-
handler: (this: T, event: SVGElementEventMap[K]) => void,
|
|
25
|
-
element: T,
|
|
26
|
-
options?: boolean | AddEventListenerOptions,
|
|
27
|
-
): void;
|
|
28
|
-
|
|
29
|
-
// HTMLElement Event based useEventListener interface
|
|
30
|
-
function useEventListener<
|
|
31
|
-
K extends keyof HTMLElementEventMap,
|
|
32
|
-
T extends HTMLElement,
|
|
33
|
-
>(
|
|
34
|
-
eventName: K,
|
|
35
|
-
handler: (this: T, event: HTMLElementEventMap[K]) => void,
|
|
36
|
-
element: T,
|
|
37
|
-
options?: boolean | AddEventListenerOptions,
|
|
38
|
-
): void;
|
|
39
|
-
|
|
40
|
-
// Document Event based useEventListener interface
|
|
41
|
-
function useEventListener<K extends keyof DocumentEventMap>(
|
|
42
|
-
eventName: K,
|
|
43
|
-
handler: (this: Document, event: DocumentEventMap[K]) => void,
|
|
44
|
-
element: Document,
|
|
45
|
-
options?: boolean | AddEventListenerOptions,
|
|
46
|
-
): void;
|
|
47
|
-
|
|
48
|
-
// Window Event based useEventListener interface
|
|
49
|
-
function useEventListener<K extends keyof WindowEventMap>(
|
|
50
|
-
eventName: K,
|
|
51
|
-
handler: (this: Window, event: WindowEventMap[K]) => void,
|
|
52
|
-
element?: Window,
|
|
53
|
-
options?: boolean | AddEventListenerOptions,
|
|
54
|
-
): void;
|
|
55
|
-
|
|
56
|
-
// Fallback overload for all other event targets and types
|
|
57
|
-
function useEventListener(
|
|
58
|
-
eventName: string,
|
|
59
|
-
handler: (this: EventTarget, event: Event) => void,
|
|
60
|
-
element?: EventTarget,
|
|
61
|
-
options?: boolean | AddEventListenerOptions,
|
|
62
|
-
): void;
|
|
63
|
-
|
|
64
|
-
function useEventListener(
|
|
65
|
-
eventName: string,
|
|
66
|
-
handler: (this: EventTarget, event: Event) => void,
|
|
67
|
-
element?: EventTarget,
|
|
68
|
-
options?: boolean | AddEventListenerOptions,
|
|
69
|
-
) {
|
|
70
|
-
// Create a ref that stores handler
|
|
71
|
-
const savedHandler = useRef(handler);
|
|
72
|
-
|
|
73
|
-
useLayoutEffect(() => {
|
|
74
|
-
savedHandler.current = handler;
|
|
75
|
-
}, [handler]);
|
|
76
|
-
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
// Define the listening target
|
|
79
|
-
const targetElement = element ?? window;
|
|
80
|
-
|
|
81
|
-
// Create event listener that calls handler function stored in ref
|
|
82
|
-
const listener: typeof handler = function (event) {
|
|
83
|
-
savedHandler.current.call(this, event);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
targetElement.addEventListener(eventName, listener, options);
|
|
87
|
-
|
|
88
|
-
// Remove event listener on cleanup
|
|
89
|
-
return () => {
|
|
90
|
-
targetElement.removeEventListener(eventName, listener, options);
|
|
91
|
-
};
|
|
92
|
-
}, [eventName, element, options]);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export default useEventListener;
|
package/lib/index.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { default as useEventListener } from './hooks/useEventListener.js';
|
|
2
|
-
export { default as useForceUpdate } from './hooks/useForceUpdate.js';
|
|
3
|
-
export { default as useReducerWithDeps } from './hooks/useReducerWithDeps.js';
|
|
4
|
-
export { default as useStateWithDeps } from './hooks/useStateWithDeps.js';
|
package/lib/utils/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { DependencyList } from 'react';
|
|
2
|
-
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
4
|
-
export function isFunction(input: unknown): input is Function {
|
|
5
|
-
return typeof input === 'function';
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function depsAreEqual(
|
|
9
|
-
prevDeps: DependencyList,
|
|
10
|
-
deps: DependencyList,
|
|
11
|
-
): boolean {
|
|
12
|
-
return (
|
|
13
|
-
prevDeps.length === deps.length &&
|
|
14
|
-
deps.every((dep, index) => Object.is(dep, prevDeps[index]))
|
|
15
|
-
);
|
|
16
|
-
}
|