@jbpark/use-hooks 4.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.
Files changed (95) hide show
  1. package/dist/hooks/index.d.mts +31 -30
  2. package/dist/hooks/index.mjs +1 -2
  3. package/dist/hooks/use-body-scroll-lock.d.mts +1 -1
  4. package/dist/hooks/use-body-scroll-lock.mjs +1 -2
  5. package/dist/hooks/use-body-scroll-lock.mjs.map +1 -1
  6. package/dist/hooks/use-click-outside.d.mts +1 -2
  7. package/dist/hooks/use-click-outside.mjs +3 -3
  8. package/dist/hooks/use-click-outside.mjs.map +1 -1
  9. package/dist/hooks/use-controllable-state.d.mts +1 -1
  10. package/dist/hooks/use-controllable-state.mjs +1 -2
  11. package/dist/hooks/use-controllable-state.mjs.map +1 -1
  12. package/dist/hooks/use-debounced-callback.d.mts +2 -6
  13. package/dist/hooks/use-debounced-callback.mjs +9 -9
  14. package/dist/hooks/use-debounced-callback.mjs.map +1 -1
  15. package/dist/hooks/use-debounced-value.d.mts +1 -1
  16. package/dist/hooks/use-debounced-value.mjs +1 -2
  17. package/dist/hooks/use-debounced-value.mjs.map +1 -1
  18. package/dist/hooks/use-element-position.d.mts +1 -2
  19. package/dist/hooks/use-element-position.mjs +1 -2
  20. package/dist/hooks/use-element-position.mjs.map +1 -1
  21. package/dist/hooks/use-element-scroll.d.mts +2 -4
  22. package/dist/hooks/use-element-scroll.mjs +3 -3
  23. package/dist/hooks/use-element-scroll.mjs.map +1 -1
  24. package/dist/hooks/use-event-listener.d.mts +1 -2
  25. package/dist/hooks/use-event-listener.mjs +1 -2
  26. package/dist/hooks/use-event-listener.mjs.map +1 -1
  27. package/dist/hooks/use-file-drop.d.mts +1 -1
  28. package/dist/hooks/use-file-drop.mjs +7 -6
  29. package/dist/hooks/use-file-drop.mjs.map +1 -1
  30. package/dist/hooks/use-file-to-data-url.d.mts +1 -1
  31. package/dist/hooks/use-file-to-data-url.mjs +1 -2
  32. package/dist/hooks/use-file-to-data-url.mjs.map +1 -1
  33. package/dist/hooks/use-history-state.d.mts +1 -1
  34. package/dist/hooks/use-history-state.mjs +3 -3
  35. package/dist/hooks/use-history-state.mjs.map +1 -1
  36. package/dist/hooks/use-image.d.mts +1 -1
  37. package/dist/hooks/use-image.mjs +24 -8
  38. package/dist/hooks/use-image.mjs.map +1 -1
  39. package/dist/hooks/use-intersection-observer.d.mts +1 -1
  40. package/dist/hooks/use-intersection-observer.mjs +1 -2
  41. package/dist/hooks/use-intersection-observer.mjs.map +1 -1
  42. package/dist/hooks/use-interval.d.mts +1 -1
  43. package/dist/hooks/use-interval.mjs +1 -2
  44. package/dist/hooks/use-interval.mjs.map +1 -1
  45. package/dist/hooks/use-key-press.d.mts +1 -2
  46. package/dist/hooks/use-key-press.mjs +7 -5
  47. package/dist/hooks/use-key-press.mjs.map +1 -1
  48. package/dist/hooks/use-local-storage.d.mts +1 -1
  49. package/dist/hooks/use-local-storage.mjs +5 -3
  50. package/dist/hooks/use-local-storage.mjs.map +1 -1
  51. package/dist/hooks/use-merged-ref.d.mts +1 -2
  52. package/dist/hooks/use-merged-ref.mjs +1 -2
  53. package/dist/hooks/use-merged-ref.mjs.map +1 -1
  54. package/dist/hooks/use-multi-select.d.mts +1 -1
  55. package/dist/hooks/use-multi-select.mjs +10 -9
  56. package/dist/hooks/use-multi-select.mjs.map +1 -1
  57. package/dist/hooks/use-mutation-observer.d.mts +1 -2
  58. package/dist/hooks/use-mutation-observer.mjs +3 -3
  59. package/dist/hooks/use-mutation-observer.mjs.map +1 -1
  60. package/dist/hooks/use-previous.d.mts +1 -1
  61. package/dist/hooks/use-previous.mjs +1 -2
  62. package/dist/hooks/use-previous.mjs.map +1 -1
  63. package/dist/hooks/use-recursive-timeout.d.mts +1 -1
  64. package/dist/hooks/use-recursive-timeout.mjs +1 -2
  65. package/dist/hooks/use-recursive-timeout.mjs.map +1 -1
  66. package/dist/hooks/use-resize-observer.d.mts +1 -1
  67. package/dist/hooks/use-resize-observer.mjs +1 -2
  68. package/dist/hooks/use-resize-observer.mjs.map +1 -1
  69. package/dist/hooks/use-responsive-size.d.mts +1 -1
  70. package/dist/hooks/use-responsive-size.mjs +1 -2
  71. package/dist/hooks/use-responsive-size.mjs.map +1 -1
  72. package/dist/hooks/use-scroll-to-elements.d.mts +1 -2
  73. package/dist/hooks/use-scroll-to-elements.mjs +1 -2
  74. package/dist/hooks/use-scroll-to-elements.mjs.map +1 -1
  75. package/dist/hooks/use-throttled-callback.d.mts +2 -5
  76. package/dist/hooks/use-throttled-callback.mjs +6 -8
  77. package/dist/hooks/use-throttled-callback.mjs.map +1 -1
  78. package/dist/hooks/use-throttled-value.d.mts +1 -1
  79. package/dist/hooks/use-throttled-value.mjs +1 -2
  80. package/dist/hooks/use-throttled-value.mjs.map +1 -1
  81. package/dist/hooks/use-timeout.d.mts +1 -1
  82. package/dist/hooks/use-timeout.mjs +1 -2
  83. package/dist/hooks/use-timeout.mjs.map +1 -1
  84. package/dist/hooks/use-toggle.d.mts +1 -1
  85. package/dist/hooks/use-toggle.mjs +1 -2
  86. package/dist/hooks/use-toggle.mjs.map +1 -1
  87. package/dist/hooks/use-viewport.d.mts +1 -1
  88. package/dist/hooks/use-viewport.mjs +1 -2
  89. package/dist/hooks/use-viewport.mjs.map +1 -1
  90. package/dist/hooks/use-window-scroll.d.mts +1 -2
  91. package/dist/hooks/use-window-scroll.mjs +1 -2
  92. package/dist/hooks/use-window-scroll.mjs.map +1 -1
  93. package/dist/index.d.mts +30 -30
  94. package/dist/index.mjs +1 -2
  95. package/package.json +26 -26
@@ -1,30 +1,31 @@
1
- import { useDebouncedCallback } from "./use-debounced-callback.mjs";
2
- import { useDebouncedValue } from "./use-debounced-value.mjs";
3
- import { useBodyScrollLock } from "./use-body-scroll-lock.mjs";
4
- import { useClickOutside } from "./use-click-outside.mjs";
5
- import { useControllableState } from "./use-controllable-state.mjs";
6
- import { useElementPosition } from "./use-element-position.mjs";
7
- import { useElementScroll } from "./use-element-scroll.mjs";
8
- import { useEventListener } from "./use-event-listener.mjs";
9
- import { useFileDrop } from "./use-file-drop.mjs";
10
- import { useFileToDataUrl } from "./use-file-to-data-url.mjs";
11
- import { useHistoryState } from "./use-history-state.mjs";
12
- import { useIntersectionObserver } from "./use-intersection-observer.mjs";
13
- import { useInterval } from "./use-interval.mjs";
14
- import { useResponsiveSize } from "./use-responsive-size.mjs";
15
- import { useImage } from "./use-image.mjs";
16
- import { useKeyPress } from "./use-key-press.mjs";
17
- import { useLocalStorage } from "./use-local-storage.mjs";
18
- import { useMergedRef } from "./use-merged-ref.mjs";
19
- import { useMultiSelect } from "./use-multi-select.mjs";
20
- import { useMutationObserver } from "./use-mutation-observer.mjs";
21
- import { usePrevious } from "./use-previous.mjs";
22
- import { useRecursiveTimeout } from "./use-recursive-timeout.mjs";
23
- import { useResizeObserver } from "./use-resize-observer.mjs";
24
- import { useScrollToElements } from "./use-scroll-to-elements.mjs";
25
- import { useThrottledValue } from "./use-throttled-value.mjs";
26
- import { useThrottledCallback } from "./use-throttled-callback.mjs";
27
- import { useTimeout } from "./use-timeout.mjs";
28
- import { useToggle } from "./use-toggle.mjs";
29
- import { useWindowScroll } from "./use-window-scroll.mjs";
30
- import { useViewport } from "./use-viewport.mjs";
1
+ import useDebouncedCallback from "./use-debounced-callback.mjs";
2
+ import useDebouncedValue from "./use-debounced-value.mjs";
3
+ import useBodyScrollLock from "./use-body-scroll-lock.mjs";
4
+ import useClickOutside from "./use-click-outside.mjs";
5
+ import useControllableState from "./use-controllable-state.mjs";
6
+ import useElementPosition from "./use-element-position.mjs";
7
+ import useElementScroll from "./use-element-scroll.mjs";
8
+ import useEventListener from "./use-event-listener.mjs";
9
+ import useFileDrop from "./use-file-drop.mjs";
10
+ import useFileToDataUrl from "./use-file-to-data-url.mjs";
11
+ import useHistoryState from "./use-history-state.mjs";
12
+ import useIntersectionObserver from "./use-intersection-observer.mjs";
13
+ import useInterval from "./use-interval.mjs";
14
+ import useResponsiveSize from "./use-responsive-size.mjs";
15
+ import useImage from "./use-image.mjs";
16
+ import useKeyPress from "./use-key-press.mjs";
17
+ import useLocalStorage from "./use-local-storage.mjs";
18
+ import useMergedRef from "./use-merged-ref.mjs";
19
+ import useMultiSelect from "./use-multi-select.mjs";
20
+ import useMutationObserver from "./use-mutation-observer.mjs";
21
+ import usePrevious from "./use-previous.mjs";
22
+ import useRecursiveTimeout from "./use-recursive-timeout.mjs";
23
+ import useResizeObserver from "./use-resize-observer.mjs";
24
+ import useScrollToElements from "./use-scroll-to-elements.mjs";
25
+ import useThrottledValue from "./use-throttled-value.mjs";
26
+ import useThrottledCallback from "./use-throttled-callback.mjs";
27
+ import useTimeout from "./use-timeout.mjs";
28
+ import useToggle from "./use-toggle.mjs";
29
+ import useWindowScroll from "./use-window-scroll.mjs";
30
+ import useViewport from "./use-viewport.mjs";
31
+ 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 };
@@ -28,5 +28,4 @@ import useTimeout from "./use-timeout.mjs";
28
28
  import useToggle from "./use-toggle.mjs";
29
29
  import useWindowScroll from "./use-window-scroll.mjs";
30
30
  import useViewport from "./use-viewport.mjs";
31
-
32
- export { };
31
+ 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 };
@@ -1,5 +1,5 @@
1
1
  //#region src/hooks/use-body-scroll-lock.d.ts
2
2
  declare const useBodyScrollLock: (enabled?: boolean) => void;
3
3
  //#endregion
4
- export { useBodyScrollLock };
4
+ export { useBodyScrollLock as default };
5
5
  //# sourceMappingURL=use-body-scroll-lock.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useEffect } from "react";
2
-
3
2
  //#region src/hooks/use-body-scroll-lock.ts
4
3
  const activeLocks = /* @__PURE__ */ new Set();
5
4
  let originalStyles = null;
@@ -86,7 +85,7 @@ const useBodyScrollLock = (enabled = true) => {
86
85
  };
87
86
  }, [enabled]);
