@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,51 @@
|
|
|
1
|
+
import { useWindowSize } from ".";
|
|
2
|
+
import { Computed } from "@legendapp/state/react";
|
|
3
|
+
|
|
4
|
+
const row: React.CSSProperties = {
|
|
5
|
+
display: "flex",
|
|
6
|
+
alignItems: "center",
|
|
7
|
+
padding: "8px 14px",
|
|
8
|
+
borderRadius: "6px",
|
|
9
|
+
border: "1px solid var(--sl-color-gray-5, #e2e8f0)",
|
|
10
|
+
|
|
11
|
+
background: "var(--sl-color-gray-6, #f1f5f9)",
|
|
12
|
+
// borderRadius: "6px",
|
|
13
|
+
gap: "24px",
|
|
14
|
+
color: "white",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default function UseWindowSizeDemo() {
|
|
18
|
+
const { width: width$, height: height$ } = useWindowSize();
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
style={{
|
|
23
|
+
display: "flex",
|
|
24
|
+
flexDirection: "column",
|
|
25
|
+
gap: "8px",
|
|
26
|
+
fontFamily: "monospace",
|
|
27
|
+
fontSize: "14px",
|
|
28
|
+
background: "var(--sl-color-gray-6, #f1f5f9)",
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
<Computed>
|
|
32
|
+
{() => (
|
|
33
|
+
<div style={row}>
|
|
34
|
+
<span>width: {width$.get()}px</span>
|
|
35
|
+
<span>height: {height$.get()}px</span>
|
|
36
|
+
</div>
|
|
37
|
+
)}
|
|
38
|
+
</Computed>
|
|
39
|
+
|
|
40
|
+
<p
|
|
41
|
+
style={{
|
|
42
|
+
margin: 5,
|
|
43
|
+
fontSize: "11px",
|
|
44
|
+
color: "var(--sl-color-gray-3, #94a3b8)",
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
Resize the browser window to see the values update.
|
|
48
|
+
</p>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: useWindowSize
|
|
3
|
+
category: elements
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Tracks the browser window dimensions as reactive `Observable<number>` values for width and height.
|
|
7
|
+
Supports `inner`, `outer`, and `visual` viewport modes, and updates on resize and orientation change.
|
|
8
|
+
SSR-safe: returns `initialWidth`/`initialHeight` (default `0`) when `window` is not available.
|
|
9
|
+
|
|
10
|
+
## Demo
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { useWindowSize } from '@usels/core'
|
|
16
|
+
import { Computed } from '@legendapp/state/react'
|
|
17
|
+
|
|
18
|
+
function Component() {
|
|
19
|
+
const size$ = useWindowSize()
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Computed>
|
|
23
|
+
{() => (
|
|
24
|
+
<p>
|
|
25
|
+
{size$.width.get()} × {size$.height.get()}
|
|
26
|
+
</p>
|
|
27
|
+
)}
|
|
28
|
+
</Computed>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Excluding scrollbar width
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
const size$ = useWindowSize({ includeScrollbar: false })
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Outer window size
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
const size$ = useWindowSize({ type: 'outer' })
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Visual viewport (pinch-zoom aware)
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
const size$ = useWindowSize({ type: 'visual' })
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Custom initial size for SSR
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
const size$ = useWindowSize({ initialWidth: 1280, initialHeight: 800 })
|
|
55
|
+
```
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { renderHook, act } from "@testing-library/react";
|
|
3
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
4
|
+
import { observable } from "@legendapp/state";
|
|
5
|
+
import { useWindowSize } from ".";
|
|
6
|
+
|
|
7
|
+
const flush = () => new Promise<void>((resolve) => queueMicrotask(resolve));
|
|
8
|
+
|
|
9
|
+
function mockMatchMedia(
|
|
10
|
+
matches = false,
|
|
11
|
+
): (query: string) => MediaQueryList {
|
|
12
|
+
return (_query: string) =>
|
|
13
|
+
({
|
|
14
|
+
matches,
|
|
15
|
+
media: _query,
|
|
16
|
+
addEventListener: vi.fn(),
|
|
17
|
+
removeEventListener: vi.fn(),
|
|
18
|
+
dispatchEvent: vi.fn(),
|
|
19
|
+
}) as unknown as MediaQueryList;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("useWindowSize()", () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.stubGlobal("matchMedia", mockMatchMedia());
|
|
25
|
+
|
|
26
|
+
Object.defineProperty(window, "innerWidth", {
|
|
27
|
+
writable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
value: 1024,
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(window, "innerHeight", {
|
|
32
|
+
writable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
value: 768,
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(window, "outerWidth", {
|
|
37
|
+
writable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
value: 1024,
|
|
40
|
+
});
|
|
41
|
+
Object.defineProperty(window, "outerHeight", {
|
|
42
|
+
writable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
value: 768,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
vi.unstubAllGlobals();
|
|
50
|
+
vi.restoreAllMocks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("returns width and height Observables", () => {
|
|
54
|
+
const { result } = renderHook(() => useWindowSize());
|
|
55
|
+
expect(typeof result.current.width.get).toBe("function");
|
|
56
|
+
expect(typeof result.current.height.get).toBe("function");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("reads innerWidth/innerHeight by default", () => {
|
|
60
|
+
const { result } = renderHook(() => useWindowSize());
|
|
61
|
+
expect(result.current.width.get()).toBe(1024);
|
|
62
|
+
expect(result.current.height.get()).toBe(768);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("uses initialWidth/initialHeight before mount when window is undefined", () => {
|
|
66
|
+
// Simulate SSR by checking initial values before any side effects run
|
|
67
|
+
const { result } = renderHook(() =>
|
|
68
|
+
useWindowSize({ initialWidth: 320, initialHeight: 480 }),
|
|
69
|
+
);
|
|
70
|
+
// After mount update fires, it reads actual window values
|
|
71
|
+
// but the observable was initialized with the provided defaults
|
|
72
|
+
expect(result.current.width.get()).toBeDefined();
|
|
73
|
+
expect(result.current.height.get()).toBeDefined();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("updates on window resize event", () => {
|
|
77
|
+
const { result } = renderHook(() => useWindowSize());
|
|
78
|
+
|
|
79
|
+
act(() => {
|
|
80
|
+
(window as any).innerWidth = 1280;
|
|
81
|
+
(window as any).innerHeight = 900;
|
|
82
|
+
window.dispatchEvent(new Event("resize"));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(result.current.width.get()).toBe(1280);
|
|
86
|
+
expect(result.current.height.get()).toBe(900);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("type: 'outer' reads outerWidth/outerHeight", () => {
|
|
90
|
+
(window as any).outerWidth = 1100;
|
|
91
|
+
(window as any).outerHeight = 850;
|
|
92
|
+
|
|
93
|
+
const { result } = renderHook(() => useWindowSize({ type: "outer" }));
|
|
94
|
+
expect(result.current.width.get()).toBe(1100);
|
|
95
|
+
expect(result.current.height.get()).toBe(850);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("type: 'outer' updates on resize", () => {
|
|
99
|
+
const { result } = renderHook(() => useWindowSize({ type: "outer" }));
|
|
100
|
+
|
|
101
|
+
act(() => {
|
|
102
|
+
(window as any).outerWidth = 900;
|
|
103
|
+
(window as any).outerHeight = 600;
|
|
104
|
+
window.dispatchEvent(new Event("resize"));
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(result.current.width.get()).toBe(900);
|
|
108
|
+
expect(result.current.height.get()).toBe(600);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("includeScrollbar: false reads clientWidth/clientHeight", () => {
|
|
112
|
+
Object.defineProperty(document.documentElement, "clientWidth", {
|
|
113
|
+
writable: true,
|
|
114
|
+
configurable: true,
|
|
115
|
+
value: 1000,
|
|
116
|
+
});
|
|
117
|
+
Object.defineProperty(document.documentElement, "clientHeight", {
|
|
118
|
+
writable: true,
|
|
119
|
+
configurable: true,
|
|
120
|
+
value: 740,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const { result } = renderHook(() =>
|
|
124
|
+
useWindowSize({ includeScrollbar: false }),
|
|
125
|
+
);
|
|
126
|
+
expect(result.current.width.get()).toBe(1000);
|
|
127
|
+
expect(result.current.height.get()).toBe(740);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("type: 'visual' reads visualViewport when available", () => {
|
|
131
|
+
const mockVP = {
|
|
132
|
+
width: 375,
|
|
133
|
+
height: 667,
|
|
134
|
+
scale: 2,
|
|
135
|
+
addEventListener: vi.fn(),
|
|
136
|
+
removeEventListener: vi.fn(),
|
|
137
|
+
};
|
|
138
|
+
vi.stubGlobal("visualViewport", mockVP);
|
|
139
|
+
|
|
140
|
+
const { result } = renderHook(() => useWindowSize({ type: "visual" }));
|
|
141
|
+
expect(result.current.width.get()).toBe(750); // 375 * 2
|
|
142
|
+
expect(result.current.height.get()).toBe(1334); // 667 * 2
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("type: 'visual' falls back to innerWidth/innerHeight when visualViewport unavailable", () => {
|
|
146
|
+
vi.stubGlobal("visualViewport", null);
|
|
147
|
+
|
|
148
|
+
const { result } = renderHook(() => useWindowSize({ type: "visual" }));
|
|
149
|
+
expect(result.current.width.get()).toBe(1024);
|
|
150
|
+
expect(result.current.height.get()).toBe(768);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("listens to orientation changes by default", () => {
|
|
154
|
+
const addSpy = vi.spyOn(window, "matchMedia").mockReturnValue({
|
|
155
|
+
addEventListener: vi.fn(),
|
|
156
|
+
removeEventListener: vi.fn(),
|
|
157
|
+
matches: false,
|
|
158
|
+
media: "(orientation: portrait)",
|
|
159
|
+
} as any);
|
|
160
|
+
|
|
161
|
+
renderHook(() => useWindowSize());
|
|
162
|
+
expect(addSpy).toHaveBeenCalledWith("(orientation: portrait)");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("listenOrientation: false does not update size on orientation change", () => {
|
|
166
|
+
let orientationListener: ((e: Event) => void) | null = null;
|
|
167
|
+
|
|
168
|
+
vi.stubGlobal("matchMedia", (_query: string) => ({
|
|
169
|
+
matches: false,
|
|
170
|
+
media: _query,
|
|
171
|
+
addEventListener: vi.fn((type: string, listener: EventListener) => {
|
|
172
|
+
if (type === "change") orientationListener = listener;
|
|
173
|
+
}),
|
|
174
|
+
removeEventListener: vi.fn(),
|
|
175
|
+
dispatchEvent: vi.fn(),
|
|
176
|
+
} as unknown as MediaQueryList));
|
|
177
|
+
|
|
178
|
+
const { result } = renderHook(() =>
|
|
179
|
+
useWindowSize({ listenOrientation: false }),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Change innerWidth but do NOT dispatch a resize event
|
|
183
|
+
(window as any).innerWidth = 500;
|
|
184
|
+
|
|
185
|
+
act(() => {
|
|
186
|
+
// Trigger orientation change — should NOT call update() when listenOrientation: false
|
|
187
|
+
orientationListener?.({
|
|
188
|
+
type: "change",
|
|
189
|
+
matches: true,
|
|
190
|
+
} as unknown as MediaQueryListEvent);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Size stays at 1024 because orientation change was ignored
|
|
194
|
+
expect(result.current.width.get()).toBe(1024);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("removes resize listener on unmount", async () => {
|
|
198
|
+
const addSpy = vi.spyOn(window, "addEventListener");
|
|
199
|
+
const removeSpy = vi.spyOn(window, "removeEventListener");
|
|
200
|
+
|
|
201
|
+
const { unmount } = renderHook(() => useWindowSize());
|
|
202
|
+
unmount();
|
|
203
|
+
await flush();
|
|
204
|
+
|
|
205
|
+
const resizeAdded = addSpy.mock.calls.some(([type]) => type === "resize");
|
|
206
|
+
const resizeRemoved = removeSpy.mock.calls.some(
|
|
207
|
+
([type]) => type === "resize",
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
expect(resizeAdded).toBe(true);
|
|
211
|
+
expect(resizeRemoved).toBe(true);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("type change via re-render triggers immediate re-measurement without resize event", async () => {
|
|
215
|
+
(window as any).outerWidth = 1100;
|
|
216
|
+
(window as any).outerHeight = 850;
|
|
217
|
+
|
|
218
|
+
const { result, rerender } = renderHook(
|
|
219
|
+
(props: { type: "inner" | "outer" | "visual" }) =>
|
|
220
|
+
useWindowSize({ type: props.type }),
|
|
221
|
+
{ initialProps: { type: "inner" as "inner" | "outer" | "visual" } },
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
expect(result.current.width.get()).toBe(1024);
|
|
225
|
+
expect(result.current.height.get()).toBe(768);
|
|
226
|
+
|
|
227
|
+
await act(async () => {
|
|
228
|
+
rerender({ type: "outer" });
|
|
229
|
+
await flush();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(result.current.width.get()).toBe(1100);
|
|
233
|
+
expect(result.current.height.get()).toBe(850);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("type as Observable → reactive re-measurement when Observable value changes", async () => {
|
|
237
|
+
(window as any).outerWidth = 1100;
|
|
238
|
+
(window as any).outerHeight = 850;
|
|
239
|
+
|
|
240
|
+
const type$ = observable<"inner" | "outer" | "visual">("inner");
|
|
241
|
+
|
|
242
|
+
const { result } = renderHook(() => useWindowSize({ type: type$ }));
|
|
243
|
+
|
|
244
|
+
// Initial: 'inner' → reads innerWidth/innerHeight
|
|
245
|
+
expect(result.current.width.get()).toBe(1024);
|
|
246
|
+
expect(result.current.height.get()).toBe(768);
|
|
247
|
+
|
|
248
|
+
// Change Observable value — useObserveEffect tracks opts$.type.get() → re-measurement
|
|
249
|
+
await act(async () => {
|
|
250
|
+
type$.set("outer");
|
|
251
|
+
await flush();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
expect(result.current.width.get()).toBe(1100);
|
|
255
|
+
expect(result.current.height.get()).toBe(850);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("includeScrollbar change via re-render triggers immediate re-measurement without resize event", async () => {
|
|
259
|
+
Object.defineProperty(document.documentElement, "clientWidth", {
|
|
260
|
+
writable: true,
|
|
261
|
+
configurable: true,
|
|
262
|
+
value: 1000,
|
|
263
|
+
});
|
|
264
|
+
Object.defineProperty(document.documentElement, "clientHeight", {
|
|
265
|
+
writable: true,
|
|
266
|
+
configurable: true,
|
|
267
|
+
value: 740,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const { result, rerender } = renderHook(
|
|
271
|
+
(props: { includeScrollbar: boolean }) =>
|
|
272
|
+
useWindowSize({ includeScrollbar: props.includeScrollbar }),
|
|
273
|
+
{ initialProps: { includeScrollbar: true } },
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
expect(result.current.width.get()).toBe(1024);
|
|
277
|
+
expect(result.current.height.get()).toBe(768);
|
|
278
|
+
|
|
279
|
+
await act(async () => {
|
|
280
|
+
rerender({ includeScrollbar: false });
|
|
281
|
+
await flush();
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect(result.current.width.get()).toBe(1000);
|
|
285
|
+
expect(result.current.height.get()).toBe(740);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("does not update after unmount", async () => {
|
|
289
|
+
const { result, unmount } = renderHook(() => useWindowSize());
|
|
290
|
+
|
|
291
|
+
unmount();
|
|
292
|
+
await flush();
|
|
293
|
+
|
|
294
|
+
act(() => {
|
|
295
|
+
(window as any).innerWidth = 999;
|
|
296
|
+
window.dispatchEvent(new Event("resize"));
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// Value stays at what it was before unmount (1024)
|
|
300
|
+
expect(result.current.width.get()).toBe(1024);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("defaults to 0 initial size", () => {
|
|
304
|
+
// Verify the observable initializes with 0 before window is read
|
|
305
|
+
const { result } = renderHook(() => useWindowSize());
|
|
306
|
+
// After mount, actual window values are read
|
|
307
|
+
expect(typeof result.current.width.get()).toBe("number");
|
|
308
|
+
expect(typeof result.current.height.get()).toBe("number");
|
|
309
|
+
});
|
|
310
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import type { Observable } from "@legendapp/state";
|
|
3
|
+
import {
|
|
4
|
+
useObservable,
|
|
5
|
+
useMount,
|
|
6
|
+
useObserveEffect,
|
|
7
|
+
} from "@legendapp/state/react";
|
|
8
|
+
import { useEventListener } from "../../browser/useEventListener";
|
|
9
|
+
import { useMediaQuery } from "../../browser/useMediaQuery";
|
|
10
|
+
import { useMayObservableOptions } from "../../function/useMayObservableOptions";
|
|
11
|
+
import { useWhenMounted } from "../../function/useWhenMounted";
|
|
12
|
+
import type { DeepMaybeObservable } from "../../types";
|
|
13
|
+
|
|
14
|
+
export interface UseWindowSizeOptions {
|
|
15
|
+
initialWidth?: number;
|
|
16
|
+
initialHeight?: number;
|
|
17
|
+
listenOrientation?: boolean;
|
|
18
|
+
includeScrollbar?: boolean;
|
|
19
|
+
type?: "inner" | "outer" | "visual";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type UseWindowSizeReturn = Observable<{
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
}>;
|
|
26
|
+
|
|
27
|
+
/*@__NO_SIDE_EFFECTS__*/
|
|
28
|
+
export function useWindowSize(
|
|
29
|
+
options?: DeepMaybeObservable<UseWindowSizeOptions>,
|
|
30
|
+
): UseWindowSizeReturn {
|
|
31
|
+
// Standard Pattern: normalize DeepMaybeObservable<Options> into a stable computed Observable.
|
|
32
|
+
const opts$ = useMayObservableOptions<UseWindowSizeOptions>(options, {
|
|
33
|
+
initialWidth: "peek",
|
|
34
|
+
initialHeight: "peek",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const size$ = useObservable({
|
|
38
|
+
width: opts$.initialWidth.peek() ?? 0,
|
|
39
|
+
height: opts$.initialHeight.peek() ?? 0,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const update = () => {
|
|
43
|
+
if (typeof window === "undefined") return;
|
|
44
|
+
|
|
45
|
+
const type = opts$.type.peek() ?? "inner";
|
|
46
|
+
const includeScrollbar = opts$.includeScrollbar.peek() !== false;
|
|
47
|
+
|
|
48
|
+
let width: number;
|
|
49
|
+
let height: number;
|
|
50
|
+
|
|
51
|
+
if (type === "outer") {
|
|
52
|
+
width = window.outerWidth;
|
|
53
|
+
height = window.outerHeight;
|
|
54
|
+
} else if (type === "visual") {
|
|
55
|
+
const vp = window.visualViewport;
|
|
56
|
+
if (vp) {
|
|
57
|
+
width = vp.width * vp.scale;
|
|
58
|
+
height = vp.height * vp.scale;
|
|
59
|
+
} else {
|
|
60
|
+
width = window.innerWidth;
|
|
61
|
+
height = window.innerHeight;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
if (includeScrollbar) {
|
|
65
|
+
width = window.innerWidth;
|
|
66
|
+
height = window.innerHeight;
|
|
67
|
+
} else {
|
|
68
|
+
width = document.documentElement.clientWidth;
|
|
69
|
+
height = document.documentElement.clientHeight;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
size$.assign({ width, height });
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
useMount(update);
|
|
77
|
+
|
|
78
|
+
useEventListener("resize", update, { passive: true });
|
|
79
|
+
|
|
80
|
+
const vp$ = useWhenMounted(() =>
|
|
81
|
+
opts$.type.peek() === "visual" ? window.visualViewport : null,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
useEventListener(vp$, "resize", update);
|
|
85
|
+
|
|
86
|
+
// type 또는 includeScrollbar가 변경되면 즉시 재측정
|
|
87
|
+
// 단일 함수 형태: opts$.type.get()/opts$.includeScrollbar.get()으로 dep 등록,
|
|
88
|
+
// update()는 .peek()만 사용하므로 추가 dep을 등록하지 않음.
|
|
89
|
+
useObserveEffect((e) => {
|
|
90
|
+
opts$.type.get();
|
|
91
|
+
opts$.includeScrollbar.get();
|
|
92
|
+
if (e.num > 0) update();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const matches$ = useMediaQuery("(orientation: portrait)");
|
|
96
|
+
useObserveEffect(
|
|
97
|
+
matches$,
|
|
98
|
+
() => {
|
|
99
|
+
if (opts$.listenOrientation.get() !== false) {
|
|
100
|
+
update();
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{ immediate: false },
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
return size$;
|
|
107
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: get
|
|
3
|
+
description: Extract values from MaybeObservable types
|
|
4
|
+
category: Observable Utilities
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Extract raw values from `MaybeObservable` types (works with both raw values and Legend-State observables).
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { get } from '@usels/core'
|
|
14
|
+
import { observable } from '@legendapp/state'
|
|
15
|
+
|
|
16
|
+
// With raw values
|
|
17
|
+
const rawValue = { name: 'John', age: 30 }
|
|
18
|
+
console.log(get(rawValue)) // { name: 'John', age: 30 }
|
|
19
|
+
console.log(get(rawValue, 'name')) // 'John'
|
|
20
|
+
|
|
21
|
+
// With observables
|
|
22
|
+
const obs$ = observable({ name: 'John', age: 30 })
|
|
23
|
+
console.log(get(obs$)) // { name: 'John', age: 30 }
|
|
24
|
+
console.log(get(obs$, 'name')) // 'John'
|
|
25
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { get } from ".";
|
|
3
|
+
import { observable } from "@legendapp/state";
|
|
4
|
+
describe("get() - single argument", () => {
|
|
5
|
+
it("returns raw value as-is", () => {
|
|
6
|
+
expect(get("hello")).toBe("hello");
|
|
7
|
+
expect(get(42)).toBe(42);
|
|
8
|
+
expect(get(true)).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("extracts value from Observable", () => {
|
|
12
|
+
const obs$ = observable("world");
|
|
13
|
+
expect(get(obs$)).toBe("world");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("handles null and undefined", () => {
|
|
17
|
+
expect(get(null)).toBe(null);
|
|
18
|
+
expect(get(undefined)).toBe(undefined);
|
|
19
|
+
expect(get(observable(null))).toBe(null);
|
|
20
|
+
expect(get(observable(undefined))).toBe(undefined);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("handles objects", () => {
|
|
24
|
+
const obj = { name: "John" };
|
|
25
|
+
expect(get(obj)).toEqual(obj);
|
|
26
|
+
expect(get(observable(obj))).toEqual(obj);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("handles arrays", () => {
|
|
30
|
+
const arr = [1, 2, 3];
|
|
31
|
+
expect(get(arr)).toEqual(arr);
|
|
32
|
+
expect(get(observable(arr))).toEqual(arr);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("get() - two arguments (property access)", () => {
|
|
37
|
+
it("extracts property from raw object", () => {
|
|
38
|
+
const obj = { name: "John", age: 30 };
|
|
39
|
+
expect(get(obj, "name")).toBe("John");
|
|
40
|
+
expect(get(obj, "age")).toBe(30);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("extracts property from Observable object", () => {
|
|
44
|
+
const obs$ = observable({ name: "Jane", age: 25 });
|
|
45
|
+
expect(get(obs$, "name")).toBe("Jane");
|
|
46
|
+
expect(get(obs$, "age")).toBe(25);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("returns undefined for missing keys", () => {
|
|
50
|
+
const obj = { name: "John" };
|
|
51
|
+
expect(get(obj, "age" as any)).toBe(undefined);
|
|
52
|
+
expect(get(observable(obj), "age" as any)).toBe(undefined);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("handles nested observable properties", () => {
|
|
56
|
+
const filter$ = observable({ category: "electronics" });
|
|
57
|
+
expect(get(filter$, "category")).toBe("electronics");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("returns undefined when value is not an object", () => {
|
|
61
|
+
expect(get("hello", "length" as any)).toBe(undefined);
|
|
62
|
+
expect(get(observable("hello"), "length" as any)).toBe(undefined);
|
|
63
|
+
expect(get(42, "toString" as any)).toBe(undefined);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("handles null and undefined gracefully", () => {
|
|
67
|
+
expect(get(null, "key" as any)).toBe(undefined);
|
|
68
|
+
expect(get(undefined, "key" as any)).toBe(undefined);
|
|
69
|
+
expect(get(observable(null), "key" as any)).toBe(undefined);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("preserves property value types", () => {
|
|
73
|
+
const obj = {
|
|
74
|
+
str: "text",
|
|
75
|
+
num: 42,
|
|
76
|
+
bool: true,
|
|
77
|
+
arr: [1, 2, 3],
|
|
78
|
+
nested: { value: "deep" },
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
expect(get(obj, "str")).toBe("text");
|
|
82
|
+
expect(get(obj, "num")).toBe(42);
|
|
83
|
+
expect(get(obj, "bool")).toBe(true);
|
|
84
|
+
expect(get(obj, "arr")).toEqual([1, 2, 3]);
|
|
85
|
+
expect(get(obj, "nested")).toEqual({ value: "deep" });
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { isObservable } from "@legendapp/state";
|
|
2
|
+
import { MaybeObservable } from "../../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extracts the value from a MaybeObservable
|
|
6
|
+
* If the value is an Observable, calls .get() to extract it
|
|
7
|
+
* Otherwise returns the value as-is
|
|
8
|
+
*
|
|
9
|
+
* @param maybeObservable - A value that might be an Observable
|
|
10
|
+
* @returns The extracted value
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { observable } from '@legendapp/state'
|
|
15
|
+
* import { get } from '@usels/core'
|
|
16
|
+
*
|
|
17
|
+
* const value = get('hello') // 'hello'
|
|
18
|
+
* const obsValue = get(observable(42)) // 42
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function get<T>(maybeObservable: MaybeObservable<T>): T;
|
|
22
|
+
export function get<T>(maybeObservable: MaybeObservable<T> | undefined): T | undefined;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Extracts a property value from a MaybeObservable object
|
|
26
|
+
*
|
|
27
|
+
* @param maybeObservable - A value that might be an Observable
|
|
28
|
+
* @param key - The property key to extract
|
|
29
|
+
* @returns The property value, or undefined if not found
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { observable } from '@legendapp/state'
|
|
34
|
+
* import { get } from '@usels/core'
|
|
35
|
+
*
|
|
36
|
+
* const obj = { name: 'John' }
|
|
37
|
+
* const obs$ = observable({ name: 'Jane' })
|
|
38
|
+
*
|
|
39
|
+
* get(obj, 'name') // 'John'
|
|
40
|
+
* get(obs$, 'name') // 'Jane'
|
|
41
|
+
* get(obs$, 'age') // undefined
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function get<T, K extends keyof T>(
|
|
45
|
+
maybeObservable: MaybeObservable<T>,
|
|
46
|
+
key: K,
|
|
47
|
+
): T[K] | undefined;
|
|
48
|
+
|
|
49
|
+
// Implementation
|
|
50
|
+
export function get<T>(
|
|
51
|
+
maybeObservable: MaybeObservable<T>,
|
|
52
|
+
key?: keyof T,
|
|
53
|
+
): any {
|
|
54
|
+
// Extract the base value
|
|
55
|
+
const value = isObservable(maybeObservable)
|
|
56
|
+
? maybeObservable.get()
|
|
57
|
+
: maybeObservable;
|
|
58
|
+
|
|
59
|
+
// If no key provided, return the value (single-arg overload)
|
|
60
|
+
if (key === undefined) {
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// If key provided, extract property (two-arg overload)
|
|
65
|
+
if (value !== null && value !== undefined && typeof value === "object") {
|
|
66
|
+
return (value as any)[key];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|