@aweebit/react-essentials 0.5.4 → 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 +12 -13
- 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 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -5
- 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} +1 -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/{lib/utils/index.ts → src/utils.ts} +9 -2
- package/dist/hooks/useIsomorphicLayoutEffect.d.ts +0 -4
- package/dist/hooks/useIsomorphicLayoutEffect.d.ts.map +0 -1
- package/dist/hooks/useIsomorphicLayoutEffect.js +0 -5
- package/dist/hooks/useIsomorphicLayoutEffect.js.map +0 -1
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/lib/hooks/useEventListener.ts +0 -101
- package/lib/hooks/useIsomorphicLayoutEffect.ts +0 -7
- package/lib/index.ts +0 -5
|
@@ -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';
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { DependencyList } from 'react';
|
|
2
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
|
+
|
|
3
11
|
export function noop() {}
|
|
4
12
|
|
|
5
|
-
|
|
6
|
-
export function isFunction(input: unknown): input is Function {
|
|
13
|
+
export function isFunction(input: unknown): input is Callable {
|
|
7
14
|
return typeof input === 'function';
|
|
8
15
|
}
|
|
9
16
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useIsomorphicLayoutEffect.d.ts","sourceRoot":"","sources":["../../lib/hooks/useIsomorphicLayoutEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAGxC,QAAA,MAAM,yBAAyB,wBACyB,CAAC;AAEzD,eAAe,yBAAyB,CAAC"}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { useLayoutEffect } from 'react';
|
|
2
|
-
import { noop } from "../utils/index.js";
|
|
3
|
-
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : noop;
|
|
4
|
-
export default useIsomorphicLayoutEffect;
|
|
5
|
-
//# sourceMappingURL=useIsomorphicLayoutEffect.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useIsomorphicLayoutEffect.js","sourceRoot":"","sources":["../../lib/hooks/useIsomorphicLayoutEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,MAAM,yBAAyB,GAC7B,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC;AAEzD,eAAe,yBAAyB,CAAC"}
|
package/dist/utils/index.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { DependencyList } from 'react';
|
|
2
|
-
export declare function noop(): void;
|
|
3
|
-
export declare function isFunction(input: unknown): input is Function;
|
|
4
|
-
export declare function depsAreEqual(prevDeps: DependencyList, deps: DependencyList): boolean;
|
|
5
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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;AAE5C,wBAAgB,IAAI,SAAK;AAGzB,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,MAAM,UAAU,IAAI,KAAI,CAAC;AAEzB,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,101 +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, useRef } from 'react';
|
|
9
|
-
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect.ts';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Adds `handler` as a listener for the event `eventName` of `element`
|
|
13
|
-
* (or `window` by default) with the provided `options` applied
|
|
14
|
-
*
|
|
15
|
-
* It is the user's responsibility to make sure `element` and `options` values
|
|
16
|
-
* are correctly memoized!
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
// SVGElement Event based useEventListener interface
|
|
20
|
-
function useEventListener<
|
|
21
|
-
K extends keyof SVGElementEventMap,
|
|
22
|
-
T extends SVGElement,
|
|
23
|
-
>(
|
|
24
|
-
eventName: K,
|
|
25
|
-
handler: (this: T, event: SVGElementEventMap[K]) => void,
|
|
26
|
-
element: T | null,
|
|
27
|
-
options?: boolean | AddEventListenerOptions,
|
|
28
|
-
): void;
|
|
29
|
-
|
|
30
|
-
// HTMLElement Event based useEventListener interface
|
|
31
|
-
function useEventListener<
|
|
32
|
-
K extends keyof HTMLElementEventMap,
|
|
33
|
-
T extends HTMLElement,
|
|
34
|
-
>(
|
|
35
|
-
eventName: K,
|
|
36
|
-
handler: (this: T, event: HTMLElementEventMap[K]) => void,
|
|
37
|
-
element: T | null,
|
|
38
|
-
options?: boolean | AddEventListenerOptions,
|
|
39
|
-
): void;
|
|
40
|
-
|
|
41
|
-
// Document Event based useEventListener interface
|
|
42
|
-
function useEventListener<K extends keyof DocumentEventMap>(
|
|
43
|
-
eventName: K,
|
|
44
|
-
handler: (this: Document, event: DocumentEventMap[K]) => void,
|
|
45
|
-
element: Document,
|
|
46
|
-
options?: boolean | AddEventListenerOptions,
|
|
47
|
-
): void;
|
|
48
|
-
|
|
49
|
-
// Window Event based useEventListener interface
|
|
50
|
-
function useEventListener<K extends keyof WindowEventMap>(
|
|
51
|
-
eventName: K,
|
|
52
|
-
handler: (this: Window, event: WindowEventMap[K]) => void,
|
|
53
|
-
element?: Window,
|
|
54
|
-
options?: boolean | AddEventListenerOptions,
|
|
55
|
-
): void;
|
|
56
|
-
|
|
57
|
-
// Fallback overload for all other event targets and types
|
|
58
|
-
function useEventListener<T extends EventTarget>(
|
|
59
|
-
eventName: string,
|
|
60
|
-
handler: (this: T, event: Event) => void,
|
|
61
|
-
element?: T | null,
|
|
62
|
-
options?: boolean | AddEventListenerOptions,
|
|
63
|
-
): void;
|
|
64
|
-
|
|
65
|
-
function useEventListener(
|
|
66
|
-
eventName: string,
|
|
67
|
-
handler: (this: EventTarget, event: Event) => void,
|
|
68
|
-
element?: EventTarget | null,
|
|
69
|
-
options?: boolean | AddEventListenerOptions,
|
|
70
|
-
) {
|
|
71
|
-
// Create a ref that stores handler
|
|
72
|
-
const savedHandler = useRef(handler);
|
|
73
|
-
|
|
74
|
-
useIsomorphicLayoutEffect(() => {
|
|
75
|
-
savedHandler.current = handler;
|
|
76
|
-
}, [handler]);
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (element === null) {
|
|
80
|
-
// No element has been attached to the ref yet
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Define the listening target
|
|
85
|
-
const targetElement = element ?? window;
|
|
86
|
-
|
|
87
|
-
// Create event listener that calls handler function stored in ref
|
|
88
|
-
const listener: typeof handler = function (event) {
|
|
89
|
-
savedHandler.current.call(this, event);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
targetElement.addEventListener(eventName, listener, options);
|
|
93
|
-
|
|
94
|
-
// Remove event listener on cleanup
|
|
95
|
-
return () => {
|
|
96
|
-
targetElement.removeEventListener(eventName, listener, options);
|
|
97
|
-
};
|
|
98
|
-
}, [eventName, element, options]);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export default useEventListener;
|
package/lib/index.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export { default as useEventListener } from './hooks/useEventListener.js';
|
|
2
|
-
export { default as useForceUpdate } from './hooks/useForceUpdate.js';
|
|
3
|
-
export { default as useIsomorphicLayoutEffect } from './hooks/useIsomorphicLayoutEffect.js';
|
|
4
|
-
export { default as useReducerWithDeps } from './hooks/useReducerWithDeps.js';
|
|
5
|
-
export { default as useStateWithDeps } from './hooks/useStateWithDeps.js';
|