88
87
  };
89
-
90
88
  //#endregion
91
89
  export { useBodyScrollLock as default };
90
+
92
91
  //# sourceMappingURL=use-body-scroll-lock.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-body-scroll-lock.mjs","names":[],"sources":["../../src/hooks/use-body-scroll-lock.ts"],"sourcesContent":["import { useEffect } from 'react';\n\ninterface LockedStyles {\n documentElement: {\n overflow: string;\n height: string;\n position: string;\n width: string;\n };\n body: {\n overflow: string;\n height: string;\n position: string;\n top: string;\n left: string;\n right: string;\n width: string;\n paddingRight: string;\n webkitOverflowScrolling: string;\n };\n}\n\n// A Set of per-instance tokens instead of a raw counter — deleting the same\n// token twice (or locking with a token that's already in the set) can't\n// drift the count the way increment/decrement pairs could.\nconst activeLocks = new Set<symbol>();\nlet originalStyles: LockedStyles | null = null;\nlet originalScrollY = 0;\n\nconst isIOS = () =>\n typeof navigator !== 'undefined' &&\n /iPad|iPhone|iPod/.test(navigator.userAgent);\n\nconst lockScroll = (token: symbol) => {\n if (activeLocks.size === 0) {\n originalScrollY = window.scrollY;\n const scrollbarWidth =\n window.innerWidth - document.documentElement.clientWidth;\n const currentPaddingRight =\n parseFloat(getComputedStyle(document.body).paddingRight) || 0;\n\n originalStyles = {\n documentElement: {\n overflow: document.documentElement.style.overflow,\n height: document.documentElement.style.height,\n position: document.documentElement.style.position,\n width: document.documentElement.style.width,\n },\n body: {\n overflow: document.body.style.overflow,\n height: document.body.style.height,\n position: document.body.style.position,\n top: document.body.style.top,\n left: document.body.style.left,\n right: document.body.style.right,\n width: document.body.style.width,\n paddingRight: document.body.style.paddingRight,\n webkitOverflowScrolling: document.body.style.getPropertyValue(\n '-webkit-overflow-scrolling',\n ),\n },\n };\n\n document.documentElement.style.overflow = 'hidden';\n document.documentElement.style.height = '100%';\n document.documentElement.style.position = 'fixed';\n document.documentElement.style.width = '100%';\n\n document.body.style.overflow = 'hidden';\n document.body.style.height = '100%';\n document.body.style.position = 'fixed';\n document.body.style.top = `-${originalScrollY}px`;\n document.body.style.left = '0';\n document.body.style.right = '0';\n document.body.style.width = '100%';\n\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`;\n }\n\n if (isIOS()) {\n document.body.style.setProperty('-webkit-overflow-scrolling', 'touch');\n }\n }\n\n activeLocks.add(token);\n};\n\nconst unlockScroll = (token: symbol) => {\n activeLocks.delete(token);\n\n if (activeLocks.size > 0 || !originalStyles) {\n return;\n }\n\n document.documentElement.style.overflow =\n originalStyles.documentElement.overflow;\n document.documentElement.style.height = originalStyles.documentElement.height;\n document.documentElement.style.position =\n originalStyles.documentElement.position;\n document.documentElement.style.width = originalStyles.documentElement.width;\n\n document.body.style.overflow = originalStyles.body.overflow;\n document.body.style.height = originalStyles.body.height;\n document.body.style.position = originalStyles.body.position;\n document.body.style.top = originalStyles.body.top;\n document.body.style.left = originalStyles.body.left;\n document.body.style.right = originalStyles.body.right;\n document.body.style.width = originalStyles.body.width;\n document.body.style.paddingRight = originalStyles.body.paddingRight;\n\n if (originalStyles.body.webkitOverflowScrolling) {\n document.body.style.setProperty(\n '-webkit-overflow-scrolling',\n originalStyles.body.webkitOverflowScrolling,\n );\n } else {\n document.body.style.removeProperty('-webkit-overflow-scrolling');\n }\n\n window.scrollTo(0, originalScrollY);\n originalStyles = null;\n};\n\nconst useBodyScrollLock = (enabled: boolean = true) => {\n useEffect(() => {\n if (typeof window === 'undefined' || !enabled) {\n return;\n }\n\n const token = Symbol('body-scroll-lock');\n lockScroll(token);\n\n // The position:fixed styles above are the real lock — this is only a\n // narrow safety net for iOS Safari, which can still rubber-band the\n // page on a touchmove that starts directly on the body/html background.\n // Only that one event is handled (no capture, no stopPropagation), so\n // a scrollable element inside the locked content (e.g. a modal body)\n // keeps scrolling normally — its touchmove target isn't body/html.\n let removeTouchListener: (() => void) | undefined;\n\n if (isIOS()) {\n const preventTouchMove = (event: TouchEvent) => {\n if (\n event.target === document.body ||\n event.target === document.documentElement\n ) {\n event.preventDefault();\n }\n };\n\n document.addEventListener('touchmove', preventTouchMove, {\n passive: false,\n });\n\n removeTouchListener = () => {\n document.removeEventListener('touchmove', preventTouchMove);\n };\n }\n\n return () => {\n removeTouchListener?.();\n unlockScroll(token);\n };\n }, [enabled]);\n};\n\nexport default useBodyScrollLock;\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-body-scroll-lock.mjs","names":[],"sources":["../../src/hooks/use-body-scroll-lock.ts"],"sourcesContent":["import { useEffect } from 'react';\n\ninterface LockedStyles {\n documentElement: {\n overflow: string;\n height: string;\n position: string;\n width: string;\n };\n body: {\n overflow: string;\n height: string;\n position: string;\n top: string;\n left: string;\n right: string;\n width: string;\n paddingRight: string;\n webkitOverflowScrolling: string;\n };\n}\n\n// A Set of per-instance tokens instead of a raw counter — deleting the same\n// token twice (or locking with a token that's already in the set) can't\n// drift the count the way increment/decrement pairs could.\nconst activeLocks = new Set<symbol>();\nlet originalStyles: LockedStyles | null = null;\nlet originalScrollY = 0;\n\nconst isIOS = () =>\n typeof navigator !== 'undefined' &&\n /iPad|iPhone|iPod/.test(navigator.userAgent);\n\nconst lockScroll = (token: symbol) => {\n if (activeLocks.size === 0) {\n originalScrollY = window.scrollY;\n const scrollbarWidth =\n window.innerWidth - document.documentElement.clientWidth;\n const currentPaddingRight =\n parseFloat(getComputedStyle(document.body).paddingRight) || 0;\n\n originalStyles = {\n documentElement: {\n overflow: document.documentElement.style.overflow,\n height: document.documentElement.style.height,\n position: document.documentElement.style.position,\n width: document.documentElement.style.width,\n },\n body: {\n overflow: document.body.style.overflow,\n height: document.body.style.height,\n position: document.body.style.position,\n top: document.body.style.top,\n left: document.body.style.left,\n right: document.body.style.right,\n width: document.body.style.width,\n paddingRight: document.body.style.paddingRight,\n webkitOverflowScrolling: document.body.style.getPropertyValue(\n '-webkit-overflow-scrolling',\n ),\n },\n };\n\n document.documentElement.style.overflow = 'hidden';\n document.documentElement.style.height = '100%';\n document.documentElement.style.position = 'fixed';\n document.documentElement.style.width = '100%';\n\n document.body.style.overflow = 'hidden';\n document.body.style.height = '100%';\n document.body.style.position = 'fixed';\n document.body.style.top = `-${originalScrollY}px`;\n document.body.style.left = '0';\n document.body.style.right = '0';\n document.body.style.width = '100%';\n\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`;\n }\n\n if (isIOS()) {\n document.body.style.setProperty('-webkit-overflow-scrolling', 'touch');\n }\n }\n\n activeLocks.add(token);\n};\n\nconst unlockScroll = (token: symbol) => {\n activeLocks.delete(token);\n\n if (activeLocks.size > 0 || !originalStyles) {\n return;\n }\n\n document.documentElement.style.overflow =\n originalStyles.documentElement.overflow;\n document.documentElement.style.height = originalStyles.documentElement.height;\n document.documentElement.style.position =\n originalStyles.documentElement.position;\n document.documentElement.style.width = originalStyles.documentElement.width;\n\n document.body.style.overflow = originalStyles.body.overflow;\n document.body.style.height = originalStyles.body.height;\n document.body.style.position = originalStyles.body.position;\n document.body.style.top = originalStyles.body.top;\n document.body.style.left = originalStyles.body.left;\n document.body.style.right = originalStyles.body.right;\n document.body.style.width = originalStyles.body.width;\n document.body.style.paddingRight = originalStyles.body.paddingRight;\n\n if (originalStyles.body.webkitOverflowScrolling) {\n document.body.style.setProperty(\n '-webkit-overflow-scrolling',\n originalStyles.body.webkitOverflowScrolling,\n );\n } else {\n document.body.style.removeProperty('-webkit-overflow-scrolling');\n }\n\n window.scrollTo(0, originalScrollY);\n originalStyles = null;\n};\n\nconst useBodyScrollLock = (enabled: boolean = true) => {\n useEffect(() => {\n if (typeof window === 'undefined' || !enabled) {\n return;\n }\n\n const token = Symbol('body-scroll-lock');\n lockScroll(token);\n\n // The position:fixed styles above are the real lock — this is only a\n // narrow safety net for iOS Safari, which can still rubber-band the\n // page on a touchmove that starts directly on the body/html background.\n // Only that one event is handled (no capture, no stopPropagation), so\n // a scrollable element inside the locked content (e.g. a modal body)\n // keeps scrolling normally — its touchmove target isn't body/html.\n let removeTouchListener: (() => void) | undefined;\n\n if (isIOS()) {\n const preventTouchMove = (event: TouchEvent) => {\n if (\n event.target === document.body ||\n event.target === document.documentElement\n ) {\n event.preventDefault();\n }\n };\n\n document.addEventListener('touchmove', preventTouchMove, {\n passive: false,\n });\n\n removeTouchListener = () => {\n document.removeEventListener('touchmove', preventTouchMove);\n };\n }\n\n return () => {\n removeTouchListener?.();\n unlockScroll(token);\n };\n }, [enabled]);\n};\n\nexport default useBodyScrollLock;\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { RefObject } from "react";
2
-
3
2
  //#region src/hooks/use-click-outside.d.ts
4
3
  type ClickOutsideRef = RefObject<HTMLElement | null> | null | undefined;
5
4
  type ClickOutsideEvent = PointerEvent | MouseEvent | TouchEvent | KeyboardEvent;
@@ -10,5 +9,5 @@ interface Options {
10
9
  }
11
10
  declare const useClickOutside: (refs: ClickOutsideRef | ClickOutsideRef[], handler: (event: ClickOutsideEvent) => void, options?: Options) => void;
12
11
  //#endregion
13
- export { useClickOutside };
12
+ export { useClickOutside as default };
14
13
  //# sourceMappingURL=use-click-outside.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useEffect, useRef } from "react";
2
-
3
2
  //#region src/hooks/use-click-outside.ts
