@jbpark/use-hooks 3.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.ko.md +40 -22
  2. package/README.md +40 -22
  3. package/dist/hooks/index.d.mts +31 -30
  4. package/dist/hooks/index.mjs +3 -4
  5. package/dist/hooks/use-body-scroll-lock.d.mts +1 -1
  6. package/dist/hooks/use-body-scroll-lock.mjs +1 -2
  7. package/dist/hooks/use-body-scroll-lock.mjs.map +1 -1
  8. package/dist/hooks/use-click-outside.d.mts +1 -2
  9. package/dist/hooks/use-click-outside.mjs +4 -4
  10. package/dist/hooks/use-click-outside.mjs.map +1 -1
  11. package/dist/hooks/use-controllable-state.d.mts +1 -1
  12. package/dist/hooks/use-controllable-state.mjs +1 -2
  13. package/dist/hooks/use-controllable-state.mjs.map +1 -1
  14. package/dist/hooks/use-debounced-callback.d.mts +10 -0
  15. package/dist/hooks/{use-debounce.mjs → use-debounced-callback.mjs} +13 -13
  16. package/dist/hooks/use-debounced-callback.mjs.map +1 -0
  17. package/dist/hooks/use-debounced-value.d.mts +1 -1
  18. package/dist/hooks/use-debounced-value.mjs +1 -2
  19. package/dist/hooks/use-debounced-value.mjs.map +1 -1
  20. package/dist/hooks/use-element-position.d.mts +1 -2
  21. package/dist/hooks/use-element-position.mjs +1 -2
  22. package/dist/hooks/use-element-position.mjs.map +1 -1
  23. package/dist/hooks/use-element-scroll.d.mts +2 -4
  24. package/dist/hooks/use-element-scroll.mjs +3 -3
  25. package/dist/hooks/use-element-scroll.mjs.map +1 -1
  26. package/dist/hooks/use-event-listener.d.mts +1 -2
  27. package/dist/hooks/use-event-listener.mjs +22 -10
  28. package/dist/hooks/use-event-listener.mjs.map +1 -1
  29. package/dist/hooks/use-file-drop.d.mts +1 -1
  30. package/dist/hooks/use-file-drop.mjs +7 -6
  31. package/dist/hooks/use-file-drop.mjs.map +1 -1
  32. package/dist/hooks/use-file-to-data-url.d.mts +1 -1
  33. package/dist/hooks/use-file-to-data-url.mjs +1 -2
  34. package/dist/hooks/use-file-to-data-url.mjs.map +1 -1
  35. package/dist/hooks/use-history-state.d.mts +1 -1
  36. package/dist/hooks/use-history-state.mjs +3 -3
  37. package/dist/hooks/use-history-state.mjs.map +1 -1
  38. package/dist/hooks/use-image.d.mts +1 -1
  39. package/dist/hooks/use-image.mjs +24 -8
  40. package/dist/hooks/use-image.mjs.map +1 -1
  41. package/dist/hooks/use-intersection-observer.d.mts +1 -1
  42. package/dist/hooks/use-intersection-observer.mjs +1 -2
  43. package/dist/hooks/use-intersection-observer.mjs.map +1 -1
  44. package/dist/hooks/use-interval.d.mts +1 -1
  45. package/dist/hooks/use-interval.mjs +1 -2
  46. package/dist/hooks/use-interval.mjs.map +1 -1
  47. package/dist/hooks/use-key-press.d.mts +1 -2
  48. package/dist/hooks/use-key-press.mjs +36 -12
  49. package/dist/hooks/use-key-press.mjs.map +1 -1
  50. package/dist/hooks/use-local-storage.d.mts +1 -1
  51. package/dist/hooks/use-local-storage.mjs +5 -3
  52. package/dist/hooks/use-local-storage.mjs.map +1 -1
  53. package/dist/hooks/use-merged-ref.d.mts +2 -3
  54. package/dist/hooks/use-merged-ref.mjs +13 -8
  55. package/dist/hooks/use-merged-ref.mjs.map +1 -1
  56. package/dist/hooks/use-multi-select.d.mts +1 -1
  57. package/dist/hooks/use-multi-select.mjs +10 -9
  58. package/dist/hooks/use-multi-select.mjs.map +1 -1
  59. package/dist/hooks/use-mutation-observer.d.mts +1 -2
  60. package/dist/hooks/use-mutation-observer.mjs +24 -10
  61. package/dist/hooks/use-mutation-observer.mjs.map +1 -1
  62. package/dist/hooks/use-previous.d.mts +1 -1
  63. package/dist/hooks/use-previous.mjs +1 -2
  64. package/dist/hooks/use-previous.mjs.map +1 -1
  65. package/dist/hooks/use-recursive-timeout.d.mts +1 -1
  66. package/dist/hooks/use-recursive-timeout.mjs +1 -2
  67. package/dist/hooks/use-recursive-timeout.mjs.map +1 -1
  68. package/dist/hooks/use-resize-observer.d.mts +1 -1
  69. package/dist/hooks/use-resize-observer.mjs +34 -11
  70. package/dist/hooks/use-resize-observer.mjs.map +1 -1
  71. package/dist/hooks/use-responsive-size.d.mts +1 -1
  72. package/dist/hooks/use-responsive-size.mjs +3 -4
  73. package/dist/hooks/use-responsive-size.mjs.map +1 -1
  74. package/dist/hooks/use-scroll-to-elements.d.mts +1 -2
  75. package/dist/hooks/use-scroll-to-elements.mjs +1 -2
  76. package/dist/hooks/use-scroll-to-elements.mjs.map +1 -1
  77. package/dist/hooks/use-throttled-callback.d.mts +2 -5
  78. package/dist/hooks/use-throttled-callback.mjs +6 -8
  79. package/dist/hooks/use-throttled-callback.mjs.map +1 -1
  80. package/dist/hooks/use-throttled-value.d.mts +9 -0
  81. package/dist/hooks/{use-throttle.mjs → use-throttled-value.mjs} +5 -6
  82. package/dist/hooks/use-throttled-value.mjs.map +1 -0
  83. package/dist/hooks/use-timeout.d.mts +1 -1
  84. package/dist/hooks/use-timeout.mjs +1 -2
  85. package/dist/hooks/use-timeout.mjs.map +1 -1
  86. package/dist/hooks/use-toggle.d.mts +1 -1
  87. package/dist/hooks/use-toggle.mjs +1 -2
  88. package/dist/hooks/use-toggle.mjs.map +1 -1
  89. package/dist/hooks/use-viewport.d.mts +1 -1
  90. package/dist/hooks/use-viewport.mjs +1 -2
  91. package/dist/hooks/use-viewport.mjs.map +1 -1
  92. package/dist/hooks/use-window-scroll.d.mts +1 -2
  93. package/dist/hooks/use-window-scroll.mjs +1 -2
  94. package/dist/hooks/use-window-scroll.mjs.map +1 -1
  95. package/dist/index.d.mts +31 -31
  96. package/dist/index.mjs +3 -4
  97. package/package.json +26 -26
  98. package/dist/hooks/use-debounce.d.mts +0 -14
  99. package/dist/hooks/use-debounce.mjs.map +0 -1
  100. package/dist/hooks/use-throttle.d.mts +0 -9
  101. package/dist/hooks/use-throttle.mjs.map +0 -1
@@ -1,17 +1,27 @@
1
1
  import { useEffect, useRef } from "react";
2
-
3
2
  //#region src/hooks/use-key-press.ts
4
3
  const isMac = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/.test(navigator.userAgent);
