@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 +1 @@
1
- {"version":3,"file":"use-multi-select.mjs","names":[],"sources":["../../src/hooks/use-multi-select.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react';\n\nconst useMultiSelect = (count: number) => {\n const [rawSelected, setRawSelected] = useState<Set<number>>(new Set());\n const [anchor, setAnchor] = useState<number | null>(null);\n\n const selected = useMemo(() => {\n const next = new Set([...rawSelected].filter(index => index < count));\n return next.size === rawSelected.size ? rawSelected : next;\n }, [rawSelected, count]);\n\n // Clamp `rawSelected`/`anchor` themselves (not just the derived\n // `selected` view above) whenever `count` shrinks — otherwise\n // out-of-range indices and a stale anchor keep accumulating in state\n // indefinitely instead of actually being dropped.\n useEffect(() => {\n setRawSelected(prev => {\n const next = new Set([...prev].filter(index => index < count));\n return next.size === prev.size ? prev : next;\n });\n setAnchor(prev => (prev !== null && prev >= count ? null : prev));\n }, [count]);\n\n const toggle = useCallback(\n (index: number, shiftKey = false) => {\n if (index < 0 || index >= count) {\n return;\n }\n\n setRawSelected(prev => {\n if (shiftKey && anchor !== null) {\n const [start, end] =\n anchor < index ? [anchor, index] : [index, anchor];\n\n // Range-select replaces the previous selection, matching the\n // usual file-explorer/table convention, instead of adding to\n // it — otherwise repeated shift-clicks just accumulate every\n // range ever selected rather than re-anchoring to the new one.\n const next = new Set<number>();\n for (let i = start; i <= end; i++) {\n next.add(i);\n }\n return next;\n }\n\n const next = new Set(prev);\n if (next.has(index)) {\n next.delete(index);\n } else {\n next.add(index);\n }\n return next;\n });\n setAnchor(index);\n },\n [anchor, count],\n );\n\n const clear = useCallback(() => {\n setRawSelected(new Set());\n setAnchor(null);\n }, []);\n\n const replace = useCallback(\n (indices: Set<number>) => {\n setRawSelected(\n new Set([...indices].filter(index => index >= 0 && index < count)),\n );\n },\n [count],\n );\n\n const isSelected = useCallback(\n (index: number) => selected.has(index),\n [selected],\n );\n\n return {\n selected,\n isSelected,\n toggle,\n clear,\n replace,\n } as const;\n};\n\nexport default useMultiSelect;\n"],"mappings":";;;AAEA,MAAM,kBAAkB,UAAkB;CACxC,MAAM,CAAC,aAAa,kBAAkB,yBAAsB,IAAI,KAAK,CAAC;CACtE,MAAM,CAAC,QAAQ,aAAa,SAAwB,KAAK;CAEzD,MAAM,WAAW,cAAc;EAC7B,MAAM,OAAO,IAAI,IAAI,CAAC,GAAG,YAAY,CAAC,QAAO,UAAS,QAAQ,MAAM,CAAC;AACrE,SAAO,KAAK,SAAS,YAAY,OAAO,cAAc;IACrD,CAAC,aAAa,MAAM,CAAC;AAMxB,iBAAgB;AACd,kBAAe,SAAQ;GACrB,MAAM,OAAO,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,QAAO,UAAS,QAAQ,MAAM,CAAC;AAC9D,UAAO,KAAK,SAAS,KAAK,OAAO,OAAO;IACxC;AACF,aAAU,SAAS,SAAS,QAAQ,QAAQ,QAAQ,OAAO,KAAM;IAChE,CAAC,MAAM,CAAC;CAEX,MAAM,SAAS,aACZ,OAAe,WAAW,UAAU;AACnC,MAAI,QAAQ,KAAK,SAAS,MACxB;AAGF,kBAAe,SAAQ;AACrB,OAAI,YAAY,WAAW,MAAM;IAC/B,MAAM,CAAC,OAAO,OACZ,SAAS,QAAQ,CAAC,QAAQ,MAAM,GAAG,CAAC,OAAO,OAAO;IAMpD,MAAM,uBAAO,IAAI,KAAa;AAC9B,SAAK,IAAI,IAAI,OAAO,KAAK,KAAK,IAC5B,MAAK,IAAI,EAAE;AAEb,WAAO;;GAGT,MAAM,OAAO,IAAI,IAAI,KAAK;AAC1B,OAAI,KAAK,IAAI,MAAM,CACjB,MAAK,OAAO,MAAM;OAElB,MAAK,IAAI,MAAM;AAEjB,UAAO;IACP;AACF,YAAU,MAAM;IAElB,CAAC,QAAQ,MAAM,CAChB;CAED,MAAM,QAAQ,kBAAkB;AAC9B,iCAAe,IAAI,KAAK,CAAC;AACzB,YAAU,KAAK;IACd,EAAE,CAAC;CAEN,MAAM,UAAU,aACb,YAAyB;AACxB,iBACE,IAAI,IAAI,CAAC,GAAG,QAAQ,CAAC,QAAO,UAAS,SAAS,KAAK,QAAQ,MAAM,CAAC,CACnE;IAEH,CAAC,MAAM,CACR;AAOD,QAAO;EACL;EACA,YAPiB,aAChB,UAAkB,SAAS,IAAI,MAAM,EACtC,CAAC,SAAS,CACX;EAKC;EACA;EACA;EACD"}
1
+ {"version":3,"file":"use-multi-select.mjs","names":[],"sources":["../../src/hooks/use-multi-select.ts"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\n\nconst useMultiSelect = (count: number) => {\n const [rawSelected, setRawSelected] = useState<Set<number>>(new Set());\n const [anchor, setAnchor] = useState<number | null>(null);\n const [prevCount, setPrevCount] = useState(count);\n\n // Clamp `rawSelected`/`anchor` themselves (not just the derived\n // `selected` view below) whenever `count` shrinks — otherwise\n // out-of-range indices and a stale anchor keep accumulating in state\n // indefinitely instead of actually being dropped. Adjusted directly in\n // render (React's \"adjust state during render\" pattern) instead of an\n // effect, so the clamp lands in the same render as the `count` change\n // rather than the render after — see\n // https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes.\n if (count !== prevCount) {\n setPrevCount(count);\n setRawSelected(prev => {\n const next = new Set([...prev].filter(index => index < count));\n return next.size === prev.size ? prev : next;\n });\n setAnchor(prev => (prev !== null && prev >= count ? null : prev));\n }\n\n const selected = useMemo(() => {\n const next = new Set([...rawSelected].filter(index => index < count));\n return next.size === rawSelected.size ? rawSelected : next;\n }, [rawSelected, count]);\n\n const toggle = useCallback(\n (index: number, shiftKey = false) => {\n if (index < 0 || index >= count) {\n return;\n }\n\n setRawSelected(prev => {\n if (shiftKey && anchor !== null) {\n const [start, end] =\n anchor < index ? [anchor, index] : [index, anchor];\n\n // Range-select replaces the previous selection, matching the\n // usual file-explorer/table convention, instead of adding to\n // it — otherwise repeated shift-clicks just accumulate every\n // range ever selected rather than re-anchoring to the new one.\n const next = new Set<number>();\n for (let i = start; i <= end; i++) {\n next.add(i);\n }\n return next;\n }\n\n const next = new Set(prev);\n if (next.has(index)) {\n next.delete(index);\n } else {\n next.add(index);\n }\n return next;\n });\n setAnchor(index);\n },\n [anchor, count],\n );\n\n const clear = useCallback(() => {\n setRawSelected(new Set());\n setAnchor(null);\n }, []);\n\n const replace = useCallback(\n (indices: Set<number>) => {\n setRawSelected(\n new Set([...indices].filter(index => index >= 0 && index < count)),\n );\n },\n [count],\n );\n\n const isSelected = useCallback(\n (index: number) => selected.has(index),\n [selected],\n );\n\n return {\n selected,\n isSelected,\n toggle,\n clear,\n replace,\n } as const;\n};\n\nexport default useMultiSelect;\n"],"mappings":";;AAEA,MAAM,kBAAkB,UAAkB;CACxC,MAAM,CAAC,aAAa,kBAAkB,yBAAsB,IAAI,IAAI,CAAC;CACrE,MAAM,CAAC,QAAQ,aAAa,SAAwB,IAAI;CACxD,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAUhD,IAAI,UAAU,WAAW;EACvB,aAAa,KAAK;EAClB,gBAAe,SAAQ;GACrB,MAAM,OAAO,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,QAAO,UAAS,QAAQ,KAAK,CAAC;GAC7D,OAAO,KAAK,SAAS,KAAK,OAAO,OAAO;EAC1C,CAAC;EACD,WAAU,SAAS,SAAS,QAAQ,QAAQ,QAAQ,OAAO,IAAK;CAClE;CAEA,MAAM,WAAW,cAAc;EAC7B,MAAM,OAAO,IAAI,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,QAAO,UAAS,QAAQ,KAAK,CAAC;EACpE,OAAO,KAAK,SAAS,YAAY,OAAO,cAAc;CACxD,GAAG,CAAC,aAAa,KAAK,CAAC;CAEvB,MAAM,SAAS,aACZ,OAAe,WAAW,UAAU;EACnC,IAAI,QAAQ,KAAK,SAAS,OACxB;EAGF,gBAAe,SAAQ;GACrB,IAAI,YAAY,WAAW,MAAM;IAC/B,MAAM,CAAC,OAAO,OACZ,SAAS,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,OAAO,MAAM;IAMnD,MAAM,uBAAO,IAAI,IAAY;IAC7B,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,KAC5B,KAAK,IAAI,CAAC;IAEZ,OAAO;GACT;GAEA,MAAM,OAAO,IAAI,IAAI,IAAI;GACzB,IAAI,KAAK,IAAI,KAAK,GAChB,KAAK,OAAO,KAAK;QAEjB,KAAK,IAAI,KAAK;GAEhB,OAAO;EACT,CAAC;EACD,UAAU,KAAK;CACjB,GACA,CAAC,QAAQ,KAAK,CAChB;CAEA,MAAM,QAAQ,kBAAkB;EAC9B,+BAAe,IAAI,IAAI,CAAC;EACxB,UAAU,IAAI;CAChB,GAAG,CAAC,CAAC;CAEL,MAAM,UAAU,aACb,YAAyB;EACxB,eACE,IAAI,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,QAAO,UAAS,SAAS,KAAK,QAAQ,KAAK,CAAC,CACnE;CACF,GACA,CAAC,KAAK,CACR;CAOA,OAAO;EACL;EACA,YAPiB,aAChB,UAAkB,SAAS,IAAI,KAAK,GACrC,CAAC,QAAQ,CAKA;EACT;EACA;EACA;CACF;AACF"}
@@ -1,5 +1,4 @@
1
1
  import { RefObject } from "react";
2
-
3
2
  //#region src/hooks/use-mutation-observer.d.ts
4
3
  type Target<T extends Node> = RefObject<T | null> | T | null | undefined;
5
4
  interface Options extends MutationObserverInit {
@@ -7,5 +6,5 @@ interface Options extends MutationObserverInit {
7
6
  }
8
7
  declare const useMutationObserver: <T extends Node>(target: Target<T>, callback: MutationCallback, options?: Options) => void;
9
8
  //#endregion
10
- export { useMutationObserver };
9
+ export { useMutationObserver as default };
11
10
  //# sourceMappingURL=use-mutation-observer.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useEffect, useRef } from "react";
2
-
3
2
  //#region src/hooks/use-mutation-observer.ts
4
3
  const resolveTarget = (target) => {
5
4
  if (!target) return null;
@@ -11,6 +10,7 @@ const useMutationObserver = (target, callback, options = {}) => {
11
10
  useEffect(() => {
12
11
  callbackRef.current = callback;
13
12
  });
13
+ const optionsKey = JSON.stringify(mutationOptions);
14
14
  useEffect(() => {
15
15
  if (!enabled || typeof MutationObserver === "undefined") return;
16
16
  let cancelled = false;
@@ -38,10 +38,10 @@ const useMutationObserver = (target, callback, options = {}) => {
38
38
  }, [
39
39
  target,
40
40
  enabled,
41
- JSON.stringify(mutationOptions)
41
+ optionsKey
42
42
  ]);
43
43
  };
44
-
45
44
  //#endregion
46
45
  export { useMutationObserver as default };
46
+
47
47
  //# sourceMappingURL=use-mutation-observer.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-mutation-observer.mjs","names":[],"sources":["../../src/hooks/use-mutation-observer.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ntype Target<T extends Node> = RefObject<T | null> | T | null | undefined;\n\ninterface Options extends MutationObserverInit {\n enabled?: boolean;\n}\n\nconst resolveTarget = <T extends Node>(target: Target<T>): T | null => {\n if (!target) {\n return null;\n }\n return 'current' in target ? target.current : target;\n};\n\n// Takes the target directly (a RefObject, or a plain Node like\n// document.head that isn't behind any React ref at all) rather than\n// producing its own — this and useResizeObserver together replace the\n// ResizeObserver+MutationObserver pair live-editor's iframe hand-rolls\n// for auto-sizing its preview content.\nconst useMutationObserver = <T extends Node>(\n target: Target<T>,\n callback: MutationCallback,\n options: Options = {},\n) => {\n const { enabled = true, ...mutationOptions } = options;\n\n const callbackRef = useRef(callback);\n\n useEffect(() => {\n callbackRef.current = callback;\n });\n\n // `mutationOptions` is typically a fresh object literal at the call\n // site — serialize it into a stable key instead of depending on the\n // object itself, which would tear down and recreate the observer on\n // every render.\n const optionsKey = JSON.stringify(mutationOptions);\n\n useEffect(() => {\n if (!enabled || typeof MutationObserver === 'undefined') {\n return;\n }\n\n let cancelled = false;\n let rafId: number | undefined;\n let observer: MutationObserver | undefined;\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, since the\n // ref object's identity doesn't change to re-run this effect. A raw\n // node has nothing to wait for.\n if (target && 'current' in target && !target.current) {\n rafId = requestAnimationFrame(attach);\n return;\n }\n\n const node = resolveTarget(target);\n\n if (!node) {\n return;\n }\n\n observer = new MutationObserver((mutations, obs) => {\n callbackRef.current(mutations, obs);\n });\n\n observer.observe(node, mutationOptions);\n };\n\n attach();\n\n return () => {\n cancelled = true;\n if (rafId !== undefined) {\n cancelAnimationFrame(rafId);\n }\n observer?.disconnect();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [target, enabled, optionsKey]);\n};\n\nexport default useMutationObserver;\n"],"mappings":";;;AAQA,MAAM,iBAAiC,WAAgC;AACrE,KAAI,CAAC,OACH,QAAO;AAET,QAAO,aAAa,SAAS,OAAO,UAAU;;AAQhD,MAAM,uBACJ,QACA,UACA,UAAmB,EAAE,KAClB;CACH,MAAM,EAAE,UAAU,MAAM,GAAG,oBAAoB;CAE/C,MAAM,cAAc,OAAO,SAAS;AAEpC,iBAAgB;AACd,cAAY,UAAU;GACtB;AAQF,iBAAgB;AACd,MAAI,CAAC,WAAW,OAAO,qBAAqB,YAC1C;EAGF,IAAI,YAAY;EAChB,IAAI;EACJ,IAAI;EAEJ,MAAM,eAAe;AACnB,OAAI,UACF;AAQF,OAAI,UAAU,aAAa,UAAU,CAAC,OAAO,SAAS;AACpD,YAAQ,sBAAsB,OAAO;AACrC;;GAGF,MAAM,OAAO,cAAc,OAAO;AAElC,OAAI,CAAC,KACH;AAGF,cAAW,IAAI,kBAAkB,WAAW,QAAQ;AAClD,gBAAY,QAAQ,WAAW,IAAI;KACnC;AAEF,YAAS,QAAQ,MAAM,gBAAgB;;AAGzC,UAAQ;AAER,eAAa;AACX,eAAY;AACZ,OAAI,UAAU,OACZ,sBAAqB,MAAM;AAE7B,aAAU,YAAY;;IAGvB;EAAC;EAAQ;EAjDO,KAAK,UAAU,gBAAgB;EAiDlB,CAAC"}
1
+ {"version":3,"file":"use-mutation-observer.mjs","names":[],"sources":["../../src/hooks/use-mutation-observer.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ntype Target<T extends Node> = RefObject<T | null> | T | null | undefined;\n\ninterface Options extends MutationObserverInit {\n enabled?: boolean;\n}\n\nconst resolveTarget = <T extends Node>(target: Target<T>): T | null => {\n if (!target) {\n return null;\n }\n return 'current' in target ? target.current : target;\n};\n\n// Takes the target directly (a RefObject, or a plain Node like\n// document.head that isn't behind any React ref at all) rather than\n// producing its own — this and useResizeObserver together replace the\n// ResizeObserver+MutationObserver pair live-editor's iframe hand-rolls\n// for auto-sizing its preview content.\nconst useMutationObserver = <T extends Node>(\n target: Target<T>,\n callback: MutationCallback,\n options: Options = {},\n) => {\n const { enabled = true, ...mutationOptions } = options;\n\n const callbackRef = useRef(callback);\n\n useEffect(() => {\n callbackRef.current = callback;\n });\n\n // `mutationOptions` is typically a fresh object literal at the call\n // site — serialize it into a stable key instead of depending on the\n // object itself, which would tear down and recreate the observer on\n // every render.\n const optionsKey = JSON.stringify(mutationOptions);\n\n useEffect(() => {\n if (!enabled || typeof MutationObserver === 'undefined') {\n return;\n }\n\n let cancelled = false;\n let rafId: number | undefined;\n let observer: MutationObserver | undefined;\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, since the\n // ref object's identity doesn't change to re-run this effect. A raw\n // node has nothing to wait for.\n if (target && 'current' in target && !target.current) {\n rafId = requestAnimationFrame(attach);\n return;\n }\n\n const node = resolveTarget(target);\n\n if (!node) {\n return;\n }\n\n observer = new MutationObserver((mutations, obs) => {\n callbackRef.current(mutations, obs);\n });\n\n observer.observe(node, mutationOptions);\n };\n\n attach();\n\n return () => {\n cancelled = true;\n if (rafId !== undefined) {\n cancelAnimationFrame(rafId);\n }\n observer?.disconnect();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [target, enabled, optionsKey]);\n};\n\nexport default useMutationObserver;\n"],"mappings":";;AAQA,MAAM,iBAAiC,WAAgC;CACrE,IAAI,CAAC,QACH,OAAO;CAET,OAAO,aAAa,SAAS,OAAO,UAAU;AAChD;AAOA,MAAM,uBACJ,QACA,UACA,UAAmB,CAAC,MACjB;CACH,MAAM,EAAE,UAAU,MAAM,GAAG,oBAAoB;CAE/C,MAAM,cAAc,OAAO,QAAQ;CAEnC,gBAAgB;EACd,YAAY,UAAU;CACxB,CAAC;CAMD,MAAM,aAAa,KAAK,UAAU,eAAe;CAEjD,gBAAgB;EACd,IAAI,CAAC,WAAW,OAAO,qBAAqB,aAC1C;EAGF,IAAI,YAAY;EAChB,IAAI;EACJ,IAAI;EAEJ,MAAM,eAAe;GACnB,IAAI,WACF;GAQF,IAAI,UAAU,aAAa,UAAU,CAAC,OAAO,SAAS;IACpD,QAAQ,sBAAsB,MAAM;IACpC;GACF;GAEA,MAAM,OAAO,cAAc,MAAM;GAEjC,IAAI,CAAC,MACH;GAGF,WAAW,IAAI,kBAAkB,WAAW,QAAQ;IAClD,YAAY,QAAQ,WAAW,GAAG;GACpC,CAAC;GAED,SAAS,QAAQ,MAAM,eAAe;EACxC;EAEA,OAAO;EAEP,aAAa;GACX,YAAY;GACZ,IAAI,UAAU,KAAA,GACZ,qBAAqB,KAAK;GAE5B,UAAU,WAAW;EACvB;CAEF,GAAG;EAAC;EAAQ;EAAS;CAAU,CAAC;AAClC"}
@@ -1,5 +1,5 @@
1
1
  //#region src/hooks/use-previous.d.ts
2
2
  declare const usePrevious: <T>(value: T) => T | undefined;
3
3
  //#endregion
4
- export { usePrevious };
4
+ export { usePrevious as default };
5
5
  //# sourceMappingURL=use-previous.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useEffect, useRef } from "react";
2
-
3
2
  //#region src/hooks/use-previous.ts
4
3
  const usePrevious = (value) => {
5
4
  const ref = useRef(void 0);
@@ -8,7 +7,7 @@ const usePrevious = (value) => {
8
7
  });
9
8
  return ref.current;
10
9
  };
11
-
12
10
  //#endregion
13
11
  export { usePrevious as default };
12
+
14
13
  //# sourceMappingURL=use-previous.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-previous.mjs","names":[],"sources":["../../src/hooks/use-previous.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\n// Returns the value from the *previous* render. Updated in an effect\n// (which runs after render/paint) rather than during render, so the\n// current render still reads whatever was current one render ago.\nconst usePrevious = <T>(value: T): T | undefined => {\n const ref = useRef<T | undefined>(undefined);\n\n useEffect(() => {\n ref.current = value;\n });\n\n return ref.current;\n};\n\nexport default usePrevious;\n"],"mappings":";;;AAKA,MAAM,eAAkB,UAA4B;CAClD,MAAM,MAAM,OAAsB,OAAU;AAE5C,iBAAgB;AACd,MAAI,UAAU;GACd;AAEF,QAAO,IAAI"}
1
+ {"version":3,"file":"use-previous.mjs","names":[],"sources":["../../src/hooks/use-previous.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\n// Returns the value from the *previous* render. Updated in an effect\n// (which runs after render/paint) rather than during render, so the\n// current render still reads whatever was current one render ago.\nconst usePrevious = <T>(value: T): T | undefined => {\n const ref = useRef<T | undefined>(undefined);\n\n useEffect(() => {\n ref.current = value;\n });\n\n // Reading `ref.current` during render is normally disallowed, but this\n // hook's entire contract *is* \"the value as of the last commit\" — there's\n // no derived-state rewrite that preserves that exact semantics (a\n // state-comparison rewrite would only track the last *distinct* value,\n // a breaking change to what's documented in the README). The react.dev\n // sanctioned \"lazy ref init\" exception doesn't apply either — this isn't\n // init-once, it updates every commit — and even that sanctioned pattern\n // still trips this same rule as of eslint-plugin-react-hooks 6.x/7.x,\n // per an open, unresolved upstream bug: facebook/react#36896.\n // eslint-disable-next-line react-hooks/refs\n return ref.current;\n};\n\nexport default usePrevious;\n"],"mappings":";;AAKA,MAAM,eAAkB,UAA4B;CAClD,MAAM,MAAM,OAAsB,KAAA,CAAS;CAE3C,gBAAgB;EACd,IAAI,UAAU;CAChB,CAAC;CAYD,OAAO,IAAI;AACb"}
@@ -4,5 +4,5 @@ interface Options {
4
4
  }
5
5
  declare const useRecursiveTimeout: <T>(callback: () => Promise<T> | void, delay: number | null, options?: Options) => void;
6
6
  //#endregion
7
- export { useRecursiveTimeout };
7
+ export { useRecursiveTimeout as default };
8
8
  //# sourceMappingURL=use-recursive-timeout.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useEffect, useRef } from "react";
2
-
3
2
  //#region src/hooks/use-recursive-timeout.ts
4
3
  const useRecursiveTimeout = (callback, delay, options = {}) => {
5
4
  const savedCallback = useRef(callback);
@@ -30,7 +29,7 @@ const useRecursiveTimeout = (callback, delay, options = {}) => {
30
29
  };
31
30
  }, [delay]);
32
31
  };
33
-
34
32
  //#endregion
35
33
  export { useRecursiveTimeout as default };
34
+
36
35
  //# sourceMappingURL=use-recursive-timeout.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-recursive-timeout.mjs","names":[],"sources":["../../src/hooks/use-recursive-timeout.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\ninterface Options {\n // Whether a rejected callback should stop the loop instead of scheduling\n // the next tick. Defaults to false — one failed tick shouldn't kill a\n // polling loop, but the rejection is still logged either way.\n stopOnError?: boolean;\n}\n\nconst useRecursiveTimeout = <T>(\n callback: () => Promise<T> | void,\n delay: number | null,\n options: Options = {},\n) => {\n const savedCallback = useRef(callback);\n const savedStopOnError = useRef(options.stopOnError ?? false);\n\n useEffect(() => {\n savedCallback.current = callback;\n savedStopOnError.current = options.stopOnError ?? false;\n });\n\n useEffect(() => {\n if (delay === null) {\n return;\n }\n\n let id: ReturnType<typeof setTimeout>;\n let cancelled = false;\n\n const scheduleNext = () => {\n if (!cancelled) {\n id = setTimeout(tick, delay);\n }\n };\n\n function tick() {\n const ret = savedCallback.current();\n\n if (ret instanceof Promise) {\n ret.then(scheduleNext, (error: unknown) => {\n console.error('useRecursiveTimeout callback rejected:', error);\n if (!savedStopOnError.current) {\n scheduleNext();\n }\n });\n } else {\n scheduleNext();\n }\n }\n\n id = setTimeout(tick, delay);\n\n return () => {\n cancelled = true;\n clearTimeout(id);\n };\n }, [delay]);\n};\n\nexport default useRecursiveTimeout;\n"],"mappings":";;;AASA,MAAM,uBACJ,UACA,OACA,UAAmB,EAAE,KAClB;CACH,MAAM,gBAAgB,OAAO,SAAS;CACtC,MAAM,mBAAmB,OAAO,QAAQ,eAAe,MAAM;AAE7D,iBAAgB;AACd,gBAAc,UAAU;AACxB,mBAAiB,UAAU,QAAQ,eAAe;GAClD;AAEF,iBAAgB;AACd,MAAI,UAAU,KACZ;EAGF,IAAI;EACJ,IAAI,YAAY;EAEhB,MAAM,qBAAqB;AACzB,OAAI,CAAC,UACH,MAAK,WAAW,MAAM,MAAM;;EAIhC,SAAS,OAAO;GACd,MAAM,MAAM,cAAc,SAAS;AAEnC,OAAI,eAAe,QACjB,KAAI,KAAK,eAAe,UAAmB;AACzC,YAAQ,MAAM,0CAA0C,MAAM;AAC9D,QAAI,CAAC,iBAAiB,QACpB,eAAc;KAEhB;OAEF,eAAc;;AAIlB,OAAK,WAAW,MAAM,MAAM;AAE5B,eAAa;AACX,eAAY;AACZ,gBAAa,GAAG;;IAEjB,CAAC,MAAM,CAAC"}
1
+ {"version":3,"file":"use-recursive-timeout.mjs","names":[],"sources":["../../src/hooks/use-recursive-timeout.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\ninterface Options {\n // Whether a rejected callback should stop the loop instead of scheduling\n // the next tick. Defaults to false — one failed tick shouldn't kill a\n // polling loop, but the rejection is still logged either way.\n stopOnError?: boolean;\n}\n\nconst useRecursiveTimeout = <T>(\n callback: () => Promise<T> | void,\n delay: number | null,\n options: Options = {},\n) => {\n const savedCallback = useRef(callback);\n const savedStopOnError = useRef(options.stopOnError ?? false);\n\n useEffect(() => {\n savedCallback.current = callback;\n savedStopOnError.current = options.stopOnError ?? false;\n });\n\n useEffect(() => {\n if (delay === null) {\n return;\n }\n\n let id: ReturnType<typeof setTimeout>;\n let cancelled = false;\n\n const scheduleNext = () => {\n if (!cancelled) {\n id = setTimeout(tick, delay);\n }\n };\n\n function tick() {\n const ret = savedCallback.current();\n\n if (ret instanceof Promise) {\n ret.then(scheduleNext, (error: unknown) => {\n console.error('useRecursiveTimeout callback rejected:', error);\n if (!savedStopOnError.current) {\n scheduleNext();\n }\n });\n } else {\n scheduleNext();\n }\n }\n\n id = setTimeout(tick, delay);\n\n return () => {\n cancelled = true;\n clearTimeout(id);\n };\n }, [delay]);\n};\n\nexport default useRecursiveTimeout;\n"],"mappings":";;AASA,MAAM,uBACJ,UACA,OACA,UAAmB,CAAC,MACjB;CACH,MAAM,gBAAgB,OAAO,QAAQ;CACrC,MAAM,mBAAmB,OAAO,QAAQ,eAAe,KAAK;CAE5D,gBAAgB;EACd,cAAc,UAAU;EACxB,iBAAiB,UAAU,QAAQ,eAAe;CACpD,CAAC;CAED,gBAAgB;EACd,IAAI,UAAU,MACZ;EAGF,IAAI;EACJ,IAAI,YAAY;EAEhB,MAAM,qBAAqB;GACzB,IAAI,CAAC,WACH,KAAK,WAAW,MAAM,KAAK;EAE/B;EAEA,SAAS,OAAO;GACd,MAAM,MAAM,cAAc,QAAQ;GAElC,IAAI,eAAe,SACjB,IAAI,KAAK,eAAe,UAAmB;IACzC,QAAQ,MAAM,0CAA0C,KAAK;IAC7D,IAAI,CAAC,iBAAiB,SACpB,aAAa;GAEjB,CAAC;QAED,aAAa;EAEjB;EAEA,KAAK,WAAW,MAAM,KAAK;EAE3B,aAAa;GACX,YAAY;GACZ,aAAa,EAAE;EACjB;CACF,GAAG,CAAC,KAAK,CAAC;AACZ"}
@@ -8,5 +8,5 @@ interface Options {
8
8
  }
9
9
  declare const useResizeObserver: <T extends Element = Element>(options?: Options) => [(node: T | null) => void, Size | null];
10
10
  //#endregion
11
- export { useResizeObserver };
11
+ export { useResizeObserver as default };
12
12
  //# sourceMappingURL=use-resize-observer.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
-
3
2
  //#region src/hooks/use-resize-observer.ts
4
3
  const useResizeObserver = (options) => {
5
4
  const box = options?.box ?? "content-box";
@@ -47,7 +46,7 @@ const useResizeObserver = (options) => {
47
46
  }, []);
48
47
  return [ref, size];
49
48
  };
50
-
51
49
  //#endregion
52
50
  export { useResizeObserver as default };
51
+
53
52
  //# sourceMappingURL=use-resize-observer.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-resize-observer.mjs","names":[],"sources":["../../src/hooks/use-resize-observer.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface Size {\n width: number;\n height: number;\n}\n\ninterface Options {\n box?: ResizeObserverBoxOptions;\n}\n\n// The unprocessed version of useResponsiveSize/useElementScroll/\n// useElementPosition — those each return values shaped for their own\n// purpose (breakpoints, scroll position, a viewport-relative DOMRect).\n// This one just reports an element's own width/height.\nconst useResizeObserver = <T extends Element = Element>(\n options?: Options,\n): [(node: T | null) => void, Size | null] => {\n const box = options?.box ?? 'content-box';\n\n const [size, setSize] = useState<Size | null>(null);\n const observerRef = useRef<ResizeObserver | null>(null);\n const nodeRef = useRef<T | null>(null);\n\n // `options` is typically a fresh object literal at the call site — track\n // the current box in a ref (read at connect time) and reconnect only when\n // the value actually changes, so `box` can be flipped at runtime without\n // tearing the observer down on every render. The previous version pinned\n // whatever `box` was current when the node first attached and never\n // reacted to changes — the same bug #118 fixed in useIntersectionObserver.\n const boxRef = useRef(box);\n\n useEffect(() => {\n boxRef.current = box;\n });\n\n const connect = useCallback(() => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n\n const node = nodeRef.current;\n\n if (!node || typeof ResizeObserver === 'undefined') {\n return;\n }\n\n const currentBox = boxRef.current;\n\n const observer = new ResizeObserver(([entry]) => {\n if (!entry) {\n return;\n }\n\n const boxSize =\n currentBox === 'border-box'\n ? entry.borderBoxSize\n : entry.contentBoxSize;\n const measurement = boxSize?.[0];\n\n if (measurement) {\n // inlineSize/blockSize are writing-mode relative; for the default\n // horizontal-tb writing mode they map to width/height as expected.\n setSize({\n width: measurement.inlineSize,\n height: measurement.blockSize,\n });\n } else {\n // contentBoxSize/borderBoxSize come back as empty arrays for a\n // `display: none` target, so destructuring the first element would\n // throw. contentRect still reports (0×0 there) and is already in\n // physical width/height, so it's also unaffected by writing-mode.\n setSize({\n width: entry.contentRect.width,\n height: entry.contentRect.height,\n });\n }\n });\n\n observer.observe(node, { box: currentBox });\n observerRef.current = observer;\n }, []);\n\n const ref = useCallback((node: T | null) => {\n nodeRef.current = node;\n connect();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n connect();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [box]);\n\n useEffect(() => {\n return () => {\n observerRef.current?.disconnect();\n };\n }, []);\n\n return [ref, size];\n};\n\nexport default useResizeObserver;\n"],"mappings":";;;AAeA,MAAM,qBACJ,YAC4C;CAC5C,MAAM,MAAM,SAAS,OAAO;CAE5B,MAAM,CAAC,MAAM,WAAW,SAAsB,KAAK;CACnD,MAAM,cAAc,OAA8B,KAAK;CACvD,MAAM,UAAU,OAAiB,KAAK;CAQtC,MAAM,SAAS,OAAO,IAAI;AAE1B,iBAAgB;AACd,SAAO,UAAU;GACjB;CAEF,MAAM,UAAU,kBAAkB;AAChC,MAAI,YAAY,SAAS;AACvB,eAAY,QAAQ,YAAY;AAChC,eAAY,UAAU;;EAGxB,MAAM,OAAO,QAAQ;AAErB,MAAI,CAAC,QAAQ,OAAO,mBAAmB,YACrC;EAGF,MAAM,aAAa,OAAO;EAE1B,MAAM,WAAW,IAAI,gBAAgB,CAAC,WAAW;AAC/C,OAAI,CAAC,MACH;GAOF,MAAM,eAHJ,eAAe,eACX,MAAM,gBACN,MAAM,kBACkB;AAE9B,OAAI,YAGF,SAAQ;IACN,OAAO,YAAY;IACnB,QAAQ,YAAY;IACrB,CAAC;OAMF,SAAQ;IACN,OAAO,MAAM,YAAY;IACzB,QAAQ,MAAM,YAAY;IAC3B,CAAC;IAEJ;AAEF,WAAS,QAAQ,MAAM,EAAE,KAAK,YAAY,CAAC;AAC3C,cAAY,UAAU;IACrB,EAAE,CAAC;CAEN,MAAM,MAAM,aAAa,SAAmB;AAC1C,UAAQ,UAAU;AAClB,WAAS;IAER,EAAE,CAAC;AAEN,iBAAgB;AACd,WAAS;IAER,CAAC,IAAI,CAAC;AAET,iBAAgB;AACd,eAAa;AACX,eAAY,SAAS,YAAY;;IAElC,EAAE,CAAC;AAEN,QAAO,CAAC,KAAK,KAAK"}
1
+ {"version":3,"file":"use-resize-observer.mjs","names":[],"sources":["../../src/hooks/use-resize-observer.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface Size {\n width: number;\n height: number;\n}\n\ninterface Options {\n box?: ResizeObserverBoxOptions;\n}\n\n// The unprocessed version of useResponsiveSize/useElementScroll/\n// useElementPosition — those each return values shaped for their own\n// purpose (breakpoints, scroll position, a viewport-relative DOMRect).\n// This one just reports an element's own width/height.\nconst useResizeObserver = <T extends Element = Element>(\n options?: Options,\n): [(node: T | null) => void, Size | null] => {\n const box = options?.box ?? 'content-box';\n\n const [size, setSize] = useState<Size | null>(null);\n const observerRef = useRef<ResizeObserver | null>(null);\n const nodeRef = useRef<T | null>(null);\n\n // `options` is typically a fresh object literal at the call site — track\n // the current box in a ref (read at connect time) and reconnect only when\n // the value actually changes, so `box` can be flipped at runtime without\n // tearing the observer down on every render. The previous version pinned\n // whatever `box` was current when the node first attached and never\n // reacted to changes — the same bug #118 fixed in useIntersectionObserver.\n const boxRef = useRef(box);\n\n useEffect(() => {\n boxRef.current = box;\n });\n\n const connect = useCallback(() => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n\n const node = nodeRef.current;\n\n if (!node || typeof ResizeObserver === 'undefined') {\n return;\n }\n\n const currentBox = boxRef.current;\n\n const observer = new ResizeObserver(([entry]) => {\n if (!entry) {\n return;\n }\n\n const boxSize =\n currentBox === 'border-box'\n ? entry.borderBoxSize\n : entry.contentBoxSize;\n const measurement = boxSize?.[0];\n\n if (measurement) {\n // inlineSize/blockSize are writing-mode relative; for the default\n // horizontal-tb writing mode they map to width/height as expected.\n setSize({\n width: measurement.inlineSize,\n height: measurement.blockSize,\n });\n } else {\n // contentBoxSize/borderBoxSize come back as empty arrays for a\n // `display: none` target, so destructuring the first element would\n // throw. contentRect still reports (0×0 there) and is already in\n // physical width/height, so it's also unaffected by writing-mode.\n setSize({\n width: entry.contentRect.width,\n height: entry.contentRect.height,\n });\n }\n });\n\n observer.observe(node, { box: currentBox });\n observerRef.current = observer;\n }, []);\n\n const ref = useCallback((node: T | null) => {\n nodeRef.current = node;\n connect();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n connect();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [box]);\n\n useEffect(() => {\n return () => {\n observerRef.current?.disconnect();\n };\n }, []);\n\n return [ref, size];\n};\n\nexport default useResizeObserver;\n"],"mappings":";;AAeA,MAAM,qBACJ,YAC4C;CAC5C,MAAM,MAAM,SAAS,OAAO;CAE5B,MAAM,CAAC,MAAM,WAAW,SAAsB,IAAI;CAClD,MAAM,cAAc,OAA8B,IAAI;CACtD,MAAM,UAAU,OAAiB,IAAI;CAQrC,MAAM,SAAS,OAAO,GAAG;CAEzB,gBAAgB;EACd,OAAO,UAAU;CACnB,CAAC;CAED,MAAM,UAAU,kBAAkB;EAChC,IAAI,YAAY,SAAS;GACvB,YAAY,QAAQ,WAAW;GAC/B,YAAY,UAAU;EACxB;EAEA,MAAM,OAAO,QAAQ;EAErB,IAAI,CAAC,QAAQ,OAAO,mBAAmB,aACrC;EAGF,MAAM,aAAa,OAAO;EAE1B,MAAM,WAAW,IAAI,gBAAgB,CAAC,WAAW;GAC/C,IAAI,CAAC,OACH;GAOF,MAAM,eAHJ,eAAe,eACX,MAAM,gBACN,MAAM,eAAA,GACkB;GAE9B,IAAI,aAGF,QAAQ;IACN,OAAO,YAAY;IACnB,QAAQ,YAAY;GACtB,CAAC;QAMD,QAAQ;IACN,OAAO,MAAM,YAAY;IACzB,QAAQ,MAAM,YAAY;GAC5B,CAAC;EAEL,CAAC;EAED,SAAS,QAAQ,MAAM,EAAE,KAAK,WAAW,CAAC;EAC1C,YAAY,UAAU;CACxB,GAAG,CAAC,CAAC;CAEL,MAAM,MAAM,aAAa,SAAmB;EAC1C,QAAQ,UAAU;EAClB,QAAQ;CAEV,GAAG,CAAC,CAAC;CAEL,gBAAgB;EACd,QAAQ;CAEV,GAAG,CAAC,GAAG,CAAC;CAER,gBAAgB;EACd,aAAa;GACX,YAAY,SAAS,WAAW;EAClC;CACF,GAAG,CAAC,CAAC;CAEL,OAAO,CAAC,KAAK,IAAI;AACnB"}
@@ -23,5 +23,5 @@ declare const useResponsiveSize: <T extends HTMLElement>(options?: Options) => {
23
23
  ref: (node: T | null) => void;
24
24
  };
25
25
  //#endregion
26
- export { useResponsiveSize };
26
+ export { useResponsiveSize as default };
27
27
  //# sourceMappingURL=use-responsive-size.d.mts.map
@@ -1,6 +1,5 @@
1
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,
@@ -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 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":"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"}
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":";;;AAeA,MAAM,oBAAoB,cAAmD;AAC3E,KAAI,CAAC,UACH,QAAO;AAET,QAAO,aAAa,YAAY,UAAU,UAAU;;AAGtD,MAAM,uBAAuB,mBAA6B;CACxD,MAAM,cAAc,uBAAO,IAAI,KAA8B,CAAC;CAC9D,MAAM,uBAAuB,uBAC3B,IAAI,KAAqD,CAC1D;CAKD,MAAM,oBAAoB,OAAO,eAAe;AAEhD,iBAAgB;AACd,oBAAkB,UAAU;GAC5B;AA0EF,QAAO;EAAE,UArEQ,aAAa,QAAoB;GAChD,IAAI,WAAW,qBAAqB,QAAQ,IAAI,IAAI;AAEpD,OAAI,CAAC,UAAU;AACb,gBAAY,SAA6B;AACvC,SAAI,KACF,aAAY,QAAQ,IAAI,KAAK,KAAK;SAElC,aAAY,QAAQ,OAAO,IAAI;;AAGnC,yBAAqB,QAAQ,IAAI,KAAK,SAAS;;AAGjD,UAAO;KACN,EAAE,CAAC;EAsDa,UApDF,aAAa,KAAiB,YAAsB;GACnE,MAAM,UAAU,YAAY,QAAQ,IAAI,IAAI;AAE5C,OAAI,CAAC,QACH;GAGF,MAAM,EAAE,QAAQ,WAAW,GAAG,0BAA0B;IACtD,GAAG,kBAAkB;IACrB,GAAG;IACJ;AAOD,OAAI,UAAU,MAAM;IAClB,MAAM,mBAAmB,iBAAiB,aAAa,KAAK;AAE5D,QAAI,kBAAkB;KACpB,MAAM,gBAAgB,iBAAiB,uBAAuB;KAC9D,MAAM,cAAc,QAAQ,uBAAuB;KACnD,MAAM,MACJ,iBAAiB,aAChB,YAAY,MAAM,cAAc,OACjC;AAEF,sBAAiB,SAAS;MACxB;MACA,UAAU,sBAAsB,YAAY;MAC7C,CAAC;AACF;;IAGF,MAAM,MAAM,QAAQ,uBAAuB,CAAC,MAAM,OAAO,UAAU;AAEnE,WAAO,SAAS;KACd;KACA,UAAU,sBAAsB,YAAY;KAC7C,CAAC;AACF;;AAGF,WAAQ,eAAe;IACrB,UAAU;IACV,OAAO;IACP,QAAQ;IACR,GAAG;IACJ,CAAC;KACD,EAAE,CAAC;EAEuB"}
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 = useRef(null);
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
- stableThrottledCallback.current = (...args) => {
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.current;
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 const stableThrottledCallback = useRef<((...args: Args) => void) | null>(\n null,\n );\n\n if (!stableThrottledCallback.current) {\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 stableThrottledCallback.current = (...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.current;\n};\n\nexport default useThrottledCallback;\n"],"mappings":";;;AAaA,MAAM,wBACJ,UACA,QAAQ,KACR,EAAE,UAAU,MAAM,WAAW,SAAkB,EAAE,KAC9C;CACH,MAAM,cAAc,OAAO,SAAS;CACpC,MAAM,WAAW,OAAO,MAAM;CAC9B,MAAM,aAAa,OAAO,QAAQ;CAClC,MAAM,cAAc,OAAO,SAAS;AAEpC,iBAAgB;AACd,cAAY,UAAU;AACtB,WAAS,UAAU;AACnB,aAAW,UAAU;AACrB,cAAY,UAAU;GACtB;CAEF,MAAM,kBAAkB,OAAsB,KAAK;CACnD,MAAM,aAAa,OAA6C,KAAK;CACrE,MAAM,cAAc,OAAoB,KAAK;CAE7C,MAAM,0BAA0B,OAC9B,KACD;AAED,KAAI,CAAC,wBAAwB,SAAS;EACpC,MAAM,eAAe;AACnB,mBAAgB,UAAU,KAAK,KAAK;AACpC,cAAW,UAAU;AAErB,OAAI,YAAY,QACd,aAAY,QAAQ,GAAG,YAAY,QAAQ;;AAI/C,0BAAwB,WAAW,GAAG,SAAe;AACnD,eAAY,UAAU;GAEtB,MAAM,MAAM,KAAK,KAAK;GACtB,MAAM,UACJ,gBAAgB,YAAY,OACxB,WACA,MAAM,gBAAgB;AAE5B,OAAI,WAAW,SAAS,SAAS;AAC/B,QAAI,WAAW,SAAS;AACtB,kBAAa,WAAW,QAAQ;AAChC,gBAAW,UAAU;;AAGvB,QAAI,WAAW,QACb,SAAQ;SACH;AACL,qBAAgB,UAAU;AAE1B,SAAI,YAAY,QACd,YAAW,UAAU,WAAW,QAAQ,SAAS,QAAQ;;AAG7D;;AAGF,OAAI,YAAY,WAAW,CAAC,WAAW,QACrC,YAAW,UAAU,WAAW,QAAQ,SAAS,UAAU,QAAQ;;;AAKzE,iBAAgB;AACd,eAAa;AACX,OAAI,WAAW,QACb,cAAa,WAAW,QAAQ;;IAGnC,EAAE,CAAC;AAEN,QAAO,wBAAwB"}
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"}
@@ -5,5 +5,5 @@ interface Options {
5
5
  }
6
6
  declare const useThrottledValue: <T>(value: T, delay?: number, options?: Options) => T;
7
7
  //#endregion
8
- export { useThrottledValue };
8
+ export { useThrottledValue as default };
9
9
  //# sourceMappingURL=use-throttled-value.d.mts.map
@@ -1,6 +1,5 @@
1
1
  import useThrottledCallback from "./use-throttled-callback.mjs";
2
2
  import { useEffect, useState } from "react";
3
-
4
3
  //#region src/hooks/use-throttled-value.ts
5
4
  const useThrottledValue = (value, delay = 100, options = {}) => {
6
5
  const [throttledValue, setThrottledValue] = useState(value);
@@ -10,7 +9,7 @@ const useThrottledValue = (value, delay = 100, options = {}) => {
10
9
  }, [value, throttledSetValue]);
11
10
  return throttledValue;
12
11
  };
13
-
14
12
  //#endregion
15
13
  export { useThrottledValue as default };
14
+
16
15
  //# sourceMappingURL=use-throttled-value.mjs.map
@@ -1 +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,EAAE,KACf;CACN,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,MAAM;CAE3D,MAAM,oBAAoB,qBACxB,mBACA,OACA,QACD;AAED,iBAAgB;AACd,oBAAkB,MAAM;IAIvB,CAAC,OAAO,kBAAkB,CAAC;AAE9B,QAAO"}
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"}
@@ -4,5 +4,5 @@ declare const useTimeout: (callback: () => void, delay: number | null) => {
4
4
  clear: () => void;
5
5
  };
6
6
  //#endregion
7
- export { useTimeout };
7
+ export { useTimeout as default };
8
8
  //# sourceMappingURL=use-timeout.d.mts.map
@@ -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":";;;AAMA,MAAM,cAAc,UAAsB,UAAyB;CACjE,MAAM,cAAc,OAAO,SAAS;CACpC,MAAM,WAAW,OAAO,MAAM;CAC9B,MAAM,aAAa,OAA6C,KAAK;AAErE,iBAAgB;AACd,cAAY,UAAU;AACtB,WAAS,UAAU;GACnB;CAEF,MAAM,QAAQ,kBAAkB;AAC9B,MAAI,WAAW,YAAY,MAAM;AAC/B,gBAAa,WAAW,QAAQ;AAChC,cAAW,UAAU;;IAEtB,EAAE,CAAC;CAKN,MAAM,QAAQ,kBAAkB;AAC9B,SAAO;AAEP,MAAI,SAAS,YAAY,KACvB;AAGF,aAAW,UAAU,iBAAiB;AACpC,cAAW,UAAU;AACrB,eAAY,SAAS;KACpB,SAAS,QAAQ;IACnB,CAAC,MAAM,CAAC;AAEX,iBAAgB;AACd,SAAO;AACP,SAAO;IAEN,CAAC,MAAM,CAAC;AAEX,QAAO;EAAE;EAAO;EAAO"}
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,5 @@
1
1
  //#region src/hooks/use-toggle.d.ts
2
2
  declare const useToggle: (initial?: boolean) => [boolean, () => void, (value: boolean) => void];
3
3
  //#endregion
4
- export { useToggle };
4
+ export { useToggle as default };
5
5
  //# sourceMappingURL=use-toggle.d.mts.map
@@ -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":";;;AAEA,MAAM,aACJ,UAAU,UAC0C;CACpD,MAAM,CAAC,OAAO,YAAY,SAAS,QAAQ;AAM3C,QAAO;EAAC;EAJO,kBAAkB;AAC/B,aAAS,SAAQ,CAAC,KAAK;KACtB,EAAE,CAAC;EAEiB;EAAS"}
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"}
@@ -14,5 +14,5 @@ interface Options {
14
14
  }
15
15
  declare const useViewport: (options?: Options) => ViewportInfo;
16
16
  //#endregion
17
- export { useViewport };
17
+ export { useViewport as default };
18
18
  //# sourceMappingURL=use-viewport.d.mts.map
@@ -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":";;;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,EAAE,KAAK;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;EACR,CAAC;CAEF,MAAM,mBAAmB,kBAAkB;EACzC,MAAM,eAAe,OAAO;EAC5B,MAAM,eAAe,OAAO,gBAAgB,UAAU;EACtD,MAAM,iBAAiB,SAAS,gBAAgB;EAChD,MAAM,aAAa,SAAS,KAAK;AAEjC,MAAI,OAAO,kBAAkB,KAAK,IAAI,eAAe,aAAa,GAAG,IACnE,QAAO;AAGT,SAAO,KAAK,IAAI,cAAc,gBAAgB,WAAW;IACxD,EAAE,CAAC;CAON,MAAM,eAAe,kBAAgC;AAMnD,SAAO;GACL,OANY,OAAO,gBAAgB,SAAS,OAAO;GAOnD,QANa,UACX,kBAAkB,GAClB,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;GACxC;IACA,CAAC,SAAS,iBAAiB,CAAC;AAE/B,iBAAgB;EACd,IAAI;EAMJ,MAAM,kBAAkB,SAAuB;AAC7C,gBAAY,SAAS,cAAc,MAAM,KAAK,GAAG,OAAO,KAAM;;EAGhE,MAAM,wBAAwB;AAC5B,gBAAa,UAAU;AACvB,eAAY,iBAAiB;AAC3B,mBAAe,cAAc,CAAC;MAC7B,SAAS;;EAGd,MAAM,wBAAwB,eAAe,cAAc,CAAC;AAE5D,mBAAiB;EAEjB,MAAM,SAAS,CAAC,UAAU,oBAAoB;AAE9C,MAAI,QACF,QAAO,KAAK,SAAS,QAAQ,cAAc,WAAW;AAGxD,SAAO,SAAQ,UAAS;AACtB,OAAI,UAAU,YAAY,UAAU,oBAClC,QAAO,iBAAiB,OAAO,gBAAgB;OAE/C,QAAO,iBAAiB,OAAO,iBAAiB,EAAE,SAAS,MAAM,CAAC;IAEpE;AAEF,MAAI,OAAO,gBAAgB;AACzB,UAAO,eAAe,iBAAiB,UAAU,gBAAgB;AACjE,UAAO,eAAe,iBAAiB,UAAU,gBAAgB;;EAGnE,IAAI;AACJ,MAAI,SAAS;GACX,IAAI,aAAa,cAAc,CAAC;AAChC,gBAAa,kBAAkB;IAC7B,MAAM,gBAAgB,cAAc,CAAC;AACrC,QAAI,KAAK,IAAI,gBAAgB,WAAW,GAAG,IAAI;AAC7C,kBAAa;AACb,sBAAiB;;MAElB,IAAI;;AAGT,eAAa;AACX,gBAAa,UAAU;AACvB,OAAI,WAAY,eAAc,WAAW;AAEzC,UAAO,SAAQ,UAAS;AACtB,WAAO,oBACL,OACA,UAAU,YAAY,UAAU,sBAC5B,kBACA,gBACL;KACD;AAEF,OAAI,OAAO,gBAAgB;AACzB,WAAO,eAAe,oBAAoB,UAAU,gBAAgB;AACpE,WAAO,eAAe,oBAAoB,UAAU,gBAAgB;;;IAGvE;EAAC;EAAc;EAAS;EAAS,CAAC;AAErC,QAAO"}
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"}