4
3
  const useClickOutside = (refs, handler, options = {}) => {
5
4
  const { enabled = true, events = ["pointerdown"], escape = false } = options;
@@ -12,6 +11,7 @@ const useClickOutside = (refs, handler, options = {}) => {
12
11
  useEffect(() => {
13
12
  refListRef.current = refList;
14
13
  });
14
+ const eventsKey = events.join(",");
15
15
  useEffect(() => {
16
16
  if (!enabled || typeof document === "undefined") return;
17
17
  const isInside = (target) => refListRef.current.some((ref) => ref?.current?.contains(target));
@@ -36,10 +36,10 @@ const useClickOutside = (refs, handler, options = {}) => {
36
36
  }, [
37
37
  enabled,
38
38
  escape,
39
- events.join(",")
39
+ eventsKey
40
40
  ]);
41
41
  };
42
-
43
42
  //#endregion
44
43
  export { useClickOutside as default };
44
+
45
45
  //# sourceMappingURL=use-click-outside.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-click-outside.mjs","names":[],"sources":["../../src/hooks/use-click-outside.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ntype ClickOutsideRef = RefObject<HTMLElement | null> | null | undefined;\n\ntype ClickOutsideEvent = PointerEvent | MouseEvent | TouchEvent | KeyboardEvent;\n\ninterface Options {\n enabled?: boolean;\n events?: ('pointerdown' | 'mousedown' | 'touchstart')[];\n escape?: boolean;\n}\n\n// Takes the \"inside\" ref(s) as an argument instead of creating and\n// returning one — lets a caller pass multiple exclusion targets (e.g. a\n// dropdown's trigger button *and* its portaled panel, which live in\n// different DOM subtrees) instead of only ever watching one element.\nconst useClickOutside = (\n refs: ClickOutsideRef | ClickOutsideRef[],\n handler: (event: ClickOutsideEvent) => void,\n options: Options = {},\n) => {\n // `escape` is opt-in (default `false`): the hook is named for clicks, and\n // an always-on Escape handler both surprises callers who already handle\n // Escape themselves (double-fire) and closes every layer at once in\n // nested UI (a dropdown inside a modal). Turn it on where you want it.\n const { enabled = true, events = ['pointerdown'], escape = false } = options;\n\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n });\n\n const refList = Array.isArray(refs) ? refs : [refs];\n // `refs` is almost always an inline array literal at the call site, so\n // depending on it directly would tear down/rebuild the listeners on\n // every render — read the latest value from a ref instead.\n const refListRef = useRef(refList);\n\n useEffect(() => {\n refListRef.current = refList;\n });\n\n const eventsKey = events.join(',');\n\n useEffect(() => {\n if (!enabled || typeof document === 'undefined') {\n return;\n }\n\n const isInside = (target: Node) =>\n refListRef.current.some(ref => ref?.current?.contains(target));\n\n const onPointerEvent = (event: Event) => {\n const target = event.target as Node | null;\n\n if (!target || isInside(target)) {\n return;\n }\n\n handlerRef.current(event as ClickOutsideEvent);\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n handlerRef.current(event);\n }\n };\n\n events.forEach(eventName => {\n document.addEventListener(eventName, onPointerEvent);\n });\n\n if (escape) {\n document.addEventListener('keydown', onKeyDown);\n }\n\n return () => {\n events.forEach(eventName => {\n document.removeEventListener(eventName, onPointerEvent);\n });\n\n if (escape) {\n document.removeEventListener('keydown', onKeyDown);\n }\n };\n // `events` is covered by `eventsKey` below instead of the array\n // itself, which (like `refs`) is usually a fresh literal every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, escape, eventsKey]);\n};\n\nexport default useClickOutside;\n"],"mappings":";;;AAgBA,MAAM,mBACJ,MACA,SACA,UAAmB,EAAE,KAClB;CAKH,MAAM,EAAE,UAAU,MAAM,SAAS,CAAC,cAAc,EAAE,SAAS,UAAU;CAErE,MAAM,aAAa,OAAO,QAAQ;AAElC,iBAAgB;AACd,aAAW,UAAU;GACrB;CAEF,MAAM,UAAU,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC,KAAK;CAInD,MAAM,aAAa,OAAO,QAAQ;AAElC,iBAAgB;AACd,aAAW,UAAU;GACrB;AAIF,iBAAgB;AACd,MAAI,CAAC,WAAW,OAAO,aAAa,YAClC;EAGF,MAAM,YAAY,WAChB,WAAW,QAAQ,MAAK,QAAO,KAAK,SAAS,SAAS,OAAO,CAAC;EAEhE,MAAM,kBAAkB,UAAiB;GACvC,MAAM,SAAS,MAAM;AAErB,OAAI,CAAC,UAAU,SAAS,OAAO,CAC7B;AAGF,cAAW,QAAQ,MAA2B;;EAGhD,MAAM,aAAa,UAAyB;AAC1C,OAAI,MAAM,QAAQ,SAChB,YAAW,QAAQ,MAAM;;AAI7B,SAAO,SAAQ,cAAa;AAC1B,YAAS,iBAAiB,WAAW,eAAe;IACpD;AAEF,MAAI,OACF,UAAS,iBAAiB,WAAW,UAAU;AAGjD,eAAa;AACX,UAAO,SAAQ,cAAa;AAC1B,aAAS,oBAAoB,WAAW,eAAe;KACvD;AAEF,OAAI,OACF,UAAS,oBAAoB,WAAW,UAAU;;IAMrD;EAAC;EAAS;EA9CK,OAAO,KAAK,IAAI;EA8CH,CAAC"}
1
+ {"version":3,"file":"use-click-outside.mjs","names":[],"sources":["../../src/hooks/use-click-outside.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ntype ClickOutsideRef = RefObject<HTMLElement | null> | null | undefined;\n\ntype ClickOutsideEvent = PointerEvent | MouseEvent | TouchEvent | KeyboardEvent;\n\ninterface Options {\n enabled?: boolean;\n events?: ('pointerdown' | 'mousedown' | 'touchstart')[];\n escape?: boolean;\n}\n\n// Takes the \"inside\" ref(s) as an argument instead of creating and\n// returning one — lets a caller pass multiple exclusion targets (e.g. a\n// dropdown's trigger button *and* its portaled panel, which live in\n// different DOM subtrees) instead of only ever watching one element.\nconst useClickOutside = (\n refs: ClickOutsideRef | ClickOutsideRef[],\n handler: (event: ClickOutsideEvent) => void,\n options: Options = {},\n) => {\n // `escape` is opt-in (default `false`): the hook is named for clicks, and\n // an always-on Escape handler both surprises callers who already handle\n // Escape themselves (double-fire) and closes every layer at once in\n // nested UI (a dropdown inside a modal). Turn it on where you want it.\n const { enabled = true, events = ['pointerdown'], escape = false } = options;\n\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n });\n\n const refList = Array.isArray(refs) ? refs : [refs];\n // `refs` is almost always an inline array literal at the call site, so\n // depending on it directly would tear down/rebuild the listeners on\n // every render — read the latest value from a ref instead.\n const refListRef = useRef(refList);\n\n useEffect(() => {\n refListRef.current = refList;\n });\n\n const eventsKey = events.join(',');\n\n useEffect(() => {\n if (!enabled || typeof document === 'undefined') {\n return;\n }\n\n const isInside = (target: Node) =>\n refListRef.current.some(ref => ref?.current?.contains(target));\n\n const onPointerEvent = (event: Event) => {\n const target = event.target as Node | null;\n\n if (!target || isInside(target)) {\n return;\n }\n\n handlerRef.current(event as ClickOutsideEvent);\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n handlerRef.current(event);\n }\n };\n\n events.forEach(eventName => {\n document.addEventListener(eventName, onPointerEvent);\n });\n\n if (escape) {\n document.addEventListener('keydown', onKeyDown);\n }\n\n return () => {\n events.forEach(eventName => {\n document.removeEventListener(eventName, onPointerEvent);\n });\n\n if (escape) {\n document.removeEventListener('keydown', onKeyDown);\n }\n };\n // `events` is covered by `eventsKey` below instead of the array\n // itself, which (like `refs`) is usually a fresh literal every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, escape, eventsKey]);\n};\n\nexport default useClickOutside;\n"],"mappings":";;AAgBA,MAAM,mBACJ,MACA,SACA,UAAmB,CAAC,MACjB;CAKH,MAAM,EAAE,UAAU,MAAM,SAAS,CAAC,aAAa,GAAG,SAAS,UAAU;CAErE,MAAM,aAAa,OAAO,OAAO;CAEjC,gBAAgB;EACd,WAAW,UAAU;CACvB,CAAC;CAED,MAAM,UAAU,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;CAIlD,MAAM,aAAa,OAAO,OAAO;CAEjC,gBAAgB;EACd,WAAW,UAAU;CACvB,CAAC;CAED,MAAM,YAAY,OAAO,KAAK,GAAG;CAEjC,gBAAgB;EACd,IAAI,CAAC,WAAW,OAAO,aAAa,aAClC;EAGF,MAAM,YAAY,WAChB,WAAW,QAAQ,MAAK,QAAO,KAAK,SAAS,SAAS,MAAM,CAAC;EAE/D,MAAM,kBAAkB,UAAiB;GACvC,MAAM,SAAS,MAAM;GAErB,IAAI,CAAC,UAAU,SAAS,MAAM,GAC5B;GAGF,WAAW,QAAQ,KAA0B;EAC/C;EAEA,MAAM,aAAa,UAAyB;GAC1C,IAAI,MAAM,QAAQ,UAChB,WAAW,QAAQ,KAAK;EAE5B;EAEA,OAAO,SAAQ,cAAa;GAC1B,SAAS,iBAAiB,WAAW,cAAc;EACrD,CAAC;EAED,IAAI,QACF,SAAS,iBAAiB,WAAW,SAAS;EAGhD,aAAa;GACX,OAAO,SAAQ,cAAa;IAC1B,SAAS,oBAAoB,WAAW,cAAc;GACxD,CAAC;GAED,IAAI,QACF,SAAS,oBAAoB,WAAW,SAAS;EAErD;CAIF,GAAG;EAAC;EAAS;EAAQ;CAAS,CAAC;AACjC"}
@@ -9,5 +9,5 @@ declare function useControllableState<T>(props: UseControllableStateProps<T> & {
9
9
  }): [T, (next: T) => void];
10
10
  declare function useControllableState<T>(props: UseControllableStateProps<T>): [T | undefined, (next: T) => void];
11
11
  //#endregion
12
- export { useControllableState };
12
+ export { useControllableState as default };
13
13
  //# sourceMappingURL=use-controllable-state.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
-
3
2
  //#region src/hooks/use-controllable-state.ts
4
3
  function useControllableState({ value, defaultValue, onChange }) {
5
4
  const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
@@ -15,7 +14,7 @@ function useControllableState({ value, defaultValue, onChange }) {
15
14
  onChange?.(next);
16
15
  }, [isControlled, onChange])];
17
16
  }
18
-
19
17
  //#endregion
20
18
  export { useControllableState as default };
19
+
21
20
  //# sourceMappingURL=use-controllable-state.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-controllable-state.mjs","names":[],"sources":["../../src/hooks/use-controllable-state.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface UseControllableStateProps<T> {\n value?: T;\n defaultValue?: T;\n onChange?: (value: T) => void;\n}\n\n// Backs a controlled/uncontrolled prop pair (`value`/`defaultValue`/\n// `onChange`) with one hook: falls back to internal state whenever `value`\n// is `undefined`, and always calls `onChange` regardless of which mode is\n// active, so callers don't have to branch on it themselves.\n//\n// `value === undefined` is what marks a render as uncontrolled, so it can't\n// also be used as a real, controlled value of `undefined` — that's an\n// inherent limitation of this \"sentinel\" style of controlled-state\n// detection (shared by most hooks of this shape, including Radix's own),\n// not something fixable without a different API (e.g. an explicit\n// `controlled` boolean prop instead of inferring it from `value`).\nfunction useControllableState<T>(\n props: UseControllableStateProps<T> & { defaultValue: T },\n): [T, (next: T) => void];\nfunction useControllableState<T>(\n props: UseControllableStateProps<T>,\n): [T | undefined, (next: T) => void];\nfunction useControllableState<T>({\n value,\n defaultValue,\n onChange,\n}: UseControllableStateProps<T>) {\n const [uncontrolledValue, setUncontrolledValue] = useState<T | undefined>(\n defaultValue,\n );\n\n const isControlled = value !== undefined;\n const currentValue = isControlled ? value : uncontrolledValue;\n\n const wasControlledRef = useRef(isControlled);\n\n useEffect(() => {\n // Read `process.env.NODE_ENV` through `globalThis` rather than the\n // bare `process` global — this hook ships as plain ESM and can end up\n // somewhere with no bundler define for it (or no `process` at all, and\n // no @types/node in scope to even name it), where referencing it\n // directly would fail instead of just missing a warning.\n const nodeEnv = (\n globalThis as { process?: { env?: { NODE_ENV?: string } } }\n ).process?.env?.NODE_ENV;\n const isProduction = nodeEnv === 'production';\n\n if (!isProduction && wasControlledRef.current !== isControlled) {\n console.warn(\n `useControllableState is changing from ${\n wasControlledRef.current ? 'controlled' : 'uncontrolled'\n } to ${\n isControlled ? 'controlled' : 'uncontrolled'\n }. A component should not switch between controlled and uncontrolled — decide on one and stick with it (e.g. by always passing a non-undefined \\`value\\`, or never passing one).`,\n );\n }\n wasControlledRef.current = isControlled;\n }, [isControlled]);\n\n const setValue = useCallback(\n (next: T) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n return [currentValue, setValue];\n}\n\nexport default useControllableState;\n"],"mappings":";;;AAyBA,SAAS,qBAAwB,EAC/B,OACA,cACA,YAC+B;CAC/B,MAAM,CAAC,mBAAmB,wBAAwB,SAChD,aACD;CAED,MAAM,eAAe,UAAU;CAC/B,MAAM,eAAe,eAAe,QAAQ;CAE5C,MAAM,mBAAmB,OAAO,aAAa;AAE7C,iBAAgB;AAWd,MAAI,EAJF,WACA,SAAS,KAAK,aACiB,iBAEZ,iBAAiB,YAAY,aAChD,SAAQ,KACN,yCACE,iBAAiB,UAAU,eAAe,eAC3C,MACC,eAAe,eAAe,eAC/B,iLACF;AAEH,mBAAiB,UAAU;IAC1B,CAAC,aAAa,CAAC;AAYlB,QAAO,CAAC,cAVS,aACd,SAAY;AACX,MAAI,CAAC,aACH,sBAAqB,KAAK;AAE5B,aAAW,KAAK;IAElB,CAAC,cAAc,SAAS,CACzB,CAE8B"}
