@usels/core 0.0.1
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 +21 -0
- package/dist/browser/useEventListener/index.d.mts +56 -0
- package/dist/browser/useEventListener/index.d.ts +56 -0
- package/dist/browser/useEventListener/index.js +112 -0
- package/dist/browser/useEventListener/index.js.map +1 -0
- package/dist/browser/useEventListener/index.mjs +88 -0
- package/dist/browser/useEventListener/index.mjs.map +1 -0
- package/dist/browser/useMediaQuery/demo.d.mts +5 -0
- package/dist/browser/useMediaQuery/demo.d.ts +5 -0
- package/dist/browser/useMediaQuery/demo.js +83 -0
- package/dist/browser/useMediaQuery/demo.js.map +1 -0
- package/dist/browser/useMediaQuery/demo.mjs +63 -0
- package/dist/browser/useMediaQuery/demo.mjs.map +1 -0
- package/dist/browser/useMediaQuery/index.d.mts +11 -0
- package/dist/browser/useMediaQuery/index.d.ts +11 -0
- package/dist/browser/useMediaQuery/index.js +89 -0
- package/dist/browser/useMediaQuery/index.js.map +1 -0
- package/dist/browser/useMediaQuery/index.mjs +64 -0
- package/dist/browser/useMediaQuery/index.mjs.map +1 -0
- package/dist/components/Auto/index.d.mts +33 -0
- package/dist/components/Auto/index.d.ts +33 -0
- package/dist/components/Auto/index.js +66 -0
- package/dist/components/Auto/index.js.map +1 -0
- package/dist/components/Auto/index.mjs +34 -0
- package/dist/components/Auto/index.mjs.map +1 -0
- package/dist/elements/useDocumentVisibility/demo.d.mts +5 -0
- package/dist/elements/useDocumentVisibility/demo.d.ts +5 -0
- package/dist/elements/useDocumentVisibility/demo.js +130 -0
- package/dist/elements/useDocumentVisibility/demo.js.map +1 -0
- package/dist/elements/useDocumentVisibility/demo.mjs +114 -0
- package/dist/elements/useDocumentVisibility/demo.mjs.map +1 -0
- package/dist/elements/useDocumentVisibility/index.d.mts +5 -0
- package/dist/elements/useDocumentVisibility/index.d.ts +5 -0
- package/dist/elements/useDocumentVisibility/index.js +45 -0
- package/dist/elements/useDocumentVisibility/index.js.map +1 -0
- package/dist/elements/useDocumentVisibility/index.mjs +21 -0
- package/dist/elements/useDocumentVisibility/index.mjs.map +1 -0
- package/dist/elements/useElementBounding/demo.d.mts +5 -0
- package/dist/elements/useElementBounding/demo.d.ts +5 -0
- package/dist/elements/useElementBounding/demo.js +87 -0
- package/dist/elements/useElementBounding/demo.js.map +1 -0
- package/dist/elements/useElementBounding/demo.mjs +67 -0
- package/dist/elements/useElementBounding/demo.mjs.map +1 -0
- package/dist/elements/useElementBounding/index.d.mts +46 -0
- package/dist/elements/useElementBounding/index.d.ts +46 -0
- package/dist/elements/useElementBounding/index.js +122 -0
- package/dist/elements/useElementBounding/index.js.map +1 -0
- package/dist/elements/useElementBounding/index.mjs +98 -0
- package/dist/elements/useElementBounding/index.mjs.map +1 -0
- package/dist/elements/useElementSize/demo.d.mts +5 -0
- package/dist/elements/useElementSize/demo.d.ts +5 -0
- package/dist/elements/useElementSize/demo.js +83 -0
- package/dist/elements/useElementSize/demo.js.map +1 -0
- package/dist/elements/useElementSize/demo.mjs +63 -0
- package/dist/elements/useElementSize/demo.mjs.map +1 -0
- package/dist/elements/useElementSize/index.d.mts +34 -0
- package/dist/elements/useElementSize/index.d.ts +34 -0
- package/dist/elements/useElementSize/index.js +85 -0
- package/dist/elements/useElementSize/index.js.map +1 -0
- package/dist/elements/useElementSize/index.mjs +61 -0
- package/dist/elements/useElementSize/index.mjs.map +1 -0
- package/dist/elements/useElementVisibility/demo.d.mts +5 -0
- package/dist/elements/useElementVisibility/demo.d.ts +5 -0
- package/dist/elements/useElementVisibility/demo.js +110 -0
- package/dist/elements/useElementVisibility/demo.js.map +1 -0
- package/dist/elements/useElementVisibility/demo.mjs +90 -0
- package/dist/elements/useElementVisibility/demo.mjs.map +1 -0
- package/dist/elements/useElementVisibility/index.d.mts +43 -0
- package/dist/elements/useElementVisibility/index.d.ts +43 -0
- package/dist/elements/useElementVisibility/index.js +58 -0
- package/dist/elements/useElementVisibility/index.js.map +1 -0
- package/dist/elements/useElementVisibility/index.mjs +34 -0
- package/dist/elements/useElementVisibility/index.mjs.map +1 -0
- package/dist/elements/useIntersectionObserver/demo.d.mts +5 -0
- package/dist/elements/useIntersectionObserver/demo.d.ts +5 -0
- package/dist/elements/useIntersectionObserver/demo.js +173 -0
- package/dist/elements/useIntersectionObserver/demo.js.map +1 -0
- package/dist/elements/useIntersectionObserver/demo.mjs +153 -0
- package/dist/elements/useIntersectionObserver/demo.mjs.map +1 -0
- package/dist/elements/useIntersectionObserver/index.d.mts +47 -0
- package/dist/elements/useIntersectionObserver/index.d.ts +47 -0
- package/dist/elements/useIntersectionObserver/index.js +111 -0
- package/dist/elements/useIntersectionObserver/index.js.map +1 -0
- package/dist/elements/useIntersectionObserver/index.mjs +87 -0
- package/dist/elements/useIntersectionObserver/index.mjs.map +1 -0
- package/dist/elements/useMouseInElement/demo.d.mts +5 -0
- package/dist/elements/useMouseInElement/demo.d.ts +5 -0
- package/dist/elements/useMouseInElement/demo.js +104 -0
- package/dist/elements/useMouseInElement/demo.js.map +1 -0
- package/dist/elements/useMouseInElement/demo.mjs +84 -0
- package/dist/elements/useMouseInElement/demo.mjs.map +1 -0
- package/dist/elements/useMouseInElement/index.d.mts +56 -0
- package/dist/elements/useMouseInElement/index.d.ts +56 -0
- package/dist/elements/useMouseInElement/index.js +148 -0
- package/dist/elements/useMouseInElement/index.js.map +1 -0
- package/dist/elements/useMouseInElement/index.mjs +124 -0
- package/dist/elements/useMouseInElement/index.mjs.map +1 -0
- package/dist/elements/useMutationObserver/demo.d.mts +5 -0
- package/dist/elements/useMutationObserver/demo.d.ts +5 -0
- package/dist/elements/useMutationObserver/demo.js +240 -0
- package/dist/elements/useMutationObserver/demo.js.map +1 -0
- package/dist/elements/useMutationObserver/demo.mjs +220 -0
- package/dist/elements/useMutationObserver/demo.mjs.map +1 -0
- package/dist/elements/useMutationObserver/index.d.mts +15 -0
- package/dist/elements/useMutationObserver/index.d.ts +15 -0
- package/dist/elements/useMutationObserver/index.js +69 -0
- package/dist/elements/useMutationObserver/index.js.map +1 -0
- package/dist/elements/useMutationObserver/index.mjs +45 -0
- package/dist/elements/useMutationObserver/index.mjs.map +1 -0
- package/dist/elements/useParentElement/demo.d.mts +5 -0
- package/dist/elements/useParentElement/demo.d.ts +5 -0
- package/dist/elements/useParentElement/demo.js +132 -0
- package/dist/elements/useParentElement/demo.js.map +1 -0
- package/dist/elements/useParentElement/demo.mjs +112 -0
- package/dist/elements/useParentElement/demo.mjs.map +1 -0
- package/dist/elements/useParentElement/index.d.mts +7 -0
- package/dist/elements/useParentElement/index.d.ts +7 -0
- package/dist/elements/useParentElement/index.js +47 -0
- package/dist/elements/useParentElement/index.js.map +1 -0
- package/dist/elements/useParentElement/index.mjs +23 -0
- package/dist/elements/useParentElement/index.mjs.map +1 -0
- package/dist/elements/useRef$/index.js +89 -0
- package/dist/elements/useRef$/index.js.map +1 -0
- package/dist/elements/useRef$/index.mjs +62 -0
- package/dist/elements/useRef$/index.mjs.map +1 -0
- package/dist/elements/useRef_/index.d.mts +60 -0
- package/dist/elements/useRef_/index.d.ts +60 -0
- package/dist/elements/useResizeObserver/demo.d.mts +5 -0
- package/dist/elements/useResizeObserver/demo.d.ts +5 -0
- package/dist/elements/useResizeObserver/demo.js +90 -0
- package/dist/elements/useResizeObserver/demo.js.map +1 -0
- package/dist/elements/useResizeObserver/demo.mjs +70 -0
- package/dist/elements/useResizeObserver/demo.mjs.map +1 -0
- package/dist/elements/useResizeObserver/index.d.mts +36 -0
- package/dist/elements/useResizeObserver/index.d.ts +36 -0
- package/dist/elements/useResizeObserver/index.js +74 -0
- package/dist/elements/useResizeObserver/index.js.map +1 -0
- package/dist/elements/useResizeObserver/index.mjs +49 -0
- package/dist/elements/useResizeObserver/index.mjs.map +1 -0
- package/dist/elements/useWindowFocus/demo.d.mts +5 -0
- package/dist/elements/useWindowFocus/demo.d.ts +5 -0
- package/dist/elements/useWindowFocus/demo.js +104 -0
- package/dist/elements/useWindowFocus/demo.js.map +1 -0
- package/dist/elements/useWindowFocus/demo.mjs +84 -0
- package/dist/elements/useWindowFocus/demo.mjs.map +1 -0
- package/dist/elements/useWindowFocus/index.d.mts +5 -0
- package/dist/elements/useWindowFocus/index.d.ts +5 -0
- package/dist/elements/useWindowFocus/index.js +42 -0
- package/dist/elements/useWindowFocus/index.js.map +1 -0
- package/dist/elements/useWindowFocus/index.mjs +18 -0
- package/dist/elements/useWindowFocus/index.mjs.map +1 -0
- package/dist/elements/useWindowSize/demo.d.mts +5 -0
- package/dist/elements/useWindowSize/demo.d.ts +5 -0
- package/dist/elements/useWindowSize/demo.js +79 -0
- package/dist/elements/useWindowSize/demo.js.map +1 -0
- package/dist/elements/useWindowSize/demo.mjs +59 -0
- package/dist/elements/useWindowSize/demo.mjs.map +1 -0
- package/dist/elements/useWindowSize/index.d.mts +17 -0
- package/dist/elements/useWindowSize/index.d.ts +17 -0
- package/dist/elements/useWindowSize/index.js +96 -0
- package/dist/elements/useWindowSize/index.js.map +1 -0
- package/dist/elements/useWindowSize/index.mjs +76 -0
- package/dist/elements/useWindowSize/index.mjs.map +1 -0
- package/dist/function/get/index.d.mts +45 -0
- package/dist/function/get/index.d.ts +45 -0
- package/dist/function/get/index.js +39 -0
- package/dist/function/get/index.js.map +1 -0
- package/dist/function/get/index.mjs +15 -0
- package/dist/function/get/index.mjs.map +1 -0
- package/dist/function/peek/index.d.mts +46 -0
- package/dist/function/peek/index.d.ts +46 -0
- package/dist/function/peek/index.js +39 -0
- package/dist/function/peek/index.js.map +1 -0
- package/dist/function/peek/index.mjs +15 -0
- package/dist/function/peek/index.mjs.map +1 -0
- package/dist/function/useMayObservableOptions/index.d.mts +59 -0
- package/dist/function/useMayObservableOptions/index.d.ts +59 -0
- package/dist/function/useMayObservableOptions/index.js +109 -0
- package/dist/function/useMayObservableOptions/index.js.map +1 -0
- package/dist/function/useMayObservableOptions/index.mjs +88 -0
- package/dist/function/useMayObservableOptions/index.mjs.map +1 -0
- package/dist/function/useSupported/index.d.mts +6 -0
- package/dist/function/useSupported/index.d.ts +6 -0
- package/dist/function/useSupported/index.js +37 -0
- package/dist/function/useSupported/index.js.map +1 -0
- package/dist/function/useSupported/index.mjs +13 -0
- package/dist/function/useSupported/index.mjs.map +1 -0
- package/dist/function/useWhenMounted/index.d.mts +6 -0
- package/dist/function/useWhenMounted/index.d.ts +6 -0
- package/dist/function/useWhenMounted/index.js +37 -0
- package/dist/function/useWhenMounted/index.js.map +1 -0
- package/dist/function/useWhenMounted/index.mjs +13 -0
- package/dist/function/useWhenMounted/index.mjs.map +1 -0
- package/dist/index.d.mts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +22 -0
- package/dist/index.mjs.map +1 -0
- package/dist/sensors/useScroll/demo.d.mts +5 -0
- package/dist/sensors/useScroll/demo.d.ts +5 -0
- package/dist/sensors/useScroll/demo.js +122 -0
- package/dist/sensors/useScroll/demo.js.map +1 -0
- package/dist/sensors/useScroll/demo.mjs +102 -0
- package/dist/sensors/useScroll/demo.mjs.map +1 -0
- package/dist/sensors/useScroll/index.d.mts +42 -0
- package/dist/sensors/useScroll/index.d.ts +42 -0
- package/dist/sensors/useScroll/index.js +149 -0
- package/dist/sensors/useScroll/index.js.map +1 -0
- package/dist/sensors/useScroll/index.mjs +125 -0
- package/dist/sensors/useScroll/index.mjs.map +1 -0
- package/dist/sensors/useWindowScroll/demo.d.mts +5 -0
- package/dist/sensors/useWindowScroll/demo.d.ts +5 -0
- package/dist/sensors/useWindowScroll/demo.js +85 -0
- package/dist/sensors/useWindowScroll/demo.js.map +1 -0
- package/dist/sensors/useWindowScroll/demo.mjs +65 -0
- package/dist/sensors/useWindowScroll/demo.mjs.map +1 -0
- package/dist/sensors/useWindowScroll/index.d.mts +9 -0
- package/dist/sensors/useWindowScroll/index.d.ts +9 -0
- package/dist/sensors/useWindowScroll/index.js +36 -0
- package/dist/sensors/useWindowScroll/index.js.map +1 -0
- package/dist/sensors/useWindowScroll/index.mjs +12 -0
- package/dist/sensors/useWindowScroll/index.mjs.map +1 -0
- package/dist/shared/configurable.d.mts +21 -0
- package/dist/shared/configurable.d.ts +21 -0
- package/dist/shared/configurable.js +39 -0
- package/dist/shared/configurable.js.map +1 -0
- package/dist/shared/configurable.mjs +12 -0
- package/dist/shared/configurable.mjs.map +1 -0
- package/dist/shared/index.d.mts +4 -0
- package/dist/shared/index.d.ts +4 -0
- package/dist/shared/index.js +31 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/index.mjs +7 -0
- package/dist/shared/index.mjs.map +1 -0
- package/dist/shared/normalizeTargets/index.d.mts +21 -0
- package/dist/shared/normalizeTargets/index.d.ts +21 -0
- package/dist/shared/normalizeTargets/index.js +36 -0
- package/dist/shared/normalizeTargets/index.js.map +1 -0
- package/dist/shared/normalizeTargets/index.mjs +12 -0
- package/dist/shared/normalizeTargets/index.mjs.map +1 -0
- package/dist/shared/utils.d.mts +15 -0
- package/dist/shared/utils.d.ts +15 -0
- package/dist/shared/utils.js +87 -0
- package/dist/shared/utils.js.map +1 -0
- package/dist/shared/utils.mjs +52 -0
- package/dist/shared/utils.mjs.map +1 -0
- package/dist/types.d.mts +52 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/dist/types.mjs +1 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +54 -0
- package/src/browser/useEventListener/index.md +109 -0
- package/src/browser/useEventListener/index.spec.ts +611 -0
- package/src/browser/useEventListener/index.ts +242 -0
- package/src/browser/useMediaQuery/demo.tsx +63 -0
- package/src/browser/useMediaQuery/index.md +43 -0
- package/src/browser/useMediaQuery/index.spec.ts +267 -0
- package/src/browser/useMediaQuery/index.ts +96 -0
- package/src/components/Auto/index.tsx +65 -0
- package/src/elements/useDocumentVisibility/demo.tsx +111 -0
- package/src/elements/useDocumentVisibility/index.md +54 -0
- package/src/elements/useDocumentVisibility/index.spec.ts +114 -0
- package/src/elements/useDocumentVisibility/index.ts +26 -0
- package/src/elements/useElementBounding/demo.tsx +68 -0
- package/src/elements/useElementBounding/index.md +64 -0
- package/src/elements/useElementBounding/index.ts +159 -0
- package/src/elements/useElementSize/demo.tsx +53 -0
- package/src/elements/useElementSize/index.md +65 -0
- package/src/elements/useElementSize/index.spec.ts +295 -0
- package/src/elements/useElementSize/index.ts +100 -0
- package/src/elements/useElementVisibility/deep-observable-pattern.spec.ts +453 -0
- package/src/elements/useElementVisibility/demo.tsx +97 -0
- package/src/elements/useElementVisibility/index.md +98 -0
- package/src/elements/useElementVisibility/index.spec.ts +227 -0
- package/src/elements/useElementVisibility/index.ts +78 -0
- package/src/elements/useIntersectionObserver/demo.tsx +180 -0
- package/src/elements/useIntersectionObserver/index.md +99 -0
- package/src/elements/useIntersectionObserver/index.spec.ts +482 -0
- package/src/elements/useIntersectionObserver/index.ts +149 -0
- package/src/elements/useMouseInElement/demo.tsx +88 -0
- package/src/elements/useMouseInElement/index.md +76 -0
- package/src/elements/useMouseInElement/index.spec.ts +398 -0
- package/src/elements/useMouseInElement/index.ts +209 -0
- package/src/elements/useMutationObserver/demo.tsx +270 -0
- package/src/elements/useMutationObserver/index.md +99 -0
- package/src/elements/useMutationObserver/index.spec.ts +421 -0
- package/src/elements/useMutationObserver/index.ts +66 -0
- package/src/elements/useParentElement/demo.tsx +120 -0
- package/src/elements/useParentElement/index.md +67 -0
- package/src/elements/useParentElement/index.spec.ts +208 -0
- package/src/elements/useParentElement/index.ts +35 -0
- package/src/elements/useRef$/index.md +62 -0
- package/src/elements/useRef$/index.spec.ts +205 -0
- package/src/elements/useRef$/index.ts +137 -0
- package/src/elements/useRef$/useImperativeHandle.spec.ts +339 -0
- package/src/elements/useResizeObserver/demo.tsx +62 -0
- package/src/elements/useResizeObserver/index.md +51 -0
- package/src/elements/useResizeObserver/index.spec.ts +312 -0
- package/src/elements/useResizeObserver/index.ts +106 -0
- package/src/elements/useWindowFocus/demo.tsx +79 -0
- package/src/elements/useWindowFocus/index.md +38 -0
- package/src/elements/useWindowFocus/index.spec.ts +103 -0
- package/src/elements/useWindowFocus/index.ts +21 -0
- package/src/elements/useWindowSize/demo.tsx +51 -0
- package/src/elements/useWindowSize/index.md +55 -0
- package/src/elements/useWindowSize/index.spec.ts +310 -0
- package/src/elements/useWindowSize/index.ts +107 -0
- package/src/function/get/index.md +25 -0
- package/src/function/get/index.spec.ts +87 -0
- package/src/function/get/index.ts +70 -0
- package/src/function/peek/index.spec.ts +97 -0
- package/src/function/peek/index.ts +69 -0
- package/src/function/useMayObservableOptions/index.spec.ts +521 -0
- package/src/function/useMayObservableOptions/index.ts +173 -0
- package/src/function/useSupported/index.md +43 -0
- package/src/function/useSupported/index.spec.ts +116 -0
- package/src/function/useSupported/index.ts +14 -0
- package/src/function/useWhenMounted/index.md +25 -0
- package/src/function/useWhenMounted/index.spec.ts +120 -0
- package/src/function/useWhenMounted/index.ts +16 -0
- package/src/index.ts +25 -0
- package/src/sensors/useScroll/demo.tsx +103 -0
- package/src/sensors/useScroll/index.md +117 -0
- package/src/sensors/useScroll/index.spec.ts +678 -0
- package/src/sensors/useScroll/index.ts +201 -0
- package/src/sensors/useWindowScroll/demo.tsx +78 -0
- package/src/sensors/useWindowScroll/index.md +98 -0
- package/src/sensors/useWindowScroll/index.spec.ts +69 -0
- package/src/sensors/useWindowScroll/index.ts +11 -0
- package/src/shared/configurable.ts +35 -0
- package/src/shared/index.ts +4 -0
- package/src/shared/normalizeTargets/index.spec.ts +76 -0
- package/src/shared/normalizeTargets/index.ts +27 -0
- package/src/shared/utils.ts +67 -0
- package/src/types.ts +56 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +10 -0
- package/vitest.config.ts +22 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { render, renderHook, act } from "@testing-library/react";
|
|
3
|
+
import { useObserve } from "@legendapp/state/react";
|
|
4
|
+
import { createElement, createRef, forwardRef, useImperativeHandle, useRef } from "react";
|
|
5
|
+
import { describe, it, expect, vi } from "vitest";
|
|
6
|
+
import { useRef$, peekElement } from ".";
|
|
7
|
+
|
|
8
|
+
interface TestHandle {
|
|
9
|
+
focus: () => void;
|
|
10
|
+
getValue: () => string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* useRef$() + useImperativeHandle compatibility tests
|
|
15
|
+
*
|
|
16
|
+
* Scenarios:
|
|
17
|
+
* 1. Component exposes custom handle to parent via useImperativeHandle
|
|
18
|
+
* while using useRef$ internally for reactive DOM tracking
|
|
19
|
+
* 2. Parent ref receives the custom handle (not the raw DOM element)
|
|
20
|
+
* 3. el$ reactivity is unaffected by useImperativeHandle updates
|
|
21
|
+
* 4. Ref$ itself can serve as the ref argument in useImperativeHandle
|
|
22
|
+
* (Ref$ observes the imperative handle object)
|
|
23
|
+
* 5. Cleanup: parent ref is nulled on unmount (React default behaviour)
|
|
24
|
+
*
|
|
25
|
+
* [Parent → Child] Scenarios:
|
|
26
|
+
* 7. Parent creates Ref$ and passes it as ref to a child that uses useImperativeHandle
|
|
27
|
+
* — Ref$ stores the handle object, not a DOM element
|
|
28
|
+
* 8. Ref$ reactivity fires when the child mounts (handle assigned) and unmounts (null)
|
|
29
|
+
* 9. Ref$ passed as ref to multiple children sequentially — updates correctly each time
|
|
30
|
+
*/
|
|
31
|
+
describe("useRef$() + useImperativeHandle compatibility", () => {
|
|
32
|
+
// ─── Scenario 1 ───────────────────────────────────────────────────────────
|
|
33
|
+
it("exposes custom handle to parent while el$ tracks the DOM element internally", () => {
|
|
34
|
+
const focusSpy = vi.fn();
|
|
35
|
+
|
|
36
|
+
const Component = forwardRef<TestHandle, object>((_, ref) => {
|
|
37
|
+
const el$ = useRef$<HTMLDivElement>();
|
|
38
|
+
|
|
39
|
+
useImperativeHandle(ref, () => ({
|
|
40
|
+
focus: () => {
|
|
41
|
+
const el = peekElement(el$) as HTMLDivElement | null;
|
|
42
|
+
el?.focus?.();
|
|
43
|
+
focusSpy();
|
|
44
|
+
},
|
|
45
|
+
getValue: () => "test-value",
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
return createElement("div", { ref: el$, tabIndex: 0 });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const parentRef = createRef<TestHandle>();
|
|
52
|
+
render(createElement(Component, { ref: parentRef }));
|
|
53
|
+
|
|
54
|
+
// Parent ref has the custom handle object
|
|
55
|
+
expect(parentRef.current).not.toBeNull();
|
|
56
|
+
expect(typeof parentRef.current?.focus).toBe("function");
|
|
57
|
+
expect(typeof parentRef.current?.getValue).toBe("function");
|
|
58
|
+
expect(parentRef.current?.getValue()).toBe("test-value");
|
|
59
|
+
|
|
60
|
+
// Custom handle methods can reach the real DOM via peekElement
|
|
61
|
+
act(() => {
|
|
62
|
+
parentRef.current?.focus();
|
|
63
|
+
});
|
|
64
|
+
expect(focusSpy).toHaveBeenCalledTimes(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ─── Scenario 2 ───────────────────────────────────────────────────────────
|
|
68
|
+
it("parent ref holds the handle object — not the raw DOM element", () => {
|
|
69
|
+
let capturedRef$: ReturnType<typeof useRef$<HTMLDivElement>> | null = null;
|
|
70
|
+
|
|
71
|
+
const Component = forwardRef<TestHandle, object>((_, ref) => {
|
|
72
|
+
const el$ = useRef$<HTMLDivElement>();
|
|
73
|
+
capturedRef$ = el$;
|
|
74
|
+
|
|
75
|
+
useImperativeHandle(ref, () => ({
|
|
76
|
+
focus: () => {},
|
|
77
|
+
getValue: () => "value",
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
return createElement("div", { ref: el$ });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const parentRef = createRef<TestHandle>();
|
|
84
|
+
render(createElement(Component, { ref: parentRef }));
|
|
85
|
+
|
|
86
|
+
// Parent ref must NOT be an HTMLElement
|
|
87
|
+
expect(parentRef.current).not.toBeInstanceOf(HTMLElement);
|
|
88
|
+
expect(typeof parentRef.current?.focus).toBe("function");
|
|
89
|
+
|
|
90
|
+
// el$ must hold the actual DOM element
|
|
91
|
+
const element = peekElement(capturedRef$!);
|
|
92
|
+
expect(element).toBeInstanceOf(HTMLDivElement);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ─── Scenario 3 ───────────────────────────────────────────────────────────
|
|
96
|
+
it("el$ reactivity fires independently of useImperativeHandle dep changes", () => {
|
|
97
|
+
const observeSpy = vi.fn();
|
|
98
|
+
|
|
99
|
+
const Component = forwardRef<TestHandle, { value: string }>(({ value }, ref) => {
|
|
100
|
+
const el$ = useRef$<HTMLDivElement>();
|
|
101
|
+
|
|
102
|
+
useImperativeHandle(
|
|
103
|
+
ref,
|
|
104
|
+
() => ({
|
|
105
|
+
focus: () => {},
|
|
106
|
+
getValue: () => value,
|
|
107
|
+
}),
|
|
108
|
+
[value],
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
useObserve(() => {
|
|
112
|
+
el$.get(); // register tracking
|
|
113
|
+
observeSpy();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return createElement("div", { ref: el$ });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const parentRef = createRef<TestHandle>();
|
|
120
|
+
const { rerender } = render(
|
|
121
|
+
createElement(Component, { ref: parentRef, value: "initial" }),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// After initial render: observer fires once (null) + once (DOM assigned)
|
|
125
|
+
const callsAfterMount = observeSpy.mock.calls.length;
|
|
126
|
+
expect(callsAfterMount).toBeGreaterThanOrEqual(1);
|
|
127
|
+
|
|
128
|
+
// Trigger useImperativeHandle re-creation via dep change (value changes)
|
|
129
|
+
act(() => {
|
|
130
|
+
rerender(createElement(Component, { ref: parentRef, value: "updated" }));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// el$ observable did NOT change → observer must NOT fire again
|
|
134
|
+
expect(observeSpy.mock.calls.length).toBe(callsAfterMount);
|
|
135
|
+
|
|
136
|
+
// But the handle's getValue reflects the new value
|
|
137
|
+
expect(parentRef.current?.getValue()).toBe("updated");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ─── Scenario 4 ───────────────────────────────────────────────────────────
|
|
141
|
+
it("Ref$ can serve as the ref argument in useImperativeHandle to observe the handle", () => {
|
|
142
|
+
// Ref$ is typed as Ref<T> (callback-ref compatible), so it can be passed
|
|
143
|
+
// directly to useImperativeHandle. React will call el$(handleObject).
|
|
144
|
+
const handle$ = (() => {
|
|
145
|
+
let ref: ReturnType<typeof useRef$<any>>;
|
|
146
|
+
renderHook(() => {
|
|
147
|
+
ref = useRef$<any>();
|
|
148
|
+
});
|
|
149
|
+
return ref!;
|
|
150
|
+
})();
|
|
151
|
+
|
|
152
|
+
// Mimic what React does internally when useImperativeHandle fires:
|
|
153
|
+
// it calls the ref callback with the constructed handle object.
|
|
154
|
+
const mockHandle = { focus: vi.fn(), getValue: () => "from-handle" };
|
|
155
|
+
act(() => {
|
|
156
|
+
handle$(mockHandle as any);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Ref$ should have stored the handle via OpaqueObject wrapping
|
|
160
|
+
expect(handle$.peek()).not.toBeNull();
|
|
161
|
+
// valueOf() on the OpaqueObject returns the original object
|
|
162
|
+
expect((handle$.peek() as any).valueOf()).toBe(mockHandle);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// ─── Scenario 5 ───────────────────────────────────────────────────────────
|
|
166
|
+
it("useRef$ and useRef can coexist with useImperativeHandle on the same component", () => {
|
|
167
|
+
const observeSpy = vi.fn();
|
|
168
|
+
|
|
169
|
+
const Component = forwardRef<TestHandle, object>((_, ref) => {
|
|
170
|
+
// useRef for DOM — useRef$ wraps it for reactivity
|
|
171
|
+
const domRef = useRef<HTMLDivElement>(null);
|
|
172
|
+
const el$ = useRef$<HTMLDivElement>(domRef);
|
|
173
|
+
|
|
174
|
+
useImperativeHandle(ref, () => ({
|
|
175
|
+
focus: () => domRef.current?.focus(),
|
|
176
|
+
getValue: () => "shared-ref",
|
|
177
|
+
}));
|
|
178
|
+
|
|
179
|
+
useObserve(() => {
|
|
180
|
+
el$.get();
|
|
181
|
+
observeSpy();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return createElement("div", { ref: el$ });
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const parentRef = createRef<TestHandle>();
|
|
188
|
+
render(createElement(Component, { ref: parentRef }));
|
|
189
|
+
|
|
190
|
+
// Observer fires: initial (null) + DOM assigned
|
|
191
|
+
expect(observeSpy).toHaveBeenCalledTimes(2);
|
|
192
|
+
expect(parentRef.current?.getValue()).toBe("shared-ref");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// ─── Scenario 6 ───────────────────────────────────────────────────────────
|
|
196
|
+
it("parent ref is nulled on unmount (standard React useImperativeHandle cleanup)", () => {
|
|
197
|
+
const Component = forwardRef<TestHandle, object>((_, ref) => {
|
|
198
|
+
const el$ = useRef$<HTMLDivElement>();
|
|
199
|
+
|
|
200
|
+
useImperativeHandle(ref, () => ({
|
|
201
|
+
focus: () => {},
|
|
202
|
+
getValue: () => "value",
|
|
203
|
+
}));
|
|
204
|
+
|
|
205
|
+
return createElement("div", { ref: el$ });
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const parentRef = createRef<TestHandle>();
|
|
209
|
+
const { unmount } = render(createElement(Component, { ref: parentRef }));
|
|
210
|
+
|
|
211
|
+
expect(parentRef.current).not.toBeNull();
|
|
212
|
+
|
|
213
|
+
act(() => {
|
|
214
|
+
unmount();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
expect(parentRef.current).toBeNull();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
222
|
+
// [Parent → Child] 부모가 Ref$를 자식의 ref로 넘기는 시나리오
|
|
223
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
224
|
+
|
|
225
|
+
describe("useRef$() as parent ref passed to child with useImperativeHandle", () => {
|
|
226
|
+
// Child component used across all parent→child tests
|
|
227
|
+
const Child = forwardRef<TestHandle, { value?: string }>((props, ref) => {
|
|
228
|
+
useImperativeHandle(ref, () => ({
|
|
229
|
+
focus: vi.fn(),
|
|
230
|
+
getValue: () => props.value ?? "default",
|
|
231
|
+
}));
|
|
232
|
+
return createElement("div");
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// ─── Scenario 7 ─────────────────────────────────────────────────────────
|
|
236
|
+
it("Ref$ stores the handle object when passed as ref to a child using useImperativeHandle", () => {
|
|
237
|
+
// useRef$<any>: Ref$ is intentionally used to store a non-Element handle object
|
|
238
|
+
const { result } = renderHook(() => useRef$<any>());
|
|
239
|
+
const handle$ = result.current;
|
|
240
|
+
|
|
241
|
+
// Render child with Ref$ as ref — useImperativeHandle will call handle$(handleObj)
|
|
242
|
+
render(createElement(Child, { ref: handle$ as any }));
|
|
243
|
+
|
|
244
|
+
const raw = handle$.peek();
|
|
245
|
+
expect(raw).not.toBeNull();
|
|
246
|
+
|
|
247
|
+
// valueOf() unwraps the OpaqueObject → the actual handle object
|
|
248
|
+
const handle = (raw as any).valueOf() as TestHandle;
|
|
249
|
+
expect(typeof handle.focus).toBe("function");
|
|
250
|
+
expect(handle.getValue()).toBe("default");
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// ─── Scenario 8 ─────────────────────────────────────────────────────────
|
|
254
|
+
it("Ref$ reactivity fires on child mount (handle assigned) and child unmount (null)", () => {
|
|
255
|
+
const observeSpy = vi.fn();
|
|
256
|
+
|
|
257
|
+
const { result } = renderHook(() => {
|
|
258
|
+
const handle$ = useRef$<any>();
|
|
259
|
+
useObserve(() => {
|
|
260
|
+
handle$.get();
|
|
261
|
+
observeSpy();
|
|
262
|
+
});
|
|
263
|
+
return handle$;
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Initial observer run (handle = null)
|
|
267
|
+
expect(observeSpy).toHaveBeenCalledTimes(1);
|
|
268
|
+
|
|
269
|
+
// Mount child → useImperativeHandle assigns handle to Ref$
|
|
270
|
+
const { unmount } = render(
|
|
271
|
+
createElement(Child, { ref: result.current as any }),
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
// Observer must fire again (handle assigned)
|
|
275
|
+
expect(observeSpy).toHaveBeenCalledTimes(2);
|
|
276
|
+
|
|
277
|
+
// Unmount child → React calls ref(null) → Ref$ resets to null
|
|
278
|
+
act(() => {
|
|
279
|
+
unmount();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Observer must fire again (handle cleared)
|
|
283
|
+
expect(observeSpy).toHaveBeenCalledTimes(3);
|
|
284
|
+
expect(result.current.peek()).toBeNull();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// ─── Scenario 9 ─────────────────────────────────────────────────────────
|
|
288
|
+
it("Ref$ updates correctly when the child re-renders with new useImperativeHandle deps", () => {
|
|
289
|
+
const { result } = renderHook(() => useRef$<any>());
|
|
290
|
+
const handle$ = result.current;
|
|
291
|
+
|
|
292
|
+
const { rerender } = render(
|
|
293
|
+
createElement(Child, { ref: handle$ as any, value: "v1" }),
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
expect((handle$.peek() as any).valueOf().getValue()).toBe("v1");
|
|
297
|
+
|
|
298
|
+
// Re-render with new value → useImperativeHandle recreates the handle
|
|
299
|
+
act(() => {
|
|
300
|
+
rerender(createElement(Child, { ref: handle$ as any, value: "v2" }));
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
expect((handle$.peek() as any).valueOf().getValue()).toBe("v2");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// ─── Scenario 10 ────────────────────────────────────────────────────────
|
|
307
|
+
it("Ref$ switches between two different children sequentially", () => {
|
|
308
|
+
const ChildA = forwardRef<{ id: string }, object>((_, ref) => {
|
|
309
|
+
useImperativeHandle(ref, () => ({ id: "A" }));
|
|
310
|
+
return createElement("div");
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const ChildB = forwardRef<{ id: string }, object>((_, ref) => {
|
|
314
|
+
useImperativeHandle(ref, () => ({ id: "B" }));
|
|
315
|
+
return createElement("div");
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
const { result } = renderHook(() => useRef$<any>());
|
|
319
|
+
const handle$ = result.current;
|
|
320
|
+
|
|
321
|
+
// Mount ChildA
|
|
322
|
+
const { unmount: unmountA } = render(
|
|
323
|
+
createElement(ChildA, { ref: handle$ as any }),
|
|
324
|
+
);
|
|
325
|
+
expect((handle$.peek() as any).valueOf().id).toBe("A");
|
|
326
|
+
|
|
327
|
+
act(() => unmountA());
|
|
328
|
+
expect(handle$.peek()).toBeNull();
|
|
329
|
+
|
|
330
|
+
// Mount ChildB
|
|
331
|
+
const { unmount: unmountB } = render(
|
|
332
|
+
createElement(ChildB, { ref: handle$ as any }),
|
|
333
|
+
);
|
|
334
|
+
expect((handle$.peek() as any).valueOf().id).toBe("B");
|
|
335
|
+
|
|
336
|
+
act(() => unmountB());
|
|
337
|
+
expect(handle$.peek()).toBeNull();
|
|
338
|
+
});
|
|
339
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useRef$ } from "../useRef$";
|
|
2
|
+
import { useResizeObserver } from ".";
|
|
3
|
+
import { Computed, useObservable } from "@legendapp/state/react";
|
|
4
|
+
|
|
5
|
+
export default function UseResizeObserverDemo() {
|
|
6
|
+
const el$ = useRef$<HTMLTextAreaElement>();
|
|
7
|
+
const size$ = useObservable({ width: 0, height: 0 });
|
|
8
|
+
|
|
9
|
+
useResizeObserver(el$, (entries) => {
|
|
10
|
+
const { width, height } = entries[0].contentRect;
|
|
11
|
+
|
|
12
|
+
size$.assign({
|
|
13
|
+
width: Math.round(width),
|
|
14
|
+
height: Math.round(height),
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
|
20
|
+
<div
|
|
21
|
+
style={{
|
|
22
|
+
display: "flex",
|
|
23
|
+
gap: "24px",
|
|
24
|
+
fontFamily: "monospace",
|
|
25
|
+
fontSize: "14px",
|
|
26
|
+
padding: "8px 12px",
|
|
27
|
+
background: "var(--sl-color-gray-6, #f1f5f9)",
|
|
28
|
+
borderRadius: "6px",
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
<Computed>
|
|
32
|
+
{() => (
|
|
33
|
+
<>
|
|
34
|
+
<span>
|
|
35
|
+
width: <strong>{size$.width.get()}px</strong>
|
|
36
|
+
</span>
|
|
37
|
+
<span>
|
|
38
|
+
height: <strong>{size$.height.get()}px</strong>
|
|
39
|
+
</span>
|
|
40
|
+
</>
|
|
41
|
+
)}
|
|
42
|
+
</Computed>
|
|
43
|
+
</div>
|
|
44
|
+
<textarea
|
|
45
|
+
ref={el$}
|
|
46
|
+
defaultValue="resize this textarea"
|
|
47
|
+
style={{
|
|
48
|
+
resize: "both",
|
|
49
|
+
overflow: "auto",
|
|
50
|
+
width: "300px",
|
|
51
|
+
height: "120px",
|
|
52
|
+
padding: "10px",
|
|
53
|
+
border: "1px solid var(--sl-color-gray-5, #cbd5e1)",
|
|
54
|
+
borderRadius: "6px",
|
|
55
|
+
fontFamily: "inherit",
|
|
56
|
+
fontSize: "14px",
|
|
57
|
+
lineHeight: "1.5",
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: useResizeObserver
|
|
3
|
+
category: elements
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Observes one or more elements for size changes using the [ResizeObserver API](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).
|
|
7
|
+
Targets can be `Ref$`, `Observable<Element|null>`, or a plain `Element`.
|
|
8
|
+
|
|
9
|
+
## Demo
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { useCallback } from 'react'
|
|
15
|
+
import { useRef$, useResizeObserver } from '@usels/core'
|
|
16
|
+
|
|
17
|
+
function Component() {
|
|
18
|
+
const el$ = useRef$<HTMLDivElement>()
|
|
19
|
+
|
|
20
|
+
const handleResize = useCallback<ResizeObserverCallback>((entries) => {
|
|
21
|
+
const { width, height } = entries[0].contentRect
|
|
22
|
+
console.log(width, height)
|
|
23
|
+
}, [])
|
|
24
|
+
|
|
25
|
+
useResizeObserver(el$, handleResize)
|
|
26
|
+
|
|
27
|
+
return <div ref={el$} />
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### With `border-box`
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
useResizeObserver(el$, handleResize, { box: 'border-box' })
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Stopping observation manually
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
const { stop } = useResizeObserver(el$, handleResize)
|
|
41
|
+
|
|
42
|
+
stop()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Checking browser support
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
const { isSupported } = useResizeObserver(el$, handleResize)
|
|
49
|
+
|
|
50
|
+
console.log(isSupported.get()) // Observable<boolean>
|
|
51
|
+
```
|