@teo-garcia/react-shared 0.1.9 → 1.2.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 +127 -99
- package/dist/components/aspect-ratio/aspect-ratio.d.ts +17 -0
- package/dist/components/aspect-ratio/aspect-ratio.d.ts.map +1 -0
- package/dist/components/aspect-ratio/aspect-ratio.js +14 -0
- package/dist/components/aspect-ratio/index.d.ts +2 -0
- package/dist/components/aspect-ratio/index.d.ts.map +1 -0
- package/dist/components/aspect-ratio/index.js +1 -0
- package/dist/components/debug-json/debug-json.d.ts +14 -0
- package/dist/components/debug-json/debug-json.d.ts.map +1 -0
- package/dist/components/debug-json/debug-json.js +32 -0
- package/dist/components/debug-json/index.d.ts +2 -0
- package/dist/components/debug-json/index.d.ts.map +1 -0
- package/dist/components/debug-json/index.js +1 -0
- package/dist/components/dev-panel/dev-panel.d.ts +30 -0
- package/dist/components/dev-panel/dev-panel.d.ts.map +1 -0
- package/dist/components/dev-panel/dev-panel.js +155 -0
- package/dist/components/dev-panel/index.d.ts +2 -0
- package/dist/components/dev-panel/index.d.ts.map +1 -0
- package/dist/components/dev-panel/index.js +1 -0
- package/dist/components/error-boundary/error-boundary.d.ts +3 -58
- package/dist/components/error-boundary/error-boundary.d.ts.map +1 -1
- package/dist/components/error-boundary/error-boundary.js +51 -77
- package/dist/components/error-boundary/index.d.ts +1 -0
- package/dist/components/error-boundary/index.d.ts.map +1 -1
- package/dist/components/focus-trap/focus-trap.d.ts +16 -0
- package/dist/components/focus-trap/focus-trap.d.ts.map +1 -0
- package/dist/components/focus-trap/focus-trap.js +57 -0
- package/dist/components/focus-trap/index.d.ts +2 -0
- package/dist/components/focus-trap/index.d.ts.map +1 -0
- package/dist/components/focus-trap/index.js +1 -0
- package/dist/components/index.d.ts +9 -5
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +9 -5
- package/dist/components/portal/index.d.ts +2 -0
- package/dist/components/portal/index.d.ts.map +1 -0
- package/dist/components/portal/index.js +1 -0
- package/dist/components/portal/portal.d.ts +14 -0
- package/dist/components/portal/portal.d.ts.map +1 -0
- package/dist/components/portal/portal.js +21 -0
- package/dist/components/separator/index.d.ts +2 -0
- package/dist/components/separator/index.d.ts.map +1 -0
- package/dist/components/separator/index.js +1 -0
- package/dist/components/separator/separator.d.ts +11 -0
- package/dist/components/separator/separator.d.ts.map +1 -0
- package/dist/components/separator/separator.js +11 -0
- package/dist/components/skeleton/index.d.ts +2 -0
- package/dist/components/skeleton/index.d.ts.map +1 -0
- package/dist/components/skeleton/index.js +1 -0
- package/dist/components/skeleton/skeleton.d.ts +3 -0
- package/dist/components/skeleton/skeleton.d.ts.map +1 -0
- package/dist/components/skeleton/skeleton.js +5 -0
- package/dist/components/skip-link/index.d.ts +2 -0
- package/dist/components/skip-link/index.d.ts.map +1 -0
- package/dist/components/skip-link/index.js +1 -0
- package/dist/components/skip-link/skip-link.d.ts +13 -0
- package/dist/components/skip-link/skip-link.d.ts.map +1 -0
- package/dist/components/skip-link/skip-link.js +26 -0
- package/dist/components/visually-hidden/index.d.ts +2 -0
- package/dist/components/visually-hidden/index.d.ts.map +1 -0
- package/dist/components/visually-hidden/index.js +1 -0
- package/dist/components/visually-hidden/visually-hidden.d.ts +3 -0
- package/dist/components/visually-hidden/visually-hidden.d.ts.map +1 -0
- package/dist/components/visually-hidden/visually-hidden.js +15 -0
- package/dist/hooks/index.d.ts +15 -5
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +15 -4
- package/dist/hooks/use-copy-to-clipboard.d.ts +7 -0
- package/dist/hooks/use-copy-to-clipboard.d.ts.map +1 -0
- package/dist/hooks/use-copy-to-clipboard.js +23 -0
- package/dist/hooks/use-debounce.d.ts +2 -0
- package/dist/hooks/use-debounce.d.ts.map +1 -0
- package/dist/hooks/use-debounce.js +9 -0
- package/dist/hooks/use-event-listener.d.ts +4 -0
- package/dist/hooks/use-event-listener.d.ts.map +1 -0
- package/dist/hooks/use-event-listener.js +13 -0
- package/dist/hooks/use-idle.d.ts +7 -0
- package/dist/hooks/use-idle.d.ts.map +1 -0
- package/dist/hooks/use-idle.js +35 -0
- package/dist/hooks/use-intersection-observer.d.ts +15 -0
- package/dist/hooks/use-intersection-observer.d.ts.map +1 -0
- package/dist/hooks/use-intersection-observer.js +22 -0
- package/dist/hooks/use-isomorphic-layout-effect.d.ts +3 -0
- package/dist/hooks/use-isomorphic-layout-effect.d.ts.map +1 -0
- package/dist/hooks/use-isomorphic-layout-effect.js +4 -0
- package/dist/hooks/use-latest.d.ts +7 -0
- package/dist/hooks/use-latest.d.ts.map +1 -0
- package/dist/hooks/use-latest.js +11 -0
- package/dist/hooks/use-local-storage.d.ts +2 -0
- package/dist/hooks/use-local-storage.d.ts.map +1 -0
- package/dist/hooks/use-local-storage.js +37 -0
- package/dist/hooks/use-media-query.d.ts +2 -0
- package/dist/hooks/use-media-query.d.ts.map +1 -0
- package/dist/hooks/use-media-query.js +18 -0
- package/dist/hooks/use-network-status.d.ts +10 -0
- package/dist/hooks/use-network-status.d.ts.map +1 -0
- package/dist/hooks/use-network-status.js +19 -0
- package/dist/hooks/use-on-click-outside.d.ts +3 -0
- package/dist/hooks/use-on-click-outside.d.ts.map +1 -0
- package/dist/hooks/use-on-click-outside.js +17 -0
- package/dist/hooks/use-previous.d.ts +2 -0
- package/dist/hooks/use-previous.d.ts.map +1 -0
- package/dist/hooks/use-previous.js +8 -0
- package/dist/hooks/use-render-count.d.ts +7 -0
- package/dist/hooks/use-render-count.d.ts.map +1 -0
- package/dist/hooks/use-render-count.js +15 -0
- package/dist/hooks/use-toggle.d.ts +6 -0
- package/dist/hooks/use-toggle.d.ts.map +1 -0
- package/dist/hooks/use-toggle.js +12 -0
- package/dist/hooks/use-why-did-you-render.d.ts +8 -0
- package/dist/hooks/use-why-did-you-render.d.ts.map +1 -0
- package/dist/hooks/use-why-did-you-render.js +38 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -9
- package/dist/test-utils/index.d.ts +14 -0
- package/dist/test-utils/index.d.ts.map +1 -0
- package/dist/test-utils/index.js +22 -0
- package/dist/types.d.ts +17 -36
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/cn.d.ts +3 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/cn.js +5 -0
- package/dist/utils/format-date.d.ts +11 -0
- package/dist/utils/format-date.d.ts.map +1 -0
- package/dist/utils/format-date.js +12 -0
- package/dist/utils/format-number.d.ts +11 -0
- package/dist/utils/format-number.d.ts.map +1 -0
- package/dist/utils/format-number.js +12 -0
- package/dist/utils/index.d.ts +4 -5
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -5
- package/dist/utils/truncate.d.ts +11 -0
- package/dist/utils/truncate.d.ts.map +1 -0
- package/dist/utils/truncate.js +14 -0
- package/package.json +141 -42
- package/dist/adapters/environment/index.d.ts +0 -6
- package/dist/adapters/environment/index.d.ts.map +0 -1
- package/dist/adapters/environment/index.js +0 -5
- package/dist/adapters/environment/next.d.ts +0 -17
- package/dist/adapters/environment/next.d.ts.map +0 -1
- package/dist/adapters/environment/next.js +0 -20
- package/dist/adapters/environment/vite.d.ts +0 -17
- package/dist/adapters/environment/vite.d.ts.map +0 -1
- package/dist/adapters/environment/vite.js +0 -20
- package/dist/adapters/index.d.ts +0 -9
- package/dist/adapters/index.d.ts.map +0 -1
- package/dist/adapters/index.js +0 -8
- package/dist/adapters/theme/custom.d.ts +0 -32
- package/dist/adapters/theme/custom.d.ts.map +0 -1
- package/dist/adapters/theme/custom.js +0 -26
- package/dist/adapters/theme/index.d.ts +0 -6
- package/dist/adapters/theme/index.d.ts.map +0 -1
- package/dist/adapters/theme/index.js +0 -5
- package/dist/adapters/theme/next-themes.d.ts +0 -18
- package/dist/adapters/theme/next-themes.d.ts.map +0 -1
- package/dist/adapters/theme/next-themes.js +0 -23
- package/dist/components/theme-switch/index.d.ts +0 -3
- package/dist/components/theme-switch/index.d.ts.map +0 -1
- package/dist/components/theme-switch/index.js +0 -1
- package/dist/components/theme-switch/theme-switch.d.ts +0 -36
- package/dist/components/theme-switch/theme-switch.d.ts.map +0 -1
- package/dist/components/theme-switch/theme-switch.js +0 -74
- package/dist/components/viewport-info/index.d.ts +0 -3
- package/dist/components/viewport-info/index.d.ts.map +0 -1
- package/dist/components/viewport-info/index.js +0 -1
- package/dist/components/viewport-info/viewport-info.d.ts +0 -40
- package/dist/components/viewport-info/viewport-info.d.ts.map +0 -1
- package/dist/components/viewport-info/viewport-info.js +0 -69
- package/dist/hooks/use-healthcheck.d.ts +0 -42
- package/dist/hooks/use-healthcheck.d.ts.map +0 -1
- package/dist/hooks/use-healthcheck.js +0 -53
- package/dist/utils/environment.d.ts +0 -71
- package/dist/utils/environment.d.ts.map +0 -1
- package/dist/utils/environment.js +0 -86
- package/dist/utils/msw.d.ts +0 -54
- package/dist/utils/msw.d.ts.map +0 -1
- package/dist/utils/msw.js +0 -62
package/dist/hooks/index.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export {
|
|
1
|
+
export { useCopyToClipboard } from './use-copy-to-clipboard.js';
|
|
2
|
+
export { useDebounce } from './use-debounce.js';
|
|
3
|
+
export { useEventListener } from './use-event-listener.js';
|
|
4
|
+
export { useIdle } from './use-idle.js';
|
|
5
|
+
export { useIntersectionObserver } from './use-intersection-observer.js';
|
|
6
|
+
export { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect.js';
|
|
7
|
+
export { useLatest } from './use-latest.js';
|
|
8
|
+
export { useLocalStorage } from './use-local-storage.js';
|
|
9
|
+
export { useMediaQuery } from './use-media-query.js';
|
|
10
|
+
export { useNetworkStatus } from './use-network-status.js';
|
|
11
|
+
export { useOnClickOutside } from './use-on-click-outside.js';
|
|
12
|
+
export { usePrevious } from './use-previous.js';
|
|
13
|
+
export { useRenderCount } from './use-render-count.js';
|
|
14
|
+
export { useToggle } from './use-toggle.js';
|
|
15
|
+
export { useWhyDidYouRender } from './use-why-did-you-render.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns `[copied, copy]`.
|
|
3
|
+
* `copy(text)` writes to the clipboard and returns a boolean indicating success.
|
|
4
|
+
* `copied` resets to `false` after `resetDelay` ms.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useCopyToClipboard(resetDelay?: number): [boolean, (text: string) => Promise<boolean>];
|
|
7
|
+
//# sourceMappingURL=use-copy-to-clipboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-copy-to-clipboard.d.ts","sourceRoot":"","sources":["../../src/hooks/use-copy-to-clipboard.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,SAAO,GAChB,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,CAoB/C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Returns `[copied, copy]`.
|
|
4
|
+
* `copy(text)` writes to the clipboard and returns a boolean indicating success.
|
|
5
|
+
* `copied` resets to `false` after `resetDelay` ms.
|
|
6
|
+
*/
|
|
7
|
+
export function useCopyToClipboard(resetDelay = 2000) {
|
|
8
|
+
const [copied, setCopied] = useState(false);
|
|
9
|
+
const copy = useCallback(async (text) => {
|
|
10
|
+
if (!navigator.clipboard)
|
|
11
|
+
return false;
|
|
12
|
+
try {
|
|
13
|
+
await navigator.clipboard.writeText(text);
|
|
14
|
+
setCopied(true);
|
|
15
|
+
setTimeout(() => setCopied(false), resetDelay);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}, [resetDelay]);
|
|
22
|
+
return [copied, copy];
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-debounce.d.ts","sourceRoot":"","sources":["../../src/hooks/use-debounce.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CASzD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
export function useDebounce(value, delay) {
|
|
3
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const timer = setTimeout(() => setDebouncedValue(value), delay);
|
|
6
|
+
return () => clearTimeout(timer);
|
|
7
|
+
}, [value, delay]);
|
|
8
|
+
return debouncedValue;
|
|
9
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function useEventListener<K extends keyof WindowEventMap>(target: Window | null, event: K, handler: (event: WindowEventMap[K]) => void, options?: AddEventListenerOptions): void;
|
|
2
|
+
export declare function useEventListener<K extends keyof DocumentEventMap>(target: Document | null, event: K, handler: (event: DocumentEventMap[K]) => void, options?: AddEventListenerOptions): void;
|
|
3
|
+
export declare function useEventListener<K extends keyof HTMLElementEventMap, T extends HTMLElement>(target: T | null, event: K, handler: (event: HTMLElementEventMap[K]) => void, options?: AddEventListenerOptions): void;
|
|
4
|
+
//# sourceMappingURL=use-event-listener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-event-listener.d.ts","sourceRoot":"","sources":["../../src/hooks/use-event-listener.ts"],"names":[],"mappings":"AAKA,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7D,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,EAC3C,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAAA;AACP,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAC/D,MAAM,EAAE,QAAQ,GAAG,IAAI,EACvB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,IAAI,EAC7C,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAAA;AACP,wBAAgB,gBAAgB,CAC9B,CAAC,SAAS,MAAM,mBAAmB,EACnC,CAAC,SAAS,WAAW,EAErB,MAAM,EAAE,CAAC,GAAG,IAAI,EAChB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,EAChD,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useLatest } from './use-latest.js';
|
|
3
|
+
export function useEventListener(target, event, handler, options) {
|
|
4
|
+
const handlerRef = useLatest(handler);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
if (!target)
|
|
7
|
+
return;
|
|
8
|
+
const listener = (e) => handlerRef.current(e);
|
|
9
|
+
target.addEventListener(event, listener, options);
|
|
10
|
+
return () => target.removeEventListener(event, listener, options);
|
|
11
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
12
|
+
}, [target, event, JSON.stringify(options)]);
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns `true` when the user has been inactive for longer than `timeout` ms.
|
|
3
|
+
* Resets on any mouse, keyboard, touch, or scroll event.
|
|
4
|
+
* Use for session-expiry warnings, pausing background work, or screensaver effects.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useIdle(timeout: number): boolean;
|
|
7
|
+
//# sourceMappingURL=use-idle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-idle.d.ts","sourceRoot":"","sources":["../../src/hooks/use-idle.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAyBhD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
const ACTIVITY_EVENTS = [
|
|
3
|
+
'mousemove',
|
|
4
|
+
'keydown',
|
|
5
|
+
'mousedown',
|
|
6
|
+
'touchstart',
|
|
7
|
+
'scroll',
|
|
8
|
+
'wheel',
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Returns `true` when the user has been inactive for longer than `timeout` ms.
|
|
12
|
+
* Resets on any mouse, keyboard, touch, or scroll event.
|
|
13
|
+
* Use for session-expiry warnings, pausing background work, or screensaver effects.
|
|
14
|
+
*/
|
|
15
|
+
export function useIdle(timeout) {
|
|
16
|
+
const [idle, setIdle] = useState(false);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
let timer = setTimeout(() => setIdle(true), timeout);
|
|
19
|
+
function handleActivity() {
|
|
20
|
+
clearTimeout(timer);
|
|
21
|
+
setIdle(false);
|
|
22
|
+
timer = setTimeout(() => setIdle(true), timeout);
|
|
23
|
+
}
|
|
24
|
+
for (const event of ACTIVITY_EVENTS) {
|
|
25
|
+
window.addEventListener(event, handleActivity, { passive: true });
|
|
26
|
+
}
|
|
27
|
+
return () => {
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
for (const event of ACTIVITY_EVENTS) {
|
|
30
|
+
window.removeEventListener(event, handleActivity);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}, [timeout]);
|
|
34
|
+
return idle;
|
|
35
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
interface UseIntersectionObserverOptions extends IntersectionObserverInit {
|
|
3
|
+
/**
|
|
4
|
+
* Once the element is visible, stop observing and keep the entry frozen.
|
|
5
|
+
* Useful for lazy-load triggers that should fire only once.
|
|
6
|
+
*/
|
|
7
|
+
freezeOnceVisible?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Tracks whether an element is within the viewport (or a custom root).
|
|
11
|
+
* Returns the latest `IntersectionObserverEntry`, or `null` before first observation.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useIntersectionObserver(ref: RefObject<Element | null>, { threshold, root, rootMargin, freezeOnceVisible, }?: UseIntersectionObserverOptions): IntersectionObserverEntry | null;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=use-intersection-observer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-intersection-observer.d.ts","sourceRoot":"","sources":["../../src/hooks/use-intersection-observer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAE3D,UAAU,8BAA+B,SAAQ,wBAAwB;IACvE;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,EAC9B,EACE,SAAa,EACb,IAAW,EACX,UAAiB,EACjB,iBAAyB,GAC1B,GAAE,8BAAmC,GACrC,yBAAyB,GAAG,IAAI,CAoBlC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Tracks whether an element is within the viewport (or a custom root).
|
|
4
|
+
* Returns the latest `IntersectionObserverEntry`, or `null` before first observation.
|
|
5
|
+
*/
|
|
6
|
+
export function useIntersectionObserver(ref, { threshold = 0, root = null, rootMargin = '0%', freezeOnceVisible = false, } = {}) {
|
|
7
|
+
const [entry, setEntry] = useState(null);
|
|
8
|
+
const frozen = entry?.isIntersecting && freezeOnceVisible;
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const el = ref.current;
|
|
11
|
+
if (!el || frozen || typeof IntersectionObserver === 'undefined')
|
|
12
|
+
return;
|
|
13
|
+
const observer = new IntersectionObserver(([e]) => setEntry(e ?? null), {
|
|
14
|
+
threshold,
|
|
15
|
+
root,
|
|
16
|
+
rootMargin,
|
|
17
|
+
});
|
|
18
|
+
observer.observe(el);
|
|
19
|
+
return () => observer.disconnect();
|
|
20
|
+
}, [ref, threshold, root, rootMargin, frozen]);
|
|
21
|
+
return entry;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-isomorphic-layout-effect.d.ts","sourceRoot":"","sources":["../../src/hooks/use-isomorphic-layout-effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,MAAM,OAAO,CAAA;AAIlD,eAAO,MAAM,yBAAyB,kBACuB,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect } from 'react';
|
|
2
|
+
// useLayoutEffect warns in SSR. This hook silences that by falling back to
|
|
3
|
+
// useEffect on the server where window is not available.
|
|
4
|
+
export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a ref always pointing to the latest value.
|
|
3
|
+
* Breaks stale closures in event listeners, intervals, and callbacks
|
|
4
|
+
* without requiring them to re-subscribe on every render.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useLatest<T>(value: T): import("react").RefObject<T>;
|
|
7
|
+
//# sourceMappingURL=use-latest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-latest.d.ts","sourceRoot":"","sources":["../../src/hooks/use-latest.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,gCAIpC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a ref always pointing to the latest value.
|
|
4
|
+
* Breaks stale closures in event listeners, intervals, and callbacks
|
|
5
|
+
* without requiring them to re-subscribe on every render.
|
|
6
|
+
*/
|
|
7
|
+
export function useLatest(value) {
|
|
8
|
+
const ref = useRef(value);
|
|
9
|
+
ref.current = value;
|
|
10
|
+
return ref;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-local-storage.d.ts","sourceRoot":"","sources":["../../src/hooks/use-local-storage.ts"],"names":[],"mappings":"AAYA,wBAAgB,eAAe,CAAC,CAAC,EAC/B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,GACd,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,MAAM,IAAI,CAAC,CAmCxD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from 'react';
|
|
2
|
+
function readFromStorage(key, fallback) {
|
|
3
|
+
if (typeof window === 'undefined')
|
|
4
|
+
return fallback;
|
|
5
|
+
try {
|
|
6
|
+
const item = window.localStorage.getItem(key);
|
|
7
|
+
return item !== null ? JSON.parse(item) : fallback;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return fallback;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function useLocalStorage(key, initialValue) {
|
|
14
|
+
const initialValueRef = useRef(initialValue);
|
|
15
|
+
const [storedValue, setStoredValue] = useState(() => readFromStorage(key, initialValueRef.current));
|
|
16
|
+
const setValue = useCallback((value) => {
|
|
17
|
+
setStoredValue((prev) => {
|
|
18
|
+
const next = value instanceof Function ? value(prev) : value;
|
|
19
|
+
if (typeof window !== 'undefined') {
|
|
20
|
+
try {
|
|
21
|
+
window.localStorage.setItem(key, JSON.stringify(next));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error(`useLocalStorage: failed to write key "${key}"`, error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return next;
|
|
28
|
+
});
|
|
29
|
+
}, [key]);
|
|
30
|
+
const removeValue = useCallback(() => {
|
|
31
|
+
if (typeof window !== 'undefined') {
|
|
32
|
+
window.localStorage.removeItem(key);
|
|
33
|
+
}
|
|
34
|
+
setStoredValue(initialValueRef.current);
|
|
35
|
+
}, [key]);
|
|
36
|
+
return [storedValue, setValue, removeValue];
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-media-query.d.ts","sourceRoot":"","sources":["../../src/hooks/use-media-query.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAkBpD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
export function useMediaQuery(query) {
|
|
3
|
+
const [matches, setMatches] = useState(() => {
|
|
4
|
+
if (typeof window === 'undefined')
|
|
5
|
+
return false;
|
|
6
|
+
return window.matchMedia(query).matches;
|
|
7
|
+
});
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (typeof window === 'undefined')
|
|
10
|
+
return;
|
|
11
|
+
const mql = window.matchMedia(query);
|
|
12
|
+
setMatches(mql.matches);
|
|
13
|
+
const handler = (event) => setMatches(event.matches);
|
|
14
|
+
mql.addEventListener('change', handler);
|
|
15
|
+
return () => mql.removeEventListener('change', handler);
|
|
16
|
+
}, [query]);
|
|
17
|
+
return matches;
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface NetworkStatus {
|
|
2
|
+
online: boolean;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Tracks browser online/offline status.
|
|
6
|
+
* SSR-safe: defaults to `true` on the server.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useNetworkStatus(): NetworkStatus;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=use-network-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-network-status.d.ts","sourceRoot":"","sources":["../../src/hooks/use-network-status.ts"],"names":[],"mappings":"AAEA,UAAU,aAAa;IACrB,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAmBhD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Tracks browser online/offline status.
|
|
4
|
+
* SSR-safe: defaults to `true` on the server.
|
|
5
|
+
*/
|
|
6
|
+
export function useNetworkStatus() {
|
|
7
|
+
const [online, setOnline] = useState(typeof navigator !== 'undefined' ? navigator.onLine : true);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const handleOnline = () => setOnline(true);
|
|
10
|
+
const handleOffline = () => setOnline(false);
|
|
11
|
+
window.addEventListener('online', handleOnline);
|
|
12
|
+
window.addEventListener('offline', handleOffline);
|
|
13
|
+
return () => {
|
|
14
|
+
window.removeEventListener('online', handleOnline);
|
|
15
|
+
window.removeEventListener('offline', handleOffline);
|
|
16
|
+
};
|
|
17
|
+
}, []);
|
|
18
|
+
return { online };
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-on-click-outside.d.ts","sourceRoot":"","sources":["../../src/hooks/use-on-click-outside.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,OAAO,CAAA;AAEjD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,EACrD,GAAG,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,EACxB,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,KAAK,IAAI,GAChD,IAAI,CAeN"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
export function useOnClickOutside(ref, handler) {
|
|
3
|
+
useEffect(() => {
|
|
4
|
+
const listener = (event) => {
|
|
5
|
+
const el = ref.current;
|
|
6
|
+
if (!el || el.contains(event.target))
|
|
7
|
+
return;
|
|
8
|
+
handler(event);
|
|
9
|
+
};
|
|
10
|
+
document.addEventListener('mousedown', listener);
|
|
11
|
+
document.addEventListener('touchstart', listener);
|
|
12
|
+
return () => {
|
|
13
|
+
document.removeEventListener('mousedown', listener);
|
|
14
|
+
document.removeEventListener('touchstart', listener);
|
|
15
|
+
};
|
|
16
|
+
}, [ref, handler]);
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-previous.d.ts","sourceRoot":"","sources":["../../src/hooks/use-previous.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAQtD"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the number of times the component has rendered.
|
|
3
|
+
* Dev-only: logs to console when a label is provided.
|
|
4
|
+
* Use this to spot unexpected re-render loops before reaching for the profiler.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useRenderCount(label?: string): number;
|
|
7
|
+
//# sourceMappingURL=use-render-count.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-render-count.d.ts","sourceRoot":"","sources":["../../src/hooks/use-render-count.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAUrD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the number of times the component has rendered.
|
|
4
|
+
* Dev-only: logs to console when a label is provided.
|
|
5
|
+
* Use this to spot unexpected re-render loops before reaching for the profiler.
|
|
6
|
+
*/
|
|
7
|
+
export function useRenderCount(label) {
|
|
8
|
+
const count = useRef(0);
|
|
9
|
+
count.current += 1;
|
|
10
|
+
if (process.env.NODE_ENV !== 'production' && label) {
|
|
11
|
+
// eslint-disable-next-line no-console
|
|
12
|
+
console.log(`[renders] ${label}: ${count.current}`);
|
|
13
|
+
}
|
|
14
|
+
return count.current;
|
|
15
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boolean state with stable toggle, setOn, and setOff callbacks.
|
|
3
|
+
* Returns `[value, toggle, setOn, setOff]`.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useToggle(initialValue?: boolean): [boolean, () => void, () => void, () => void];
|
|
6
|
+
//# sourceMappingURL=use-toggle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-toggle.d.ts","sourceRoot":"","sources":["../../src/hooks/use-toggle.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,SAAS,CACvB,YAAY,UAAQ,GACnB,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,MAAM,IAAI,EAAE,MAAM,IAAI,CAAC,CAQ/C"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Boolean state with stable toggle, setOn, and setOff callbacks.
|
|
4
|
+
* Returns `[value, toggle, setOn, setOff]`.
|
|
5
|
+
*/
|
|
6
|
+
export function useToggle(initialValue = false) {
|
|
7
|
+
const [value, setValue] = useState(initialValue);
|
|
8
|
+
const toggle = useCallback(() => setValue((v) => !v), []);
|
|
9
|
+
const setOn = useCallback(() => setValue(true), []);
|
|
10
|
+
const setOff = useCallback(() => setValue(false), []);
|
|
11
|
+
return [value, toggle, setOn, setOff];
|
|
12
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logs which props changed between renders.
|
|
3
|
+
* Dev-only (no-ops in production).
|
|
4
|
+
* Use before reaching for the React DevTools profiler — shows changed props
|
|
5
|
+
* inline in the console with before/after values.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useWhyDidYouRender(name: string, props: Record<string, unknown>): void;
|
|
8
|
+
//# sourceMappingURL=use-why-did-you-render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-why-did-you-render.d.ts","sourceRoot":"","sources":["../../src/hooks/use-why-did-you-render.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAmCN"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Logs which props changed between renders.
|
|
4
|
+
* Dev-only (no-ops in production).
|
|
5
|
+
* Use before reaching for the React DevTools profiler — shows changed props
|
|
6
|
+
* inline in the console with before/after values.
|
|
7
|
+
*/
|
|
8
|
+
export function useWhyDidYouRender(name, props) {
|
|
9
|
+
const hasMounted = useRef(false);
|
|
10
|
+
const previousProps = useRef({});
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (process.env.NODE_ENV === 'production')
|
|
13
|
+
return;
|
|
14
|
+
// Skip first render — nothing to diff against
|
|
15
|
+
if (!hasMounted.current) {
|
|
16
|
+
hasMounted.current = true;
|
|
17
|
+
previousProps.current = { ...props };
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const changed = {};
|
|
21
|
+
for (const key of Object.keys(props)) {
|
|
22
|
+
if (previousProps.current[key] !== props[key]) {
|
|
23
|
+
changed[key] = {
|
|
24
|
+
from: previousProps.current[key],
|
|
25
|
+
to: props[key],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (Object.keys(changed).length > 0) {
|
|
30
|
+
console.group(`[why-render] ${name}`);
|
|
31
|
+
for (const [key, diff] of Object.entries(changed)) {
|
|
32
|
+
console.log(` ${key}:`, diff);
|
|
33
|
+
}
|
|
34
|
+
console.groupEnd();
|
|
35
|
+
}
|
|
36
|
+
previousProps.current = { ...props };
|
|
37
|
+
});
|
|
38
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @teo-garcia/react-shared
|
|
3
3
|
*
|
|
4
|
-
* Shared React
|
|
4
|
+
* Shared React hooks, utilities, and test helpers for the teo-garcia template portfolio.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Exports:
|
|
7
|
+
* - Hooks: useDebounce, useIsomorphicLayoutEffect, useLocalStorage, useMediaQuery, useOnClickOutside, usePrevious
|
|
8
|
+
* - Components: ErrorBoundary
|
|
9
|
+
* - Utils: cn (clsx + tailwind-merge)
|
|
10
|
+
* - Test utilities: createWrapper, renderWithProviders (import from react-shared/test-utils)
|
|
8
11
|
*
|
|
9
12
|
* @packageDocumentation
|
|
10
13
|
*/
|
|
11
14
|
export * from './components/index.js';
|
|
12
15
|
export * from './hooks/index.js';
|
|
13
16
|
export * from './utils/index.js';
|
|
14
|
-
export * from './adapters/index.js';
|
|
15
17
|
export * from './types.js';
|
|
16
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @teo-garcia/react-shared
|
|
3
3
|
*
|
|
4
|
-
* Shared React
|
|
4
|
+
* Shared React hooks, utilities, and test helpers for the teo-garcia template portfolio.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Exports:
|
|
7
|
+
* - Hooks: useDebounce, useIsomorphicLayoutEffect, useLocalStorage, useMediaQuery, useOnClickOutside, usePrevious
|
|
8
|
+
* - Components: ErrorBoundary
|
|
9
|
+
* - Utils: cn (clsx + tailwind-merge)
|
|
10
|
+
* - Test utilities: createWrapper, renderWithProviders (import from react-shared/test-utils)
|
|
8
11
|
*
|
|
9
12
|
* @packageDocumentation
|
|
10
13
|
*/
|
|
11
|
-
// Export all components
|
|
12
14
|
export * from './components/index.js';
|
|
13
|
-
// Export all hooks
|
|
14
15
|
export * from './hooks/index.js';
|
|
15
|
-
// Export all utilities
|
|
16
16
|
export * from './utils/index.js';
|
|
17
|
-
// Export all adapters
|
|
18
|
-
export * from './adapters/index.js';
|
|
19
|
-
// Export all types
|
|
20
17
|
export * from './types.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { type RenderOptions, type RenderResult } from '@testing-library/react';
|
|
3
|
+
import { type ReactNode } from 'react';
|
|
4
|
+
export interface WrapperOptions {
|
|
5
|
+
queryClient?: QueryClient;
|
|
6
|
+
}
|
|
7
|
+
export declare function createWrapper(options?: WrapperOptions): ({ children }: {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}) => import("react").FunctionComponentElement<import("@tanstack/react-query").QueryClientProviderProps>;
|
|
10
|
+
export interface RenderWithProvidersOptions extends Omit<RenderOptions, 'wrapper'> {
|
|
11
|
+
queryClient?: QueryClient;
|
|
12
|
+
}
|
|
13
|
+
export declare function renderWithProviders(ui: React.ReactElement, options?: RenderWithProvidersOptions): RenderResult;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAErD,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,wBAAgB,aAAa,CAAC,OAAO,GAAE,cAAmB,IAUhC,cAAc;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,wGAG9D;AAED,MAAM,WAAW,0BACf,SAAQ,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC;IACtC,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,KAAK,CAAC,YAAY,EACtB,OAAO,GAAE,0BAA+B,GACvC,YAAY,CAMd"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
2
|
+
import { render, } from '@testing-library/react';
|
|
3
|
+
import { createElement } from 'react';
|
|
4
|
+
export function createWrapper(options = {}) {
|
|
5
|
+
const client = options.queryClient ??
|
|
6
|
+
new QueryClient({
|
|
7
|
+
defaultOptions: {
|
|
8
|
+
queries: { retry: false, gcTime: 0 },
|
|
9
|
+
mutations: { retry: false },
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
return function Wrapper({ children }) {
|
|
13
|
+
return createElement(QueryClientProvider, { client }, children);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function renderWithProviders(ui, options = {}) {
|
|
17
|
+
const { queryClient, ...renderOptions } = options;
|
|
18
|
+
return render(ui, {
|
|
19
|
+
wrapper: createWrapper({ queryClient }),
|
|
20
|
+
...renderOptions,
|
|
21
|
+
});
|
|
22
|
+
}
|