1
+ {"version":3,"file":"use-controllable-state.mjs","names":[],"sources":["../../src/hooks/use-controllable-state.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface UseControllableStateProps<T> {\n value?: T;\n defaultValue?: T;\n onChange?: (value: T) => void;\n}\n\n// Backs a controlled/uncontrolled prop pair (`value`/`defaultValue`/\n// `onChange`) with one hook: falls back to internal state whenever `value`\n// is `undefined`, and always calls `onChange` regardless of which mode is\n// active, so callers don't have to branch on it themselves.\n//\n// `value === undefined` is what marks a render as uncontrolled, so it can't\n// also be used as a real, controlled value of `undefined` — that's an\n// inherent limitation of this \"sentinel\" style of controlled-state\n// detection (shared by most hooks of this shape, including Radix's own),\n// not something fixable without a different API (e.g. an explicit\n// `controlled` boolean prop instead of inferring it from `value`).\nfunction useControllableState<T>(\n props: UseControllableStateProps<T> & { defaultValue: T },\n): [T, (next: T) => void];\nfunction useControllableState<T>(\n props: UseControllableStateProps<T>,\n): [T | undefined, (next: T) => void];\nfunction useControllableState<T>({\n value,\n defaultValue,\n onChange,\n}: UseControllableStateProps<T>) {\n const [uncontrolledValue, setUncontrolledValue] = useState<T | undefined>(\n defaultValue,\n );\n\n const isControlled = value !== undefined;\n const currentValue = isControlled ? value : uncontrolledValue;\n\n const wasControlledRef = useRef(isControlled);\n\n useEffect(() => {\n // Read `process.env.NODE_ENV` through `globalThis` rather than the\n // bare `process` global — this hook ships as plain ESM and can end up\n // somewhere with no bundler define for it (or no `process` at all, and\n // no @types/node in scope to even name it), where referencing it\n // directly would fail instead of just missing a warning.\n const nodeEnv = (\n globalThis as { process?: { env?: { NODE_ENV?: string } } }\n ).process?.env?.NODE_ENV;\n const isProduction = nodeEnv === 'production';\n\n if (!isProduction && wasControlledRef.current !== isControlled) {\n console.warn(\n `useControllableState is changing from ${\n wasControlledRef.current ? 'controlled' : 'uncontrolled'\n } to ${\n isControlled ? 'controlled' : 'uncontrolled'\n }. A component should not switch between controlled and uncontrolled — decide on one and stick with it (e.g. by always passing a non-undefined \\`value\\`, or never passing one).`,\n );\n }\n wasControlledRef.current = isControlled;\n }, [isControlled]);\n\n const setValue = useCallback(\n (next: T) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n return [currentValue, setValue];\n}\n\nexport default useControllableState;\n"],"mappings":";;AAyBA,SAAS,qBAAwB,EAC/B,OACA,cACA,YAC+B;CAC/B,MAAM,CAAC,mBAAmB,wBAAwB,SAChD,YACF;CAEA,MAAM,eAAe,UAAU,KAAA;CAC/B,MAAM,eAAe,eAAe,QAAQ;CAE5C,MAAM,mBAAmB,OAAO,YAAY;CAE5C,gBAAgB;EAWd,IAAI,EAJF,WACA,SAAS,KAAK,aACiB,iBAEZ,iBAAiB,YAAY,cAChD,QAAQ,KACN,yCACE,iBAAiB,UAAU,eAAe,eAC3C,MACC,eAAe,eAAe,eAC/B,gLACH;EAEF,iBAAiB,UAAU;CAC7B,GAAG,CAAC,YAAY,CAAC;CAYjB,OAAO,CAAC,cAVS,aACd,SAAY;EACX,IAAI,CAAC,cACH,qBAAqB,IAAI;EAE3B,WAAW,IAAI;CACjB,GACA,CAAC,cAAc,QAAQ,CAGI,CAAC;AAChC"}
@@ -4,11 +4,7 @@ interface Options {
4
4
  autoInvoke?: boolean;
5
5
  leading?: boolean;
6
6
  }
7
- declare const useDebouncedCallback: (callback: () => unknown, {
8
- delay,
9
- autoInvoke,
10
- leading
11
- }: Options, deps?: React.DependencyList) => () => void;
7
+ declare const useDebouncedCallback: (callback: () => unknown, { delay, autoInvoke, leading }: Options, deps?: React.DependencyList) => () => void;
12
8
  //#endregion
13
- export { useDebouncedCallback };
9
+ export { useDebouncedCallback as default };
14
10
  //# sourceMappingURL=use-debounced-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-debounced-callback.ts
4
3
  const useDebouncedCallback = (callback, { delay = 100, autoInvoke = true, leading = true }, deps = []) => {
5
4
  const timeoutRef = useRef(null);
@@ -10,18 +9,19 @@ const useDebouncedCallback = (callback, { delay = 100, autoInvoke = true, leadin
10
9
  callbackRef.current = callback;
11
10
  delayRef.current = delay;
12
11
  });
13
- const stableDebouncedCallback = useRef(null);
14
- if (!stableDebouncedCallback.current) stableDebouncedCallback.current = () => {
12
+ const [stableDebouncedCallback] = useState(() => () => {
15
13
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
16
14
  timeoutRef.current = setTimeout(() => {
17
15
  callbackRef.current();
18
16
  }, delayRef.current);
19
- };
17
+ });
20
18
  useEffect(() => {
21
19
  const depsChanged = prevDeps.current === void 0 || prevDeps.current.length !== deps.length || prevDeps.current.some((dep, i) => dep !== deps[i]);
22
20
  if (depsChanged && timeoutRef.current) clearTimeout(timeoutRef.current);
23
- if (autoInvoke && depsChanged) if (prevDeps.current === void 0 && leading) callbackRef.current();
24
- else stableDebouncedCallback.current?.();
21
+ if (autoInvoke && depsChanged) {
22
+ if (prevDeps.current === void 0 && leading) callbackRef.current();
23
+ else stableDebouncedCallback();
24
+ }
25
25
  prevDeps.current = deps;
26
26
  });
27
27
  useEffect(() => {
@@ -29,9 +29,9 @@ const useDebouncedCallback = (callback, { delay = 100, autoInvoke = true, leadin
29
29
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
30
30
  };
31
31
  }, []);
32
- return stableDebouncedCallback.current;
32
+ return stableDebouncedCallback;
33
33
  };
34
-
35
34
  //#endregion
36
35
  export { useDebouncedCallback as default };
36
+
37
37
  //# sourceMappingURL=use-debounced-callback.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-debounced-callback.mjs","names":[],"sources":["../../src/hooks/use-debounced-callback.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\ninterface Options {\n delay?: number;\n autoInvoke?: boolean;\n leading?: boolean;\n}\n\n// Auto-invokes `callback` (debounced by `delay`ms) whenever `deps` change,\n// and also returns a stable debounced version of `callback` for manual use.\n// `callback` intentionally takes no arguments — the deps-triggered\n// auto-invoke has no natural argument to supply, so an arg-taking callback\n// here was a latent type hole (it was always invoked with zero args\n// regardless of what the callback's own signature claimed).\nconst useDebouncedCallback = (\n callback: () => unknown,\n { delay = 100, autoInvoke = true, leading = true }: Options,\n deps: React.DependencyList = [],\n) => {\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const callbackRef = useRef(callback);\n // Read the latest `delay` when the timeout actually fires, instead of\n // baking in whatever `delay` was on the render that created the stable\n // debounced function (which otherwise never updates again).\n const delayRef = useRef(delay);\n const prevDeps = useRef<React.DependencyList | undefined>(undefined);\n\n useEffect(() => {\n callbackRef.current = callback;\n delayRef.current = delay;\n });\n\n const stableDebouncedCallback = useRef<(() => void) | null>(null);\n\n if (!stableDebouncedCallback.current) {\n stableDebouncedCallback.current = () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n callbackRef.current();\n }, delayRef.current);\n };\n }\n\n useEffect(() => {\n const depsChanged =\n prevDeps.current === undefined ||\n prevDeps.current.length !== deps.length ||\n prevDeps.current.some((dep, i) => dep !== deps[i]);\n\n if (depsChanged && timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n if (autoInvoke && depsChanged) {\n const isFirstRender = prevDeps.current === undefined;\n\n // `leading` makes the \"first invocation fires immediately, without\n // waiting `delay`ms\" behavior explicit and opt-out-able, instead of\n // an unconditional special case baked into the first render.\n if (isFirstRender && leading) {\n callbackRef.current();\n } else {\n stableDebouncedCallback.current?.();\n }\n }\n\n prevDeps.current = deps;\n });\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n return stableDebouncedCallback.current;\n};\n\nexport default useDebouncedCallback;\n"],"mappings":";;;AAcA,MAAM,wBACJ,UACA,EAAE,QAAQ,KAAK,aAAa,MAAM,UAAU,QAC5C,OAA6B,EAAE,KAC5B;CACH,MAAM,aAAa,OAA6C,KAAK;CACrE,MAAM,cAAc,OAAO,SAAS;CAIpC,MAAM,WAAW,OAAO,MAAM;CAC9B,MAAM,WAAW,OAAyC,OAAU;AAEpE,iBAAgB;AACd,cAAY,UAAU;AACtB,WAAS,UAAU;GACnB;CAEF,MAAM,0BAA0B,OAA4B,KAAK;AAEjE,KAAI,CAAC,wBAAwB,QAC3B,yBAAwB,gBAAgB;AACtC,MAAI,WAAW,QACb,cAAa,WAAW,QAAQ;AAGlC,aAAW,UAAU,iBAAiB;AACpC,eAAY,SAAS;KACpB,SAAS,QAAQ;;AAIxB,iBAAgB;EACd,MAAM,cACJ,SAAS,YAAY,UACrB,SAAS,QAAQ,WAAW,KAAK,UACjC,SAAS,QAAQ,MAAM,KAAK,MAAM,QAAQ,KAAK,GAAG;AAEpD,MAAI,eAAe,WAAW,QAC5B,cAAa,WAAW,QAAQ;AAGlC,MAAI,cAAc,YAMhB,KALsB,SAAS,YAAY,UAKtB,QACnB,aAAY,SAAS;MAErB,yBAAwB,WAAW;AAIvC,WAAS,UAAU;GACnB;AAEF,iBAAgB;AACd,eAAa;AACX,OAAI,WAAW,QACb,cAAa,WAAW,QAAQ;;IAGnC,EAAE,CAAC;AAEN,QAAO,wBAAwB"}
1
+ {"version":3,"file":"use-debounced-callback.mjs","names":[],"sources":["../../src/hooks/use-debounced-callback.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\ninterface Options {\n delay?: number;\n autoInvoke?: boolean;\n leading?: boolean;\n}\n\n// Auto-invokes `callback` (debounced by `delay`ms) whenever `deps` change,\n// and also returns a stable debounced version of `callback` for manual use.\n// `callback` intentionally takes no arguments — the deps-triggered\n// auto-invoke has no natural argument to supply, so an arg-taking callback\n// here was a latent type hole (it was always invoked with zero args\n// regardless of what the callback's own signature claimed).\nconst useDebouncedCallback = (\n callback: () => unknown,\n { delay = 100, autoInvoke = true, leading = true }: Options,\n deps: React.DependencyList = [],\n) => {\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const callbackRef = useRef(callback);\n // Read the latest `delay` when the timeout actually fires, instead of\n // baking in whatever `delay` was on the render that created the stable\n // debounced function (which otherwise never updates again).\n const delayRef = useRef(delay);\n const prevDeps = useRef<React.DependencyList | undefined>(undefined);\n\n useEffect(() => {\n callbackRef.current = callback;\n delayRef.current = delay;\n });\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 [stableDebouncedCallback] = useState(() => () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n callbackRef.current();\n }, delayRef.current);\n });\n\n useEffect(() => {\n const depsChanged =\n prevDeps.current === undefined ||\n prevDeps.current.length !== deps.length ||\n prevDeps.current.some((dep, i) => dep !== deps[i]);\n\n if (depsChanged && timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n if (autoInvoke && depsChanged) {\n const isFirstRender = prevDeps.current === undefined;\n\n // `leading` makes the \"first invocation fires immediately, without\n // waiting `delay`ms\" behavior explicit and opt-out-able, instead of\n // an unconditional special case baked into the first render.\n if (isFirstRender && leading) {\n callbackRef.current();\n } else {\n stableDebouncedCallback();\n }\n }\n\n prevDeps.current = deps;\n });\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n return stableDebouncedCallback;\n};\n\nexport default useDebouncedCallback;\n"],"mappings":";;AAcA,MAAM,wBACJ,UACA,EAAE,QAAQ,KAAK,aAAa,MAAM,UAAU,QAC5C,OAA6B,CAAC,MAC3B;CACH,MAAM,aAAa,OAA6C,IAAI;CACpE,MAAM,cAAc,OAAO,QAAQ;CAInC,MAAM,WAAW,OAAO,KAAK;CAC7B,MAAM,WAAW,OAAyC,KAAA,CAAS;CAEnE,gBAAgB;EACd,YAAY,UAAU;EACtB,SAAS,UAAU;CACrB,CAAC;CAOD,MAAM,CAAC,2BAA2B,qBAAqB;EACrD,IAAI,WAAW,SACb,aAAa,WAAW,OAAO;EAGjC,WAAW,UAAU,iBAAiB;GACpC,YAAY,QAAQ;EACtB,GAAG,SAAS,OAAO;CACrB,CAAC;CAED,gBAAgB;EACd,MAAM,cACJ,SAAS,YAAY,KAAA,KACrB,SAAS,QAAQ,WAAW,KAAK,UACjC,SAAS,QAAQ,MAAM,KAAK,MAAM,QAAQ,KAAK,EAAE;EAEnD,IAAI,eAAe,WAAW,SAC5B,aAAa,WAAW,OAAO;EAGjC,IAAI,cAAc,aAAa;GAM7B,IALsB,SAAS,YAAY,KAAA,KAKtB,SACnB,YAAY,QAAQ;QAEpB,wBAAwB;EAE5B;EAEA,SAAS,UAAU;CACrB,CAAC;CAED,gBAAgB;EACd,aAAa;GACX,IAAI,WAAW,SACb,aAAa,WAAW,OAAO;EAEnC;CACF,GAAG,CAAC,CAAC;CAEL,OAAO;AACT"}
@@ -1,5 +1,5 @@
1
1
  //#region src/hooks/use-debounced-value.d.ts
2
2
  declare const useDebouncedValue: <T>(value: T, delay?: number) => T;
3
3
  //#endregion
4
- export { useDebouncedValue };
4
+ export { useDebouncedValue as default };
5
5
  //# sourceMappingURL=use-debounced-value.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
-
3
2
  //#region src/hooks/use-debounced-value.ts
4
3
  const useDebouncedValue = (value, delay = 100) => {
5
4
  const [debouncedValue, setDebouncedValue] = useState(value);
@@ -14,7 +13,7 @@ const useDebouncedValue = (value, delay = 100) => {
14
13
  }, [value, delay]);
15
14
  return debouncedValue;
16
15
  };