4
+ const KEY_ALIASES = {
5
+ space: " ",
6
+ spacebar: " ",
7
+ esc: "escape"
8
+ };
5
9
  const parseCombo = (combo) => {
6
- const parts = combo.split("+").map((part) => part.trim().toLowerCase());
7
- const key = parts[parts.length - 1] ?? "";
8
- const modifiers = parts.slice(0, -1);
10
+ const parts = combo.split("+");
11
+ const modifiers = parts.slice(0, -1).map((part) => part.trim().toLowerCase());
12
+ const rawKey = parts[parts.length - 1] ?? "";
13
+ const trimmedKey = rawKey.trim();
14
+ let key = (trimmedKey === "" && rawKey !== "" ? rawKey : trimmedKey).toLowerCase();
15
+ if (key === "" && parts.length > 1) key = "+";
16
+ key = KEY_ALIASES[key] ?? key;
9
17
  let ctrl = modifiers.includes("ctrl");
10
18
  let meta = modifiers.includes("meta") || modifiers.includes("cmd");
11
19
  const shift = modifiers.includes("shift");
12
20
  const alt = modifiers.includes("alt") || modifiers.includes("option");
13
- if (modifiers.includes("mod")) if (isMac()) meta = true;
14
- else ctrl = true;
21
+ if (modifiers.includes("mod")) {
22
+ if (isMac()) meta = true;
23
+ else ctrl = true;
24
+ }
15
25
  return {
16
26
  key,
17
27
  ctrl,
@@ -33,10 +43,12 @@ const useKeyPress = (combo, handler, options = {}) => {
33
43
  });
34
44
  const combos = Array.isArray(combo) ? combo : [combo];
35
45
  const parsedCombos = combos.map(parseCombo);
46
+ const comboKey = combos.join(",");
36
47
  useEffect(() => {
37
48
  if (!enabled) return;
38
- const resolvedTarget = resolveTarget(target);
39
- if (!resolvedTarget) return;
49
+ let cancelled = false;
50
+ let rafId;
51
+ let resolvedTarget = null;
40
52
  const onKeyDown = (event) => {
41
53
  const keyboardEvent = event;
42
54
  const eventTarget = keyboardEvent.target;
@@ -45,19 +57,31 @@ const useKeyPress = (combo, handler, options = {}) => {
45
57
  if (preventDefault) keyboardEvent.preventDefault();
46
58
  handlerRef.current(keyboardEvent);
47
59
  };
48
- resolvedTarget.addEventListener("keydown", onKeyDown);
60
+ const attach = () => {
61
+ if (cancelled) return;
62
+ if (target && "current" in target && !target.current) {
63
+ rafId = requestAnimationFrame(attach);
64
+ return;
65
+ }
66
+ resolvedTarget = resolveTarget(target);
67
+ if (!resolvedTarget) return;
68
+ resolvedTarget.addEventListener("keydown", onKeyDown);
69
+ };
70
+ attach();
49
71
  return () => {
50
- resolvedTarget.removeEventListener("keydown", onKeyDown);
72
+ cancelled = true;
73
+ if (rafId !== void 0) cancelAnimationFrame(rafId);
74
+ resolvedTarget?.removeEventListener("keydown", onKeyDown);
51
75
  };
52
76
  }, [
53
77
  enabled,
54
- combos.join(","),
78
+ comboKey,
55
79
  target,
56
80
  preventDefault,
57
81
  ignore
58
82
  ]);
59
83
  };
60
-
61
84
  //#endregion
62
85
  export { useKeyPress as default };
86
+
63
87
  //# sourceMappingURL=use-key-press.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-key-press.mjs","names":[],"sources":["../../src/hooks/use-key-press.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ntype KeyPressTarget =\n | RefObject<HTMLElement | null>\n | Window\n | Document\n | HTMLElement\n | null\n | undefined;\n\ninterface Options {\n target?: KeyPressTarget;\n enabled?: boolean;\n preventDefault?: boolean;\n // CSS selector — a keydown whose target is inside a matching element is\n // ignored (e.g. don't hijack Cmd+Z while the user is typing in a code\n // editor that has its own undo).\n ignore?: string;\n}\n\ninterface ParsedCombo {\n key: string;\n ctrl: boolean;\n meta: boolean;\n shift: boolean;\n alt: boolean;\n}\n\nconst isMac = () =>\n typeof navigator !== 'undefined' &&\n /Mac|iPhone|iPad|iPod/.test(navigator.userAgent);\n\n// `mod` normalizes to the platform's usual \"primary\" modifier (Cmd on\n// macOS, Ctrl elsewhere) so a single combo string covers both without the\n// caller branching on platform themselves.\nconst parseCombo = (combo: string): ParsedCombo => {\n const parts = combo.split('+').map(part => part.trim().toLowerCase());\n const key = parts[parts.length - 1] ?? '';\n const modifiers = parts.slice(0, -1);\n\n let ctrl = modifiers.includes('ctrl');\n let meta = modifiers.includes('meta') || modifiers.includes('cmd');\n const shift = modifiers.includes('shift');\n const alt = modifiers.includes('alt') || modifiers.includes('option');\n\n if (modifiers.includes('mod')) {\n if (isMac()) {\n meta = true;\n } else {\n ctrl = true;\n }\n }\n\n return { key, ctrl, meta, shift, alt };\n};\n\n// Modifiers not named in the combo are required to be *absent*, not just\n// ignored — otherwise 'mod+z' and 'mod+shift+z' registered as separate\n// bindings (the exact motivating case for this hook) would both fire on\n// the same Cmd+Shift+Z keypress instead of only the latter.\nconst matchesCombo = (event: KeyboardEvent, combo: ParsedCombo) =>\n event.key.toLowerCase() === combo.key &&\n event.ctrlKey === combo.ctrl &&\n event.metaKey === combo.meta &&\n event.shiftKey === combo.shift &&\n event.altKey === combo.alt;\n\nconst resolveTarget = (target: KeyPressTarget): EventTarget | null => {\n if (!target) {\n return typeof window === 'undefined' ? null : window;\n }\n return 'current' in target ? target.current : target;\n};\n\nconst useKeyPress = (\n combo: string | string[],\n handler: (event: KeyboardEvent) => void,\n options: Options = {},\n) => {\n const { target, enabled = true, preventDefault = false, ignore } = options;\n\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n });\n\n const combos = Array.isArray(combo) ? combo : [combo];\n const parsedCombos = combos.map(parseCombo);\n // `combos` is typically a fresh array literal at the call site — depend\n // on a stable string proxy instead so this doesn't tear down and\n // re-register the listener on every render. `target` itself (a\n // RefObject, or window/document/an element variable) is normally\n // already stable across renders, so it's used directly below.\n const comboKey = combos.join(',');\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n const resolvedTarget = resolveTarget(target);\n\n if (!resolvedTarget) {\n return;\n }\n\n const onKeyDown = (event: Event) => {\n const keyboardEvent = event as KeyboardEvent;\n const eventTarget = keyboardEvent.target as HTMLElement | null;\n\n if (ignore && eventTarget?.closest(ignore)) {\n return;\n }\n\n if (!parsedCombos.some(parsed => matchesCombo(keyboardEvent, parsed))) {\n return;\n }\n\n if (preventDefault) {\n keyboardEvent.preventDefault();\n }\n\n handlerRef.current(keyboardEvent);\n };\n\n resolvedTarget.addEventListener('keydown', onKeyDown);\n\n return () => {\n resolvedTarget.removeEventListener('keydown', onKeyDown);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, comboKey, target, preventDefault, ignore]);\n};\n\nexport default useKeyPress;\n"],"mappings":";;;AA4BA,MAAM,cACJ,OAAO,cAAc,eACrB,uBAAuB,KAAK,UAAU,UAAU;AAKlD,MAAM,cAAc,UAA+B;CACjD,MAAM,QAAQ,MAAM,MAAM,IAAI,CAAC,KAAI,SAAQ,KAAK,MAAM,CAAC,aAAa,CAAC;CACrE,MAAM,MAAM,MAAM,MAAM,SAAS,MAAM;CACvC,MAAM,YAAY,MAAM,MAAM,GAAG,GAAG;CAEpC,IAAI,OAAO,UAAU,SAAS,OAAO;CACrC,IAAI,OAAO,UAAU,SAAS,OAAO,IAAI,UAAU,SAAS,MAAM;CAClE,MAAM,QAAQ,UAAU,SAAS,QAAQ;CACzC,MAAM,MAAM,UAAU,SAAS,MAAM,IAAI,UAAU,SAAS,SAAS;AAErE,KAAI,UAAU,SAAS,MAAM,CAC3B,KAAI,OAAO,CACT,QAAO;KAEP,QAAO;AAIX,QAAO;EAAE;EAAK;EAAM;EAAM;EAAO;EAAK;;AAOxC,MAAM,gBAAgB,OAAsB,UAC1C,MAAM,IAAI,aAAa,KAAK,MAAM,OAClC,MAAM,YAAY,MAAM,QACxB,MAAM,YAAY,MAAM,QACxB,MAAM,aAAa,MAAM,SACzB,MAAM,WAAW,MAAM;AAEzB,MAAM,iBAAiB,WAA+C;AACpE,KAAI,CAAC,OACH,QAAO,OAAO,WAAW,cAAc,OAAO;AAEhD,QAAO,aAAa,SAAS,OAAO,UAAU;;AAGhD,MAAM,eACJ,OACA,SACA,UAAmB,EAAE,KAClB;CACH,MAAM,EAAE,QAAQ,UAAU,MAAM,iBAAiB,OAAO,WAAW;CAEnE,MAAM,aAAa,OAAO,QAAQ;AAElC,iBAAgB;AACd,aAAW,UAAU;GACrB;CAEF,MAAM,SAAS,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM;CACrD,MAAM,eAAe,OAAO,IAAI,WAAW;AAQ3C,iBAAgB;AACd,MAAI,CAAC,QACH;EAGF,MAAM,iBAAiB,cAAc,OAAO;AAE5C,MAAI,CAAC,eACH;EAGF,MAAM,aAAa,UAAiB;GAClC,MAAM,gBAAgB;GACtB,MAAM,cAAc,cAAc;AAElC,OAAI,UAAU,aAAa,QAAQ,OAAO,CACxC;AAGF,OAAI,CAAC,aAAa,MAAK,WAAU,aAAa,eAAe,OAAO,CAAC,CACnE;AAGF,OAAI,eACF,eAAc,gBAAgB;AAGhC,cAAW,QAAQ,cAAc;;AAGnC,iBAAe,iBAAiB,WAAW,UAAU;AAErD,eAAa;AACX,kBAAe,oBAAoB,WAAW,UAAU;;IAGzD;EAAC;EAtCa,OAAO,KAAK,IAAI;EAsCV;EAAQ;EAAgB;EAAO,CAAC"}
1
+ {"version":3,"file":"use-key-press.mjs","names":[],"sources":["../../src/hooks/use-key-press.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ntype KeyPressTarget =\n | RefObject<HTMLElement | null>\n | Window\n | Document\n | HTMLElement\n | null\n | undefined;\n\ninterface Options {\n target?: KeyPressTarget;\n enabled?: boolean;\n preventDefault?: boolean;\n // CSS selector — a keydown whose target is inside a matching element is\n // ignored (e.g. don't hijack Cmd+Z while the user is typing in a code\n // editor that has its own undo).\n ignore?: string;\n}\n\ninterface ParsedCombo {\n key: string;\n ctrl: boolean;\n meta: boolean;\n shift: boolean;\n alt: boolean;\n}\n\nconst isMac = () =>\n typeof navigator !== 'undefined' &&\n /Mac|iPhone|iPad|iPod/.test(navigator.userAgent);\n\n// Human-friendly spellings mapped to the actual `event.key` value, so\n// callers can write `'space'` instead of a literal ' ' (whose trimmed form\n// is empty and easy to lose) or `'esc'` for Escape.\nconst KEY_ALIASES: Record<string, string> = {\n space: ' ',\n spacebar: ' ',\n esc: 'escape',\n};\n\n// `mod` normalizes to the platform's usual \"primary\" modifier (Cmd on\n// macOS, Ctrl elsewhere) so a single combo string covers both without the\n// caller branching on platform themselves.\nconst parseCombo = (combo: string): ParsedCombo => {\n const parts = combo.split('+');\n const modifiers = parts.slice(0, -1).map(part => part.trim().toLowerCase());\n\n // The last segment is the key. Trim it like the modifiers so 'ctrl + z'\n // still resolves to 'z' — but if trimming would wipe a non-empty segment\n // entirely, the key *is* whitespace: the space key, whose event.key is ' '.\n const rawKey = parts[parts.length - 1] ?? '';\n const trimmedKey = rawKey.trim();\n let key = (\n trimmedKey === '' && rawKey !== '' ? rawKey : trimmedKey\n ).toLowerCase();\n\n // A bare '+' or a combo like 'ctrl++' splits to an empty final segment —\n // the '+' separator itself is the intended key.\n if (key === '' && parts.length > 1) {\n key = '+';\n }\n\n key = KEY_ALIASES[key] ?? key;\n\n let ctrl = modifiers.includes('ctrl');\n let meta = modifiers.includes('meta') || modifiers.includes('cmd');\n const shift = modifiers.includes('shift');\n const alt = modifiers.includes('alt') || modifiers.includes('option');\n\n if (modifiers.includes('mod')) {\n if (isMac()) {\n meta = true;\n } else {\n ctrl = true;\n }\n }\n\n return { key, ctrl, meta, shift, alt };\n};\n\n// Modifiers not named in the combo are required to be *absent*, not just\n// ignored — otherwise 'mod+z' and 'mod+shift+z' registered as separate\n// bindings (the exact motivating case for this hook) would both fire on\n// the same Cmd+Shift+Z keypress instead of only the latter.\nconst matchesCombo = (event: KeyboardEvent, combo: ParsedCombo) =>\n event.key.toLowerCase() === combo.key &&\n event.ctrlKey === combo.ctrl &&\n event.metaKey === combo.meta &&\n event.shiftKey === combo.shift &&\n event.altKey === combo.alt;\n\nconst resolveTarget = (target: KeyPressTarget): EventTarget | null => {\n if (!target) {\n return typeof window === 'undefined' ? null : window;\n }\n return 'current' in target ? target.current : target;\n};\n\nconst useKeyPress = (\n combo: string | string[],\n handler: (event: KeyboardEvent) => void,\n options: Options = {},\n) => {\n const { target, enabled = true, preventDefault = false, ignore } = options;\n\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n });\n\n const combos = Array.isArray(combo) ? combo : [combo];\n const parsedCombos = combos.map(parseCombo);\n // `combos` is typically a fresh array literal at the call site — depend\n // on a stable string proxy instead so this doesn't tear down and\n // re-register the listener on every render. `target` itself (a\n // RefObject, or window/document/an element variable) is normally\n // already stable across renders, so it's used directly below.\n const comboKey = combos.join(',');\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n let cancelled = false;\n let rafId: number | undefined;\n let resolvedTarget: EventTarget | null = null;\n\n const onKeyDown = (event: Event) => {\n const keyboardEvent = event as KeyboardEvent;\n const eventTarget = keyboardEvent.target as HTMLElement | null;\n\n if (ignore && eventTarget?.closest(ignore)) {\n return;\n }\n\n if (!parsedCombos.some(parsed => matchesCombo(keyboardEvent, parsed))) {\n return;\n }\n\n if (preventDefault) {\n keyboardEvent.preventDefault();\n }\n\n handlerRef.current(keyboardEvent);\n };\n\n const attach = () => {\n if (cancelled) {\n return;\n }\n\n // A RefObject target can still be null right after mount (conditional\n // render, portal, lazy mount) — retry every frame until it's\n // populated instead of giving up on the first effect run. The default\n // window and raw element/document targets have nothing to wait for.\n if (target && 'current' in target && !target.current) {\n rafId = requestAnimationFrame(attach);\n return;\n }\n\n resolvedTarget = resolveTarget(target);\n\n if (!resolvedTarget) {\n return;\n }\n\n resolvedTarget.addEventListener('keydown', onKeyDown);\n };\n\n attach();\n\n return () => {\n cancelled = true;\n if (rafId !== undefined) {\n cancelAnimationFrame(rafId);\n }\n resolvedTarget?.removeEventListener('keydown', onKeyDown);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, comboKey, target, preventDefault, ignore]);\n};\n\nexport default useKeyPress;\n"],"mappings":";;AA4BA,MAAM,cACJ,OAAO,cAAc,eACrB,uBAAuB,KAAK,UAAU,SAAS;AAKjD,MAAM,cAAsC;CAC1C,OAAO;CACP,UAAU;CACV,KAAK;AACP;AAKA,MAAM,cAAc,UAA+B;CACjD,MAAM,QAAQ,MAAM,MAAM,GAAG;CAC7B,MAAM,YAAY,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,KAAI,SAAQ,KAAK,KAAK,CAAC,CAAC,YAAY,CAAC;CAK1E,MAAM,SAAS,MAAM,MAAM,SAAS,MAAM;CAC1C,MAAM,aAAa,OAAO,KAAK;CAC/B,IAAI,OACF,eAAe,MAAM,WAAW,KAAK,SAAS,WAAA,CAC9C,YAAY;CAId,IAAI,QAAQ,MAAM,MAAM,SAAS,GAC/B,MAAM;CAGR,MAAM,YAAY,QAAQ;CAE1B,IAAI,OAAO,UAAU,SAAS,MAAM;CACpC,IAAI,OAAO,UAAU,SAAS,MAAM,KAAK,UAAU,SAAS,KAAK;CACjE,MAAM,QAAQ,UAAU,SAAS,OAAO;CACxC,MAAM,MAAM,UAAU,SAAS,KAAK,KAAK,UAAU,SAAS,QAAQ;CAEpE,IAAI,UAAU,SAAS,KAAK,GAAG;EAC7B,IAAI,MAAM,GACR,OAAO;OAEP,OAAO;CAEX;CAEA,OAAO;EAAE;EAAK;EAAM;EAAM;EAAO;CAAI;AACvC;AAMA,MAAM,gBAAgB,OAAsB,UAC1C,MAAM,IAAI,YAAY,MAAM,MAAM,OAClC,MAAM,YAAY,MAAM,QACxB,MAAM,YAAY,MAAM,QACxB,MAAM,aAAa,MAAM,SACzB,MAAM,WAAW,MAAM;AAEzB,MAAM,iBAAiB,WAA+C;CACpE,IAAI,CAAC,QACH,OAAO,OAAO,WAAW,cAAc,OAAO;CAEhD,OAAO,aAAa,SAAS,OAAO,UAAU;AAChD;AAEA,MAAM,eACJ,OACA,SACA,UAAmB,CAAC,MACjB;CACH,MAAM,EAAE,QAAQ,UAAU,MAAM,iBAAiB,OAAO,WAAW;CAEnE,MAAM,aAAa,OAAO,OAAO;CAEjC,gBAAgB;EACd,WAAW,UAAU;CACvB,CAAC;CAED,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;CACpD,MAAM,eAAe,OAAO,IAAI,UAAU;CAM1C,MAAM,WAAW,OAAO,KAAK,GAAG;CAEhC,gBAAgB;EACd,IAAI,CAAC,SACH;EAGF,IAAI,YAAY;EAChB,IAAI;EACJ,IAAI,iBAAqC;EAEzC,MAAM,aAAa,UAAiB;GAClC,MAAM,gBAAgB;GACtB,MAAM,cAAc,cAAc;GAElC,IAAI,UAAU,aAAa,QAAQ,MAAM,GACvC;GAGF,IAAI,CAAC,aAAa,MAAK,WAAU,aAAa,eAAe,MAAM,CAAC,GAClE;GAGF,IAAI,gBACF,cAAc,eAAe;GAG/B,WAAW,QAAQ,aAAa;EAClC;EAEA,MAAM,eAAe;GACnB,IAAI,WACF;GAOF,IAAI,UAAU,aAAa,UAAU,CAAC,OAAO,SAAS;IACpD,QAAQ,sBAAsB,MAAM;IACpC;GACF;GAEA,iBAAiB,cAAc,MAAM;GAErC,IAAI,CAAC,gBACH;GAGF,eAAe,iBAAiB,WAAW,SAAS;EACtD;EAEA,OAAO;EAEP,aAAa;GACX,YAAY;GACZ,IAAI,UAAU,KAAA,GACZ,qBAAqB,KAAK;GAE5B,gBAAgB,oBAAoB,WAAW,SAAS;EAC1D;CAEF,GAAG;EAAC;EAAS;EAAU;EAAQ;EAAgB;CAAM,CAAC;AACxD"}
@@ -1,5 +1,5 @@
1
1
  //#region src/hooks/use-local-storage.d.ts
2
2
  declare const useLocalStorage: <T>(key: string, initialValue: T) => readonly [T, (value: T | ((val: T) => T)) => void];
3
3
  //#endregion
4
- export { useLocalStorage };
4
+ export { useLocalStorage as default };
5
5
  //# sourceMappingURL=use-local-storage.d.mts.map
@@ -1,5 +1,4 @@
1
1
  import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
2
-
3
2
  //#region src/hooks/use-local-storage.ts
4
3
  const cache = /* @__PURE__ */ new Map();
5
4
  const listeners = /* @__PURE__ */ new Map();
@@ -63,7 +62,10 @@ const useLocalStorage = (key, initialValue) => {
63
62
  console.error(`Error seeding localStorage key "${key}":`, e);
64
63
  }
65
64
  }, [key]);
