@aweebit/react-essentials 0.6.0 → 0.8.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 +832 -115
- package/dist/hooks/useEventListener.d.ts +60 -46
- package/dist/hooks/useEventListener.d.ts.map +1 -1
- package/dist/hooks/useEventListener.js +46 -10
- package/dist/hooks/useEventListener.js.map +1 -1
- package/dist/hooks/useForceUpdate.d.ts +49 -0
- package/dist/hooks/useForceUpdate.d.ts.map +1 -1
- package/dist/hooks/useForceUpdate.js +54 -5
- package/dist/hooks/useForceUpdate.js.map +1 -1
- package/dist/hooks/useReducerWithDeps.d.ts +4 -1
- package/dist/hooks/useReducerWithDeps.d.ts.map +1 -1
- package/dist/hooks/useReducerWithDeps.js +7 -4
- package/dist/hooks/useReducerWithDeps.js.map +1 -1
- package/dist/hooks/useStateWithDeps.d.ts +35 -0
- package/dist/hooks/useStateWithDeps.d.ts.map +1 -1
- package/dist/hooks/useStateWithDeps.js +42 -9
- package/dist/hooks/useStateWithDeps.js.map +1 -1
- package/dist/misc/createSafeContext.d.ts +48 -9
- package/dist/misc/createSafeContext.d.ts.map +1 -1
- package/dist/misc/createSafeContext.js +41 -7
- package/dist/misc/createSafeContext.js.map +1 -1
- package/package.json +1 -1
- package/src/hooks/useEventListener.ts +113 -100
- package/src/hooks/useForceUpdate.ts +54 -5
- package/src/hooks/useReducerWithDeps.ts +7 -4
- package/src/hooks/useStateWithDeps.ts +41 -9
- package/src/misc/createSafeContext.ts +48 -9
|
@@ -4,60 +4,73 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright 2020 Julien CARON
|
|
6
6
|
*/
|
|
7
|
+
import { type RefObject } from 'react';
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
-
* provided `options` applied
|
|
10
|
-
*
|
|
11
|
-
* If `element` is `undefined`, `window` is used instead.
|
|
12
|
-
*
|
|
13
|
-
* If `element` is `null`, no event listener is added.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```tsx
|
|
17
|
-
* useEventListener('resize', () => {
|
|
18
|
-
* console.log(window.innerWidth, window.innerHeight);
|
|
19
|
-
* });
|
|
20
|
-
*
|
|
21
|
-
* useEventListener(
|
|
22
|
-
* 'visibilitychange',
|
|
23
|
-
* () => console.log(document.visibilityState),
|
|
24
|
-
* document
|
|
25
|
-
* );
|
|
9
|
+
* The type of {@linkcode useEventListener}
|
|
26
10
|
*
|
|
27
|
-
*
|
|
28
|
-
* useEventListener
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* @
|
|
11
|
+
* @see
|
|
12
|
+
* {@linkcode useEventListener},
|
|
13
|
+
* {@linkcode UseEventListenerWithImplicitWindowTarget},
|
|
14
|
+
* {@linkcode UseEventListenerWithExplicitTarget},
|
|
15
|
+
* {@linkcode UseEventListenerWithAnyExplicitTarget}
|
|
32
16
|
*/
|
|
33
|
-
export
|
|
17
|
+
export type UseEventListener = UseEventListenerWithImplicitWindowTarget & UseEventListenerWithExplicitTarget<Window, WindowEventMap> & UseEventListenerWithExplicitTarget<Document, DocumentEventMap> & UseEventListenerWithExplicitTarget<HTMLElement, HTMLElementEventMap> & UseEventListenerWithExplicitTarget<SVGElement, SVGElementEventMap> & UseEventListenerWithExplicitTarget<MathMLElement, MathMLElementEventMap> & UseEventListenerWithAnyExplicitTarget;
|
|
34
18
|
/**
|
|
35
|
-
* @see
|
|
36
|
-
* @
|
|
19
|
+
* @see
|
|
20
|
+
* {@linkcode useEventListener},
|
|
21
|
+
* {@linkcode UseEventListenerWithImplicitWindowTargetArgs} */
|
|
22
|
+
export type UseEventListenerWithImplicitWindowTarget = <K extends keyof WindowEventMap>(...args: UseEventListenerWithImplicitWindowTargetArgs<K>) => void;
|
|
23
|
+
/**
|
|
24
|
+
* @see
|
|
25
|
+
* {@linkcode useEventListener},
|
|
26
|
+
* {@linkcode UseEventListenerWithExplicitTargetArgs}
|
|
37
27
|
*/
|
|
38
|
-
export
|
|
28
|
+
export type UseEventListenerWithExplicitTarget<Target extends EventTarget, EventMap = Record<string, Event>> = <T extends Target, K extends keyof EventMap>(...args: UseEventListenerWithExplicitTargetArgs<EventMap, T, K>) => void;
|
|
39
29
|
/**
|
|
40
|
-
* @see
|
|
41
|
-
* @
|
|
30
|
+
* @see
|
|
31
|
+
* {@linkcode useEventListener},
|
|
32
|
+
* {@linkcode UseEventListenerWithExplicitTarget}
|
|
42
33
|
*/
|
|
43
|
-
export
|
|
34
|
+
export type UseEventListenerWithAnyExplicitTarget = UseEventListenerWithExplicitTarget<EventTarget>;
|
|
44
35
|
/**
|
|
45
|
-
* @see
|
|
46
|
-
* @
|
|
36
|
+
* @see
|
|
37
|
+
* {@linkcode useEventListener},
|
|
38
|
+
* {@linkcode UseEventListenerWithExplicitTargetArgs}
|
|
47
39
|
*/
|
|
48
|
-
export
|
|
40
|
+
export type UseEventListenerWithImplicitWindowTargetArgs<K extends keyof WindowEventMap> = UseEventListenerWithExplicitTargetArgs<WindowEventMap, Window, K> extends [
|
|
41
|
+
unknown,
|
|
42
|
+
...infer Args
|
|
43
|
+
] ? Args : never;
|
|
49
44
|
/**
|
|
50
|
-
* @see
|
|
51
|
-
* @
|
|
45
|
+
* @see
|
|
46
|
+
* {@linkcode useEventListener}
|
|
52
47
|
*/
|
|
53
|
-
export
|
|
48
|
+
export type UseEventListenerWithExplicitTargetArgs<EventMap, T extends EventTarget, K extends keyof EventMap> = [
|
|
49
|
+
target: T | (RefObject<T> & {
|
|
50
|
+
addEventListener?: never;
|
|
51
|
+
}) | null,
|
|
52
|
+
eventName: K,
|
|
53
|
+
handler: (this: NoInfer<T>, event: EventMap[K]) => void,
|
|
54
|
+
options?: AddEventListenerOptions | boolean | undefined
|
|
55
|
+
];
|
|
54
56
|
/**
|
|
55
|
-
* Adds `handler` as a listener for the event `eventName` of `
|
|
57
|
+
* Adds `handler` as a listener for the event `eventName` of `target` with the
|
|
56
58
|
* provided `options` applied
|
|
57
59
|
*
|
|
58
|
-
*
|
|
60
|
+
* The following call signatures are available:
|
|
61
|
+
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* function useEventListener(eventName, handler, options?): void;
|
|
64
|
+
* function useEventListener(target, eventName, handler, options?): void;
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* For the full definition of the hook's type, see {@linkcode UseEventListener}.
|
|
68
|
+
*
|
|
69
|
+
* If `target` is not provided, `window` is used instead.
|
|
59
70
|
*
|
|
60
|
-
* If `
|
|
71
|
+
* If `target` is `null`, no event listener is added. This is useful when
|
|
72
|
+
* working with DOM element refs, or when the event listener needs to be removed
|
|
73
|
+
* temporarily.
|
|
61
74
|
*
|
|
62
75
|
* @example
|
|
63
76
|
* ```tsx
|
|
@@ -65,15 +78,16 @@ export declare function useEventListener<K extends keyof WindowEventMap>(eventNa
|
|
|
65
78
|
* console.log(window.innerWidth, window.innerHeight);
|
|
66
79
|
* });
|
|
67
80
|
*
|
|
68
|
-
* useEventListener(
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* document
|
|
72
|
-
* );
|
|
81
|
+
* useEventListener(document, 'visibilitychange', () => {
|
|
82
|
+
* console.log(document.visibilityState);
|
|
83
|
+
* });
|
|
73
84
|
*
|
|
74
85
|
* const buttonRef = useRef<HTMLButtonElement>(null);
|
|
75
|
-
* useEventListener(
|
|
86
|
+
* useEventListener(buttonRef, 'click', () => console.log('click'));
|
|
76
87
|
* ```
|
|
88
|
+
*
|
|
89
|
+
* @see
|
|
90
|
+
* {@linkcode UseEventListener}
|
|
77
91
|
*/
|
|
78
|
-
export declare
|
|
92
|
+
export declare const useEventListener: UseEventListener;
|
|
79
93
|
//# sourceMappingURL=useEventListener.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEventListener.d.ts","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"useEventListener.d.ts","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnE;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,wCAAwC,GACrE,kCAAkC,CAAC,MAAM,EAAE,cAAc,CAAC,GAC1D,kCAAkC,CAAC,QAAQ,EAAE,gBAAgB,CAAC,GAC9D,kCAAkC,CAAC,WAAW,EAAE,mBAAmB,CAAC,GACpE,kCAAkC,CAAC,UAAU,EAAE,kBAAkB,CAAC,GAClE,kCAAkC,CAAC,aAAa,EAAE,qBAAqB,CAAC,GACxE,qCAAqC,CAAC;AAExC;;;8DAG8D;AAC9D,MAAM,MAAM,wCAAwC,GAAG,CACrD,CAAC,SAAS,MAAM,cAAc,EAE9B,GAAG,IAAI,EAAE,4CAA4C,CAAC,CAAC,CAAC,KACrD,IAAI,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,kCAAkC,CAC5C,MAAM,SAAS,WAAW,EAC1B,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAC9B,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,QAAQ,EAC7C,GAAG,IAAI,EAAE,sCAAsC,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,KAC5D,IAAI,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,qCAAqC,GAC/C,kCAAkC,CAAC,WAAW,CAAC,CAAC;AAElD;;;;GAIG;AACH,MAAM,MAAM,4CAA4C,CACtD,CAAC,SAAS,MAAM,cAAc,IAE9B,sCAAsC,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS;IACxE,OAAO;IACP,GAAG,MAAM,IAAI;CACd,GACG,IAAI,GACJ,KAAK,CAAC;AAEZ;;;GAGG;AACH,MAAM,MAAM,sCAAsC,CAChD,QAAQ,EACR,CAAC,SAAS,WAAW,EACrB,CAAC,SAAS,MAAM,QAAQ,IACtB;IACF,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG;QAAE,gBAAgB,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,GAAG,IAAI;IAChE,SAAS,EAAE,CAAC;IACZ,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI;IACvD,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,GAAG,SAAS;CACxD,CAAC;AAYF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,gBAAgB,EAAE,gBAkDV,CAAC"}
|
|
@@ -5,7 +5,47 @@
|
|
|
5
5
|
* @copyright 2020 Julien CARON
|
|
6
6
|
*/
|
|
7
7
|
import { useEffect, useMemo, useRef } from 'react';
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Adds `handler` as a listener for the event `eventName` of `target` with the
|
|
10
|
+
* provided `options` applied
|
|
11
|
+
*
|
|
12
|
+
* The following call signatures are available:
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* function useEventListener(eventName, handler, options?): void;
|
|
16
|
+
* function useEventListener(target, eventName, handler, options?): void;
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* For the full definition of the hook's type, see {@linkcode UseEventListener}.
|
|
20
|
+
*
|
|
21
|
+
* If `target` is not provided, `window` is used instead.
|
|
22
|
+
*
|
|
23
|
+
* If `target` is `null`, no event listener is added. This is useful when
|
|
24
|
+
* working with DOM element refs, or when the event listener needs to be removed
|
|
25
|
+
* temporarily.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* useEventListener('resize', () => {
|
|
30
|
+
* console.log(window.innerWidth, window.innerHeight);
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* useEventListener(document, 'visibilitychange', () => {
|
|
34
|
+
* console.log(document.visibilityState);
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* const buttonRef = useRef<HTMLButtonElement>(null);
|
|
38
|
+
* useEventListener(buttonRef, 'click', () => console.log('click'));
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @see
|
|
42
|
+
* {@linkcode UseEventListener}
|
|
43
|
+
*/
|
|
44
|
+
export const useEventListener = function useEventListener(...args) {
|
|
45
|
+
const [target, eventName, handler, options] = typeof args[0] === 'string'
|
|
46
|
+
? [window, ...args]
|
|
47
|
+
: args;
|
|
48
|
+
const unwrappedTarget = target && !('addEventListener' in target) ? target.current : target;
|
|
9
49
|
const handlerRef = useRef(handler);
|
|
10
50
|
handlerRef.current = handler;
|
|
11
51
|
const { capture = false, once = false, passive, signal, } = typeof options === 'boolean' ? { capture: options } : (options ?? {});
|
|
@@ -13,21 +53,17 @@ export function useEventListener(eventName, handler, element, options) {
|
|
|
13
53
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
14
54
|
[capture, once, passive, signal]);
|
|
15
55
|
useEffect(() => {
|
|
16
|
-
if (
|
|
56
|
+
if (unwrappedTarget === null) {
|
|
17
57
|
// No element has been attached to the ref yet
|
|
18
58
|
return;
|
|
19
59
|
}
|
|
20
|
-
// Define the listening target
|
|
21
|
-
const targetElement = element ?? window;
|
|
22
|
-
// Create event listener that calls handler function stored in ref
|
|
23
60
|
const listener = function (event) {
|
|
24
61
|
handlerRef.current.call(this, event);
|
|
25
62
|
};
|
|
26
|
-
|
|
27
|
-
// Remove event listener on cleanup
|
|
63
|
+
unwrappedTarget.addEventListener(eventName, listener, memoizedOptions);
|
|
28
64
|
return () => {
|
|
29
|
-
|
|
65
|
+
unwrappedTarget.removeEventListener(eventName, listener, memoizedOptions);
|
|
30
66
|
};
|
|
31
|
-
}, [
|
|
32
|
-
}
|
|
67
|
+
}, [unwrappedTarget, eventName, memoizedOptions]);
|
|
68
|
+
};
|
|
33
69
|
//# sourceMappingURL=useEventListener.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEventListener.js","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"useEventListener.js","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAkB,MAAM,OAAO,CAAC;AAyFnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAqB,SAAS,gBAAgB,CACzE,GAAG,IAE0C;IAE7C,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,GAMzC,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;QACzB,CAAC,CAAC,CAAC,MAAM,EAAE,GAAI,IAAwD,CAAC;QACxE,CAAC,CAAE,IAAkD,CAAC;IAE1D,MAAM,eAAe,GACnB,MAAM,IAAI,CAAC,CAAC,kBAAkB,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IAEtE,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,MAAM,EACJ,OAAO,GAAG,KAAK,EACf,IAAI,GAAG,KAAK,EACZ,OAAO,EACP,MAAM,GACP,GAAG,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAE1E,MAAM,eAAe,GAAG,OAAO,CAC7B,GAAG,EAAE,CAAC,OAAO;IACb,uDAAuD;IACvD,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CACjC,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;YAC7B,8CAA8C;YAC9C,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAmB,UAAU,KAAK;YAC9C,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,eAAe,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAEvE,OAAO,GAAG,EAAE;YACV,eAAe,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAC5E,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,eAAe,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;AACpD,CAAqB,CAAC"}
|
|
@@ -4,6 +4,55 @@
|
|
|
4
4
|
* This hook is designed in the most general way possible in order to cover all
|
|
5
5
|
* imaginable use cases.
|
|
6
6
|
*
|
|
7
|
+
* @example
|
|
8
|
+
* Sometimes, React's immutability constraints mean too much unnecessary copying
|
|
9
|
+
* of data when new data arrives at a high frequency. In such cases, it might be
|
|
10
|
+
* desirable to ignore the constraints by embracing imperative patterns.
|
|
11
|
+
* Here is an example of a scenario where that can make sense:
|
|
12
|
+
*
|
|
13
|
+
* ```tsx
|
|
14
|
+
* type SensorData = { timestamp: number; value: number };
|
|
15
|
+
* const sensorDataRef = useRef<SensorData[]>([]);
|
|
16
|
+
* const mostRecentSensorDataTimestampRef = useRef<number>(0);
|
|
17
|
+
*
|
|
18
|
+
* const [forceUpdate, updateCount] = useForceUpdate();
|
|
19
|
+
* // Limiting the frequency of forced re-renders with some throttle function:
|
|
20
|
+
* const throttledForceUpdateRef = useRef(throttle(forceUpdate));
|
|
21
|
+
*
|
|
22
|
+
* useEffect(() => {
|
|
23
|
+
* return sensorDataObservable.subscribe((data: SensorData) => {
|
|
24
|
+
* // Imagine new sensor data arrives every 1 millisecond. If we were following
|
|
25
|
+
* // React's immutability rules by creating a new array every time, the data
|
|
26
|
+
* // that's already there would have to be copied many times before the new
|
|
27
|
+
* // data would even get a chance to be reflected in the UI for the first time
|
|
28
|
+
* // because it typically takes much longer than 1 millisecond for a new frame
|
|
29
|
+
* // to be displayed. To prevent the waste of computational resources, we just
|
|
30
|
+
* // mutate the existing array every time instead:
|
|
31
|
+
* sensorDataRef.current.push(data);
|
|
32
|
+
* if (data.timestamp > mostRecentSensorDataTimestampRef.current) {
|
|
33
|
+
* mostRecentSensorDataTimestampRef.current = data.timestamp;
|
|
34
|
+
* }
|
|
35
|
+
* throttledForceUpdateRef.current();
|
|
36
|
+
* });
|
|
37
|
+
* }, []);
|
|
38
|
+
*
|
|
39
|
+
* const [timeWindow, setTimeWindow] = useState(1000);
|
|
40
|
+
* const selectedSensorData = useMemo(
|
|
41
|
+
* () => {
|
|
42
|
+
* // Keep this line if you don't want to disable the
|
|
43
|
+
* // react-hooks/exhaustive-deps ESLint rule:
|
|
44
|
+
* updateCount;
|
|
45
|
+
* const threshold = mostRecentSensorDataTimestampRef.current - timeWindow;
|
|
46
|
+
* return sensorDataRef.current.filter(
|
|
47
|
+
* ({ timestamp }) => timestamp >= threshold,
|
|
48
|
+
* );
|
|
49
|
+
* },
|
|
50
|
+
* // sensorDataRef.current always references the same array, so listing it as a
|
|
51
|
+
* // dependency is pointless. Instead, updateCount should be used:
|
|
52
|
+
* [updateCount, timeWindow],
|
|
53
|
+
* );
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
7
56
|
* @param callback An optional callback function to call during renders that
|
|
8
57
|
* were triggered with `forceUpdate()`
|
|
9
58
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useForceUpdate.d.ts","sourceRoot":"","sources":["../../src/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAOA
|
|
1
|
+
{"version":3,"file":"useForceUpdate.d.ts","sourceRoot":"","sources":["../../src/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,CAAC,CAU1E"}
|
|
@@ -6,6 +6,55 @@ import { useReducer, useRef } from 'react';
|
|
|
6
6
|
* This hook is designed in the most general way possible in order to cover all
|
|
7
7
|
* imaginable use cases.
|
|
8
8
|
*
|
|
9
|
+
* @example
|
|
10
|
+
* Sometimes, React's immutability constraints mean too much unnecessary copying
|
|
11
|
+
* of data when new data arrives at a high frequency. In such cases, it might be
|
|
12
|
+
* desirable to ignore the constraints by embracing imperative patterns.
|
|
13
|
+
* Here is an example of a scenario where that can make sense:
|
|
14
|
+
*
|
|
15
|
+
* ```tsx
|
|
16
|
+
* type SensorData = { timestamp: number; value: number };
|
|
17
|
+
* const sensorDataRef = useRef<SensorData[]>([]);
|
|
18
|
+
* const mostRecentSensorDataTimestampRef = useRef<number>(0);
|
|
19
|
+
*
|
|
20
|
+
* const [forceUpdate, updateCount] = useForceUpdate();
|
|
21
|
+
* // Limiting the frequency of forced re-renders with some throttle function:
|
|
22
|
+
* const throttledForceUpdateRef = useRef(throttle(forceUpdate));
|
|
23
|
+
*
|
|
24
|
+
* useEffect(() => {
|
|
25
|
+
* return sensorDataObservable.subscribe((data: SensorData) => {
|
|
26
|
+
* // Imagine new sensor data arrives every 1 millisecond. If we were following
|
|
27
|
+
* // React's immutability rules by creating a new array every time, the data
|
|
28
|
+
* // that's already there would have to be copied many times before the new
|
|
29
|
+
* // data would even get a chance to be reflected in the UI for the first time
|
|
30
|
+
* // because it typically takes much longer than 1 millisecond for a new frame
|
|
31
|
+
* // to be displayed. To prevent the waste of computational resources, we just
|
|
32
|
+
* // mutate the existing array every time instead:
|
|
33
|
+
* sensorDataRef.current.push(data);
|
|
34
|
+
* if (data.timestamp > mostRecentSensorDataTimestampRef.current) {
|
|
35
|
+
* mostRecentSensorDataTimestampRef.current = data.timestamp;
|
|
36
|
+
* }
|
|
37
|
+
* throttledForceUpdateRef.current();
|
|
38
|
+
* });
|
|
39
|
+
* }, []);
|
|
40
|
+
*
|
|
41
|
+
* const [timeWindow, setTimeWindow] = useState(1000);
|
|
42
|
+
* const selectedSensorData = useMemo(
|
|
43
|
+
* () => {
|
|
44
|
+
* // Keep this line if you don't want to disable the
|
|
45
|
+
* // react-hooks/exhaustive-deps ESLint rule:
|
|
46
|
+
* updateCount;
|
|
47
|
+
* const threshold = mostRecentSensorDataTimestampRef.current - timeWindow;
|
|
48
|
+
* return sensorDataRef.current.filter(
|
|
49
|
+
* ({ timestamp }) => timestamp >= threshold,
|
|
50
|
+
* );
|
|
51
|
+
* },
|
|
52
|
+
* // sensorDataRef.current always references the same array, so listing it as a
|
|
53
|
+
* // dependency is pointless. Instead, updateCount should be used:
|
|
54
|
+
* [updateCount, timeWindow],
|
|
55
|
+
* );
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
9
58
|
* @param callback An optional callback function to call during renders that
|
|
10
59
|
* were triggered with `forceUpdate()`
|
|
11
60
|
*
|
|
@@ -27,12 +76,12 @@ import { useReducer, useRef } from 'react';
|
|
|
27
76
|
export function useForceUpdate(callback) {
|
|
28
77
|
// It is very unlikely that the number of updates will exceed
|
|
29
78
|
// Number.MAX_SAFE_INTEGER, but not impossible. That is why we use bigints.
|
|
30
|
-
const [
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
33
|
-
|
|
79
|
+
const [updateCount, forceUpdate] = useReducer((prev) => prev + 1n, 0n);
|
|
80
|
+
const updateCountRef = useRef(updateCount);
|
|
81
|
+
if (updateCount !== updateCountRef.current) {
|
|
82
|
+
updateCountRef.current = updateCount;
|
|
34
83
|
callback?.();
|
|
35
84
|
}
|
|
36
|
-
return [forceUpdate,
|
|
85
|
+
return [forceUpdate, updateCount];
|
|
37
86
|
}
|
|
38
87
|
//# sourceMappingURL=useForceUpdate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useForceUpdate.js","sourceRoot":"","sources":["../../src/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAK3C,mBAAmB;AAEnB
|
|
1
|
+
{"version":3,"file":"useForceUpdate.js","sourceRoot":"","sources":["../../src/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAK3C,mBAAmB;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,MAAM,UAAU,cAAc,CAAC,QAAqB;IAClD,6DAA6D;IAC7D,2EAA2E;IAC3E,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IACvE,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,WAAW,KAAK,cAAc,CAAC,OAAO,EAAE,CAAC;QAC3C,cAAc,CAAC,OAAO,GAAG,WAAW,CAAC;QACrC,QAAQ,EAAE,EAAE,CAAC;IACf,CAAC;IACD,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -7,6 +7,9 @@ export type ActionDispatch<ActionArg extends AnyActionArg> = (...args: ActionArg
|
|
|
7
7
|
* `useReducer` hook with an additional dependency array `deps` that resets the
|
|
8
8
|
* state to `initialState` when dependencies change
|
|
9
9
|
*
|
|
10
|
+
* For motivation and examples, see
|
|
11
|
+
* https://github.com/facebook/react/issues/33041.
|
|
12
|
+
*
|
|
10
13
|
* ### On linter support
|
|
11
14
|
*
|
|
12
15
|
* The `react-hooks/exhaustive-deps` ESLint rule doesn't support hooks where
|
|
@@ -15,7 +18,7 @@ export type ActionDispatch<ActionArg extends AnyActionArg> = (...args: ActionArg
|
|
|
15
18
|
* possible, we don't want to artificially change the parameter's position.
|
|
16
19
|
* Therefore, there will be no warnings about missing dependencies.
|
|
17
20
|
* Because of that, additional caution is advised!
|
|
18
|
-
* Be sure to check no dependencies are missing from the `deps` array.
|
|
21
|
+
* Be sure to check that no dependencies are missing from the `deps` array.
|
|
19
22
|
*
|
|
20
23
|
* Related issue: {@link https://github.com/facebook/react/issues/25443}.
|
|
21
24
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useReducerWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,
|
|
1
|
+
{"version":3,"file":"useReducerWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAU,MAAM,OAAO,CAAC;AAOpD,cAAc;AAEd,MAAM,MAAM,YAAY,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAEtC,cAAc;AACd,MAAM,MAAM,cAAc,CAAC,SAAS,SAAS,YAAY,IAAI,CAC3D,GAAG,IAAI,EAAE,SAAS,KACf,IAAI,CAAC;AAEV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,SAAS,YAAY,EAC1D,OAAO,EAAE,CAAC,SAAS,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,EACxC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAC5C,IAAI,EAAE,cAAc,GACnB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAYxB"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRef } from 'react';
|
|
2
2
|
import { useStateWithDeps } from './useStateWithDeps.js';
|
|
3
3
|
/**
|
|
4
4
|
* `useReducer` hook with an additional dependency array `deps` that resets the
|
|
5
5
|
* state to `initialState` when dependencies change
|
|
6
6
|
*
|
|
7
|
+
* For motivation and examples, see
|
|
8
|
+
* https://github.com/facebook/react/issues/33041.
|
|
9
|
+
*
|
|
7
10
|
* ### On linter support
|
|
8
11
|
*
|
|
9
12
|
* The `react-hooks/exhaustive-deps` ESLint rule doesn't support hooks where
|
|
@@ -12,7 +15,7 @@ import { useStateWithDeps } from './useStateWithDeps.js';
|
|
|
12
15
|
* possible, we don't want to artificially change the parameter's position.
|
|
13
16
|
* Therefore, there will be no warnings about missing dependencies.
|
|
14
17
|
* Because of that, additional caution is advised!
|
|
15
|
-
* Be sure to check no dependencies are missing from the `deps` array.
|
|
18
|
+
* Be sure to check that no dependencies are missing from the `deps` array.
|
|
16
19
|
*
|
|
17
20
|
* Related issue: {@link https://github.com/facebook/react/issues/25443}.
|
|
18
21
|
*
|
|
@@ -37,9 +40,9 @@ export function useReducerWithDeps(reducer, initialState, deps) {
|
|
|
37
40
|
const [state, setState] = useStateWithDeps(initialState, deps);
|
|
38
41
|
// Only the initially provided reducer is used
|
|
39
42
|
const reducerRef = useRef(reducer);
|
|
40
|
-
const dispatch =
|
|
43
|
+
const dispatch = useRef(function dispatch(...args) {
|
|
41
44
|
setState((previousState) => reducerRef.current(previousState, ...args));
|
|
42
|
-
}
|
|
45
|
+
}).current;
|
|
43
46
|
return [state, dispatch];
|
|
44
47
|
}
|
|
45
48
|
//# sourceMappingURL=useReducerWithDeps.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useReducerWithDeps.js","sourceRoot":"","sources":["../../src/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,
|
|
1
|
+
{"version":3,"file":"useReducerWithDeps.js","sourceRoot":"","sources":["../../src/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,MAAM,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAezD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAwC,EACxC,YAA4C,EAC5C,IAAoB;IAEpB,uDAAuD;IACvD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,gBAAgB,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAE/D,8CAA8C;IAC9C,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAEnC,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,QAAQ,CAAC,GAAG,IAAO;QAClD,QAAQ,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC,OAAO,CAAC;IAEX,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -9,6 +9,41 @@ import { type DependencyList, type Dispatch, type SetStateAction } from 'react';
|
|
|
9
9
|
* `useState` hook with an additional dependency array `deps` that resets the
|
|
10
10
|
* state to `initialState` when dependencies change
|
|
11
11
|
*
|
|
12
|
+
* For motivation and more examples, see
|
|
13
|
+
* https://github.com/facebook/react/issues/33041.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* type Activity = 'breakfast' | 'exercise' | 'swim' | 'board games' | 'dinner';
|
|
18
|
+
*
|
|
19
|
+
* const timeOfDayOptions = ['morning', 'afternoon', 'evening'] as const;
|
|
20
|
+
* type TimeOfDay = (typeof timeOfDayOptions)[number];
|
|
21
|
+
*
|
|
22
|
+
* const activityOptionsByTimeOfDay: {
|
|
23
|
+
* [K in TimeOfDay]: [Activity, ...Activity[]];
|
|
24
|
+
* } = {
|
|
25
|
+
* morning: ['breakfast', 'exercise', 'swim'],
|
|
26
|
+
* afternoon: ['exercise', 'swim', 'board games'],
|
|
27
|
+
* evening: ['board games', 'dinner'],
|
|
28
|
+
* };
|
|
29
|
+
*
|
|
30
|
+
* export function Example() {
|
|
31
|
+
* const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('morning');
|
|
32
|
+
*
|
|
33
|
+
* const activityOptions = activityOptionsByTimeOfDay[timeOfDay];
|
|
34
|
+
* const [activity, setActivity] = useStateWithDeps<Activity>(
|
|
35
|
+
* (prev) => {
|
|
36
|
+
* // Make sure activity is always valid for the current timeOfDay value,
|
|
37
|
+
* // but also don't reset it unless necessary:
|
|
38
|
+
* return prev && activityOptions.includes(prev) ? prev : activityOptions[0];
|
|
39
|
+
* },
|
|
40
|
+
* [activityOptions],
|
|
41
|
+
* );
|
|
42
|
+
*
|
|
43
|
+
* return '...';
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
12
47
|
* @param initialState The value to which the state is set when the component is
|
|
13
48
|
* mounted or dependencies change
|
|
14
49
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStateWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"useStateWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAIf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAC5C,IAAI,EAAE,cAAc,GACnB,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CA0ClC"}
|
|
@@ -4,13 +4,48 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright 2020 Peter Juras
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { useRef, } from 'react';
|
|
8
8
|
import { depsAreEqual, isFunction } from '../utils.js';
|
|
9
9
|
import { useForceUpdate } from './useForceUpdate.js';
|
|
10
10
|
/**
|
|
11
11
|
* `useState` hook with an additional dependency array `deps` that resets the
|
|
12
12
|
* state to `initialState` when dependencies change
|
|
13
13
|
*
|
|
14
|
+
* For motivation and more examples, see
|
|
15
|
+
* https://github.com/facebook/react/issues/33041.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* type Activity = 'breakfast' | 'exercise' | 'swim' | 'board games' | 'dinner';
|
|
20
|
+
*
|
|
21
|
+
* const timeOfDayOptions = ['morning', 'afternoon', 'evening'] as const;
|
|
22
|
+
* type TimeOfDay = (typeof timeOfDayOptions)[number];
|
|
23
|
+
*
|
|
24
|
+
* const activityOptionsByTimeOfDay: {
|
|
25
|
+
* [K in TimeOfDay]: [Activity, ...Activity[]];
|
|
26
|
+
* } = {
|
|
27
|
+
* morning: ['breakfast', 'exercise', 'swim'],
|
|
28
|
+
* afternoon: ['exercise', 'swim', 'board games'],
|
|
29
|
+
* evening: ['board games', 'dinner'],
|
|
30
|
+
* };
|
|
31
|
+
*
|
|
32
|
+
* export function Example() {
|
|
33
|
+
* const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('morning');
|
|
34
|
+
*
|
|
35
|
+
* const activityOptions = activityOptionsByTimeOfDay[timeOfDay];
|
|
36
|
+
* const [activity, setActivity] = useStateWithDeps<Activity>(
|
|
37
|
+
* (prev) => {
|
|
38
|
+
* // Make sure activity is always valid for the current timeOfDay value,
|
|
39
|
+
* // but also don't reset it unless necessary:
|
|
40
|
+
* return prev && activityOptions.includes(prev) ? prev : activityOptions[0];
|
|
41
|
+
* },
|
|
42
|
+
* [activityOptions],
|
|
43
|
+
* );
|
|
44
|
+
*
|
|
45
|
+
* return '...';
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
14
49
|
* @param initialState The value to which the state is set when the component is
|
|
15
50
|
* mounted or dependencies change
|
|
16
51
|
*
|
|
@@ -21,12 +56,10 @@ import { useForceUpdate } from './useForceUpdate.js';
|
|
|
21
56
|
* @param deps Dependencies that reset the state to `initialState`
|
|
22
57
|
*/
|
|
23
58
|
export function useStateWithDeps(initialState, deps) {
|
|
24
|
-
// It would be possible to use useState instead of
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// re-renders, the state is stored in a ref and an
|
|
29
|
-
// update is triggered via forceUpdate below when necessary
|
|
59
|
+
// It would be possible to use useState instead of useRef to store the state,
|
|
60
|
+
// however this would trigger re-renders whenever the state is reset due to a
|
|
61
|
+
// change in dependencies. In order to avoid these re-renders, the state is
|
|
62
|
+
// stored in a ref, and updates are triggered with forceUpdate when necessary.
|
|
30
63
|
const state = useRef(undefined);
|
|
31
64
|
const prevDeps = useRef(deps);
|
|
32
65
|
const isMounted = useRef(false);
|
|
@@ -45,7 +78,7 @@ export function useStateWithDeps(initialState, deps) {
|
|
|
45
78
|
isMounted.current = true;
|
|
46
79
|
}
|
|
47
80
|
const [forceUpdate] = useForceUpdate();
|
|
48
|
-
const updateState =
|
|
81
|
+
const updateState = useRef(function updateState(newState) {
|
|
49
82
|
let nextState;
|
|
50
83
|
if (isFunction(newState)) {
|
|
51
84
|
nextState = newState(state.current);
|
|
@@ -57,7 +90,7 @@ export function useStateWithDeps(initialState, deps) {
|
|
|
57
90
|
state.current = nextState;
|
|
58
91
|
forceUpdate();
|
|
59
92
|
}
|
|
60
|
-
}
|
|
93
|
+
}).current;
|
|
61
94
|
return [state.current, updateState];
|
|
62
95
|
}
|
|
63
96
|
//# sourceMappingURL=useStateWithDeps.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStateWithDeps.js","sourceRoot":"","sources":["../../src/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"useStateWithDeps.js","sourceRoot":"","sources":["../../src/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,MAAM,GAIP,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,UAAU,gBAAgB,CAC9B,YAA4C,EAC5C,IAAoB;IAEpB,6EAA6E;IAC7E,6EAA6E;IAC7E,2EAA2E;IAC3E,8EAA8E;IAC9E,MAAM,KAAK,GAAG,MAAM,CAAC,SAAc,CAAC,CAAC;IAErC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAEhC,mEAAmE;IACnE,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;QAChE,wBAAwB;QACxB,IAAI,SAAY,CAAC;QACjB,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,SAAS,GAAG,YAAY,CAAC;QAC3B,CAAC;QACD,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;QAC1B,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QACxB,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,GAAG,cAAc,EAAE,CAAC;IAEvC,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,WAAW,CAC7C,QAAuC;QAEvC,IAAI,SAAY,CAAC;QACjB,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,SAAS,GAAG,QAAQ,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;YACzC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;YAC1B,WAAW,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC,OAAO,CAAC;IAEX,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;AACtC,CAAC"}
|