17
-
18
16
  //#endregion
19
17
  export { useDebouncedValue as default };
18
+
20
19
  //# sourceMappingURL=use-debounced-value.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-debounced-value.mjs","names":[],"sources":["../../src/hooks/use-debounced-value.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\n// The value-shaped counterpart to useDebounce (which is callback-shaped),\n// symmetric with useThrottle's `(value, delay) => value` signature. The\n// common \"just give me the debounced value\" case otherwise needs two\n// pieces of state and an effect built on useDebounce by hand.\nconst useDebouncedValue = <T>(value: T, delay = 100): T => {\n const [debouncedValue, setDebouncedValue] = useState(value);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n timeoutRef.current = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [value, delay]);\n\n return debouncedValue;\n};\n\nexport default useDebouncedValue;\n"],"mappings":";;;AAMA,MAAM,qBAAwB,OAAU,QAAQ,QAAW;CACzD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,MAAM;CAC3D,MAAM,aAAa,OAA6C,KAAK;AAErE,iBAAgB;AACd,aAAW,UAAU,iBAAiB;AACpC,qBAAkB,MAAM;KACvB,MAAM;AAET,eAAa;AACX,OAAI,WAAW,QACb,cAAa,WAAW,QAAQ;;IAGnC,CAAC,OAAO,MAAM,CAAC;AAElB,QAAO"}
1
+ {"version":3,"file":"use-debounced-value.mjs","names":[],"sources":["../../src/hooks/use-debounced-value.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\n// The value-shaped counterpart to useDebounce (which is callback-shaped),\n// symmetric with useThrottle's `(value, delay) => value` signature. The\n// common \"just give me the debounced value\" case otherwise needs two\n// pieces of state and an effect built on useDebounce by hand.\nconst useDebouncedValue = <T>(value: T, delay = 100): T => {\n const [debouncedValue, setDebouncedValue] = useState(value);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n timeoutRef.current = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [value, delay]);\n\n return debouncedValue;\n};\n\nexport default useDebouncedValue;\n"],"mappings":";;AAMA,MAAM,qBAAwB,OAAU,QAAQ,QAAW;CACzD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,KAAK;CAC1D,MAAM,aAAa,OAA6C,IAAI;CAEpE,gBAAgB;EACd,WAAW,UAAU,iBAAiB;GACpC,kBAAkB,KAAK;EACzB,GAAG,KAAK;EAER,aAAa;GACX,IAAI,WAAW,SACb,aAAa,WAAW,OAAO;EAEnC;CACF,GAAG,CAAC,OAAO,KAAK,CAAC;CAEjB,OAAO;AACT"}
@@ -1,8 +1,7 @@
1
1
  import { RefObject } from "react";
2
-
3
2
  //#region src/hooks/use-element-position.d.ts
4
3
  type ElementReference<T> = string | RefObject<T>;
5
4
  declare const useElementPosition: <T>(elementRef: ElementReference<T>) => DOMRect | null;
6
5
  //#endregion
7
- export { useElementPosition };
6
+ export { useElementPosition as default };
8
7
  //# sourceMappingURL=use-element-position.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
-
3
2
  //#region src/hooks/use-element-position.ts
4
3
  const rectsEqual = (a, b) => {
5
4
  if (a === b) return true;
@@ -55,7 +54,7 @@ const useElementPosition = (elementRef) => {
55
54
  }, [elementRef]);
56
55
  return rect;
57
56
  };
58
-
59
57
  //#endregion
60
58
  export { useElementPosition as default };
59
+
61
60
  //# sourceMappingURL=use-element-position.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-element-position.mjs","names":[],"sources":["../../src/hooks/use-element-position.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef, useState } from 'react';\n\ntype ElementReference<T> = string | RefObject<T>;\n\nconst rectsEqual = (a: DOMRect | null, b: DOMRect | null) => {\n if (a === b) {\n return true;\n }\n if (!a || !b) {\n return false;\n }\n return (\n a.x === b.x &&\n a.y === b.y &&\n a.width === b.width &&\n a.height === b.height &&\n a.top === b.top &&\n a.left === b.left\n );\n};\n\nconst useElementPosition = <T>(elementRef: ElementReference<T>) => {\n const [rect, setRect] = useState<DOMRect | null>(null);\n const rectRef = useRef<DOMRect | null>(null);\n\n useEffect(() => {\n const getElement = (ref: ElementReference<T>): T | null => {\n if (typeof ref === 'string') {\n return document.querySelector(ref) as T | null;\n }\n return ref.current;\n };\n\n let rafId: number | undefined;\n let currentElement: HTMLElement | null = null;\n\n const commitRect = (next: DOMRect | null) => {\n if (rectsEqual(rectRef.current, next)) {\n return;\n }\n rectRef.current = next;\n setRect(next);\n };\n\n // ResizeObserver only fires for the observed element's own size/layout\n // changes, so it's kept pointed at whichever element is actually\n // current — which can change under a string selector, or if the DOM\n // node behind a ref gets swapped out.\n const resizeObserver =\n typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(() => updateRect())\n : undefined;\n\n function updateRect() {\n const element = getElement(elementRef) as HTMLElement | null;\n\n if (element !== currentElement) {\n if (currentElement) {\n resizeObserver?.unobserve(currentElement);\n }\n if (element) {\n resizeObserver?.observe(element);\n }\n currentElement = element;\n }\n\n commitRect(element ? element.getBoundingClientRect() : null);\n }\n\n const onUpdate = () => {\n rafId = requestAnimationFrame(updateRect);\n };\n\n updateRect();\n\n // `capture: true` also catches scroll events from scrollable ancestor\n // containers, which don't bubble and so wouldn't otherwise reach a\n // listener on `window`.\n window.addEventListener('scroll', onUpdate, {\n passive: true,\n capture: true,\n });\n window.addEventListener('resize', onUpdate, { passive: true });\n\n // A string selector's target may not exist yet when this effect first\n // runs — watch the DOM for it instead of leaving `rect` permanently\n // null once it does mount.\n const mutationObserver =\n typeof elementRef === 'string' && typeof MutationObserver !== 'undefined'\n ? new MutationObserver(onUpdate)\n : undefined;\n mutationObserver?.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n if (rafId !== undefined) {\n cancelAnimationFrame(rafId);\n }\n window.removeEventListener('scroll', onUpdate, { capture: true });\n window.removeEventListener('resize', onUpdate);\n resizeObserver?.disconnect();\n mutationObserver?.disconnect();\n };\n }, [elementRef]);\n\n return rect;\n};\n\nexport default useElementPosition;\n"],"mappings":";;;AAIA,MAAM,cAAc,GAAmB,MAAsB;AAC3D,KAAI,MAAM,EACR,QAAO;AAET,KAAI,CAAC,KAAK,CAAC,EACT,QAAO;AAET,QACE,EAAE,MAAM,EAAE,KACV,EAAE,MAAM,EAAE,KACV,EAAE,UAAU,EAAE,SACd,EAAE,WAAW,EAAE,UACf,EAAE,QAAQ,EAAE,OACZ,EAAE,SAAS,EAAE;;AAIjB,MAAM,sBAAyB,eAAoC;CACjE,MAAM,CAAC,MAAM,WAAW,SAAyB,KAAK;CACtD,MAAM,UAAU,OAAuB,KAAK;AAE5C,iBAAgB;EACd,MAAM,cAAc,QAAuC;AACzD,OAAI,OAAO,QAAQ,SACjB,QAAO,SAAS,cAAc,IAAI;AAEpC,UAAO,IAAI;;EAGb,IAAI;EACJ,IAAI,iBAAqC;EAEzC,MAAM,cAAc,SAAyB;AAC3C,OAAI,WAAW,QAAQ,SAAS,KAAK,CACnC;AAEF,WAAQ,UAAU;AAClB,WAAQ,KAAK;;EAOf,MAAM,iBACJ,OAAO,mBAAmB,cACtB,IAAI,qBAAqB,YAAY,CAAC,GACtC;EAEN,SAAS,aAAa;GACpB,MAAM,UAAU,WAAW,WAAW;AAEtC,OAAI,YAAY,gBAAgB;AAC9B,QAAI,eACF,iBAAgB,UAAU,eAAe;AAE3C,QAAI,QACF,iBAAgB,QAAQ,QAAQ;AAElC,qBAAiB;;AAGnB,cAAW,UAAU,QAAQ,uBAAuB,GAAG,KAAK;;EAG9D,MAAM,iBAAiB;AACrB,WAAQ,sBAAsB,WAAW;;AAG3C,cAAY;AAKZ,SAAO,iBAAiB,UAAU,UAAU;GAC1C,SAAS;GACT,SAAS;GACV,CAAC;AACF,SAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;EAK9D,MAAM,mBACJ,OAAO,eAAe,YAAY,OAAO,qBAAqB,cAC1D,IAAI,iBAAiB,SAAS,GAC9B;AACN,oBAAkB,QAAQ,SAAS,MAAM;GACvC,WAAW;GACX,SAAS;GACV,CAAC;AAEF,eAAa;AACX,OAAI,UAAU,OACZ,sBAAqB,MAAM;AAE7B,UAAO,oBAAoB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;AACjE,UAAO,oBAAoB,UAAU,SAAS;AAC9C,mBAAgB,YAAY;AAC5B,qBAAkB,YAAY;;IAE/B,CAAC,WAAW,CAAC;AAEhB,QAAO"}