66
- return [useSyncExternalStore(useCallback((callback) => subscribe(key, callback), [key]), useCallback(() => getSnapshot(key, initialValueRef.current), [key]), useCallback(() => initialValueRef.current, [])), useCallback((value) => {
65
+ const subscribeForKey = useCallback((callback) => subscribe(key, callback), [key]);
66
+ const getSnapshotForKey = useCallback(() => getSnapshot(key, initialValueRef.current), [key]);
67
+ const getServerSnapshot = useCallback(() => initialValueRef.current, []);
68
+ return [useSyncExternalStore(subscribeForKey, getSnapshotForKey, getServerSnapshot), useCallback((value) => {
67
69
  try {
68
70
  const prev = getSnapshot(key, initialValueRef.current);
69
71
  const valueToStore = value instanceof Function ? value(prev) : value;
@@ -79,7 +81,7 @@ const useLocalStorage = (key, initialValue) => {
79
81
  }
80
82
  }, [key])];
81
83
  };
82
-
83
84
  //#endregion
84
85
  export { useLocalStorage as default };
86
+
85
87
  //# sourceMappingURL=use-local-storage.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-local-storage.mjs","names":[],"sources":["../../src/hooks/use-local-storage.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react';\n\n// Module-level so every `useLocalStorage(key)` instance for the same key —\n// even across unrelated components in the same tab — shares one cache and\n// subscriber list. That's what makes same-tab sync possible: the native\n// `storage` event only fires in *other* tabs, so same-tab updates have to\n// be broadcast by us via `notify()`.\ninterface CacheEntry<T> {\n raw: string | null;\n value: T;\n}\n\nconst cache = new Map<string, CacheEntry<unknown>>();\nconst listeners = new Map<string, Set<() => void>>();\n\nfunction getListeners(key: string) {\n let set = listeners.get(key);\n if (!set) {\n set = new Set();\n listeners.set(key, set);\n }\n return set;\n}\n\nfunction notify(key: string) {\n getListeners(key).forEach(callback => callback());\n}\n\n// Returns the same cached value reference as long as the raw localStorage\n// string hasn't changed, so `useSyncExternalStore` doesn't see a \"new\"\n// snapshot (and re-render) on every call — only when the value actually\n// changed.\nfunction getSnapshot<T>(key: string, initialValue: T): T {\n const raw = typeof window === 'undefined' ? null : localStorage.getItem(key);\n const cached = cache.get(key) as CacheEntry<T> | undefined;\n\n if (cached && cached.raw === raw) {\n return cached.value;\n }\n\n let value: T;\n try {\n value = raw === null ? initialValue : (JSON.parse(raw) as T);\n } catch {\n value = initialValue;\n }\n\n cache.set(key, { raw, value });\n return value;\n}\n\nfunction subscribe(key: string, callback: () => void) {\n const set = getListeners(key);\n set.add(callback);\n\n const onStorage = (event: StorageEvent) => {\n if (event.key === key) {\n callback();\n }\n };\n\n if (typeof window !== 'undefined') {\n window.addEventListener('storage', onStorage);\n }\n\n return () => {\n set.delete(callback);\n if (typeof window !== 'undefined') {\n window.removeEventListener('storage', onStorage);\n }\n };\n}\n\nconst useLocalStorage = <T>(key: string, initialValue: T) => {\n const initialValueRef = useRef(initialValue);\n\n useEffect(() => {\n initialValueRef.current = initialValue;\n });\n\n // Seed the key with the initial value if it isn't set yet — done in an\n // effect (not inside the state updater below) so it's a one-shot side\n // effect rather than something React could invoke twice under\n // StrictMode/concurrent rendering.\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n try {\n if (localStorage.getItem(key) === null) {\n const raw = JSON.stringify(initialValueRef.current);\n localStorage.setItem(key, raw);\n cache.set(key, { raw, value: initialValueRef.current });\n notify(key);\n }\n } catch (e) {\n console.error(`Error seeding localStorage key \"${key}\":`, e);\n }\n }, [key]);\n\n const subscribeForKey = useCallback(\n (callback: () => void) => subscribe(key, callback),\n [key],\n );\n\n const getSnapshotForKey = useCallback(\n () => getSnapshot(key, initialValueRef.current),\n [key],\n );\n\n const getServerSnapshot = useCallback(() => initialValueRef.current, []);\n\n const storedValue = useSyncExternalStore(\n subscribeForKey,\n getSnapshotForKey,\n getServerSnapshot,\n );\n\n const setValue = useCallback(\n (value: T | ((val: T) => T)) => {\n try {\n const prev = getSnapshot(key, initialValueRef.current);\n const valueToStore = value instanceof Function ? value(prev) : value;\n const raw = JSON.stringify(valueToStore);\n\n localStorage.setItem(key, raw);\n cache.set(key, { raw, value: valueToStore });\n notify(key);\n } catch (e) {\n console.error(`Error setting localStorage key \"${key}\":`, e);\n }\n },\n [key],\n );\n\n return [storedValue, setValue] as const;\n};\n\nexport default useLocalStorage;\n"],"mappings":";;;AAYA,MAAM,wBAAQ,IAAI,KAAkC;AACpD,MAAM,4BAAY,IAAI,KAA8B;AAEpD,SAAS,aAAa,KAAa;CACjC,IAAI,MAAM,UAAU,IAAI,IAAI;AAC5B,KAAI,CAAC,KAAK;AACR,wBAAM,IAAI,KAAK;AACf,YAAU,IAAI,KAAK,IAAI;;AAEzB,QAAO;;AAGT,SAAS,OAAO,KAAa;AAC3B,cAAa,IAAI,CAAC,SAAQ,aAAY,UAAU,CAAC;;AAOnD,SAAS,YAAe,KAAa,cAAoB;CACvD,MAAM,MAAM,OAAO,WAAW,cAAc,OAAO,aAAa,QAAQ,IAAI;CAC5E,MAAM,SAAS,MAAM,IAAI,IAAI;AAE7B,KAAI,UAAU,OAAO,QAAQ,IAC3B,QAAO,OAAO;CAGhB,IAAI;AACJ,KAAI;AACF,UAAQ,QAAQ,OAAO,eAAgB,KAAK,MAAM,IAAI;SAChD;AACN,UAAQ;;AAGV,OAAM,IAAI,KAAK;EAAE;EAAK;EAAO,CAAC;AAC9B,QAAO;;AAGT,SAAS,UAAU,KAAa,UAAsB;CACpD,MAAM,MAAM,aAAa,IAAI;AAC7B,KAAI,IAAI,SAAS;CAEjB,MAAM,aAAa,UAAwB;AACzC,MAAI,MAAM,QAAQ,IAChB,WAAU;;AAId,KAAI,OAAO,WAAW,YACpB,QAAO,iBAAiB,WAAW,UAAU;AAG/C,cAAa;AACX,MAAI,OAAO,SAAS;AACpB,MAAI,OAAO,WAAW,YACpB,QAAO,oBAAoB,WAAW,UAAU;;;AAKtD,MAAM,mBAAsB,KAAa,iBAAoB;CAC3D,MAAM,kBAAkB,OAAO,aAAa;AAE5C,iBAAgB;AACd,kBAAgB,UAAU;GAC1B;AAMF,iBAAgB;AACd,MAAI,OAAO,WAAW,YACpB;AAGF,MAAI;AACF,OAAI,aAAa,QAAQ,IAAI,KAAK,MAAM;IACtC,MAAM,MAAM,KAAK,UAAU,gBAAgB,QAAQ;AACnD,iBAAa,QAAQ,KAAK,IAAI;AAC9B,UAAM,IAAI,KAAK;KAAE;KAAK,OAAO,gBAAgB;KAAS,CAAC;AACvD,WAAO,IAAI;;WAEN,GAAG;AACV,WAAQ,MAAM,mCAAmC,IAAI,KAAK,EAAE;;IAE7D,CAAC,IAAI,CAAC;AAqCT,QAAO,CAvBa,qBAZI,aACrB,aAAyB,UAAU,KAAK,SAAS,EAClD,CAAC,IAAI,CACN,EAEyB,kBAClB,YAAY,KAAK,gBAAgB,QAAQ,EAC/C,CAAC,IAAI,CACN,EAEyB,kBAAkB,gBAAgB,SAAS,EAAE,CAAC,CAMvE,EAEgB,aACd,UAA+B;AAC9B,MAAI;GACF,MAAM,OAAO,YAAY,KAAK,gBAAgB,QAAQ;GACtD,MAAM,eAAe,iBAAiB,WAAW,MAAM,KAAK,GAAG;GAC/D,MAAM,MAAM,KAAK,UAAU,aAAa;AAExC,gBAAa,QAAQ,KAAK,IAAI;AAC9B,SAAM,IAAI,KAAK;IAAE;IAAK,OAAO;IAAc,CAAC;AAC5C,UAAO,IAAI;WACJ,GAAG;AACV,WAAQ,MAAM,mCAAmC,IAAI,KAAK,EAAE;;IAGhE,CAAC,IAAI,CACN,CAE6B"}
1
+ {"version":3,"file":"use-local-storage.mjs","names":[],"sources":["../../src/hooks/use-local-storage.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react';\n\n// Module-level so every `useLocalStorage(key)` instance for the same key —\n// even across unrelated components in the same tab — shares one cache and\n// subscriber list. That's what makes same-tab sync possible: the native\n// `storage` event only fires in *other* tabs, so same-tab updates have to\n// be broadcast by us via `notify()`.\ninterface CacheEntry<T> {\n raw: string | null;\n value: T;\n}\n\nconst cache = new Map<string, CacheEntry<unknown>>();\nconst listeners = new Map<string, Set<() => void>>();\n\nfunction getListeners(key: string) {\n let set = listeners.get(key);\n if (!set) {\n set = new Set();\n listeners.set(key, set);\n }\n return set;\n}\n\nfunction notify(key: string) {\n getListeners(key).forEach(callback => callback());\n}\n\n// Returns the same cached value reference as long as the raw localStorage\n// string hasn't changed, so `useSyncExternalStore` doesn't see a \"new\"\n// snapshot (and re-render) on every call — only when the value actually\n// changed.\nfunction getSnapshot<T>(key: string, initialValue: T): T {\n const raw = typeof window === 'undefined' ? null : localStorage.getItem(key);\n const cached = cache.get(key) as CacheEntry<T> | undefined;\n\n if (cached && cached.raw === raw) {\n return cached.value;\n }\n\n let value: T;\n try {\n value = raw === null ? initialValue : (JSON.parse(raw) as T);\n } catch {\n value = initialValue;\n }\n\n cache.set(key, { raw, value });\n return value;\n}\n\nfunction subscribe(key: string, callback: () => void) {\n const set = getListeners(key);\n set.add(callback);\n\n const onStorage = (event: StorageEvent) => {\n if (event.key === key) {\n callback();\n }\n };\n\n if (typeof window !== 'undefined') {\n window.addEventListener('storage', onStorage);\n }\n\n return () => {\n set.delete(callback);\n if (typeof window !== 'undefined') {\n window.removeEventListener('storage', onStorage);\n }\n };\n}\n\nconst useLocalStorage = <T>(key: string, initialValue: T) => {\n const initialValueRef = useRef(initialValue);\n\n useEffect(() => {\n initialValueRef.current = initialValue;\n });\n\n // Seed the key with the initial value if it isn't set yet — done in an\n // effect (not inside the state updater below) so it's a one-shot side\n // effect rather than something React could invoke twice under\n // StrictMode/concurrent rendering.\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n try {\n if (localStorage.getItem(key) === null) {\n const raw = JSON.stringify(initialValueRef.current);\n localStorage.setItem(key, raw);\n cache.set(key, { raw, value: initialValueRef.current });\n notify(key);\n }\n } catch (e) {\n console.error(`Error seeding localStorage key \"${key}\":`, e);\n }\n }, [key]);\n\n const subscribeForKey = useCallback(\n (callback: () => void) => subscribe(key, callback),\n [key],\n );\n\n const getSnapshotForKey = useCallback(\n () => getSnapshot(key, initialValueRef.current),\n [key],\n );\n\n const getServerSnapshot = useCallback(() => initialValueRef.current, []);\n\n const storedValue = useSyncExternalStore(\n subscribeForKey,\n getSnapshotForKey,\n getServerSnapshot,\n );\n\n const setValue = useCallback(\n (value: T | ((val: T) => T)) => {\n try {\n const prev = getSnapshot(key, initialValueRef.current);\n const valueToStore = value instanceof Function ? value(prev) : value;\n const raw = JSON.stringify(valueToStore);\n\n localStorage.setItem(key, raw);\n cache.set(key, { raw, value: valueToStore });\n notify(key);\n } catch (e) {\n console.error(`Error setting localStorage key \"${key}\":`, e);\n }\n },\n [key],\n );\n\n return [storedValue, setValue] as const;\n};\n\nexport default useLocalStorage;\n"],"mappings":";;AAYA,MAAM,wBAAQ,IAAI,IAAiC;AACnD,MAAM,4BAAY,IAAI,IAA6B;AAEnD,SAAS,aAAa,KAAa;CACjC,IAAI,MAAM,UAAU,IAAI,GAAG;CAC3B,IAAI,CAAC,KAAK;EACR,sBAAM,IAAI,IAAI;EACd,UAAU,IAAI,KAAK,GAAG;CACxB;CACA,OAAO;AACT;AAEA,SAAS,OAAO,KAAa;CAC3B,aAAa,GAAG,CAAC,CAAC,SAAQ,aAAY,SAAS,CAAC;AAClD;AAMA,SAAS,YAAe,KAAa,cAAoB;CACvD,MAAM,MAAM,OAAO,WAAW,cAAc,OAAO,aAAa,QAAQ,GAAG;CAC3E,MAAM,SAAS,MAAM,IAAI,GAAG;CAE5B,IAAI,UAAU,OAAO,QAAQ,KAC3B,OAAO,OAAO;CAGhB,IAAI;CACJ,IAAI;EACF,QAAQ,QAAQ,OAAO,eAAgB,KAAK,MAAM,GAAG;CACvD,QAAQ;EACN,QAAQ;CACV;CAEA,MAAM,IAAI,KAAK;EAAE;EAAK;CAAM,CAAC;CAC7B,OAAO;AACT;AAEA,SAAS,UAAU,KAAa,UAAsB;CACpD,MAAM,MAAM,aAAa,GAAG;CAC5B,IAAI,IAAI,QAAQ;CAEhB,MAAM,aAAa,UAAwB;EACzC,IAAI,MAAM,QAAQ,KAChB,SAAS;CAEb;CAEA,IAAI,OAAO,WAAW,aACpB,OAAO,iBAAiB,WAAW,SAAS;CAG9C,aAAa;EACX,IAAI,OAAO,QAAQ;EACnB,IAAI,OAAO,WAAW,aACpB,OAAO,oBAAoB,WAAW,SAAS;CAEnD;AACF;AAEA,MAAM,mBAAsB,KAAa,iBAAoB;CAC3D,MAAM,kBAAkB,OAAO,YAAY;CAE3C,gBAAgB;EACd,gBAAgB,UAAU;CAC5B,CAAC;CAMD,gBAAgB;EACd,IAAI,OAAO,WAAW,aACpB;EAGF,IAAI;GACF,IAAI,aAAa,QAAQ,GAAG,MAAM,MAAM;IACtC,MAAM,MAAM,KAAK,UAAU,gBAAgB,OAAO;IAClD,aAAa,QAAQ,KAAK,GAAG;IAC7B,MAAM,IAAI,KAAK;KAAE;KAAK,OAAO,gBAAgB;IAAQ,CAAC;IACtD,OAAO,GAAG;GACZ;EACF,SAAS,GAAG;GACV,QAAQ,MAAM,mCAAmC,IAAI,KAAK,CAAC;EAC7D;CACF,GAAG,CAAC,GAAG,CAAC;CAER,MAAM,kBAAkB,aACrB,aAAyB,UAAU,KAAK,QAAQ,GACjD,CAAC,GAAG,CACN;CAEA,MAAM,oBAAoB,kBAClB,YAAY,KAAK,gBAAgB,OAAO,GAC9C,CAAC,GAAG,CACN;CAEA,MAAM,oBAAoB,kBAAkB,gBAAgB,SAAS,CAAC,CAAC;CAyBvE,OAAO,CAvBa,qBAClB,iBACA,mBACA,iBAoBgB,GAjBD,aACd,UAA+B;EAC9B,IAAI;GACF,MAAM,OAAO,YAAY,KAAK,gBAAgB,OAAO;GACrD,MAAM,eAAe,iBAAiB,WAAW,MAAM,IAAI,IAAI;GAC/D,MAAM,MAAM,KAAK,UAAU,YAAY;GAEvC,aAAa,QAAQ,KAAK,GAAG;GAC7B,MAAM,IAAI,KAAK;IAAE;IAAK,OAAO;GAAa,CAAC;GAC3C,OAAO,GAAG;EACZ,SAAS,GAAG;GACV,QAAQ,MAAM,mCAAmC,IAAI,KAAK,CAAC;EAC7D;CACF,GACA,CAAC,GAAG,CAGsB,CAAC;AAC/B"}
@@ -1,8 +1,7 @@
1
1
  import { RefObject } from "react";
2
-
3
2
  //#region src/hooks/use-merged-ref.d.ts
4
3
  type MergeableRef<T> = ((node: T | null) => void | (() => void)) | RefObject<T | null> | null | undefined;
5
- declare const useMergedRef: <T>(...refs: MergeableRef<T>[]) => (node: T | null) => (() => void) | undefined;
4
+ declare const useMergedRef: <T>(...refs: MergeableRef<T>[]) => (node: T | null) => () => void;
6
5
  //#endregion
7
- export { useMergedRef };
6
+ export { useMergedRef as default };
8
7
  //# sourceMappingURL=use-merged-ref.d.mts.map
@@ -1,25 +1,30 @@
1
1
  import { useCallback } from "react";
2
-
3
2
  //#region src/hooks/use-merged-ref.ts
4
3
  const useMergedRef = (...refs) => {
5
4
  return useCallback((node) => {
6
- const cleanups = [];
7
- refs.forEach((ref) => {
5
+ const cleanups = refs.map((ref) => {
8
6
  if (!ref) return;
9
7
  if (typeof ref === "function") {
10
8
  const cleanup = ref(node);
11
- if (typeof cleanup === "function") cleanups.push(cleanup);
12
- return;
9
+ return typeof cleanup === "function" ? cleanup : void 0;
13
10
  }
14
11
  ref.current = node;
15
12
  });
16
- if (cleanups.length === 0) return;
17
13
  return () => {
18
- cleanups.forEach((cleanup) => cleanup());
14
+ refs.forEach((ref, i) => {
15
+ if (!ref) return;
16
+ if (typeof ref === "function") {
17
+ const cleanup = cleanups[i];
18
+ if (cleanup) cleanup();
19
+ else ref(null);
20
+ return;
21
+ }
22
+ ref.current = null;
23
+ });
19
24
  };
20
25
  }, refs);
21
26
  };
22
-
23
27
  //#endregion
24
28
  export { useMergedRef as default };
29
+
25
30
  //# sourceMappingURL=use-merged-ref.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-merged-ref.mjs","names":[],"sources":["../../src/hooks/use-merged-ref.ts"],"sourcesContent":["import { type RefObject, useCallback } from 'react';\n\ntype MergeableRef<T> =\n | ((node: T | null) => void | (() => void))\n | RefObject<T | null>\n | null\n | undefined;\n\n// Merges any number of refs (forwarded function refs, RefObjects, or\n// either left null/undefined) into one callback ref that updates all of\n// them. Collects React 19's optional per-ref cleanup return values and\n// runs them together when the node detaches.\nconst useMergedRef = <T>(...refs: MergeableRef<T>[]) => {\n return useCallback((node: T | null) => {\n const cleanups: (() => void)[] = [];\n\n refs.forEach(ref => {\n if (!ref) {\n return;\n }\n\n if (typeof ref === 'function') {\n const cleanup = ref(node);\n\n if (typeof cleanup === 'function') {\n cleanups.push(cleanup);\n }\n return;\n }\n\n ref.current = node;\n });\n\n if (cleanups.length === 0) {\n return;\n }\n\n return () => {\n cleanups.forEach(cleanup => cleanup());\n };\n // The number of refs passed at a given call site is stable across\n // renders even though this array literal isn't — same pattern every\n // ref-merging hook of this shape relies on.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, refs);\n};\n\nexport default useMergedRef;\n"],"mappings":";;;AAYA,MAAM,gBAAmB,GAAG,SAA4B;AACtD,QAAO,aAAa,SAAmB;EACrC,MAAM,WAA2B,EAAE;AAEnC,OAAK,SAAQ,QAAO;AAClB,OAAI,CAAC,IACH;AAGF,OAAI,OAAO,QAAQ,YAAY;IAC7B,MAAM,UAAU,IAAI,KAAK;AAEzB,QAAI,OAAO,YAAY,WACrB,UAAS,KAAK,QAAQ;AAExB;;AAGF,OAAI,UAAU;IACd;AAEF,MAAI,SAAS,WAAW,EACtB;AAGF,eAAa;AACX,YAAS,SAAQ,YAAW,SAAS,CAAC;;IAMvC,KAAK"}
1
+ {"version":3,"file":"use-merged-ref.mjs","names":[],"sources":["../../src/hooks/use-merged-ref.ts"],"sourcesContent":["import { type RefObject, useCallback } from 'react';\n\ntype MergeableRef<T> =\n | ((node: T | null) => void | (() => void))\n | RefObject<T | null>\n | null\n | undefined;\n\n// Merges any number of refs (forwarded function refs, RefObjects, or\n// either left null/undefined) into one callback ref that updates all of\n// them. Collects React 19's optional per-ref cleanup return values and\n// runs them together when the node detaches.\nconst useMergedRef = <T>(...refs: MergeableRef<T>[]) => {\n return useCallback((node: T | null) => {\n // Per-ref cleanups, aligned to `refs` by index: a function ref that\n // returns its own cleanup keeps it here, everything else is undefined.\n const cleanups = refs.map(ref => {\n if (!ref) {\n return undefined;\n }\n\n if (typeof ref === 'function') {\n const cleanup = ref(node);\n return typeof cleanup === 'function' ? cleanup : undefined;\n }\n\n ref.current = node;\n return undefined;\n });\n\n // Always return a cleanup and detach *every* ref here. When any ref\n // returns a cleanup, React 19 runs this instead of re-invoking the\n // callback with null on detach — so if we only ran the collected\n // cleanups, the object refs and cleanup-less function refs merged\n // alongside would never be released and would pin a stale node.\n return () => {\n refs.forEach((ref, i) => {\n if (!ref) {\n return;\n }\n\n if (typeof ref === 'function') {\n const cleanup = cleanups[i];\n if (cleanup) {\n cleanup();\n } else {\n ref(null);\n }\n return;\n }\n\n ref.current = null;\n });\n };\n // The number of refs passed at a given call site is stable across\n // renders even though this array literal isn't — same pattern every\n // ref-merging hook of this shape relies on.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, refs);\n};\n\nexport default useMergedRef;\n"],"mappings":";;AAYA,MAAM,gBAAmB,GAAG,SAA4B;CACtD,OAAO,aAAa,SAAmB;EAGrC,MAAM,WAAW,KAAK,KAAI,QAAO;GAC/B,IAAI,CAAC,KACH;GAGF,IAAI,OAAO,QAAQ,YAAY;IAC7B,MAAM,UAAU,IAAI,IAAI;IACxB,OAAO,OAAO,YAAY,aAAa,UAAU,KAAA;GACnD;GAEA,IAAI,UAAU;EAEhB,CAAC;EAOD,aAAa;GACX,KAAK,SAAS,KAAK,MAAM;IACvB,IAAI,CAAC,KACH;IAGF,IAAI,OAAO,QAAQ,YAAY;KAC7B,MAAM,UAAU,SAAS;KACzB,IAAI,SACF,QAAQ;UAER,IAAI,IAAI;KAEV;IACF;IAEA,IAAI,UAAU;GAChB,CAAC;EACH;CAKF,GAAG,IAAI;AACT"}
@@ -7,5 +7,5 @@ declare const useMultiSelect: (count: number) => {
7
7
  readonly replace: (indices: Set<number>) => void;
8
8
  };
9
9
  //#endregion
10
- export { useMultiSelect };
10
+ export { useMultiSelect as default };
11
11
  //# sourceMappingURL=use-multi-select.d.mts.map
@@ -1,20 +1,21 @@
1
- import { useCallback, useEffect, useMemo, useState } from "react";
2
-
1
+ import { useCallback, useMemo, useState } from "react";
3
2
  //#region src/hooks/use-multi-select.ts
4
3
  const useMultiSelect = (count) => {
5
4
  const [rawSelected, setRawSelected] = useState(/* @__PURE__ */ new Set());
6
5
  const [anchor, setAnchor] = useState(null);
7
- const selected = useMemo(() => {
8
- const next = new Set([...rawSelected].filter((index) => index < count));
9
- return next.size === rawSelected.size ? rawSelected : next;
10
- }, [rawSelected, count]);
11
- useEffect(() => {
6
+ const [prevCount, setPrevCount] = useState(count);
7
+ if (count !== prevCount) {
8
+ setPrevCount(count);
12
9
  setRawSelected((prev) => {
13
10
  const next = new Set([...prev].filter((index) => index < count));
14
11
  return next.size === prev.size ? prev : next;
15
12
  });
16
13
  setAnchor((prev) => prev !== null && prev >= count ? null : prev);
17
- }, [count]);
14
+ }
15
+ const selected = useMemo(() => {
16
+ const next = new Set([...rawSelected].filter((index) => index < count));
17
+ return next.size === rawSelected.size ? rawSelected : next;
18
+ }, [rawSelected, count]);
18
19
  const toggle = useCallback((index, shiftKey = false) => {
19
20
  if (index < 0 || index >= count) return;
20
21
  setRawSelected((prev) => {
@@ -46,7 +47,7 @@ const useMultiSelect = (count) => {
46
47
  replace
47
48
  };
48
49
  };
49
-
50
50
  //#endregion
51
51
  export { useMultiSelect as default };
52
+
52
53
  //# sourceMappingURL=use-multi-select.mjs.map
@@ -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,23 +10,38 @@ 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
- const node = resolveTarget(target);
16
- if (!enabled || !node || typeof MutationObserver === "undefined") return;
17
- const observer = new MutationObserver((mutations, obs) => {
18
- callbackRef.current(mutations, obs);
19
- });
20
- observer.observe(node, mutationOptions);
15
+ if (!enabled || typeof MutationObserver === "undefined") return;
16
+ let cancelled = false;
17
+ let rafId;
18
+ let observer;
19
+ const attach = () => {
20
+ if (cancelled) return;
21
+ if (target && "current" in target && !target.current) {
22
+ rafId = requestAnimationFrame(attach);
23
+ return;
24
+ }
25
+ const node = resolveTarget(target);
26
+ if (!node) return;
27
+ observer = new MutationObserver((mutations, obs) => {
28
+ callbackRef.current(mutations, obs);
29
+ });
30
+ observer.observe(node, mutationOptions);
31
+ };
32
+ attach();
21
33
  return () => {
22
- observer.disconnect();
34
+ cancelled = true;
35
+ if (rafId !== void 0) cancelAnimationFrame(rafId);
36
+ observer?.disconnect();
23
37
  };
24
38
  }, [
25
39
  target,
26
40
  enabled,
27
- JSON.stringify(mutationOptions)
41
+ optionsKey
28
42
  ]);
29
43
  };
30
-
31
44
  //#endregion
32
45
  export { useMutationObserver as default };
46
+
33
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 const node = resolveTarget(target);\n\n if (!enabled || !node || typeof MutationObserver === 'undefined') {\n return;\n }\n\n const observer = new MutationObserver((mutations, obs) => {\n callbackRef.current(mutations, obs);\n });\n\n observer.observe(node, mutationOptions);\n\n return () => {\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;EACd,MAAM,OAAO,cAAc,OAAO;AAElC,MAAI,CAAC,WAAW,CAAC,QAAQ,OAAO,qBAAqB,YACnD;EAGF,MAAM,WAAW,IAAI,kBAAkB,WAAW,QAAQ;AACxD,eAAY,QAAQ,WAAW,IAAI;IACnC;AAEF,WAAS,QAAQ,MAAM,gBAAgB;AAEvC,eAAa;AACX,YAAS,YAAY;;IAGtB;EAAC;EAAQ;EAnBO,KAAK,UAAU,gBAAgB;EAmBlB,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,29 +1,52 @@
1
- import { useCallback, useRef, useState } from "react";
2
-
1
+ import { useCallback, useEffect, useRef, useState } from "react";
3
2
  //#region src/hooks/use-resize-observer.ts
4
3
  const useResizeObserver = (options) => {
4
+ const box = options?.box ?? "content-box";
5
5
  const [size, setSize] = useState(null);
6
6
  const observerRef = useRef(null);
7
- return [useCallback((node) => {
7
+ const nodeRef = useRef(null);
8
+ const boxRef = useRef(box);
9
+ useEffect(() => {
10
+ boxRef.current = box;
11
+ });
12
+ const connect = useCallback(() => {
8
13
  if (observerRef.current) {
9
14
  observerRef.current.disconnect();
10
15
  observerRef.current = null;
11
16
  }
17
+ const node = nodeRef.current;
12
18
  if (!node || typeof ResizeObserver === "undefined") return;
13
- const box = options?.box ?? "content-box";
19
+ const currentBox = boxRef.current;
14
20
  const observer = new ResizeObserver(([entry]) => {
15
21
  if (!entry) return;
16
- const [{ inlineSize, blockSize }] = box === "border-box" ? entry.borderBoxSize : entry.contentBoxSize;
17
- setSize({
18
- width: inlineSize,
19
- height: blockSize
22
+ const measurement = (currentBox === "border-box" ? entry.borderBoxSize : entry.contentBoxSize)?.[0];
23
+ if (measurement) setSize({
24
+ width: measurement.inlineSize,
25
+ height: measurement.blockSize
26
+ });
27
+ else setSize({
28
+ width: entry.contentRect.width,
29
+ height: entry.contentRect.height
20
30
  });
21
31
  });
22
- observer.observe(node, { box });
32
+ observer.observe(node, { box: currentBox });
23
33
  observerRef.current = observer;
24
- }, []), size];
34
+ }, []);
35
+ const ref = useCallback((node) => {
36
+ nodeRef.current = node;
37
+ connect();
38
+ }, []);
39
+ useEffect(() => {
40
+ connect();
41
+ }, [box]);
42
+ useEffect(() => {
43
+ return () => {
44
+ observerRef.current?.disconnect();
45
+ };
46
+ }, []);
47
+ return [ref, size];
25
48
  };
26
-
27
49
  //#endregion
28
50
  export { useResizeObserver as default };
51
+
29
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, 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 [size, setSize] = useState<Size | null>(null);\n const observerRef = useRef<ResizeObserver | null>(null);\n\n // Same convention as useIntersectionObserver: `options` is typically a\n // fresh object literal at the call site, so it isn't a dependency here\n // — that would tear down and recreate the observer on every render. The\n // ref callback only re-runs when the observed node itself changes,\n // capturing whatever `options` was current at that time.\n const ref = useCallback((node: T | null) => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n\n if (!node || typeof ResizeObserver === 'undefined') {\n return;\n }\n\n const box = options?.box ?? 'content-box';\n\n const observer = new ResizeObserver(([entry]) => {\n if (!entry) {\n return;\n }\n\n const [{ inlineSize, blockSize }] =\n box === 'border-box' ? entry.borderBoxSize : entry.contentBoxSize;\n\n setSize({ width: inlineSize, height: blockSize });\n });\n\n observer.observe(node, { box });\n observerRef.current = observer;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [ref, size];\n};\n\nexport default useResizeObserver;\n"],"mappings":";;;AAeA,MAAM,qBACJ,YAC4C;CAC5C,MAAM,CAAC,MAAM,WAAW,SAAsB,KAAK;CACnD,MAAM,cAAc,OAA8B,KAAK;AAmCvD,QAAO,CA5BK,aAAa,SAAmB;AAC1C,MAAI,YAAY,SAAS;AACvB,eAAY,QAAQ,YAAY;AAChC,eAAY,UAAU;;AAGxB,MAAI,CAAC,QAAQ,OAAO,mBAAmB,YACrC;EAGF,MAAM,MAAM,SAAS,OAAO;EAE5B,MAAM,WAAW,IAAI,gBAAgB,CAAC,WAAW;AAC/C,OAAI,CAAC,MACH;GAGF,MAAM,CAAC,EAAE,YAAY,eACnB,QAAQ,eAAe,MAAM,gBAAgB,MAAM;AAErD,WAAQ;IAAE,OAAO;IAAY,QAAQ;IAAW,CAAC;IACjD;AAEF,WAAS,QAAQ,MAAM,EAAE,KAAK,CAAC;AAC/B,cAAY,UAAU;IAErB,EAAE,CAAC,EAEO,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