@jbpark/use-hooks 3.0.0 → 4.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.ko.md +40 -22
- package/README.md +40 -22
- package/dist/hooks/index.d.mts +31 -30
- package/dist/hooks/index.mjs +3 -4
- package/dist/hooks/use-body-scroll-lock.d.mts +1 -1
- package/dist/hooks/use-body-scroll-lock.mjs +1 -2
- package/dist/hooks/use-body-scroll-lock.mjs.map +1 -1
- package/dist/hooks/use-click-outside.d.mts +1 -2
- package/dist/hooks/use-click-outside.mjs +4 -4
- package/dist/hooks/use-click-outside.mjs.map +1 -1
- package/dist/hooks/use-controllable-state.d.mts +1 -1
- package/dist/hooks/use-controllable-state.mjs +1 -2
- package/dist/hooks/use-controllable-state.mjs.map +1 -1
- package/dist/hooks/use-debounced-callback.d.mts +10 -0
- package/dist/hooks/{use-debounce.mjs → use-debounced-callback.mjs} +13 -13
- package/dist/hooks/use-debounced-callback.mjs.map +1 -0
- package/dist/hooks/use-debounced-value.d.mts +1 -1
- package/dist/hooks/use-debounced-value.mjs +1 -2
- package/dist/hooks/use-debounced-value.mjs.map +1 -1
- package/dist/hooks/use-element-position.d.mts +1 -2
- package/dist/hooks/use-element-position.mjs +1 -2
- package/dist/hooks/use-element-position.mjs.map +1 -1
- package/dist/hooks/use-element-scroll.d.mts +2 -4
- package/dist/hooks/use-element-scroll.mjs +3 -3
- package/dist/hooks/use-element-scroll.mjs.map +1 -1
- package/dist/hooks/use-event-listener.d.mts +1 -2
- package/dist/hooks/use-event-listener.mjs +22 -10
- package/dist/hooks/use-event-listener.mjs.map +1 -1
- package/dist/hooks/use-file-drop.d.mts +1 -1
- package/dist/hooks/use-file-drop.mjs +7 -6
- package/dist/hooks/use-file-drop.mjs.map +1 -1
- package/dist/hooks/use-file-to-data-url.d.mts +1 -1
- package/dist/hooks/use-file-to-data-url.mjs +1 -2
- package/dist/hooks/use-file-to-data-url.mjs.map +1 -1
- package/dist/hooks/use-history-state.d.mts +1 -1
- package/dist/hooks/use-history-state.mjs +3 -3
- package/dist/hooks/use-history-state.mjs.map +1 -1
- package/dist/hooks/use-image.d.mts +1 -1
- package/dist/hooks/use-image.mjs +24 -8
- package/dist/hooks/use-image.mjs.map +1 -1
- package/dist/hooks/use-intersection-observer.d.mts +1 -1
- package/dist/hooks/use-intersection-observer.mjs +1 -2
- package/dist/hooks/use-intersection-observer.mjs.map +1 -1
- package/dist/hooks/use-interval.d.mts +1 -1
- package/dist/hooks/use-interval.mjs +1 -2
- package/dist/hooks/use-interval.mjs.map +1 -1
- package/dist/hooks/use-key-press.d.mts +1 -2
- package/dist/hooks/use-key-press.mjs +36 -12
- package/dist/hooks/use-key-press.mjs.map +1 -1
- package/dist/hooks/use-local-storage.d.mts +1 -1
- package/dist/hooks/use-local-storage.mjs +5 -3
- package/dist/hooks/use-local-storage.mjs.map +1 -1
- package/dist/hooks/use-merged-ref.d.mts +2 -3
- package/dist/hooks/use-merged-ref.mjs +13 -8
- package/dist/hooks/use-merged-ref.mjs.map +1 -1
- package/dist/hooks/use-multi-select.d.mts +1 -1
- package/dist/hooks/use-multi-select.mjs +10 -9
- package/dist/hooks/use-multi-select.mjs.map +1 -1
- package/dist/hooks/use-mutation-observer.d.mts +1 -2
- package/dist/hooks/use-mutation-observer.mjs +24 -10
- package/dist/hooks/use-mutation-observer.mjs.map +1 -1
- package/dist/hooks/use-previous.d.mts +1 -1
- package/dist/hooks/use-previous.mjs +1 -2
- package/dist/hooks/use-previous.mjs.map +1 -1
- package/dist/hooks/use-recursive-timeout.d.mts +1 -1
- package/dist/hooks/use-recursive-timeout.mjs +1 -2
- package/dist/hooks/use-recursive-timeout.mjs.map +1 -1
- package/dist/hooks/use-resize-observer.d.mts +1 -1
- package/dist/hooks/use-resize-observer.mjs +34 -11
- package/dist/hooks/use-resize-observer.mjs.map +1 -1
- package/dist/hooks/use-responsive-size.d.mts +1 -1
- package/dist/hooks/use-responsive-size.mjs +3 -4
- package/dist/hooks/use-responsive-size.mjs.map +1 -1
- package/dist/hooks/use-scroll-to-elements.d.mts +1 -2
- package/dist/hooks/use-scroll-to-elements.mjs +1 -2
- package/dist/hooks/use-scroll-to-elements.mjs.map +1 -1
- package/dist/hooks/use-throttled-callback.d.mts +2 -5
- package/dist/hooks/use-throttled-callback.mjs +6 -8
- package/dist/hooks/use-throttled-callback.mjs.map +1 -1
- package/dist/hooks/use-throttled-value.d.mts +9 -0
- package/dist/hooks/{use-throttle.mjs → use-throttled-value.mjs} +5 -6
- package/dist/hooks/use-throttled-value.mjs.map +1 -0
- package/dist/hooks/use-timeout.d.mts +1 -1
- package/dist/hooks/use-timeout.mjs +1 -2
- package/dist/hooks/use-timeout.mjs.map +1 -1
- package/dist/hooks/use-toggle.d.mts +1 -1
- package/dist/hooks/use-toggle.mjs +1 -2
- package/dist/hooks/use-toggle.mjs.map +1 -1
- package/dist/hooks/use-viewport.d.mts +1 -1
- package/dist/hooks/use-viewport.mjs +1 -2
- package/dist/hooks/use-viewport.mjs.map +1 -1
- package/dist/hooks/use-window-scroll.d.mts +1 -2
- package/dist/hooks/use-window-scroll.mjs +1 -2
- package/dist/hooks/use-window-scroll.mjs.map +1 -1
- package/dist/index.d.mts +31 -31
- package/dist/index.mjs +3 -4
- package/package.json +26 -26
- package/dist/hooks/use-debounce.d.mts +0 -14
- package/dist/hooks/use-debounce.mjs.map +0 -1
- package/dist/hooks/use-throttle.d.mts +0 -9
- package/dist/hooks/use-throttle.mjs.map +0 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import useDebouncedCallback from "./use-debounced-callback.mjs";
|
|
2
2
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
-
|
|
4
3
|
//#region src/hooks/use-responsive-size.ts
|
|
5
4
|
const BREAKPOINTS = {
|
|
6
5
|
xs: 0,
|
|
@@ -65,7 +64,7 @@ const useResponsiveSize = (options) => {
|
|
|
65
64
|
const nextBreakpoint = getBreakpointInfo(next.width);
|
|
66
65
|
setBreakpoint((prev) => breakpointEqual(prev, nextBreakpoint) ? prev : nextBreakpoint);
|
|
67
66
|
}, []);
|
|
68
|
-
const debouncedCommit =
|
|
67
|
+
const debouncedCommit = useDebouncedCallback(commit, {
|
|
69
68
|
delay,
|
|
70
69
|
autoInvoke: false
|
|
71
70
|
});
|
|
@@ -118,7 +117,7 @@ const useResponsiveSize = (options) => {
|
|
|
118
117
|
ref
|
|
119
118
|
};
|
|
120
119
|
};
|
|
121
|
-
|
|
122
120
|
//#endregion
|
|
123
121
|
export { useResponsiveSize as default };
|
|
122
|
+
|
|
124
123
|
//# sourceMappingURL=use-responsive-size.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-responsive-size.mjs","names":[],"sources":["../../src/hooks/use-responsive-size.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\n\nimport
|
|
1
|
+
{"version":3,"file":"use-responsive-size.mjs","names":[],"sources":["../../src/hooks/use-responsive-size.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\n\nimport useDebouncedCallback from './use-debounced-callback';\n\ntype Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\ninterface BreakpointInfo {\n current: Breakpoint;\n xs: boolean;\n sm: boolean;\n md: boolean;\n lg: boolean;\n xl: boolean;\n '2xl': boolean;\n}\n\ninterface Options {\n delay?: number;\n container?: HTMLElement | null;\n // Measure the viewport (window.innerWidth/innerHeight) instead of an\n // element/document.body. Useful when no `container`/ref is attached and\n // the breakpoint should reflect the viewport rather than document.body's\n // box, which can diverge from it if body has margin/transform.\n viewport?: boolean;\n}\n\nconst BREAKPOINTS = {\n xs: 0, // < 640px\n sm: 640, // >= 640px\n md: 768, // >= 768px\n lg: 1024, // >= 1024px\n xl: 1280, // >= 1280px\n '2xl': 1536, // >= 1536px\n} as const;\n\nconst getBreakpointInfo = (width: number): BreakpointInfo => {\n let current: Breakpoint = 'xs';\n\n if (width >= BREAKPOINTS['2xl']) {\n current = '2xl';\n } else if (width >= BREAKPOINTS.xl) {\n current = 'xl';\n } else if (width >= BREAKPOINTS.lg) {\n current = 'lg';\n } else if (width >= BREAKPOINTS.md) {\n current = 'md';\n } else if (width >= BREAKPOINTS.sm) {\n current = 'sm';\n } else {\n current = 'xs';\n }\n\n return {\n current,\n xs: width < BREAKPOINTS.sm,\n sm: width >= BREAKPOINTS.sm && width < BREAKPOINTS.md,\n md: width >= BREAKPOINTS.md && width < BREAKPOINTS.lg,\n lg: width >= BREAKPOINTS.lg && width < BREAKPOINTS.xl,\n xl: width >= BREAKPOINTS.xl && width < BREAKPOINTS['2xl'],\n '2xl': width >= BREAKPOINTS['2xl'],\n };\n};\n\nconst breakpointEqual = (a: BreakpointInfo, b: BreakpointInfo) =>\n a.current === b.current &&\n a.xs === b.xs &&\n a.sm === b.sm &&\n a.md === b.md &&\n a.lg === b.lg &&\n a.xl === b.xl &&\n a['2xl'] === b['2xl'];\n\nconst measureTarget = (target: HTMLElement) => ({\n width: target.offsetWidth,\n height: target.offsetHeight,\n});\n\nconst measureViewport = () => ({\n width: window.innerWidth,\n height: window.innerHeight,\n});\n\nconst useResponsiveSize = <T extends HTMLElement>(options?: Options) => {\n const { delay = 100, container, viewport = false } = options || {};\n\n const [element, setElement] = useState<T | null>(null);\n const [size, setSize] = useState({ width: 0, height: 0 });\n const [breakpoint, setBreakpoint] = useState<BreakpointInfo>(() =>\n getBreakpointInfo(0),\n );\n\n const observerRef = useRef<ResizeObserver | null>(null);\n const latestRef = useRef({ width: 0, height: 0 });\n const committedRef = useRef({ width: 0, height: 0 });\n\n const ref = useCallback((node: T | null) => {\n setElement(node);\n }, []);\n\n // `size` and `breakpoint` are always committed together here, so\n // consumers never observe one reflecting a newer measurement than the\n // other.\n const commit = useCallback(() => {\n const next = latestRef.current;\n\n if (\n committedRef.current.width === next.width &&\n committedRef.current.height === next.height\n ) {\n return;\n }\n\n committedRef.current = next;\n setSize(next);\n\n const nextBreakpoint = getBreakpointInfo(next.width);\n setBreakpoint(prev =>\n breakpointEqual(prev, nextBreakpoint) ? prev : nextBreakpoint,\n );\n }, []);\n\n // Manual invocation only (`autoInvoke: false`) — reuses\n // useDebouncedCallback's machinery instead of duplicating it here.\n const debouncedCommit = useDebouncedCallback(commit, {\n delay,\n autoInvoke: false,\n });\n\n // Measure synchronously before paint so the very first render reflects\n // the real size/breakpoint instead of the `{0,0}`/`xs` placeholder —\n // only later updates (from the observer/listener below) go through the\n // debounced path.\n useLayoutEffect(() => {\n const measured = viewport\n ? measureViewport()\n : (() => {\n const target = container ?? element ?? document.body;\n return target ? measureTarget(target) : null;\n })();\n\n if (!measured) {\n return;\n }\n\n latestRef.current = measured;\n commit();\n }, [container, element, viewport, commit]);\n\n useEffect(() => {\n if (viewport) {\n const onResize = () => {\n latestRef.current = measureViewport();\n debouncedCommit();\n };\n\n window.addEventListener('resize', onResize);\n return () => window.removeEventListener('resize', onResize);\n }\n\n const target = container ?? element ?? document.body;\n\n if (!target) {\n return;\n }\n\n if (observerRef.current) {\n observerRef.current.disconnect();\n }\n\n observerRef.current = new ResizeObserver(() => {\n requestAnimationFrame(() => {\n latestRef.current = measureTarget(target);\n debouncedCommit();\n });\n });\n\n observerRef.current.observe(target);\n\n return () => {\n observerRef.current?.disconnect();\n observerRef.current = null;\n };\n }, [container, element, viewport, debouncedCommit]);\n\n return {\n size,\n breakpoint,\n ref,\n };\n};\n\nexport default useResponsiveSize;\n"],"mappings":";;;AAgCA,MAAM,cAAc;CAClB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;AACT;AAEA,MAAM,qBAAqB,UAAkC;CAC3D,IAAI,UAAsB;CAE1B,IAAI,SAAS,YAAY,QACvB,UAAU;MACL,IAAI,SAAS,YAAY,IAC9B,UAAU;MACL,IAAI,SAAS,YAAY,IAC9B,UAAU;MACL,IAAI,SAAS,YAAY,IAC9B,UAAU;MACL,IAAI,SAAS,YAAY,IAC9B,UAAU;MAEV,UAAU;CAGZ,OAAO;EACL;EACA,IAAI,QAAQ,YAAY;EACxB,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,OAAO,SAAS,YAAY;CAC9B;AACF;AAEA,MAAM,mBAAmB,GAAmB,MAC1C,EAAE,YAAY,EAAE,WAChB,EAAE,OAAO,EAAE,MACX,EAAE,OAAO,EAAE,MACX,EAAE,OAAO,EAAE,MACX,EAAE,OAAO,EAAE,MACX,EAAE,OAAO,EAAE,MACX,EAAE,WAAW,EAAE;AAEjB,MAAM,iBAAiB,YAAyB;CAC9C,OAAO,OAAO;CACd,QAAQ,OAAO;AACjB;AAEA,MAAM,yBAAyB;CAC7B,OAAO,OAAO;CACd,QAAQ,OAAO;AACjB;AAEA,MAAM,qBAA4C,YAAsB;CACtE,MAAM,EAAE,QAAQ,KAAK,WAAW,WAAW,UAAU,WAAW,CAAC;CAEjE,MAAM,CAAC,SAAS,cAAc,SAAmB,IAAI;CACrD,MAAM,CAAC,MAAM,WAAW,SAAS;EAAE,OAAO;EAAG,QAAQ;CAAE,CAAC;CACxD,MAAM,CAAC,YAAY,iBAAiB,eAClC,kBAAkB,CAAC,CACrB;CAEA,MAAM,cAAc,OAA8B,IAAI;CACtD,MAAM,YAAY,OAAO;EAAE,OAAO;EAAG,QAAQ;CAAE,CAAC;CAChD,MAAM,eAAe,OAAO;EAAE,OAAO;EAAG,QAAQ;CAAE,CAAC;CAEnD,MAAM,MAAM,aAAa,SAAmB;EAC1C,WAAW,IAAI;CACjB,GAAG,CAAC,CAAC;CAKL,MAAM,SAAS,kBAAkB;EAC/B,MAAM,OAAO,UAAU;EAEvB,IACE,aAAa,QAAQ,UAAU,KAAK,SACpC,aAAa,QAAQ,WAAW,KAAK,QAErC;EAGF,aAAa,UAAU;EACvB,QAAQ,IAAI;EAEZ,MAAM,iBAAiB,kBAAkB,KAAK,KAAK;EACnD,eAAc,SACZ,gBAAgB,MAAM,cAAc,IAAI,OAAO,cACjD;CACF,GAAG,CAAC,CAAC;CAIL,MAAM,kBAAkB,qBAAqB,QAAQ;EACnD;EACA,YAAY;CACd,CAAC;CAMD,sBAAsB;EACpB,MAAM,WAAW,WACb,gBAAgB,WACT;GACL,MAAM,SAAS,aAAa,WAAW,SAAS;GAChD,OAAO,SAAS,cAAc,MAAM,IAAI;EAC1C,EAAA,CAAG;EAEP,IAAI,CAAC,UACH;EAGF,UAAU,UAAU;EACpB,OAAO;CACT,GAAG;EAAC;EAAW;EAAS;EAAU;CAAM,CAAC;CAEzC,gBAAgB;EACd,IAAI,UAAU;GACZ,MAAM,iBAAiB;IACrB,UAAU,UAAU,gBAAgB;IACpC,gBAAgB;GAClB;GAEA,OAAO,iBAAiB,UAAU,QAAQ;GAC1C,aAAa,OAAO,oBAAoB,UAAU,QAAQ;EAC5D;EAEA,MAAM,SAAS,aAAa,WAAW,SAAS;EAEhD,IAAI,CAAC,QACH;EAGF,IAAI,YAAY,SACd,YAAY,QAAQ,WAAW;EAGjC,YAAY,UAAU,IAAI,qBAAqB;GAC7C,4BAA4B;IAC1B,UAAU,UAAU,cAAc,MAAM;IACxC,gBAAgB;GAClB,CAAC;EACH,CAAC;EAED,YAAY,QAAQ,QAAQ,MAAM;EAElC,aAAa;GACX,YAAY,SAAS,WAAW;GAChC,YAAY,UAAU;EACxB;CACF,GAAG;EAAC;EAAW;EAAS;EAAU;CAAe,CAAC;CAElD,OAAO;EACL;EACA;EACA;CACF;AACF"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-scroll-to-elements.d.ts
|
|
4
3
|
type ElementKey = string;
|
|
5
4
|
type ScrollContainer = HTMLElement | RefObject<HTMLElement | null> | null;
|
|
@@ -12,5 +11,5 @@ declare const useScrollToElements: (defaultOptions?: Options) => {
|
|
|
12
11
|
scrollTo: (key: ElementKey, options?: Options) => void;
|
|
13
12
|
};
|
|
14
13
|
//#endregion
|
|
15
|
-
export { useScrollToElements };
|
|
14
|
+
export { useScrollToElements as default };
|
|
16
15
|
//# sourceMappingURL=use-scroll-to-elements.d.mts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-scroll-to-elements.ts
|
|
4
3
|
const resolveContainer = (container) => {
|
|
5
4
|
if (!container) return null;
|
|
@@ -59,7 +58,7 @@ const useScrollToElements = (defaultOptions) => {
|
|
|
59
58
|
}, [])
|
|
60
59
|
};
|
|
61
60
|
};
|
|
62
|
-
|
|
63
61
|
//#endregion
|
|
64
62
|
export { useScrollToElements as default };
|
|
63
|
+
|
|
65
64
|
//# sourceMappingURL=use-scroll-to-elements.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-scroll-to-elements.mjs","names":[],"sources":["../../src/hooks/use-scroll-to-elements.ts"],"sourcesContent":["import { type RefObject, useCallback, useEffect, useRef } from 'react';\n\ntype ElementKey = string;\n\ntype ScrollContainer = HTMLElement | RefObject<HTMLElement | null> | null;\n\ninterface Options extends ScrollIntoViewOptions {\n offset?: number;\n // Element to scroll instead of `window` when `offset` is set — e.g. a\n // modal's own scrollable body. `scrollIntoView` (the no-offset path)\n // already finds its nearest scrollable ancestor on its own and doesn't\n // need this.\n container?: ScrollContainer;\n}\n\nconst resolveContainer = (container: ScrollContainer): HTMLElement | null => {\n if (!container) {\n return null;\n }\n return 'current' in container ? container.current : container;\n};\n\nconst useScrollToElements = (defaultOptions?: Options) => {\n const elementsRef = useRef(new Map<ElementKey, HTMLElement>());\n const registerCallbacksRef = useRef(\n new Map<ElementKey, (node: HTMLElement | null) => void>(),\n );\n\n // `defaultOptions` is almost always an inline object literal at the call\n // site, so depending on it directly would recreate `scrollTo` on every\n // render — read the latest value from a ref instead.\n const defaultOptionsRef = useRef(defaultOptions);\n\n useEffect(() => {\n defaultOptionsRef.current = defaultOptions;\n });\n\n // Returns a stable callback per key so React doesn't detach/reattach the\n // ref (and thrash the Map) on every render — only a genuine unmount\n // (node === null) removes the entry.\n const register = useCallback((key: ElementKey) => {\n let callback = registerCallbacksRef.current.get(key);\n\n if (!callback) {\n callback = (node: HTMLElement | null) => {\n if (node) {\n elementsRef.current.set(key, node);\n } else {\n elementsRef.current.delete(key);\n }\n };\n registerCallbacksRef.current.set(key, callback);\n }\n\n return callback;\n }, []);\n\n const scrollTo = useCallback((key: ElementKey, options?: Options) => {\n const element = elementsRef.current.get(key);\n\n if (!element) {\n return;\n }\n\n const { offset, container, ...scrollIntoViewOptions } = {\n ...defaultOptionsRef.current,\n ...options,\n };\n\n // With an offset, `scrollIntoView` and `scrollTo` would both animate\n // the same scroll, fighting each other and settling in the wrong\n // place — use `scrollTo` alone, computed from the element's position\n // before any scrolling starts (not after `scrollIntoView`, where the\n // rect would reflect a mid-animation position).\n if (offset != null) {\n const containerElement = resolveContainer(container ?? null);\n\n if (containerElement) {\n const containerRect = containerElement.getBoundingClientRect();\n const elementRect = element.getBoundingClientRect();\n const top =\n containerElement.scrollTop +\n (elementRect.top - containerRect.top) -\n offset;\n\n containerElement.scrollTo({\n top,\n behavior: scrollIntoViewOptions.behavior || 'smooth',\n });\n return;\n }\n\n const top = element.getBoundingClientRect().top + window.scrollY - offset;\n\n window.scrollTo({\n top,\n behavior: scrollIntoViewOptions.behavior || 'smooth',\n });\n return;\n }\n\n element.scrollIntoView({\n behavior: 'smooth',\n block: 'start',\n inline: 'start',\n ...scrollIntoViewOptions,\n });\n }, []);\n\n return { register, scrollTo };\n};\n\nexport default useScrollToElements;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-scroll-to-elements.mjs","names":[],"sources":["../../src/hooks/use-scroll-to-elements.ts"],"sourcesContent":["import { type RefObject, useCallback, useEffect, useRef } from 'react';\n\ntype ElementKey = string;\n\ntype ScrollContainer = HTMLElement | RefObject<HTMLElement | null> | null;\n\ninterface Options extends ScrollIntoViewOptions {\n offset?: number;\n // Element to scroll instead of `window` when `offset` is set — e.g. a\n // modal's own scrollable body. `scrollIntoView` (the no-offset path)\n // already finds its nearest scrollable ancestor on its own and doesn't\n // need this.\n container?: ScrollContainer;\n}\n\nconst resolveContainer = (container: ScrollContainer): HTMLElement | null => {\n if (!container) {\n return null;\n }\n return 'current' in container ? container.current : container;\n};\n\nconst useScrollToElements = (defaultOptions?: Options) => {\n const elementsRef = useRef(new Map<ElementKey, HTMLElement>());\n const registerCallbacksRef = useRef(\n new Map<ElementKey, (node: HTMLElement | null) => void>(),\n );\n\n // `defaultOptions` is almost always an inline object literal at the call\n // site, so depending on it directly would recreate `scrollTo` on every\n // render — read the latest value from a ref instead.\n const defaultOptionsRef = useRef(defaultOptions);\n\n useEffect(() => {\n defaultOptionsRef.current = defaultOptions;\n });\n\n // Returns a stable callback per key so React doesn't detach/reattach the\n // ref (and thrash the Map) on every render — only a genuine unmount\n // (node === null) removes the entry.\n const register = useCallback((key: ElementKey) => {\n let callback = registerCallbacksRef.current.get(key);\n\n if (!callback) {\n callback = (node: HTMLElement | null) => {\n if (node) {\n elementsRef.current.set(key, node);\n } else {\n elementsRef.current.delete(key);\n }\n };\n registerCallbacksRef.current.set(key, callback);\n }\n\n return callback;\n }, []);\n\n const scrollTo = useCallback((key: ElementKey, options?: Options) => {\n const element = elementsRef.current.get(key);\n\n if (!element) {\n return;\n }\n\n const { offset, container, ...scrollIntoViewOptions } = {\n ...defaultOptionsRef.current,\n ...options,\n };\n\n // With an offset, `scrollIntoView` and `scrollTo` would both animate\n // the same scroll, fighting each other and settling in the wrong\n // place — use `scrollTo` alone, computed from the element's position\n // before any scrolling starts (not after `scrollIntoView`, where the\n // rect would reflect a mid-animation position).\n if (offset != null) {\n const containerElement = resolveContainer(container ?? null);\n\n if (containerElement) {\n const containerRect = containerElement.getBoundingClientRect();\n const elementRect = element.getBoundingClientRect();\n const top =\n containerElement.scrollTop +\n (elementRect.top - containerRect.top) -\n offset;\n\n containerElement.scrollTo({\n top,\n behavior: scrollIntoViewOptions.behavior || 'smooth',\n });\n return;\n }\n\n const top = element.getBoundingClientRect().top + window.scrollY - offset;\n\n window.scrollTo({\n top,\n behavior: scrollIntoViewOptions.behavior || 'smooth',\n });\n return;\n }\n\n element.scrollIntoView({\n behavior: 'smooth',\n block: 'start',\n inline: 'start',\n ...scrollIntoViewOptions,\n });\n }, []);\n\n return { register, scrollTo };\n};\n\nexport default useScrollToElements;\n"],"mappings":";;AAeA,MAAM,oBAAoB,cAAmD;CAC3E,IAAI,CAAC,WACH,OAAO;CAET,OAAO,aAAa,YAAY,UAAU,UAAU;AACtD;AAEA,MAAM,uBAAuB,mBAA6B;CACxD,MAAM,cAAc,uBAAO,IAAI,IAA6B,CAAC;CAC7D,MAAM,uBAAuB,uBAC3B,IAAI,IAAoD,CAC1D;CAKA,MAAM,oBAAoB,OAAO,cAAc;CAE/C,gBAAgB;EACd,kBAAkB,UAAU;CAC9B,CAAC;CA0ED,OAAO;EAAE,UArEQ,aAAa,QAAoB;GAChD,IAAI,WAAW,qBAAqB,QAAQ,IAAI,GAAG;GAEnD,IAAI,CAAC,UAAU;IACb,YAAY,SAA6B;KACvC,IAAI,MACF,YAAY,QAAQ,IAAI,KAAK,IAAI;UAEjC,YAAY,QAAQ,OAAO,GAAG;IAElC;IACA,qBAAqB,QAAQ,IAAI,KAAK,QAAQ;GAChD;GAEA,OAAO;EACT,GAAG,CAAC,CAsDY;EAAG,UApDF,aAAa,KAAiB,YAAsB;GACnE,MAAM,UAAU,YAAY,QAAQ,IAAI,GAAG;GAE3C,IAAI,CAAC,SACH;GAGF,MAAM,EAAE,QAAQ,WAAW,GAAG,0BAA0B;IACtD,GAAG,kBAAkB;IACrB,GAAG;GACL;GAOA,IAAI,UAAU,MAAM;IAClB,MAAM,mBAAmB,iBAAiB,aAAa,IAAI;IAE3D,IAAI,kBAAkB;KACpB,MAAM,gBAAgB,iBAAiB,sBAAsB;KAC7D,MAAM,cAAc,QAAQ,sBAAsB;KAClD,MAAM,MACJ,iBAAiB,aAChB,YAAY,MAAM,cAAc,OACjC;KAEF,iBAAiB,SAAS;MACxB;MACA,UAAU,sBAAsB,YAAY;KAC9C,CAAC;KACD;IACF;IAEA,MAAM,MAAM,QAAQ,sBAAsB,CAAC,CAAC,MAAM,OAAO,UAAU;IAEnE,OAAO,SAAS;KACd;KACA,UAAU,sBAAsB,YAAY;IAC9C,CAAC;IACD;GACF;GAEA,QAAQ,eAAe;IACrB,UAAU;IACV,OAAO;IACP,QAAQ;IACR,GAAG;GACL,CAAC;EACH,GAAG,CAAC,CAEsB;CAAE;AAC9B"}
|
|
@@ -3,10 +3,7 @@ interface Options {
|
|
|
3
3
|
leading?: boolean;
|
|
4
4
|
trailing?: boolean;
|
|
5
5
|
}
|
|
6
|
-
declare const useThrottledCallback: <Args extends unknown[]>(callback: (...args: Args) => void, delay?: number, {
|
|
7
|
-
leading,
|
|
8
|
-
trailing
|
|
9
|
-
}?: Options) => (...args: Args) => void;
|
|
6
|
+
declare const useThrottledCallback: <Args extends unknown[]>(callback: (...args: Args) => void, delay?: number, { leading, trailing }?: Options) => (...args: Args) => void;
|
|
10
7
|
//#endregion
|
|
11
|
-
export { useThrottledCallback };
|
|
8
|
+
export { useThrottledCallback as default };
|
|
12
9
|
//# sourceMappingURL=use-throttled-callback.d.mts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
2
|
//#region src/hooks/use-throttled-callback.ts
|
|
4
3
|
const useThrottledCallback = (callback, delay = 100, { leading = true, trailing = true } = {}) => {
|
|
5
4
|
const callbackRef = useRef(callback);
|
|
@@ -15,14 +14,13 @@ const useThrottledCallback = (callback, delay = 100, { leading = true, trailing
|
|
|
15
14
|
const lastExecutedRef = useRef(null);
|
|
16
15
|
const timeoutRef = useRef(null);
|
|
17
16
|
const lastArgsRef = useRef(null);
|
|
18
|
-
const stableThrottledCallback =
|
|
19
|
-
if (!stableThrottledCallback.current) {
|
|
17
|
+
const [stableThrottledCallback] = useState(() => {
|
|
20
18
|
const invoke = () => {
|
|
21
19
|
lastExecutedRef.current = Date.now();
|
|
22
20
|
timeoutRef.current = null;
|
|
23
21
|
if (lastArgsRef.current) callbackRef.current(...lastArgsRef.current);
|
|
24
22
|
};
|
|
25
|
-
|
|
23
|
+
return (...args) => {
|
|
26
24
|
lastArgsRef.current = args;
|
|
27
25
|
const now = Date.now();
|
|
28
26
|
const elapsed = lastExecutedRef.current === null ? Infinity : now - lastExecutedRef.current;
|
|
@@ -40,15 +38,15 @@ const useThrottledCallback = (callback, delay = 100, { leading = true, trailing
|
|
|
40
38
|
}
|
|
41
39
|
if (trailingRef.current && !timeoutRef.current) timeoutRef.current = setTimeout(invoke, delayRef.current - elapsed);
|
|
42
40
|
};
|
|
43
|
-
}
|
|
41
|
+
});
|
|
44
42
|
useEffect(() => {
|
|
45
43
|
return () => {
|
|
46
44
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
47
45
|
};
|
|
48
46
|
}, []);
|
|
49
|
-
return stableThrottledCallback
|
|
47
|
+
return stableThrottledCallback;
|
|
50
48
|
};
|
|
51
|
-
|
|
52
49
|
//#endregion
|
|
53
50
|
export { useThrottledCallback as default };
|
|
51
|
+
|
|
54
52
|
//# sourceMappingURL=use-throttled-callback.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-throttled-callback.mjs","names":[],"sources":["../../src/hooks/use-throttled-callback.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\ninterface Options {\n leading?: boolean;\n trailing?: boolean;\n}\n\n// Invokes `callback` at most once per `delay`ms, regardless of how often\n// the returned function is called — the callback-shaped counterpart to\n// useThrottle (which throttles a value). `leading` fires on the first call\n// of a burst immediately; `trailing` schedules one more call (with\n// whatever args arrived last) at the end of the window if calls kept\n// coming. Both default to true, matching useThrottle's fixed behavior.\nconst useThrottledCallback = <Args extends unknown[]>(\n callback: (...args: Args) => void,\n delay = 100,\n { leading = true, trailing = true }: Options = {},\n) => {\n const callbackRef = useRef(callback);\n const delayRef = useRef(delay);\n const leadingRef = useRef(leading);\n const trailingRef = useRef(trailing);\n\n useEffect(() => {\n callbackRef.current = callback;\n delayRef.current = delay;\n leadingRef.current = leading;\n trailingRef.current = trailing;\n });\n\n const lastExecutedRef = useRef<number | null>(null);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastArgsRef = useRef<Args | null>(null);\n\n
|
|
1
|
+
{"version":3,"file":"use-throttled-callback.mjs","names":[],"sources":["../../src/hooks/use-throttled-callback.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\ninterface Options {\n leading?: boolean;\n trailing?: boolean;\n}\n\n// Invokes `callback` at most once per `delay`ms, regardless of how often\n// the returned function is called — the callback-shaped counterpart to\n// useThrottle (which throttles a value). `leading` fires on the first call\n// of a burst immediately; `trailing` schedules one more call (with\n// whatever args arrived last) at the end of the window if calls kept\n// coming. Both default to true, matching useThrottle's fixed behavior.\nconst useThrottledCallback = <Args extends unknown[]>(\n callback: (...args: Args) => void,\n delay = 100,\n { leading = true, trailing = true }: Options = {},\n) => {\n const callbackRef = useRef(callback);\n const delayRef = useRef(delay);\n const leadingRef = useRef(leading);\n const trailingRef = useRef(trailing);\n\n useEffect(() => {\n callbackRef.current = callback;\n delayRef.current = delay;\n leadingRef.current = leading;\n trailingRef.current = trailing;\n });\n\n const lastExecutedRef = useRef<number | null>(null);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastArgsRef = useRef<Args | null>(null);\n\n // Built once via useState's lazy initializer (runs only on mount) rather\n // than the ref-guarded-by-if pattern this used to use — same \"create\n // once, keep a stable identity\" effect, but without ever reading/writing\n // a ref during render, which react-hooks/refs now disallows even for the\n // otherwise-idempotent lazy-ref-init form (see facebook/react#36896).\n const [stableThrottledCallback] = useState(() => {\n const invoke = () => {\n lastExecutedRef.current = Date.now();\n timeoutRef.current = null;\n\n if (lastArgsRef.current) {\n callbackRef.current(...lastArgsRef.current);\n }\n };\n\n return (...args: Args) => {\n lastArgsRef.current = args;\n\n const now = Date.now();\n const elapsed =\n lastExecutedRef.current === null\n ? Infinity\n : now - lastExecutedRef.current;\n\n if (elapsed >= delayRef.current) {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n\n if (leadingRef.current) {\n invoke();\n } else {\n lastExecutedRef.current = now;\n\n if (trailingRef.current) {\n timeoutRef.current = setTimeout(invoke, delayRef.current);\n }\n }\n return;\n }\n\n if (trailingRef.current && !timeoutRef.current) {\n timeoutRef.current = setTimeout(invoke, delayRef.current - elapsed);\n }\n };\n });\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n return stableThrottledCallback;\n};\n\nexport default useThrottledCallback;\n"],"mappings":";;AAaA,MAAM,wBACJ,UACA,QAAQ,KACR,EAAE,UAAU,MAAM,WAAW,SAAkB,CAAC,MAC7C;CACH,MAAM,cAAc,OAAO,QAAQ;CACnC,MAAM,WAAW,OAAO,KAAK;CAC7B,MAAM,aAAa,OAAO,OAAO;CACjC,MAAM,cAAc,OAAO,QAAQ;CAEnC,gBAAgB;EACd,YAAY,UAAU;EACtB,SAAS,UAAU;EACnB,WAAW,UAAU;EACrB,YAAY,UAAU;CACxB,CAAC;CAED,MAAM,kBAAkB,OAAsB,IAAI;CAClD,MAAM,aAAa,OAA6C,IAAI;CACpE,MAAM,cAAc,OAAoB,IAAI;CAO5C,MAAM,CAAC,2BAA2B,eAAe;EAC/C,MAAM,eAAe;GACnB,gBAAgB,UAAU,KAAK,IAAI;GACnC,WAAW,UAAU;GAErB,IAAI,YAAY,SACd,YAAY,QAAQ,GAAG,YAAY,OAAO;EAE9C;EAEA,QAAQ,GAAG,SAAe;GACxB,YAAY,UAAU;GAEtB,MAAM,MAAM,KAAK,IAAI;GACrB,MAAM,UACJ,gBAAgB,YAAY,OACxB,WACA,MAAM,gBAAgB;GAE5B,IAAI,WAAW,SAAS,SAAS;IAC/B,IAAI,WAAW,SAAS;KACtB,aAAa,WAAW,OAAO;KAC/B,WAAW,UAAU;IACvB;IAEA,IAAI,WAAW,SACb,OAAO;SACF;KACL,gBAAgB,UAAU;KAE1B,IAAI,YAAY,SACd,WAAW,UAAU,WAAW,QAAQ,SAAS,OAAO;IAE5D;IACA;GACF;GAEA,IAAI,YAAY,WAAW,CAAC,WAAW,SACrC,WAAW,UAAU,WAAW,QAAQ,SAAS,UAAU,OAAO;EAEtE;CACF,CAAC;CAED,gBAAgB;EACd,aAAa;GACX,IAAI,WAAW,SACb,aAAa,WAAW,OAAO;EAEnC;CACF,GAAG,CAAC,CAAC;CAEL,OAAO;AACT"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/hooks/use-throttled-value.d.ts
|
|
2
|
+
interface Options {
|
|
3
|
+
leading?: boolean;
|
|
4
|
+
trailing?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const useThrottledValue: <T>(value: T, delay?: number, options?: Options) => T;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { useThrottledValue as default };
|
|
9
|
+
//# sourceMappingURL=use-throttled-value.d.mts.map
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import useThrottledCallback from "./use-throttled-callback.mjs";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const useThrottle = (value, delay = 100, options = {}) => {
|
|
3
|
+
//#region src/hooks/use-throttled-value.ts
|
|
4
|
+
const useThrottledValue = (value, delay = 100, options = {}) => {
|
|
6
5
|
const [throttledValue, setThrottledValue] = useState(value);
|
|
7
6
|
const throttledSetValue = useThrottledCallback(setThrottledValue, delay, options);
|
|
8
7
|
useEffect(() => {
|
|
@@ -10,7 +9,7 @@ const useThrottle = (value, delay = 100, options = {}) => {
|
|
|
10
9
|
}, [value, throttledSetValue]);
|
|
11
10
|
return throttledValue;
|
|
12
11
|
};
|
|
13
|
-
|
|
14
12
|
//#endregion
|
|
15
|
-
export {
|
|
16
|
-
|
|
13
|
+
export { useThrottledValue as default };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=use-throttled-value.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-throttled-value.mjs","names":[],"sources":["../../src/hooks/use-throttled-value.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nimport useThrottledCallback from './use-throttled-callback';\n\ninterface Options {\n leading?: boolean;\n trailing?: boolean;\n}\n\nconst useThrottledValue = <T>(\n value: T,\n delay = 100,\n options: Options = {},\n): T => {\n const [throttledValue, setThrottledValue] = useState(value);\n\n const throttledSetValue = useThrottledCallback(\n setThrottledValue,\n delay,\n options,\n );\n\n useEffect(() => {\n throttledSetValue(value);\n // `throttledSetValue`'s identity is stable for the lifetime of the\n // component (see useThrottledCallback), so listing it here doesn't\n // cause any extra re-runs beyond `value` actually changing.\n }, [value, throttledSetValue]);\n\n return throttledValue;\n};\n\nexport default useThrottledValue;\n"],"mappings":";;;AASA,MAAM,qBACJ,OACA,QAAQ,KACR,UAAmB,CAAC,MACd;CACN,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,KAAK;CAE1D,MAAM,oBAAoB,qBACxB,mBACA,OACA,OACF;CAEA,gBAAgB;EACd,kBAAkB,KAAK;CAIzB,GAAG,CAAC,OAAO,iBAAiB,CAAC;CAE7B,OAAO;AACT"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-timeout.ts
|
|
4
3
|
const useTimeout = (callback, delay) => {
|
|
5
4
|
const callbackRef = useRef(callback);
|
|
@@ -32,7 +31,7 @@ const useTimeout = (callback, delay) => {
|
|
|
32
31
|
clear
|
|
33
32
|
};
|
|
34
33
|
};
|
|
35
|
-
|
|
36
34
|
//#endregion
|
|
37
35
|
export { useTimeout as default };
|
|
36
|
+
|
|
38
37
|
//# sourceMappingURL=use-timeout.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-timeout.mjs","names":[],"sources":["../../src/hooks/use-timeout.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\n// `delay === null` means inactive (no timer scheduled) — `0` is a valid,\n// distinct delay, same rule useRecursiveTimeout uses. A bare `if (!delay)`\n// guard (as opposed to `if (delay === null)`) is the classic bug this\n// hook exists to avoid: it silently no-ops for a delay of 0 too.\nconst useTimeout = (callback: () => void, delay: number | null) => {\n const callbackRef = useRef(callback);\n const delayRef = useRef(delay);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n callbackRef.current = callback;\n delayRef.current = delay;\n });\n\n const clear = useCallback(() => {\n if (timeoutRef.current !== null) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n }, []);\n\n // Restarts the timer from now, using whatever `delay` is current —\n // e.g. a toast's auto-dismiss timer that should start over each time\n // the user hovers away from it.\n const reset = useCallback(() => {\n clear();\n\n if (delayRef.current === null) {\n return;\n }\n\n timeoutRef.current = setTimeout(() => {\n timeoutRef.current = null;\n callbackRef.current();\n }, delayRef.current);\n }, [clear]);\n\n useEffect(() => {\n reset();\n return clear;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [delay]);\n\n return { reset, clear };\n};\n\nexport default useTimeout;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-timeout.mjs","names":[],"sources":["../../src/hooks/use-timeout.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\n// `delay === null` means inactive (no timer scheduled) — `0` is a valid,\n// distinct delay, same rule useRecursiveTimeout uses. A bare `if (!delay)`\n// guard (as opposed to `if (delay === null)`) is the classic bug this\n// hook exists to avoid: it silently no-ops for a delay of 0 too.\nconst useTimeout = (callback: () => void, delay: number | null) => {\n const callbackRef = useRef(callback);\n const delayRef = useRef(delay);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n callbackRef.current = callback;\n delayRef.current = delay;\n });\n\n const clear = useCallback(() => {\n if (timeoutRef.current !== null) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n }, []);\n\n // Restarts the timer from now, using whatever `delay` is current —\n // e.g. a toast's auto-dismiss timer that should start over each time\n // the user hovers away from it.\n const reset = useCallback(() => {\n clear();\n\n if (delayRef.current === null) {\n return;\n }\n\n timeoutRef.current = setTimeout(() => {\n timeoutRef.current = null;\n callbackRef.current();\n }, delayRef.current);\n }, [clear]);\n\n useEffect(() => {\n reset();\n return clear;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [delay]);\n\n return { reset, clear };\n};\n\nexport default useTimeout;\n"],"mappings":";;AAMA,MAAM,cAAc,UAAsB,UAAyB;CACjE,MAAM,cAAc,OAAO,QAAQ;CACnC,MAAM,WAAW,OAAO,KAAK;CAC7B,MAAM,aAAa,OAA6C,IAAI;CAEpE,gBAAgB;EACd,YAAY,UAAU;EACtB,SAAS,UAAU;CACrB,CAAC;CAED,MAAM,QAAQ,kBAAkB;EAC9B,IAAI,WAAW,YAAY,MAAM;GAC/B,aAAa,WAAW,OAAO;GAC/B,WAAW,UAAU;EACvB;CACF,GAAG,CAAC,CAAC;CAKL,MAAM,QAAQ,kBAAkB;EAC9B,MAAM;EAEN,IAAI,SAAS,YAAY,MACvB;EAGF,WAAW,UAAU,iBAAiB;GACpC,WAAW,UAAU;GACrB,YAAY,QAAQ;EACtB,GAAG,SAAS,OAAO;CACrB,GAAG,CAAC,KAAK,CAAC;CAEV,gBAAgB;EACd,MAAM;EACN,OAAO;CAET,GAAG,CAAC,KAAK,CAAC;CAEV,OAAO;EAAE;EAAO;CAAM;AACxB"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useState } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-toggle.ts
|
|
4
3
|
const useToggle = (initial = false) => {
|
|
5
4
|
const [value, setValue] = useState(initial);
|
|
@@ -11,7 +10,7 @@ const useToggle = (initial = false) => {
|
|
|
11
10
|
setValue
|
|
12
11
|
];
|
|
13
12
|
};
|
|
14
|
-
|
|
15
13
|
//#endregion
|
|
16
14
|
export { useToggle as default };
|
|
15
|
+
|
|
17
16
|
//# sourceMappingURL=use-toggle.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-toggle.mjs","names":[],"sources":["../../src/hooks/use-toggle.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\n\nconst useToggle = (\n initial = false,\n): [boolean, () => void, (value: boolean) => void] => {\n const [value, setValue] = useState(initial);\n\n const toggle = useCallback(() => {\n setValue(prev => !prev);\n }, []);\n\n return [value, toggle, setValue];\n};\n\nexport default useToggle;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-toggle.mjs","names":[],"sources":["../../src/hooks/use-toggle.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\n\nconst useToggle = (\n initial = false,\n): [boolean, () => void, (value: boolean) => void] => {\n const [value, setValue] = useState(initial);\n\n const toggle = useCallback(() => {\n setValue(prev => !prev);\n }, []);\n\n return [value, toggle, setValue];\n};\n\nexport default useToggle;\n"],"mappings":";;AAEA,MAAM,aACJ,UAAU,UAC0C;CACpD,MAAM,CAAC,OAAO,YAAY,SAAS,OAAO;CAM1C,OAAO;EAAC;EAJO,kBAAkB;GAC/B,UAAS,SAAQ,CAAC,IAAI;EACxB,GAAG,CAAC,CAEgB;EAAG;CAAQ;AACjC"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-viewport.ts
|
|
4
3
|
const viewportEqual = (a, b) => a.width === b.width && a.height === b.height && a.offsetLeft === b.offsetLeft && a.offsetTop === b.offsetTop && a.pageLeft === b.pageLeft && a.pageTop === b.pageTop && a.scale === b.scale;
|
|
5
4
|
const useViewport = (options = {}) => {
|
|
@@ -84,7 +83,7 @@ const useViewport = (options = {}) => {
|
|
|
84
83
|
]);
|
|
85
84
|
return viewport;
|
|
86
85
|
};
|
|
87
|
-
|
|
88
86
|
//#endregion
|
|
89
87
|
export { useViewport as default };
|
|
88
|
+
|
|
90
89
|
//# sourceMappingURL=use-viewport.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-viewport.mjs","names":[],"sources":["../../src/hooks/use-viewport.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\ninterface ViewportInfo {\n width: number;\n height: number;\n offsetLeft: number;\n offsetTop: number;\n pageLeft: number;\n pageTop: number;\n scale: number;\n}\n\nconst viewportEqual = (a: ViewportInfo, b: ViewportInfo) =>\n a.width === b.width &&\n a.height === b.height &&\n a.offsetLeft === b.offsetLeft &&\n a.offsetTop === b.offsetTop &&\n a.pageLeft === b.pageLeft &&\n a.pageTop === b.pageTop &&\n a.scale === b.scale;\n\ninterface Options {\n isInApp?: boolean;\n debounce?: number;\n}\n\nconst useViewport = (options: Options = {}) => {\n const { isInApp = false, debounce = 100 } = options;\n\n const [viewport, setViewport] = useState<ViewportInfo>({\n width: 0,\n height: 0,\n offsetLeft: 0,\n offsetTop: 0,\n pageLeft: 0,\n pageTop: 0,\n scale: 1,\n });\n\n const getAppViewHeight = useCallback(() => {\n const windowHeight = window.innerHeight;\n const visualHeight = window.visualViewport?.height || windowHeight;\n const documentHeight = document.documentElement.clientHeight;\n const bodyHeight = document.body.clientHeight;\n\n if (window.visualViewport && Math.abs(visualHeight - windowHeight) > 100) {\n return visualHeight;\n }\n\n return Math.max(windowHeight, documentHeight, bodyHeight);\n }, []);\n\n // Always builds a fresh plain-object snapshot rather than returning\n // `window.visualViewport` itself — that instance is a single mutable\n // object the browser updates in place, so returning it directly meant\n // every read produced the exact same reference and `setViewport` never\n // saw a change (no re-render, ever).\n const readViewport = useCallback((): ViewportInfo => {\n const width = window.visualViewport?.width || window.innerWidth;\n const height = isInApp\n ? getAppViewHeight()\n : window.visualViewport?.height || window.innerHeight;\n\n return {\n width,\n height,\n offsetLeft: window.visualViewport?.offsetLeft || 0,\n offsetTop: window.visualViewport?.offsetTop || 0,\n pageLeft: window.scrollX ?? window.pageXOffset ?? 0,\n pageTop: window.scrollY ?? window.pageYOffset ?? 0,\n scale: window.visualViewport?.scale || 1,\n };\n }, [isInApp, getAppViewHeight]);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n // Snapshots are freshly-allocated objects on every read, so gate the\n // state update on an actual value change instead of always replacing\n // the reference — otherwise every resize/scroll tick would re-render\n // consumers even when nothing visibly moved.\n const commitViewport = (next: ViewportInfo) => {\n setViewport(prev => (viewportEqual(prev, next) ? prev : next));\n };\n\n const debouncedUpdate = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n commitViewport(readViewport());\n }, debounce);\n };\n\n const immediateUpdate = () => commitViewport(readViewport());\n\n immediateUpdate();\n\n const events = ['resize', 'orientationchange'];\n\n if (isInApp) {\n events.push('focus', 'blur', 'touchstart', 'touchend');\n }\n\n events.forEach(event => {\n if (event === 'resize' || event === 'orientationchange') {\n window.addEventListener(event, debouncedUpdate);\n } else {\n window.addEventListener(event, immediateUpdate, { passive: true });\n }\n });\n\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', immediateUpdate);\n window.visualViewport.addEventListener('scroll', immediateUpdate);\n }\n\n let intervalId: ReturnType<typeof setInterval>;\n if (isInApp) {\n let lastHeight = readViewport().height;\n intervalId = setInterval(() => {\n const currentHeight = readViewport().height;\n if (Math.abs(currentHeight - lastHeight) > 50) {\n lastHeight = currentHeight;\n immediateUpdate();\n }\n }, 500);\n }\n\n return () => {\n clearTimeout(timeoutId);\n if (intervalId) clearInterval(intervalId);\n\n events.forEach(event => {\n window.removeEventListener(\n event,\n event === 'resize' || event === 'orientationchange'\n ? debouncedUpdate\n : immediateUpdate,\n );\n });\n\n if (window.visualViewport) {\n window.visualViewport.removeEventListener('resize', immediateUpdate);\n window.visualViewport.removeEventListener('scroll', immediateUpdate);\n }\n };\n }, [readViewport, isInApp, debounce]);\n\n return viewport;\n};\n\nexport default useViewport;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-viewport.mjs","names":[],"sources":["../../src/hooks/use-viewport.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\ninterface ViewportInfo {\n width: number;\n height: number;\n offsetLeft: number;\n offsetTop: number;\n pageLeft: number;\n pageTop: number;\n scale: number;\n}\n\nconst viewportEqual = (a: ViewportInfo, b: ViewportInfo) =>\n a.width === b.width &&\n a.height === b.height &&\n a.offsetLeft === b.offsetLeft &&\n a.offsetTop === b.offsetTop &&\n a.pageLeft === b.pageLeft &&\n a.pageTop === b.pageTop &&\n a.scale === b.scale;\n\ninterface Options {\n isInApp?: boolean;\n debounce?: number;\n}\n\nconst useViewport = (options: Options = {}) => {\n const { isInApp = false, debounce = 100 } = options;\n\n const [viewport, setViewport] = useState<ViewportInfo>({\n width: 0,\n height: 0,\n offsetLeft: 0,\n offsetTop: 0,\n pageLeft: 0,\n pageTop: 0,\n scale: 1,\n });\n\n const getAppViewHeight = useCallback(() => {\n const windowHeight = window.innerHeight;\n const visualHeight = window.visualViewport?.height || windowHeight;\n const documentHeight = document.documentElement.clientHeight;\n const bodyHeight = document.body.clientHeight;\n\n if (window.visualViewport && Math.abs(visualHeight - windowHeight) > 100) {\n return visualHeight;\n }\n\n return Math.max(windowHeight, documentHeight, bodyHeight);\n }, []);\n\n // Always builds a fresh plain-object snapshot rather than returning\n // `window.visualViewport` itself — that instance is a single mutable\n // object the browser updates in place, so returning it directly meant\n // every read produced the exact same reference and `setViewport` never\n // saw a change (no re-render, ever).\n const readViewport = useCallback((): ViewportInfo => {\n const width = window.visualViewport?.width || window.innerWidth;\n const height = isInApp\n ? getAppViewHeight()\n : window.visualViewport?.height || window.innerHeight;\n\n return {\n width,\n height,\n offsetLeft: window.visualViewport?.offsetLeft || 0,\n offsetTop: window.visualViewport?.offsetTop || 0,\n pageLeft: window.scrollX ?? window.pageXOffset ?? 0,\n pageTop: window.scrollY ?? window.pageYOffset ?? 0,\n scale: window.visualViewport?.scale || 1,\n };\n }, [isInApp, getAppViewHeight]);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n // Snapshots are freshly-allocated objects on every read, so gate the\n // state update on an actual value change instead of always replacing\n // the reference — otherwise every resize/scroll tick would re-render\n // consumers even when nothing visibly moved.\n const commitViewport = (next: ViewportInfo) => {\n setViewport(prev => (viewportEqual(prev, next) ? prev : next));\n };\n\n const debouncedUpdate = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n commitViewport(readViewport());\n }, debounce);\n };\n\n const immediateUpdate = () => commitViewport(readViewport());\n\n immediateUpdate();\n\n const events = ['resize', 'orientationchange'];\n\n if (isInApp) {\n events.push('focus', 'blur', 'touchstart', 'touchend');\n }\n\n events.forEach(event => {\n if (event === 'resize' || event === 'orientationchange') {\n window.addEventListener(event, debouncedUpdate);\n } else {\n window.addEventListener(event, immediateUpdate, { passive: true });\n }\n });\n\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', immediateUpdate);\n window.visualViewport.addEventListener('scroll', immediateUpdate);\n }\n\n let intervalId: ReturnType<typeof setInterval>;\n if (isInApp) {\n let lastHeight = readViewport().height;\n intervalId = setInterval(() => {\n const currentHeight = readViewport().height;\n if (Math.abs(currentHeight - lastHeight) > 50) {\n lastHeight = currentHeight;\n immediateUpdate();\n }\n }, 500);\n }\n\n return () => {\n clearTimeout(timeoutId);\n if (intervalId) clearInterval(intervalId);\n\n events.forEach(event => {\n window.removeEventListener(\n event,\n event === 'resize' || event === 'orientationchange'\n ? debouncedUpdate\n : immediateUpdate,\n );\n });\n\n if (window.visualViewport) {\n window.visualViewport.removeEventListener('resize', immediateUpdate);\n window.visualViewport.removeEventListener('scroll', immediateUpdate);\n }\n };\n }, [readViewport, isInApp, debounce]);\n\n return viewport;\n};\n\nexport default useViewport;\n"],"mappings":";;AAYA,MAAM,iBAAiB,GAAiB,MACtC,EAAE,UAAU,EAAE,SACd,EAAE,WAAW,EAAE,UACf,EAAE,eAAe,EAAE,cACnB,EAAE,cAAc,EAAE,aAClB,EAAE,aAAa,EAAE,YACjB,EAAE,YAAY,EAAE,WAChB,EAAE,UAAU,EAAE;AAOhB,MAAM,eAAe,UAAmB,CAAC,MAAM;CAC7C,MAAM,EAAE,UAAU,OAAO,WAAW,QAAQ;CAE5C,MAAM,CAAC,UAAU,eAAe,SAAuB;EACrD,OAAO;EACP,QAAQ;EACR,YAAY;EACZ,WAAW;EACX,UAAU;EACV,SAAS;EACT,OAAO;CACT,CAAC;CAED,MAAM,mBAAmB,kBAAkB;EACzC,MAAM,eAAe,OAAO;EAC5B,MAAM,eAAe,OAAO,gBAAgB,UAAU;EACtD,MAAM,iBAAiB,SAAS,gBAAgB;EAChD,MAAM,aAAa,SAAS,KAAK;EAEjC,IAAI,OAAO,kBAAkB,KAAK,IAAI,eAAe,YAAY,IAAI,KACnE,OAAO;EAGT,OAAO,KAAK,IAAI,cAAc,gBAAgB,UAAU;CAC1D,GAAG,CAAC,CAAC;CAOL,MAAM,eAAe,kBAAgC;EAMnD,OAAO;GACL,OANY,OAAO,gBAAgB,SAAS,OAAO;GAOnD,QANa,UACX,iBAAiB,IACjB,OAAO,gBAAgB,UAAU,OAAO;GAK1C,YAAY,OAAO,gBAAgB,cAAc;GACjD,WAAW,OAAO,gBAAgB,aAAa;GAC/C,UAAU,OAAO,WAAW,OAAO,eAAe;GAClD,SAAS,OAAO,WAAW,OAAO,eAAe;GACjD,OAAO,OAAO,gBAAgB,SAAS;EACzC;CACF,GAAG,CAAC,SAAS,gBAAgB,CAAC;CAE9B,gBAAgB;EACd,IAAI;EAMJ,MAAM,kBAAkB,SAAuB;GAC7C,aAAY,SAAS,cAAc,MAAM,IAAI,IAAI,OAAO,IAAK;EAC/D;EAEA,MAAM,wBAAwB;GAC5B,aAAa,SAAS;GACtB,YAAY,iBAAiB;IAC3B,eAAe,aAAa,CAAC;GAC/B,GAAG,QAAQ;EACb;EAEA,MAAM,wBAAwB,eAAe,aAAa,CAAC;EAE3D,gBAAgB;EAEhB,MAAM,SAAS,CAAC,UAAU,mBAAmB;EAE7C,IAAI,SACF,OAAO,KAAK,SAAS,QAAQ,cAAc,UAAU;EAGvD,OAAO,SAAQ,UAAS;GACtB,IAAI,UAAU,YAAY,UAAU,qBAClC,OAAO,iBAAiB,OAAO,eAAe;QAE9C,OAAO,iBAAiB,OAAO,iBAAiB,EAAE,SAAS,KAAK,CAAC;EAErE,CAAC;EAED,IAAI,OAAO,gBAAgB;GACzB,OAAO,eAAe,iBAAiB,UAAU,eAAe;GAChE,OAAO,eAAe,iBAAiB,UAAU,eAAe;EAClE;EAEA,IAAI;EACJ,IAAI,SAAS;GACX,IAAI,aAAa,aAAa,CAAC,CAAC;GAChC,aAAa,kBAAkB;IAC7B,MAAM,gBAAgB,aAAa,CAAC,CAAC;IACrC,IAAI,KAAK,IAAI,gBAAgB,UAAU,IAAI,IAAI;KAC7C,aAAa;KACb,gBAAgB;IAClB;GACF,GAAG,GAAG;EACR;EAEA,aAAa;GACX,aAAa,SAAS;GACtB,IAAI,YAAY,cAAc,UAAU;GAExC,OAAO,SAAQ,UAAS;IACtB,OAAO,oBACL,OACA,UAAU,YAAY,UAAU,sBAC5B,kBACA,eACN;GACF,CAAC;GAED,IAAI,OAAO,gBAAgB;IACzB,OAAO,eAAe,oBAAoB,UAAU,eAAe;IACnE,OAAO,eAAe,oBAAoB,UAAU,eAAe;GACrE;EACF;CACF,GAAG;EAAC;EAAc;EAAS;CAAQ,CAAC;CAEpC,OAAO;AACT"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-window-scroll.d.ts
|
|
4
3
|
declare const useWindowScroll: (targetRef?: RefObject<Element | null>) => {
|
|
5
4
|
x: number;
|
|
@@ -10,5 +9,5 @@ declare const useWindowScroll: (targetRef?: RefObject<Element | null>) => {
|
|
|
10
9
|
};
|
|
11
10
|
};
|
|
12
11
|
//#endregion
|
|
13
|
-
export { useWindowScroll };
|
|
12
|
+
export { useWindowScroll as default };
|
|
14
13
|
//# sourceMappingURL=use-window-scroll.d.mts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-window-scroll.ts
|
|
4
3
|
const useWindowScroll = (targetRef) => {
|
|
5
4
|
const [state, setState] = useState({
|
|
@@ -78,7 +77,7 @@ const useWindowScroll = (targetRef) => {
|
|
|
78
77
|
}, [targetRef]);
|
|
79
78
|
return state;
|
|
80
79
|
};
|
|
81
|
-
|
|
82
80
|
//#endregion
|
|
83
81
|
export { useWindowScroll as default };
|
|
82
|
+
|
|
84
83
|
//# sourceMappingURL=use-window-scroll.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-window-scroll.mjs","names":[],"sources":["../../src/hooks/use-window-scroll.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from 'react';\n\n// Pass a ref to an element rendered inside the window you actually want to\n// track (e.g. a node portaled into an iframe). Without one, this defaults\n// to the host `window`, which is wrong whenever the caller doesn't live in\n// the top-level document.\nconst useWindowScroll = (targetRef?: RefObject<Element | null>) => {\n const [state, setState] = useState({\n x: 0,\n y: 0,\n percent: {\n x: 0,\n y: 0,\n },\n });\n\n useEffect(() => {\n let cancelled = false;\n let rafId: number | undefined;\n let cleanup: (() => void) | undefined;\n\n const attach = () => {\n if (cancelled) {\n return;\n }\n\n // `targetRef.current` can still be null right after mount (e.g. a\n // conditionally-rendered or portaled node) — retry every frame\n // until it's populated instead of permanently falling back to the\n // host `window`.\n if (targetRef && !targetRef.current) {\n rafId = requestAnimationFrame(attach);\n return;\n }\n\n const target = targetRef?.current?.ownerDocument.defaultView ?? window;\n const doc = target.document;\n\n const calculate = () => {\n const x = target.scrollX || 0;\n const y = target.scrollY || 0;\n\n const isIOS = /iPad|iPhone|iPod/.test(target.navigator.userAgent);\n const visualViewport = target.visualViewport;\n\n const viewportWidth =\n isIOS && visualViewport ? visualViewport.width : target.innerWidth;\n\n const viewportHeight =\n isIOS && visualViewport ? visualViewport.height : target.innerHeight;\n\n const maxScrollX = Math.max(\n 0,\n doc.documentElement.scrollWidth - viewportWidth,\n );\n const maxScrollY = Math.max(\n 0,\n doc.documentElement.scrollHeight - viewportHeight,\n );\n\n const percentX =\n maxScrollX === 0 ? 0 : Math.min(100, (x / maxScrollX) * 100);\n const percentY =\n maxScrollY === 0 ? 0 : Math.min(100, (y / maxScrollY) * 100);\n\n setState({\n x,\n y,\n percent: {\n // Rounded (not floored) so reaching the true bottom/right edge\n // reads as 100 instead of falling just short of it.\n x: Math.round(Math.max(0, percentX)),\n y: Math.round(Math.max(0, percentY)),\n },\n });\n };\n\n calculate();\n\n const onScroll = () => {\n calculate();\n };\n\n const onResize = () => {\n setTimeout(calculate, 100);\n };\n\n const onVisualViewportChange = () => {\n setTimeout(calculate, 50);\n };\n\n target.addEventListener('scroll', onScroll, { passive: true });\n target.addEventListener('resize', onResize);\n target.addEventListener('orientationchange', onResize);\n\n if (target.visualViewport) {\n target.visualViewport.addEventListener(\n 'resize',\n onVisualViewportChange,\n );\n }\n\n // `resize`/`orientationchange` only fire for the viewport itself —\n // content that grows/shrinks the document height without resizing\n // the window (images loading, dynamic content) needs its own\n // observer to keep maxScrollY accurate.\n let resizeObserver: ResizeObserver | undefined;\n if (typeof ResizeObserver !== 'undefined') {\n resizeObserver = new ResizeObserver(() => calculate());\n resizeObserver.observe(doc.documentElement);\n }\n\n cleanup = () => {\n target.removeEventListener('scroll', onScroll);\n target.removeEventListener('resize', onResize);\n target.removeEventListener('orientationchange', onResize);\n\n if (target.visualViewport) {\n target.visualViewport.removeEventListener(\n 'resize',\n onVisualViewportChange,\n );\n }\n\n resizeObserver?.disconnect();\n };\n };\n\n attach();\n\n return () => {\n cancelled = true;\n if (rafId !== undefined) {\n cancelAnimationFrame(rafId);\n }\n cleanup?.();\n };\n }, [targetRef]);\n\n return state;\n};\n\nexport default useWindowScroll;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-window-scroll.mjs","names":[],"sources":["../../src/hooks/use-window-scroll.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from 'react';\n\n// Pass a ref to an element rendered inside the window you actually want to\n// track (e.g. a node portaled into an iframe). Without one, this defaults\n// to the host `window`, which is wrong whenever the caller doesn't live in\n// the top-level document.\nconst useWindowScroll = (targetRef?: RefObject<Element | null>) => {\n const [state, setState] = useState({\n x: 0,\n y: 0,\n percent: {\n x: 0,\n y: 0,\n },\n });\n\n useEffect(() => {\n let cancelled = false;\n let rafId: number | undefined;\n let cleanup: (() => void) | undefined;\n\n const attach = () => {\n if (cancelled) {\n return;\n }\n\n // `targetRef.current` can still be null right after mount (e.g. a\n // conditionally-rendered or portaled node) — retry every frame\n // until it's populated instead of permanently falling back to the\n // host `window`.\n if (targetRef && !targetRef.current) {\n rafId = requestAnimationFrame(attach);\n return;\n }\n\n const target = targetRef?.current?.ownerDocument.defaultView ?? window;\n const doc = target.document;\n\n const calculate = () => {\n const x = target.scrollX || 0;\n const y = target.scrollY || 0;\n\n const isIOS = /iPad|iPhone|iPod/.test(target.navigator.userAgent);\n const visualViewport = target.visualViewport;\n\n const viewportWidth =\n isIOS && visualViewport ? visualViewport.width : target.innerWidth;\n\n const viewportHeight =\n isIOS && visualViewport ? visualViewport.height : target.innerHeight;\n\n const maxScrollX = Math.max(\n 0,\n doc.documentElement.scrollWidth - viewportWidth,\n );\n const maxScrollY = Math.max(\n 0,\n doc.documentElement.scrollHeight - viewportHeight,\n );\n\n const percentX =\n maxScrollX === 0 ? 0 : Math.min(100, (x / maxScrollX) * 100);\n const percentY =\n maxScrollY === 0 ? 0 : Math.min(100, (y / maxScrollY) * 100);\n\n setState({\n x,\n y,\n percent: {\n // Rounded (not floored) so reaching the true bottom/right edge\n // reads as 100 instead of falling just short of it.\n x: Math.round(Math.max(0, percentX)),\n y: Math.round(Math.max(0, percentY)),\n },\n });\n };\n\n calculate();\n\n const onScroll = () => {\n calculate();\n };\n\n const onResize = () => {\n setTimeout(calculate, 100);\n };\n\n const onVisualViewportChange = () => {\n setTimeout(calculate, 50);\n };\n\n target.addEventListener('scroll', onScroll, { passive: true });\n target.addEventListener('resize', onResize);\n target.addEventListener('orientationchange', onResize);\n\n if (target.visualViewport) {\n target.visualViewport.addEventListener(\n 'resize',\n onVisualViewportChange,\n );\n }\n\n // `resize`/`orientationchange` only fire for the viewport itself —\n // content that grows/shrinks the document height without resizing\n // the window (images loading, dynamic content) needs its own\n // observer to keep maxScrollY accurate.\n let resizeObserver: ResizeObserver | undefined;\n if (typeof ResizeObserver !== 'undefined') {\n resizeObserver = new ResizeObserver(() => calculate());\n resizeObserver.observe(doc.documentElement);\n }\n\n cleanup = () => {\n target.removeEventListener('scroll', onScroll);\n target.removeEventListener('resize', onResize);\n target.removeEventListener('orientationchange', onResize);\n\n if (target.visualViewport) {\n target.visualViewport.removeEventListener(\n 'resize',\n onVisualViewportChange,\n );\n }\n\n resizeObserver?.disconnect();\n };\n };\n\n attach();\n\n return () => {\n cancelled = true;\n if (rafId !== undefined) {\n cancelAnimationFrame(rafId);\n }\n cleanup?.();\n };\n }, [targetRef]);\n\n return state;\n};\n\nexport default useWindowScroll;\n"],"mappings":";;AAMA,MAAM,mBAAmB,cAA0C;CACjE,MAAM,CAAC,OAAO,YAAY,SAAS;EACjC,GAAG;EACH,GAAG;EACH,SAAS;GACP,GAAG;GACH,GAAG;EACL;CACF,CAAC;CAED,gBAAgB;EACd,IAAI,YAAY;EAChB,IAAI;EACJ,IAAI;EAEJ,MAAM,eAAe;GACnB,IAAI,WACF;GAOF,IAAI,aAAa,CAAC,UAAU,SAAS;IACnC,QAAQ,sBAAsB,MAAM;IACpC;GACF;GAEA,MAAM,SAAS,WAAW,SAAS,cAAc,eAAe;GAChE,MAAM,MAAM,OAAO;GAEnB,MAAM,kBAAkB;IACtB,MAAM,IAAI,OAAO,WAAW;IAC5B,MAAM,IAAI,OAAO,WAAW;IAE5B,MAAM,QAAQ,mBAAmB,KAAK,OAAO,UAAU,SAAS;IAChE,MAAM,iBAAiB,OAAO;IAE9B,MAAM,gBACJ,SAAS,iBAAiB,eAAe,QAAQ,OAAO;IAE1D,MAAM,iBACJ,SAAS,iBAAiB,eAAe,SAAS,OAAO;IAE3D,MAAM,aAAa,KAAK,IACtB,GACA,IAAI,gBAAgB,cAAc,aACpC;IACA,MAAM,aAAa,KAAK,IACtB,GACA,IAAI,gBAAgB,eAAe,cACrC;IAEA,MAAM,WACJ,eAAe,IAAI,IAAI,KAAK,IAAI,KAAM,IAAI,aAAc,GAAG;IAC7D,MAAM,WACJ,eAAe,IAAI,IAAI,KAAK,IAAI,KAAM,IAAI,aAAc,GAAG;IAE7D,SAAS;KACP;KACA;KACA,SAAS;MAGP,GAAG,KAAK,MAAM,KAAK,IAAI,GAAG,QAAQ,CAAC;MACnC,GAAG,KAAK,MAAM,KAAK,IAAI,GAAG,QAAQ,CAAC;KACrC;IACF,CAAC;GACH;GAEA,UAAU;GAEV,MAAM,iBAAiB;IACrB,UAAU;GACZ;GAEA,MAAM,iBAAiB;IACrB,WAAW,WAAW,GAAG;GAC3B;GAEA,MAAM,+BAA+B;IACnC,WAAW,WAAW,EAAE;GAC1B;GAEA,OAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;GAC7D,OAAO,iBAAiB,UAAU,QAAQ;GAC1C,OAAO,iBAAiB,qBAAqB,QAAQ;GAErD,IAAI,OAAO,gBACT,OAAO,eAAe,iBACpB,UACA,sBACF;GAOF,IAAI;GACJ,IAAI,OAAO,mBAAmB,aAAa;IACzC,iBAAiB,IAAI,qBAAqB,UAAU,CAAC;IACrD,eAAe,QAAQ,IAAI,eAAe;GAC5C;GAEA,gBAAgB;IACd,OAAO,oBAAoB,UAAU,QAAQ;IAC7C,OAAO,oBAAoB,UAAU,QAAQ;IAC7C,OAAO,oBAAoB,qBAAqB,QAAQ;IAExD,IAAI,OAAO,gBACT,OAAO,eAAe,oBACpB,UACA,sBACF;IAGF,gBAAgB,WAAW;GAC7B;EACF;EAEA,OAAO;EAEP,aAAa;GACX,YAAY;GACZ,IAAI,UAAU,KAAA,GACZ,qBAAqB,KAAK;GAE5B,UAAU;EACZ;CACF,GAAG,CAAC,SAAS,CAAC;CAEd,OAAO;AACT"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
-
import
|
|
29
|
-
import
|
|
30
|
-
import
|
|
1
|
+
import useDebouncedCallback from "./hooks/use-debounced-callback.mjs";
|
|
2
|
+
import useDebouncedValue from "./hooks/use-debounced-value.mjs";
|
|
3
|
+
import useBodyScrollLock from "./hooks/use-body-scroll-lock.mjs";
|
|
4
|
+
import useClickOutside from "./hooks/use-click-outside.mjs";
|
|
5
|
+
import useControllableState from "./hooks/use-controllable-state.mjs";
|
|
6
|
+
import useElementPosition from "./hooks/use-element-position.mjs";
|
|
7
|
+
import useElementScroll from "./hooks/use-element-scroll.mjs";
|
|
8
|
+
import useEventListener from "./hooks/use-event-listener.mjs";
|
|
9
|
+
import useFileDrop from "./hooks/use-file-drop.mjs";
|
|
10
|
+
import useFileToDataUrl from "./hooks/use-file-to-data-url.mjs";
|
|
11
|
+
import useHistoryState from "./hooks/use-history-state.mjs";
|
|
12
|
+
import useIntersectionObserver from "./hooks/use-intersection-observer.mjs";
|
|
13
|
+
import useInterval from "./hooks/use-interval.mjs";
|
|
14
|
+
import useResponsiveSize from "./hooks/use-responsive-size.mjs";
|
|
15
|
+
import useImage from "./hooks/use-image.mjs";
|
|
16
|
+
import useKeyPress from "./hooks/use-key-press.mjs";
|
|
17
|
+
import useLocalStorage from "./hooks/use-local-storage.mjs";
|
|
18
|
+
import useMergedRef from "./hooks/use-merged-ref.mjs";
|
|
19
|
+
import useMultiSelect from "./hooks/use-multi-select.mjs";
|
|
20
|
+
import useMutationObserver from "./hooks/use-mutation-observer.mjs";
|
|
21
|
+
import usePrevious from "./hooks/use-previous.mjs";
|
|
22
|
+
import useRecursiveTimeout from "./hooks/use-recursive-timeout.mjs";
|
|
23
|
+
import useResizeObserver from "./hooks/use-resize-observer.mjs";
|
|
24
|
+
import useScrollToElements from "./hooks/use-scroll-to-elements.mjs";
|
|
25
|
+
import useThrottledValue from "./hooks/use-throttled-value.mjs";
|
|
26
|
+
import useThrottledCallback from "./hooks/use-throttled-callback.mjs";
|
|
27
|
+
import useTimeout from "./hooks/use-timeout.mjs";
|
|
28
|
+
import useToggle from "./hooks/use-toggle.mjs";
|
|
29
|
+
import useWindowScroll from "./hooks/use-window-scroll.mjs";
|
|
30
|
+
import useViewport from "./hooks/use-viewport.mjs";
|
|
31
31
|
import "./hooks/index.mjs";
|
|
32
|
-
export { useBodyScrollLock, useClickOutside, useControllableState, useDebounce, useDebouncedValue, useElementPosition, useElementScroll, useEventListener, useFileDrop, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useInterval, useKeyPress, useLocalStorage, useMergedRef, useMultiSelect, useMutationObserver, usePrevious, useRecursiveTimeout, useResizeObserver, useResponsiveSize, useScrollToElements, useThrottle, useThrottledCallback, useTimeout, useToggle, useViewport, useWindowScroll };
|
|
32
|
+
export { useBodyScrollLock, useClickOutside, useControllableState, useDebouncedCallback as useDebounce, useDebouncedCallback, useDebouncedValue, useElementPosition, useElementScroll, useEventListener, useFileDrop, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useInterval, useKeyPress, useLocalStorage, useMergedRef, useMultiSelect, useMutationObserver, usePrevious, useRecursiveTimeout, useResizeObserver, useResponsiveSize, useScrollToElements, useThrottledValue as useThrottle, useThrottledCallback, useThrottledValue, useTimeout, useToggle, useViewport, useWindowScroll };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import useDebouncedCallback from "./hooks/use-debounced-callback.mjs";
|
|
2
2
|
import useDebouncedValue from "./hooks/use-debounced-value.mjs";
|
|
3
3
|
import useBodyScrollLock from "./hooks/use-body-scroll-lock.mjs";
|
|
4
4
|
import useClickOutside from "./hooks/use-click-outside.mjs";
|
|
@@ -23,11 +23,10 @@ import useRecursiveTimeout from "./hooks/use-recursive-timeout.mjs";
|
|
|
23
23
|
import useResizeObserver from "./hooks/use-resize-observer.mjs";
|
|
24
24
|
import useScrollToElements from "./hooks/use-scroll-to-elements.mjs";
|
|
25
25
|
import useThrottledCallback from "./hooks/use-throttled-callback.mjs";
|
|
26
|
-
import
|
|
26
|
+
import useThrottledValue from "./hooks/use-throttled-value.mjs";
|
|
27
27
|
import useTimeout from "./hooks/use-timeout.mjs";
|
|
28
28
|
import useToggle from "./hooks/use-toggle.mjs";
|
|
29
29
|
import useWindowScroll from "./hooks/use-window-scroll.mjs";
|
|
30
30
|
import useViewport from "./hooks/use-viewport.mjs";
|
|
31
31
|
import "./hooks/index.mjs";
|
|
32
|
-
|
|
33
|
-
export { useBodyScrollLock, useClickOutside, useControllableState, useDebounce, useDebouncedValue, useElementPosition, useElementScroll, useEventListener, useFileDrop, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useInterval, useKeyPress, useLocalStorage, useMergedRef, useMultiSelect, useMutationObserver, usePrevious, useRecursiveTimeout, useResizeObserver, useResponsiveSize, useScrollToElements, useThrottle, useThrottledCallback, useTimeout, useToggle, useViewport, useWindowScroll };
|
|
32
|
+
export { useBodyScrollLock, useClickOutside, useControllableState, useDebouncedCallback as useDebounce, useDebouncedCallback, useDebouncedValue, useElementPosition, useElementScroll, useEventListener, useFileDrop, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useInterval, useKeyPress, useLocalStorage, useMergedRef, useMultiSelect, useMutationObserver, usePrevious, useRecursiveTimeout, useResizeObserver, useResponsiveSize, useScrollToElements, useThrottledValue as useThrottle, useThrottledCallback, useThrottledValue, useTimeout, useToggle, useViewport, useWindowScroll };
|