1
+ {"version":3,"file":"use-element-position.mjs","names":[],"sources":["../../src/hooks/use-element-position.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef, useState } from 'react';\n\ntype ElementReference<T> = string | RefObject<T>;\n\nconst rectsEqual = (a: DOMRect | null, b: DOMRect | null) => {\n if (a === b) {\n return true;\n }\n if (!a || !b) {\n return false;\n }\n return (\n a.x === b.x &&\n a.y === b.y &&\n a.width === b.width &&\n a.height === b.height &&\n a.top === b.top &&\n a.left === b.left\n );\n};\n\nconst useElementPosition = <T>(elementRef: ElementReference<T>) => {\n const [rect, setRect] = useState<DOMRect | null>(null);\n const rectRef = useRef<DOMRect | null>(null);\n\n useEffect(() => {\n const getElement = (ref: ElementReference<T>): T | null => {\n if (typeof ref === 'string') {\n return document.querySelector(ref) as T | null;\n }\n return ref.current;\n };\n\n let rafId: number | undefined;\n let currentElement: HTMLElement | null = null;\n\n const commitRect = (next: DOMRect | null) => {\n if (rectsEqual(rectRef.current, next)) {\n return;\n }\n rectRef.current = next;\n setRect(next);\n };\n\n // ResizeObserver only fires for the observed element's own size/layout\n // changes, so it's kept pointed at whichever element is actually\n // current — which can change under a string selector, or if the DOM\n // node behind a ref gets swapped out.\n const resizeObserver =\n typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(() => updateRect())\n : undefined;\n\n function updateRect() {\n const element = getElement(elementRef) as HTMLElement | null;\n\n if (element !== currentElement) {\n if (currentElement) {\n resizeObserver?.unobserve(currentElement);\n }\n if (element) {\n resizeObserver?.observe(element);\n }\n currentElement = element;\n }\n\n commitRect(element ? element.getBoundingClientRect() : null);\n }\n\n const onUpdate = () => {\n rafId = requestAnimationFrame(updateRect);\n };\n\n updateRect();\n\n // `capture: true` also catches scroll events from scrollable ancestor\n // containers, which don't bubble and so wouldn't otherwise reach a\n // listener on `window`.\n window.addEventListener('scroll', onUpdate, {\n passive: true,\n capture: true,\n });\n window.addEventListener('resize', onUpdate, { passive: true });\n\n // A string selector's target may not exist yet when this effect first\n // runs — watch the DOM for it instead of leaving `rect` permanently\n // null once it does mount.\n const mutationObserver =\n typeof elementRef === 'string' && typeof MutationObserver !== 'undefined'\n ? new MutationObserver(onUpdate)\n : undefined;\n mutationObserver?.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n if (rafId !== undefined) {\n cancelAnimationFrame(rafId);\n }\n window.removeEventListener('scroll', onUpdate, { capture: true });\n window.removeEventListener('resize', onUpdate);\n resizeObserver?.disconnect();\n mutationObserver?.disconnect();\n };\n }, [elementRef]);\n\n return rect;\n};\n\nexport default useElementPosition;\n"],"mappings":";;AAIA,MAAM,cAAc,GAAmB,MAAsB;CAC3D,IAAI,MAAM,GACR,OAAO;CAET,IAAI,CAAC,KAAK,CAAC,GACT,OAAO;CAET,OACE,EAAE,MAAM,EAAE,KACV,EAAE,MAAM,EAAE,KACV,EAAE,UAAU,EAAE,SACd,EAAE,WAAW,EAAE,UACf,EAAE,QAAQ,EAAE,OACZ,EAAE,SAAS,EAAE;AAEjB;AAEA,MAAM,sBAAyB,eAAoC;CACjE,MAAM,CAAC,MAAM,WAAW,SAAyB,IAAI;CACrD,MAAM,UAAU,OAAuB,IAAI;CAE3C,gBAAgB;EACd,MAAM,cAAc,QAAuC;GACzD,IAAI,OAAO,QAAQ,UACjB,OAAO,SAAS,cAAc,GAAG;GAEnC,OAAO,IAAI;EACb;EAEA,IAAI;EACJ,IAAI,iBAAqC;EAEzC,MAAM,cAAc,SAAyB;GAC3C,IAAI,WAAW,QAAQ,SAAS,IAAI,GAClC;GAEF,QAAQ,UAAU;GAClB,QAAQ,IAAI;EACd;EAMA,MAAM,iBACJ,OAAO,mBAAmB,cACtB,IAAI,qBAAqB,WAAW,CAAC,IACrC,KAAA;EAEN,SAAS,aAAa;GACpB,MAAM,UAAU,WAAW,UAAU;GAErC,IAAI,YAAY,gBAAgB;IAC9B,IAAI,gBACF,gBAAgB,UAAU,cAAc;IAE1C,IAAI,SACF,gBAAgB,QAAQ,OAAO;IAEjC,iBAAiB;GACnB;GAEA,WAAW,UAAU,QAAQ,sBAAsB,IAAI,IAAI;EAC7D;EAEA,MAAM,iBAAiB;GACrB,QAAQ,sBAAsB,UAAU;EAC1C;EAEA,WAAW;EAKX,OAAO,iBAAiB,UAAU,UAAU;GAC1C,SAAS;GACT,SAAS;EACX,CAAC;EACD,OAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;EAK7D,MAAM,mBACJ,OAAO,eAAe,YAAY,OAAO,qBAAqB,cAC1D,IAAI,iBAAiB,QAAQ,IAC7B,KAAA;EACN,kBAAkB,QAAQ,SAAS,MAAM;GACvC,WAAW;GACX,SAAS;EACX,CAAC;EAED,aAAa;GACX,IAAI,UAAU,KAAA,GACZ,qBAAqB,KAAK;GAE5B,OAAO,oBAAoB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;GAChE,OAAO,oBAAoB,UAAU,QAAQ;GAC7C,gBAAgB,WAAW;GAC3B,kBAAkB,WAAW;EAC/B;CACF,GAAG,CAAC,UAAU,CAAC;CAEf,OAAO;AACT"}
@@ -2,9 +2,7 @@
2
2
  interface Options {
3
3
  threshold?: number;
4
4
  }
5
- declare const useElementScroll: ({
6
- threshold
7
- }?: Options) => {
5
+ declare const useElementScroll: ({ threshold }?: Options) => {
8
6
  element: HTMLElement | null;
9
7
  setRef: (el: HTMLElement | null) => void;
10
8
  scrollY: number;
@@ -16,5 +14,5 @@ declare const useElementScroll: ({
16
14
  scrollHeight: number;
17
15
  };
18
16
  //#endregion
19
- export { useElementScroll };
17
+ export { useElementScroll as default };
20
18
  //# sourceMappingURL=use-element-scroll.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
-
3
2
  //#region src/hooks/use-element-scroll.ts
4
3
  const scrollPositionEqual = (a, b) => a.scrollY === b.scrollY && a.scrollPercentage === b.scrollPercentage && a.isAtTop === b.isAtTop && a.isAtBottom === b.isAtBottom && a.scrollableHeight === b.scrollableHeight && a.clientHeight === b.clientHeight && a.scrollHeight === b.scrollHeight;
5
4
  const useElementScroll = ({ threshold = 1 } = {}) => {
@@ -39,9 +38,10 @@ const useElementScroll = ({ threshold = 1 } = {}) => {
39
38
  });
40
39
  return;
41
40
  }
41
+ const percentage = Math.min(100, Math.max(0, scrollTop / scrollableHeight * 100));
42
42
  commit({
43
43
  scrollY: scrollTop,
44
- scrollPercentage: Math.min(100, Math.max(0, scrollTop / scrollableHeight * 100)),
44
+ scrollPercentage: percentage,
45
45
  isAtTop: scrollTop <= 0,
46
46
  isAtBottom: scrollTop >= scrollableHeight - threshold,
47
47
  scrollableHeight,
@@ -84,7 +84,7 @@ const useElementScroll = ({ threshold = 1 } = {}) => {
84
84
  setRef
85
85
  };
86
86
  };
87
-
88
87
  //#endregion
89
88
  export { useElementScroll as default };
89
+
90
90
  //# sourceMappingURL=use-element-scroll.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-element-scroll.mjs","names":[],"sources":["../../src/hooks/use-element-scroll.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface ScrollPosition {\n scrollY: number;\n scrollPercentage: number;\n isAtTop: boolean;\n isAtBottom: boolean;\n scrollableHeight: number;\n clientHeight: number;\n scrollHeight: number;\n}\n\nconst scrollPositionEqual = (a: ScrollPosition, b: ScrollPosition) =>\n a.scrollY === b.scrollY &&\n a.scrollPercentage === b.scrollPercentage &&\n a.isAtTop === b.isAtTop &&\n a.isAtBottom === b.isAtBottom &&\n a.scrollableHeight === b.scrollableHeight &&\n a.clientHeight === b.clientHeight &&\n a.scrollHeight === b.scrollHeight;\n\ninterface Options {\n // How close to the bottom (in px) still counts as \"at bottom\". Matches\n // the previous hardcoded value by default.\n threshold?: number;\n}\n\nconst useElementScroll = ({ threshold = 1 }: Options = {}) => {\n const [element, setElement] = useState<HTMLElement | null>(null);\n const [scrollPosition, setScrollPosition] = useState<ScrollPosition>({\n scrollY: 0,\n scrollPercentage: 0,\n isAtTop: true,\n isAtBottom: false,\n scrollableHeight: 0,\n clientHeight: 0,\n scrollHeight: 0,\n });\n const scrollPositionRef = useRef(scrollPosition);\n\n const setRef = useCallback((el: HTMLElement | null) => {\n setElement(el);\n }, []);\n\n useEffect(() => {\n if (!element) {\n return;\n }\n\n const commit = (next: ScrollPosition) => {\n if (scrollPositionEqual(scrollPositionRef.current, next)) {\n return;\n }\n scrollPositionRef.current = next;\n setScrollPosition(next);\n };\n\n const updateScrollPosition = () => {\n const { scrollTop, scrollHeight, clientHeight } = element;\n const scrollableHeight = scrollHeight - clientHeight;\n\n if (scrollableHeight <= 0) {\n commit({\n scrollY: 0,\n scrollPercentage: 0,\n isAtTop: true,\n isAtBottom: true,\n scrollableHeight: 0,\n clientHeight,\n scrollHeight,\n });\n return;\n }\n\n const percentage = Math.min(\n 100,\n Math.max(0, (scrollTop / scrollableHeight) * 100),\n );\n\n commit({\n scrollY: scrollTop,\n scrollPercentage: percentage,\n isAtTop: scrollTop <= 0,\n isAtBottom: scrollTop >= scrollableHeight - threshold,\n scrollableHeight,\n clientHeight,\n scrollHeight,\n });\n };\n\n updateScrollPosition();\n\n const onScroll = () => {\n updateScrollPosition();\n };\n\n element.addEventListener('scroll', onScroll, { passive: true });\n\n const resizeObserver = new ResizeObserver(() => {\n updateScrollPosition();\n });\n\n // Observing only the container misses content that grows/shrinks\n // `scrollHeight` without resizing the container's own box (a list\n // gaining items, an image finishing its load) — also watch every\n // child so their own size changes are caught.\n const observeChildren = () => {\n resizeObserver.observe(element);\n Array.from(element.children).forEach(child => {\n resizeObserver.observe(child);\n });\n };\n\n observeChildren();\n\n // Catches children being added/removed (re-syncs which elements the\n // ResizeObserver above watches, and recalculates immediately) —\n // ResizeObserver alone doesn't see brand new children until this runs.\n const mutationObserver = new MutationObserver(() => {\n observeChildren();\n updateScrollPosition();\n });\n mutationObserver.observe(element, { childList: true, subtree: true });\n\n return () => {\n element.removeEventListener('scroll', onScroll);\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n };\n }, [element, threshold]);\n\n return { ...scrollPosition, element, setRef };\n};\n\nexport default useElementScroll;\n"],"mappings":";;;AAYA,MAAM,uBAAuB,GAAmB,MAC9C,EAAE,YAAY,EAAE,WAChB,EAAE,qBAAqB,EAAE,oBACzB,EAAE,YAAY,EAAE,WAChB,EAAE,eAAe,EAAE,cACnB,EAAE,qBAAqB,EAAE,oBACzB,EAAE,iBAAiB,EAAE,gBACrB,EAAE,iBAAiB,EAAE;AAQvB,MAAM,oBAAoB,EAAE,YAAY,MAAe,EAAE,KAAK;CAC5D,MAAM,CAAC,SAAS,cAAc,SAA6B,KAAK;CAChE,MAAM,CAAC,gBAAgB,qBAAqB,SAAyB;EACnE,SAAS;EACT,kBAAkB;EAClB,SAAS;EACT,YAAY;EACZ,kBAAkB;EAClB,cAAc;EACd,cAAc;EACf,CAAC;CACF,MAAM,oBAAoB,OAAO,eAAe;CAEhD,MAAM,SAAS,aAAa,OAA2B;AACrD,aAAW,GAAG;IACb,EAAE,CAAC;AAEN,iBAAgB;AACd,MAAI,CAAC,QACH;EAGF,MAAM,UAAU,SAAyB;AACvC,OAAI,oBAAoB,kBAAkB,SAAS,KAAK,CACtD;AAEF,qBAAkB,UAAU;AAC5B,qBAAkB,KAAK;;EAGzB,MAAM,6BAA6B;GACjC,MAAM,EAAE,WAAW,cAAc,iBAAiB;GAClD,MAAM,mBAAmB,eAAe;AAExC,OAAI,oBAAoB,GAAG;AACzB,WAAO;KACL,SAAS;KACT,kBAAkB;KAClB,SAAS;KACT,YAAY;KACZ,kBAAkB;KAClB;KACA;KACD,CAAC;AACF;;AAQF,UAAO;IACL,SAAS;IACT,kBAPiB,KAAK,IACtB,KACA,KAAK,IAAI,GAAI,YAAY,mBAAoB,IAAI,CAClD;IAKC,SAAS,aAAa;IACtB,YAAY,aAAa,mBAAmB;IAC5C;IACA;IACA;IACD,CAAC;;AAGJ,wBAAsB;EAEtB,MAAM,iBAAiB;AACrB,yBAAsB;;AAGxB,UAAQ,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;EAE/D,MAAM,iBAAiB,IAAI,qBAAqB;AAC9C,yBAAsB;IACtB;EAMF,MAAM,wBAAwB;AAC5B,kBAAe,QAAQ,QAAQ;AAC/B,SAAM,KAAK,QAAQ,SAAS,CAAC,SAAQ,UAAS;AAC5C,mBAAe,QAAQ,MAAM;KAC7B;;AAGJ,mBAAiB;EAKjB,MAAM,mBAAmB,IAAI,uBAAuB;AAClD,oBAAiB;AACjB,yBAAsB;IACtB;AACF,mBAAiB,QAAQ,SAAS;GAAE,WAAW;GAAM,SAAS;GAAM,CAAC;AAErE,eAAa;AACX,WAAQ,oBAAoB,UAAU,SAAS;AAC/C,kBAAe,YAAY;AAC3B,oBAAiB,YAAY;;IAE9B,CAAC,SAAS,UAAU,CAAC;AAExB,QAAO;EAAE,GAAG;EAAgB;EAAS;EAAQ"}
1
+ {"version":3,"file":"use-element-scroll.mjs","names":[],"sources":["../../src/hooks/use-element-scroll.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface ScrollPosition {\n scrollY: number;\n scrollPercentage: number;\n isAtTop: boolean;\n isAtBottom: boolean;\n scrollableHeight: number;\n clientHeight: number;\n scrollHeight: number;\n}\n\nconst scrollPositionEqual = (a: ScrollPosition, b: ScrollPosition) =>\n a.scrollY === b.scrollY &&\n a.scrollPercentage === b.scrollPercentage &&\n a.isAtTop === b.isAtTop &&\n a.isAtBottom === b.isAtBottom &&\n a.scrollableHeight === b.scrollableHeight &&\n a.clientHeight === b.clientHeight &&\n a.scrollHeight === b.scrollHeight;\n\ninterface Options {\n // How close to the bottom (in px) still counts as \"at bottom\". Matches\n // the previous hardcoded value by default.\n threshold?: number;\n}\n\nconst useElementScroll = ({ threshold = 1 }: Options = {}) => {\n const [element, setElement] = useState<HTMLElement | null>(null);\n const [scrollPosition, setScrollPosition] = useState<ScrollPosition>({\n scrollY: 0,\n scrollPercentage: 0,\n isAtTop: true,\n isAtBottom: false,\n scrollableHeight: 0,\n clientHeight: 0,\n scrollHeight: 0,\n });\n const scrollPositionRef = useRef(scrollPosition);\n\n const setRef = useCallback((el: HTMLElement | null) => {\n setElement(el);\n }, []);\n\n useEffect(() => {\n if (!element) {\n return;\n }\n\n const commit = (next: ScrollPosition) => {\n if (scrollPositionEqual(scrollPositionRef.current, next)) {\n return;\n }\n scrollPositionRef.current = next;\n setScrollPosition(next);\n };\n\n const updateScrollPosition = () => {\n const { scrollTop, scrollHeight, clientHeight } = element;\n const scrollableHeight = scrollHeight - clientHeight;\n\n if (scrollableHeight <= 0) {\n commit({\n scrollY: 0,\n scrollPercentage: 0,\n isAtTop: true,\n isAtBottom: true,\n scrollableHeight: 0,\n clientHeight,\n scrollHeight,\n });\n return;\n }\n\n const percentage = Math.min(\n 100,\n Math.max(0, (scrollTop / scrollableHeight) * 100),\n );\n\n commit({\n scrollY: scrollTop,\n scrollPercentage: percentage,\n isAtTop: scrollTop <= 0,\n isAtBottom: scrollTop >= scrollableHeight - threshold,\n scrollableHeight,\n clientHeight,\n scrollHeight,\n });\n };\n\n updateScrollPosition();\n\n const onScroll = () => {\n updateScrollPosition();\n };\n\n element.addEventListener('scroll', onScroll, { passive: true });\n\n const resizeObserver = new ResizeObserver(() => {\n updateScrollPosition();\n });\n\n // Observing only the container misses content that grows/shrinks\n // `scrollHeight` without resizing the container's own box (a list\n // gaining items, an image finishing its load) — also watch every\n // child so their own size changes are caught.\n const observeChildren = () => {\n resizeObserver.observe(element);\n Array.from(element.children).forEach(child => {\n resizeObserver.observe(child);\n });\n };\n\n observeChildren();\n\n // Catches children being added/removed (re-syncs which elements the\n // ResizeObserver above watches, and recalculates immediately) —\n // ResizeObserver alone doesn't see brand new children until this runs.\n const mutationObserver = new MutationObserver(() => {\n observeChildren();\n updateScrollPosition();\n });\n mutationObserver.observe(element, { childList: true, subtree: true });\n\n return () => {\n element.removeEventListener('scroll', onScroll);\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n };\n }, [element, threshold]);\n\n return { ...scrollPosition, element, setRef };\n};\n\nexport default useElementScroll;\n"],"mappings":";;AAYA,MAAM,uBAAuB,GAAmB,MAC9C,EAAE,YAAY,EAAE,WAChB,EAAE,qBAAqB,EAAE,oBACzB,EAAE,YAAY,EAAE,WAChB,EAAE,eAAe,EAAE,cACnB,EAAE,qBAAqB,EAAE,oBACzB,EAAE,iBAAiB,EAAE,gBACrB,EAAE,iBAAiB,EAAE;AAQvB,MAAM,oBAAoB,EAAE,YAAY,MAAe,CAAC,MAAM;CAC5D,MAAM,CAAC,SAAS,cAAc,SAA6B,IAAI;CAC/D,MAAM,CAAC,gBAAgB,qBAAqB,SAAyB;EACnE,SAAS;EACT,kBAAkB;EAClB,SAAS;EACT,YAAY;EACZ,kBAAkB;EAClB,cAAc;EACd,cAAc;CAChB,CAAC;CACD,MAAM,oBAAoB,OAAO,cAAc;CAE/C,MAAM,SAAS,aAAa,OAA2B;EACrD,WAAW,EAAE;CACf,GAAG,CAAC,CAAC;CAEL,gBAAgB;EACd,IAAI,CAAC,SACH;EAGF,MAAM,UAAU,SAAyB;GACvC,IAAI,oBAAoB,kBAAkB,SAAS,IAAI,GACrD;GAEF,kBAAkB,UAAU;GAC5B,kBAAkB,IAAI;EACxB;EAEA,MAAM,6BAA6B;GACjC,MAAM,EAAE,WAAW,cAAc,iBAAiB;GAClD,MAAM,mBAAmB,eAAe;GAExC,IAAI,oBAAoB,GAAG;IACzB,OAAO;KACL,SAAS;KACT,kBAAkB;KAClB,SAAS;KACT,YAAY;KACZ,kBAAkB;KAClB;KACA;IACF,CAAC;IACD;GACF;GAEA,MAAM,aAAa,KAAK,IACtB,KACA,KAAK,IAAI,GAAI,YAAY,mBAAoB,GAAG,CAClD;GAEA,OAAO;IACL,SAAS;IACT,kBAAkB;IAClB,SAAS,aAAa;IACtB,YAAY,aAAa,mBAAmB;IAC5C;IACA;IACA;GACF,CAAC;EACH;EAEA,qBAAqB;EAErB,MAAM,iBAAiB;GACrB,qBAAqB;EACvB;EAEA,QAAQ,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;EAE9D,MAAM,iBAAiB,IAAI,qBAAqB;GAC9C,qBAAqB;EACvB,CAAC;EAMD,MAAM,wBAAwB;GAC5B,eAAe,QAAQ,OAAO;GAC9B,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC,SAAQ,UAAS;IAC5C,eAAe,QAAQ,KAAK;GAC9B,CAAC;EACH;EAEA,gBAAgB;EAKhB,MAAM,mBAAmB,IAAI,uBAAuB;GAClD,gBAAgB;GAChB,qBAAqB;EACvB,CAAC;EACD,iBAAiB,QAAQ,SAAS;GAAE,WAAW;GAAM,SAAS;EAAK,CAAC;EAEpE,aAAa;GACX,QAAQ,oBAAoB,UAAU,QAAQ;GAC9C,eAAe,WAAW;GAC1B,iBAAiB,WAAW;EAC9B;CACF,GAAG,CAAC,SAAS,SAAS,CAAC;CAEvB,OAAO;EAAE,GAAG;EAAgB;EAAS;CAAO;AAC9C"}
@@ -1,5 +1,4 @@
1
1
  import { RefObject } from "react";
2
-
3
2
  //#region src/hooks/use-event-listener.d.ts
4
3
  interface Options {
5
4
  capture?: boolean;
@@ -17,5 +16,5 @@ declare function useEventListener<T extends HTMLElement, K extends keyof HTMLEle
17
16
  target: RefObject<T | null>;
18
17
  }): void;
19
18
  //#endregion
20
- export { useEventListener };
19
+ export { useEventListener as default };
21
20
  //# sourceMappingURL=use-event-listener.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useEffect, useRef } from "react";
2
-
3
2
  //#region src/hooks/use-event-listener.ts
4
3
  function useEventListener(type, handler, options = {}) {
5
4
  const { target, capture, passive, once, enabled = true } = options;
@@ -44,7 +43,7 @@ function useEventListener(type, handler, options = {}) {
44
43
  enabled
45
44
  ]);
46
45
  }
47
-
48
46
  //#endregion
49
47
  export { useEventListener as default };
48
+
50
49
  //# sourceMappingURL=use-event-listener.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-event-listener.mjs","names":[],"sources":["../../src/hooks/use-event-listener.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ninterface Options {\n capture?: boolean;\n passive?: boolean;\n once?: boolean;\n enabled?: boolean;\n}\n\ntype EventListenerTarget = EventTarget | RefObject<EventTarget | null> | null;\n\nfunction useEventListener<K extends keyof WindowEventMap>(\n type: K,\n handler: (event: WindowEventMap[K]) => void,\n options?: Options & { target?: Window | null },\n): void;\nfunction useEventListener<K extends keyof DocumentEventMap>(\n type: K,\n handler: (event: DocumentEventMap[K]) => void,\n options: Options & { target: Document | RefObject<Document | null> },\n): void;\nfunction useEventListener<\n T extends HTMLElement,\n K extends keyof HTMLElementEventMap,\n>(\n type: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n options: Options & { target: RefObject<T | null> },\n): void;\n\n// Resolves `target` (default `window`, or a raw EventTarget, or a\n// RefObject pointing at one) and (de)registers `type` on it — the pair of\n// addEventListener/removeEventListener calls duplicated across ui-kit\n// (marquee item) and live-editor (error handlers). `handler` is read\n// through a ref so a fresh function every render doesn't tear down and\n// re-add the listener.\nfunction useEventListener(\n type: string,\n handler: (event: Event) => void,\n options: Options & { target?: EventListenerTarget } = {},\n) {\n const { target, capture, passive, once, enabled = true } = options;\n\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n });\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n let cancelled = false;\n let rafId: number | undefined;\n let resolvedTarget: EventTarget | null = null;\n\n const listener = (event: Event) => {\n handlerRef.current(event);\n };\n\n const attach = () => {\n if (cancelled) {\n return;\n }\n\n // A RefObject target can still be null right after mount (conditional\n // render, portal, lazy mount) — retry every frame until it's\n // populated instead of giving up on the first effect run. A raw\n // target or the default window has nothing to wait for.\n if (target && 'current' in target && !target.current) {\n rafId = requestAnimationFrame(attach);\n return;\n }\n\n resolvedTarget =\n target && 'current' in target\n ? target.current\n : (target ?? (typeof window === 'undefined' ? null : window));\n\n if (!resolvedTarget) {\n return;\n }\n\n resolvedTarget.addEventListener(type, listener, {\n capture,\n passive,\n once,\n });\n };\n\n attach();\n\n return () => {\n cancelled = true;\n if (rafId !== undefined) {\n cancelAnimationFrame(rafId);\n }\n resolvedTarget?.removeEventListener(type, listener, { capture });\n };\n }, [type, target, capture, passive, once, enabled]);\n}\n\nexport default useEventListener;\n"],"mappings":";;;AAoCA,SAAS,iBACP,MACA,SACA,UAAsD,EAAE,EACxD;CACA,MAAM,EAAE,QAAQ,SAAS,SAAS,MAAM,UAAU,SAAS;CAE3D,MAAM,aAAa,OAAO,QAAQ;AAElC,iBAAgB;AACd,aAAW,UAAU;GACrB;AAEF,iBAAgB;AACd,MAAI,CAAC,QACH;EAGF,IAAI,YAAY;EAChB,IAAI;EACJ,IAAI,iBAAqC;EAEzC,MAAM,YAAY,UAAiB;AACjC,cAAW,QAAQ,MAAM;;EAG3B,MAAM,eAAe;AACnB,OAAI,UACF;AAOF,OAAI,UAAU,aAAa,UAAU,CAAC,OAAO,SAAS;AACpD,YAAQ,sBAAsB,OAAO;AACrC;;AAGF,oBACE,UAAU,aAAa,SACnB,OAAO,UACN,WAAW,OAAO,WAAW,cAAc,OAAO;AAEzD,OAAI,CAAC,eACH;AAGF,kBAAe,iBAAiB,MAAM,UAAU;IAC9C;IACA;IACA;IACD,CAAC;;AAGJ,UAAQ;AAER,eAAa;AACX,eAAY;AACZ,OAAI,UAAU,OACZ,sBAAqB,MAAM;AAE7B,mBAAgB,oBAAoB,MAAM,UAAU,EAAE,SAAS,CAAC;;IAEjE;EAAC;EAAM;EAAQ;EAAS;EAAS;EAAM;EAAQ,CAAC"}
1
+ {"version":3,"file":"use-event-listener.mjs","names":[],"sources":["../../src/hooks/use-event-listener.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ninterface Options {\n capture?: boolean;\n passive?: boolean;\n once?: boolean;\n enabled?: boolean;\n}\n\ntype EventListenerTarget = EventTarget | RefObject<EventTarget | null> | null;\n\nfunction useEventListener<K extends keyof WindowEventMap>(\n type: K,\n handler: (event: WindowEventMap[K]) => void,\n options?: Options & { target?: Window | null },\n): void;\nfunction useEventListener<K extends keyof DocumentEventMap>(\n type: K,\n handler: (event: DocumentEventMap[K]) => void,\n options: Options & { target: Document | RefObject<Document | null> },\n): void;\nfunction useEventListener<\n T extends HTMLElement,\n K extends keyof HTMLElementEventMap,\n>(\n type: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n options: Options & { target: RefObject<T | null> },\n): void;\n\n// Resolves `target` (default `window`, or a raw EventTarget, or a\n// RefObject pointing at one) and (de)registers `type` on it — the pair of\n// addEventListener/removeEventListener calls duplicated across ui-kit\n// (marquee item) and live-editor (error handlers). `handler` is read\n// through a ref so a fresh function every render doesn't tear down and\n// re-add the listener.\nfunction useEventListener(\n type: string,\n handler: (event: Event) => void,\n options: Options & { target?: EventListenerTarget } = {},\n) {\n const { target, capture, passive, once, enabled = true } = options;\n\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n });\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n let cancelled = false;\n let rafId: number | undefined;\n let resolvedTarget: EventTarget | null = null;\n\n const listener = (event: Event) => {\n handlerRef.current(event);\n };\n\n const attach = () => {\n if (cancelled) {\n return;\n }\n\n // A RefObject target can still be null right after mount (conditional\n // render, portal, lazy mount) — retry every frame until it's\n // populated instead of giving up on the first effect run. A raw\n // target or the default window has nothing to wait for.\n if (target && 'current' in target && !target.current) {\n rafId = requestAnimationFrame(attach);\n return;\n }\n\n resolvedTarget =\n target && 'current' in target\n ? target.current\n : (target ?? (typeof window === 'undefined' ? null : window));\n\n if (!resolvedTarget) {\n return;\n }\n\n resolvedTarget.addEventListener(type, listener, {\n capture,\n passive,\n once,\n });\n };\n\n attach();\n\n return () => {\n cancelled = true;\n if (rafId !== undefined) {\n cancelAnimationFrame(rafId);\n }\n resolvedTarget?.removeEventListener(type, listener, { capture });\n };\n }, [type, target, capture, passive, once, enabled]);\n}\n\nexport default useEventListener;\n"],"mappings":";;AAoCA,SAAS,iBACP,MACA,SACA,UAAsD,CAAC,GACvD;CACA,MAAM,EAAE,QAAQ,SAAS,SAAS,MAAM,UAAU,SAAS;CAE3D,MAAM,aAAa,OAAO,OAAO;CAEjC,gBAAgB;EACd,WAAW,UAAU;CACvB,CAAC;CAED,gBAAgB;EACd,IAAI,CAAC,SACH;EAGF,IAAI,YAAY;EAChB,IAAI;EACJ,IAAI,iBAAqC;EAEzC,MAAM,YAAY,UAAiB;GACjC,WAAW,QAAQ,KAAK;EAC1B;EAEA,MAAM,eAAe;GACnB,IAAI,WACF;GAOF,IAAI,UAAU,aAAa,UAAU,CAAC,OAAO,SAAS;IACpD,QAAQ,sBAAsB,MAAM;IACpC;GACF;GAEA,iBACE,UAAU,aAAa,SACnB,OAAO,UACN,WAAW,OAAO,WAAW,cAAc,OAAO;GAEzD,IAAI,CAAC,gBACH;GAGF,eAAe,iBAAiB,MAAM,UAAU;IAC9C;IACA;IACA;GACF,CAAC;EACH;EAEA,OAAO;EAEP,aAAa;GACX,YAAY;GACZ,IAAI,UAAU,KAAA,GACZ,qBAAqB,KAAK;GAE5B,gBAAgB,oBAAoB,MAAM,UAAU,EAAE,QAAQ,CAAC;EACjE;CACF,GAAG;EAAC;EAAM;EAAQ;EAAS;EAAS;EAAM;CAAO,CAAC;AACpD"}
@@ -10,5 +10,5 @@ declare const useFileDrop: <T extends HTMLElement = HTMLElement>(options?: Optio
10
10
  isDragging: boolean;
11
11
  };
12
12
  //#endregion
13
- export { useFileDrop };
13
+ export { useFileDrop as default };
14
14
  //# sourceMappingURL=use-file-drop.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
-
3
2
  //#region src/hooks/use-file-drop.ts
4
3
  const matchesAccept = (file, accept) => {
5
4
  if (!accept) return true;
@@ -25,11 +24,13 @@ const useFileDrop = (options = {}) => {
25
24
  multipleRef.current = multiple;
26
25
  disabledRef.current = disabled;
27
26
  });
27
+ const [prevDisabled, setPrevDisabled] = useState(disabled);
28
+ if (disabled !== prevDisabled) {
29
+ setPrevDisabled(disabled);
30
+ if (disabled) setIsDragging(false);
31
+ }
28
32
  useEffect(() => {
29
- if (disabled) {
30
- dragCounterRef.current = 0;
31
- setIsDragging(false);
32
- }
33
+ if (disabled) dragCounterRef.current = 0;
33
34
  }, [disabled]);
34
35
  const cleanupRef = useRef(null);
35
36
  return {
@@ -79,7 +80,7 @@ const useFileDrop = (options = {}) => {
79
80
  isDragging
80
81
  };
81
82
  };
82
-
83
83
  //#endregion
84
84
  export { useFileDrop as default };
85
+
85
86
  //# sourceMappingURL=use-file-drop.mjs.map