@craftzbay/ui 0.10.0 → 0.11.0

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 (187) hide show
  1. package/README.md +14 -8
  2. package/dist-lib/__tests__/helpers/color.d.ts +9 -0
  3. package/dist-lib/__tests__/helpers/theme.d.ts +18 -0
  4. package/dist-lib/components/ui/Accordion.js +18 -7
  5. package/dist-lib/components/ui/Accordion.js.map +1 -1
  6. package/dist-lib/components/ui/Alert.d.ts +16 -0
  7. package/dist-lib/components/ui/Alert.js +47 -34
  8. package/dist-lib/components/ui/Alert.js.map +1 -1
  9. package/dist-lib/components/ui/Avatar.d.ts +1 -1
  10. package/dist-lib/components/ui/Avatar.js +45 -47
  11. package/dist-lib/components/ui/Avatar.js.map +1 -1
  12. package/dist-lib/components/ui/Badge.d.ts +3 -1
  13. package/dist-lib/components/ui/Badge.js +24 -15
  14. package/dist-lib/components/ui/Badge.js.map +1 -1
  15. package/dist-lib/components/ui/Breadcrumbs.d.ts +2 -0
  16. package/dist-lib/components/ui/Breadcrumbs.js +46 -28
  17. package/dist-lib/components/ui/Breadcrumbs.js.map +1 -1
  18. package/dist-lib/components/ui/Button.d.ts +5 -3
  19. package/dist-lib/components/ui/Button.js +59 -45
  20. package/dist-lib/components/ui/Button.js.map +1 -1
  21. package/dist-lib/components/ui/Calendar.d.ts +1 -1
  22. package/dist-lib/components/ui/Calendar.js +87 -59
  23. package/dist-lib/components/ui/Calendar.js.map +1 -1
  24. package/dist-lib/components/ui/Card.d.ts +2 -0
  25. package/dist-lib/components/ui/Card.js +48 -67
  26. package/dist-lib/components/ui/Card.js.map +1 -1
  27. package/dist-lib/components/ui/Carousel.d.ts +6 -6
  28. package/dist-lib/components/ui/Carousel.js +133 -102
  29. package/dist-lib/components/ui/Carousel.js.map +1 -1
  30. package/dist-lib/components/ui/Chart.d.ts +49 -10
  31. package/dist-lib/components/ui/Chart.js +415 -181
  32. package/dist-lib/components/ui/Chart.js.map +1 -1
  33. package/dist-lib/components/ui/Checkbox.js +42 -28
  34. package/dist-lib/components/ui/Checkbox.js.map +1 -1
  35. package/dist-lib/components/ui/Combobox.d.ts +2 -0
  36. package/dist-lib/components/ui/Combobox.js +142 -111
  37. package/dist-lib/components/ui/Combobox.js.map +1 -1
  38. package/dist-lib/components/ui/CommandPalette.d.ts +6 -3
  39. package/dist-lib/components/ui/CommandPalette.js +92 -87
  40. package/dist-lib/components/ui/CommandPalette.js.map +1 -1
  41. package/dist-lib/components/ui/ContextMenu.d.ts +7 -2
  42. package/dist-lib/components/ui/ContextMenu.js +77 -62
  43. package/dist-lib/components/ui/ContextMenu.js.map +1 -1
  44. package/dist-lib/components/ui/DataGrid.d.ts +17 -4
  45. package/dist-lib/components/ui/DataGrid.js +65 -50
  46. package/dist-lib/components/ui/DataGrid.js.map +1 -1
  47. package/dist-lib/components/ui/DatePicker.js +118 -99
  48. package/dist-lib/components/ui/DatePicker.js.map +1 -1
  49. package/dist-lib/components/ui/DesignSystemProvider.d.ts +94 -40
  50. package/dist-lib/components/ui/DesignSystemProvider.js +111 -55
  51. package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -1
  52. package/dist-lib/components/ui/Dialog.d.ts +18 -3
  53. package/dist-lib/components/ui/Dialog.js +105 -80
  54. package/dist-lib/components/ui/Dialog.js.map +1 -1
  55. package/dist-lib/components/ui/Drawer.d.ts +17 -2
  56. package/dist-lib/components/ui/Drawer.js +89 -55
  57. package/dist-lib/components/ui/Drawer.js.map +1 -1
  58. package/dist-lib/components/ui/DropdownMenu.js +14 -9
  59. package/dist-lib/components/ui/DropdownMenu.js.map +1 -1
  60. package/dist-lib/components/ui/EmptyState.d.ts +2 -0
  61. package/dist-lib/components/ui/EmptyState.js +30 -16
  62. package/dist-lib/components/ui/EmptyState.js.map +1 -1
  63. package/dist-lib/components/ui/ErrorState.d.ts +13 -2
  64. package/dist-lib/components/ui/ErrorState.js +44 -23
  65. package/dist-lib/components/ui/ErrorState.js.map +1 -1
  66. package/dist-lib/components/ui/FileUpload.js +59 -56
  67. package/dist-lib/components/ui/FileUpload.js.map +1 -1
  68. package/dist-lib/components/ui/Form.d.ts +15 -2
  69. package/dist-lib/components/ui/Form.js +97 -71
  70. package/dist-lib/components/ui/Form.js.map +1 -1
  71. package/dist-lib/components/ui/Icon.d.ts +1 -1
  72. package/dist-lib/components/ui/Icon.js +20 -14
  73. package/dist-lib/components/ui/Icon.js.map +1 -1
  74. package/dist-lib/components/ui/IconButton.d.ts +2 -2
  75. package/dist-lib/components/ui/IconButton.js +28 -20
  76. package/dist-lib/components/ui/IconButton.js.map +1 -1
  77. package/dist-lib/components/ui/Input.d.ts +3 -1
  78. package/dist-lib/components/ui/Input.js +92 -78
  79. package/dist-lib/components/ui/Input.js.map +1 -1
  80. package/dist-lib/components/ui/Kbd.js +1 -1
  81. package/dist-lib/components/ui/Kbd.js.map +1 -1
  82. package/dist-lib/components/ui/MultiSelect.js +121 -107
  83. package/dist-lib/components/ui/MultiSelect.js.map +1 -1
  84. package/dist-lib/components/ui/Pagination.js +67 -62
  85. package/dist-lib/components/ui/Pagination.js.map +1 -1
  86. package/dist-lib/components/ui/Popover.js +14 -14
  87. package/dist-lib/components/ui/Popover.js.map +1 -1
  88. package/dist-lib/components/ui/Progress.d.ts +3 -3
  89. package/dist-lib/components/ui/Progress.js +77 -72
  90. package/dist-lib/components/ui/Progress.js.map +1 -1
  91. package/dist-lib/components/ui/RadioGroup.d.ts +1 -1
  92. package/dist-lib/components/ui/RadioGroup.js +69 -52
  93. package/dist-lib/components/ui/RadioGroup.js.map +1 -1
  94. package/dist-lib/components/ui/RelativeTime.d.ts +19 -0
  95. package/dist-lib/components/ui/RelativeTime.js +31 -0
  96. package/dist-lib/components/ui/RelativeTime.js.map +1 -0
  97. package/dist-lib/components/ui/ScrollArea.d.ts +9 -1
  98. package/dist-lib/components/ui/ScrollArea.js +43 -27
  99. package/dist-lib/components/ui/ScrollArea.js.map +1 -1
  100. package/dist-lib/components/ui/Select.d.ts +6 -1
  101. package/dist-lib/components/ui/Select.js +79 -71
  102. package/dist-lib/components/ui/Select.js.map +1 -1
  103. package/dist-lib/components/ui/Separator.js +1 -1
  104. package/dist-lib/components/ui/Separator.js.map +1 -1
  105. package/dist-lib/components/ui/Sheet.d.ts +9 -0
  106. package/dist-lib/components/ui/Sheet.js +69 -46
  107. package/dist-lib/components/ui/Sheet.js.map +1 -1
  108. package/dist-lib/components/ui/Sidebar.d.ts +12 -9
  109. package/dist-lib/components/ui/Sidebar.js +152 -109
  110. package/dist-lib/components/ui/Sidebar.js.map +1 -1
  111. package/dist-lib/components/ui/Skeleton.d.ts +13 -2
  112. package/dist-lib/components/ui/Skeleton.js +15 -14
  113. package/dist-lib/components/ui/Skeleton.js.map +1 -1
  114. package/dist-lib/components/ui/Slider.js +41 -29
  115. package/dist-lib/components/ui/Slider.js.map +1 -1
  116. package/dist-lib/components/ui/Snackbar.js +23 -22
  117. package/dist-lib/components/ui/Snackbar.js.map +1 -1
  118. package/dist-lib/components/ui/Spinner.js +18 -6
  119. package/dist-lib/components/ui/Spinner.js.map +1 -1
  120. package/dist-lib/components/ui/Stepper.d.ts +6 -0
  121. package/dist-lib/components/ui/Stepper.js +71 -48
  122. package/dist-lib/components/ui/Stepper.js.map +1 -1
  123. package/dist-lib/components/ui/Switch.js +46 -43
  124. package/dist-lib/components/ui/Switch.js.map +1 -1
  125. package/dist-lib/components/ui/Table.d.ts +33 -5
  126. package/dist-lib/components/ui/Table.js +141 -119
  127. package/dist-lib/components/ui/Table.js.map +1 -1
  128. package/dist-lib/components/ui/Tabs.js +50 -48
  129. package/dist-lib/components/ui/Tabs.js.map +1 -1
  130. package/dist-lib/components/ui/TagInput.js +45 -45
  131. package/dist-lib/components/ui/TagInput.js.map +1 -1
  132. package/dist-lib/components/ui/Textarea.js +65 -54
  133. package/dist-lib/components/ui/Textarea.js.map +1 -1
  134. package/dist-lib/components/ui/Timeline.js +39 -35
  135. package/dist-lib/components/ui/Timeline.js.map +1 -1
  136. package/dist-lib/components/ui/Toast.js +44 -57
  137. package/dist-lib/components/ui/Toast.js.map +1 -1
  138. package/dist-lib/components/ui/Tooltip.d.ts +4 -1
  139. package/dist-lib/components/ui/Tooltip.js +24 -22
  140. package/dist-lib/components/ui/Tooltip.js.map +1 -1
  141. package/dist-lib/components/ui/TopNav.d.ts +7 -3
  142. package/dist-lib/components/ui/TopNav.js +35 -25
  143. package/dist-lib/components/ui/TopNav.js.map +1 -1
  144. package/dist-lib/components/ui/Tree.d.ts +15 -10
  145. package/dist-lib/components/ui/Tree.js +82 -79
  146. package/dist-lib/components/ui/Tree.js.map +1 -1
  147. package/dist-lib/hooks/use-debounce.d.ts +19 -0
  148. package/dist-lib/hooks/use-debounce.js +36 -0
  149. package/dist-lib/hooks/use-debounce.js.map +1 -0
  150. package/dist-lib/hooks/use-delayed-loading.d.ts +21 -0
  151. package/dist-lib/hooks/use-delayed-loading.js +20 -0
  152. package/dist-lib/hooks/use-delayed-loading.js.map +1 -0
  153. package/dist-lib/hooks/use-field-ids.d.ts +2 -1
  154. package/dist-lib/hooks/use-field-ids.js +9 -9
  155. package/dist-lib/hooks/use-field-ids.js.map +1 -1
  156. package/dist-lib/hooks/use-media-query.d.ts +3 -1
  157. package/dist-lib/hooks/use-media-query.js +16 -12
  158. package/dist-lib/hooks/use-media-query.js.map +1 -1
  159. package/dist-lib/hooks/use-modifier-key.d.ts +13 -0
  160. package/dist-lib/hooks/use-modifier-key.js +19 -0
  161. package/dist-lib/hooks/use-modifier-key.js.map +1 -0
  162. package/dist-lib/hooks/use-toast.d.ts +7 -2
  163. package/dist-lib/hooks/use-toast.js +61 -30
  164. package/dist-lib/hooks/use-toast.js.map +1 -1
  165. package/dist-lib/icons/index.d.ts +9 -0
  166. package/dist-lib/icons/index.js +148 -130
  167. package/dist-lib/icons/index.js.map +1 -1
  168. package/dist-lib/illustrations/index.d.ts +1 -0
  169. package/dist-lib/illustrations/index.js +15 -6
  170. package/dist-lib/illustrations/index.js.map +1 -1
  171. package/dist-lib/index.d.ts +6 -0
  172. package/dist-lib/index.js +280 -260
  173. package/dist-lib/index.js.map +1 -1
  174. package/dist-lib/lib/format.d.ts +65 -0
  175. package/dist-lib/lib/format.js +95 -0
  176. package/dist-lib/lib/format.js.map +1 -0
  177. package/dist-lib/lib/return-focus.d.ts +11 -0
  178. package/dist-lib/lib/return-focus.js +12 -0
  179. package/dist-lib/lib/return-focus.js.map +1 -0
  180. package/dist-lib/lib/strings.d.ts +62 -1
  181. package/dist-lib/lib/strings.js +75 -19
  182. package/dist-lib/lib/strings.js.map +1 -1
  183. package/dist-lib/lib/strings.mn.js +50 -5
  184. package/dist-lib/lib/strings.mn.js.map +1 -1
  185. package/dist-lib/styles.css +1 -1
  186. package/dist-lib/theme.css +141 -78
  187. package/package.json +10 -3
@@ -1,135 +1,136 @@
1
1
  "use client";
2
- import { jsx as f, jsxs as L } from "react/jsx-runtime";
3
- import { useState as A, useMemo as S, useRef as Z, useCallback as O } from "react";
4
- import { ChevronRight as _, FolderOpen as $, Folder as P, File as C } from "../../icons/index.js";
5
- import { cn as R } from "../../lib/utils.js";
6
- import { useStrings as ee } from "../../hooks/use-strings.js";
7
- function te({
8
- data: p,
9
- defaultExpanded: U = [],
10
- expanded: d,
11
- onExpandedChange: b,
12
- selectedId: D,
13
- selected: I,
14
- defaultSelected: q,
15
- onSelect: h,
16
- onSelectedChange: g,
17
- className: B,
18
- "aria-label": G,
19
- "aria-labelledby": T
20
- }) {
21
- var H;
22
- const J = ee(), [j, Q] = A(
23
- () => new Set(U)
24
- ), o = S(
25
- () => d ? new Set(d) : j,
26
- [d, j]
27
- ), [V, W] = A(q), v = I ?? D ?? V, [N, y] = A(void 0), F = Z(/* @__PURE__ */ new Map()), K = O(
2
+ import { jsx as f, jsxs as j } from "react/jsx-runtime";
3
+ import { forwardRef as _, useState as F, useMemo as K, useRef as ee, useCallback as R } from "react";
4
+ import { ChevronRight as te, FolderOpen as re, Folder as ie, File as se } from "../../icons/index.js";
5
+ import { cn as T } from "../../lib/utils.js";
6
+ import { useStrings as ae } from "../../hooks/use-strings.js";
7
+ const ne = _(function(I, M) {
8
+ var P;
9
+ const {
10
+ data: m,
11
+ defaultExpanded: H = [],
12
+ expanded: u,
13
+ onExpandedChange: h,
14
+ selectedId: U,
15
+ selected: $,
16
+ defaultSelected: q,
17
+ onSelect: b,
18
+ onSelectedChange: g,
19
+ className: B,
20
+ "aria-label": G,
21
+ "aria-labelledby": z,
22
+ ...J
23
+ } = I, D = "selected" in I || "selectedId" in I, Q = ae(), [A, V] = F(
24
+ () => new Set(H)
25
+ ), c = K(
26
+ () => u ? new Set(u) : A,
27
+ [u, A]
28
+ ), [W, X] = F(q), x = D ? $ ?? U : W, [N, y] = F(void 0), S = ee(/* @__PURE__ */ new Map()), C = R(
28
29
  (e) => {
29
- d === void 0 && Q(e), b == null || b([...e]);
30
+ u === void 0 && V(e), h == null || h([...e]);
30
31
  },
31
- [d, b]
32
- ), k = O(
32
+ [u, h]
33
+ ), v = R(
33
34
  (e, t) => {
34
- if (o.has(e) === t) return;
35
- const r = new Set(o);
36
- t ? r.add(e) : r.delete(e), K(r);
35
+ if (c.has(e) === t) return;
36
+ const r = new Set(c);
37
+ t ? r.add(e) : r.delete(e), C(r);
37
38
  },
38
- [o, K]
39
- ), M = O(
39
+ [c, C]
40
+ ), L = R(
40
41
  (e) => {
41
- I === void 0 && D === void 0 && W(e), h == null || h(e), g == null || g(e);
42
+ D || X(e), b == null || b(e), g == null || g(e);
42
43
  },
43
- [h, g, I, D]
44
- ), a = S(() => {
45
- const e = [], t = (r, l, c) => {
46
- r.forEach((s, n) => {
47
- e.push({ node: s, level: l, setSize: r.length, posInSet: n + 1, parentId: c }), s.children && o.has(s.id) && t(s.children, l + 1, s.id);
44
+ [b, g, D]
45
+ ), n = K(() => {
46
+ const e = [], t = (r, l, d) => {
47
+ r.forEach((s, a) => {
48
+ e.push({ node: s, level: l, setSize: r.length, posInSet: a + 1, parentId: d }), s.children && c.has(s.id) && t(s.children, l + 1, s.id);
48
49
  });
49
50
  };
50
- return t(p, 1, null), e;
51
- }, [p, o]), X = N && a.some((e) => e.node.id === N) && N || v && a.some((e) => e.node.id === v) && v || ((H = a[0]) == null ? void 0 : H.node.id), u = (e) => {
51
+ return t(m, 1, null), e;
52
+ }, [m, c]), Y = N && n.some((e) => e.node.id === N) && N || x && n.some((e) => e.node.id === x) && x || ((P = n[0]) == null ? void 0 : P.node.id), o = (e) => {
52
53
  var t;
53
- e && (y(e), (t = F.current.get(e)) == null || t.focus());
54
- }, Y = (e, t) => {
55
- var n, m, w, x, i;
56
- const { node: r } = t, l = !!r.children, c = o.has(r.id), s = a.findIndex((z) => z.node.id === r.id);
54
+ e && (y(e), (t = S.current.get(e)) == null || t.focus());
55
+ }, Z = (e, t) => {
56
+ var a, p, k, w, i;
57
+ const { node: r } = t, l = !!r.children, d = c.has(r.id), s = n.findIndex((E) => E.node.id === r.id);
57
58
  switch (e.key) {
58
59
  case "ArrowDown":
59
- e.preventDefault(), u((n = a[s + 1]) == null ? void 0 : n.node.id);
60
+ e.preventDefault(), o((a = n[s + 1]) == null ? void 0 : a.node.id);
60
61
  break;
61
62
  case "ArrowUp":
62
- e.preventDefault(), u((m = a[s - 1]) == null ? void 0 : m.node.id);
63
+ e.preventDefault(), o((p = n[s - 1]) == null ? void 0 : p.node.id);
63
64
  break;
64
65
  case "ArrowRight":
65
- e.preventDefault(), l && !c ? k(r.id, !0) : l && c && u((w = r.children[0]) == null ? void 0 : w.id);
66
+ e.preventDefault(), l && !d ? v(r.id, !0) : l && d && o((k = r.children[0]) == null ? void 0 : k.id);
66
67
  break;
67
68
  case "ArrowLeft":
68
- e.preventDefault(), l && c ? k(r.id, !1) : t.parentId && u(t.parentId);
69
+ e.preventDefault(), l && d ? v(r.id, !1) : t.parentId && o(t.parentId);
69
70
  break;
70
71
  case "Home":
71
- e.preventDefault(), u((x = a[0]) == null ? void 0 : x.node.id);
72
+ e.preventDefault(), o((w = n[0]) == null ? void 0 : w.node.id);
72
73
  break;
73
74
  case "End":
74
- e.preventDefault(), u((i = a[a.length - 1]) == null ? void 0 : i.node.id);
75
+ e.preventDefault(), o((i = n[n.length - 1]) == null ? void 0 : i.node.id);
75
76
  break;
76
77
  case "Enter":
77
78
  case " ":
78
- e.preventDefault(), M(r.id), l && k(r.id, !c);
79
+ e.preventDefault(), L(r.id), l && v(r.id, !d);
79
80
  break;
80
81
  default:
81
82
  return;
82
83
  }
83
84
  e.stopPropagation();
84
85
  };
85
- function E(e, t, r, l, c) {
86
- const s = !!e.children, n = o.has(e.id), m = v === e.id, w = s ? n ? $ : P : C, x = { node: e, parentId: c };
87
- return /* @__PURE__ */ L(
86
+ function O(e, t, r, l, d) {
87
+ const s = !!e.children, a = c.has(e.id), p = x === e.id, k = s ? a ? re : ie : se, w = { node: e, parentId: d };
88
+ return /* @__PURE__ */ j(
88
89
  "li",
89
90
  {
90
91
  ref: (i) => {
91
- i ? F.current.set(e.id, i) : F.current.delete(e.id);
92
+ i ? S.current.set(e.id, i) : S.current.delete(e.id);
92
93
  },
93
94
  role: "treeitem",
94
- "aria-expanded": s ? n : void 0,
95
- "aria-selected": m,
95
+ "aria-expanded": s ? a : void 0,
96
+ "aria-selected": p,
96
97
  "aria-level": t,
97
98
  "aria-setsize": r,
98
99
  "aria-posinset": l,
99
- tabIndex: X === e.id ? 0 : -1,
100
+ tabIndex: Y === e.id ? 0 : -1,
100
101
  onFocus: (i) => {
101
102
  i.target === i.currentTarget && y(e.id);
102
103
  },
103
104
  onKeyDown: (i) => {
104
- i.target === i.currentTarget && Y(i, x);
105
+ i.target === i.currentTarget && Z(i, w);
105
106
  },
106
107
  onClick: (i) => {
107
- i.stopPropagation(), y(e.id), M(e.id), s && k(e.id, !n);
108
+ i.stopPropagation(), y(e.id), L(e.id), s && v(e.id, !a);
108
109
  },
109
110
  className: "outline-none",
110
111
  children: [
111
- /* @__PURE__ */ L(
112
+ /* @__PURE__ */ j(
112
113
  "div",
113
114
  {
114
- style: { paddingLeft: (t - 1) * 16 + 8 },
115
- className: R(
115
+ style: { paddingLeft: `${t - 1 + 0.5}rem` },
116
+ className: T(
116
117
  "flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-2 text-left text-sm",
117
118
  "hover:bg-background-muted",
118
119
  "[li:focus-visible>&]:ring-ring [li:focus-visible>&]:ring-offset-background [li:focus-visible>&]:ring-2 [li:focus-visible>&]:ring-offset-1",
119
- m && "bg-accent-soft text-foreground"
120
+ p && "bg-accent-soft text-foreground"
120
121
  ),
121
122
  children: [
122
123
  s ? /* @__PURE__ */ f(
123
- _,
124
+ te,
124
125
  {
125
- className: R(
126
+ className: T(
126
127
  "text-foreground-subtle size-3.5 shrink-0 transition-transform",
127
- n && "rotate-90"
128
+ a && "rotate-90"
128
129
  ),
129
130
  "aria-hidden": !0
130
131
  }
131
132
  ) : /* @__PURE__ */ f("span", { className: "w-3.5 shrink-0", "aria-hidden": !0 }),
132
- e.icon ?? /* @__PURE__ */ f(w, { className: "text-foreground-muted size-4 shrink-0", "aria-hidden": !0 }),
133
+ e.icon ?? /* @__PURE__ */ f(k, { className: "text-foreground-muted size-4 shrink-0", "aria-hidden": !0 }),
133
134
  /* @__PURE__ */ f(
134
135
  "span",
135
136
  {
@@ -141,8 +142,8 @@ function te({
141
142
  ]
142
143
  }
143
144
  ),
144
- s && n && /* @__PURE__ */ f("ul", { role: "group", className: "mt-0.5", children: e.children.map(
145
- (i, z) => E(i, t + 1, e.children.length, z + 1, e.id)
145
+ s && a && /* @__PURE__ */ f("ul", { role: "group", className: "mt-0.5", children: e.children.map(
146
+ (i, E) => O(i, t + 1, e.children.length, E + 1, e.id)
146
147
  ) })
147
148
  ]
148
149
  },
@@ -152,16 +153,18 @@ function te({
152
153
  return /* @__PURE__ */ f(
153
154
  "ul",
154
155
  {
156
+ ref: M,
155
157
  role: "tree",
156
- "aria-label": G ?? (T ? void 0 : J.tree.label),
157
- "aria-labelledby": T,
158
- className: R("flex flex-col gap-0.5", B),
159
- children: p.map((e, t) => E(e, 1, p.length, t + 1, null))
158
+ "aria-label": G ?? (z ? void 0 : Q.tree.label),
159
+ "aria-labelledby": z,
160
+ className: T("flex flex-col gap-0.5", B),
161
+ ...J,
162
+ children: m.map((e, t) => O(e, 1, m.length, t + 1, null))
160
163
  }
161
164
  );
162
- }
163
- te.displayName = "Tree";
165
+ });
166
+ ne.displayName = "Tree";
164
167
  export {
165
- te as Tree
168
+ ne as Tree
166
169
  };
167
170
  //# sourceMappingURL=Tree.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tree.js","sources":["../../../src/components/ui/Tree.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from 'react';\nimport { ChevronRight, File as FileIcon, Folder, FolderOpen } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\nexport interface TreeNode {\n id: string;\n label: ReactNode;\n /** If undefined the node is a leaf; if empty array it is an empty folder. */\n children?: TreeNode[];\n /** Optional custom icon (overrides default file/folder icons). */\n icon?: ReactNode;\n}\n\nexport interface TreeProps {\n /** The root nodes. */\n data: TreeNode[];\n /** Ids to expand by default (uncontrolled). */\n defaultExpanded?: string[];\n /** Controlled set of expanded ids. */\n expanded?: string[];\n /** Called with the full next set of expanded ids. */\n onExpandedChange?: (next: string[]) => void;\n /** Currently selected node id (controlled). Alias: `selected`. */\n selectedId?: string;\n /** Controlled selected id. Takes precedence over `selectedId`. */\n selected?: string;\n /** Default selected id (uncontrolled). */\n defaultSelected?: string;\n /** Called when a node is activated (click / Enter / Space). */\n onSelect?: (id: string) => void;\n /** Alias of `onSelect` for the controlled `selected` pair. */\n onSelectedChange?: (id: string) => void;\n /** Accessible name for the tree. */\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n}\n\ninterface FlatNode {\n node: TreeNode;\n level: number;\n setSize: number;\n posInSet: number;\n parentId: string | null;\n}\n\n/**\n * Expandable file-tree style list following the WAI-ARIA tree pattern.\n * Single selection, roving tabindex (one tab stop), and full arrow-key\n * navigation: Up/Down move, Right expands or moves into a folder, Left\n * collapses or moves to the parent, Home/End jump, Enter/Space select.\n *\n * Expansion and selection are uncontrolled by default; pass\n * `expanded`/`onExpandedChange` and `selected`/`onSelectedChange` to control.\n */\nexport function Tree({\n data,\n defaultExpanded = [],\n expanded: expandedProp,\n onExpandedChange,\n selectedId,\n selected: selectedProp,\n defaultSelected,\n onSelect,\n onSelectedChange,\n className,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n}: TreeProps) {\n const strings = useStrings();\n const [internalExpanded, setInternalExpanded] = useState<Set<string>>(\n () => new Set(defaultExpanded),\n );\n const expanded = useMemo(\n () => (expandedProp ? new Set(expandedProp) : internalExpanded),\n [expandedProp, internalExpanded],\n );\n\n const [internalSelected, setInternalSelected] = useState<string | undefined>(defaultSelected);\n const selected = selectedProp ?? selectedId ?? internalSelected;\n\n const [focusedId, setFocusedId] = useState<string | undefined>(undefined);\n const itemRefs = useRef(new Map<string, HTMLLIElement>());\n\n const setExpanded = useCallback(\n (next: Set<string>) => {\n if (expandedProp === undefined) setInternalExpanded(next);\n onExpandedChange?.([...next]);\n },\n [expandedProp, onExpandedChange],\n );\n\n const setOpen = useCallback(\n (id: string, open: boolean) => {\n if (expanded.has(id) === open) return;\n const next = new Set(expanded);\n if (open) next.add(id);\n else next.delete(id);\n setExpanded(next);\n },\n [expanded, setExpanded],\n );\n\n const select = useCallback(\n (id: string) => {\n if (selectedProp === undefined && selectedId === undefined) setInternalSelected(id);\n onSelect?.(id);\n onSelectedChange?.(id);\n },\n [onSelect, onSelectedChange, selectedProp, selectedId],\n );\n\n // Flatten the *visible* nodes in DOM order — this is what arrow keys walk.\n const visible = useMemo(() => {\n const out: FlatNode[] = [];\n const walk = (nodes: TreeNode[], level: number, parentId: string | null) => {\n nodes.forEach((node, i) => {\n out.push({ node, level, setSize: nodes.length, posInSet: i + 1, parentId });\n if (node.children && expanded.has(node.id)) walk(node.children, level + 1, node.id);\n });\n };\n walk(data, 1, null);\n return out;\n }, [data, expanded]);\n\n // The single tab stop: focused item if still visible, else selected, else first.\n const tabStopId =\n (focusedId && visible.some((v) => v.node.id === focusedId) && focusedId) ||\n (selected && visible.some((v) => v.node.id === selected) && selected) ||\n visible[0]?.node.id;\n\n const focusItem = (id: string | undefined) => {\n if (!id) return;\n setFocusedId(id);\n itemRefs.current.get(id)?.focus();\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLLIElement>, flat: FlatNode) => {\n const { node } = flat;\n const hasChildren = !!node.children;\n const isOpen = expanded.has(node.id);\n const index = visible.findIndex((v) => v.node.id === node.id);\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n focusItem(visible[index + 1]?.node.id);\n break;\n case 'ArrowUp':\n e.preventDefault();\n focusItem(visible[index - 1]?.node.id);\n break;\n case 'ArrowRight':\n e.preventDefault();\n if (hasChildren && !isOpen) setOpen(node.id, true);\n else if (hasChildren && isOpen) focusItem(node.children![0]?.id);\n break;\n case 'ArrowLeft':\n e.preventDefault();\n if (hasChildren && isOpen) setOpen(node.id, false);\n else if (flat.parentId) focusItem(flat.parentId);\n break;\n case 'Home':\n e.preventDefault();\n focusItem(visible[0]?.node.id);\n break;\n case 'End':\n e.preventDefault();\n focusItem(visible[visible.length - 1]?.node.id);\n break;\n case 'Enter':\n case ' ':\n e.preventDefault();\n select(node.id);\n if (hasChildren) setOpen(node.id, !isOpen);\n break;\n default:\n return;\n }\n e.stopPropagation();\n };\n\n function renderNode(\n node: TreeNode,\n level: number,\n setSize: number,\n posInSet: number,\n parentId: string | null,\n ): ReactNode {\n const hasChildren = !!node.children;\n const isOpen = expanded.has(node.id);\n const isSelected = selected === node.id;\n const DefaultIcon = hasChildren ? (isOpen ? FolderOpen : Folder) : FileIcon;\n const flat: FlatNode = { node, level, setSize, posInSet, parentId };\n\n return (\n <li\n key={node.id}\n ref={(el) => {\n if (el) itemRefs.current.set(node.id, el);\n else itemRefs.current.delete(node.id);\n }}\n role=\"treeitem\"\n aria-expanded={hasChildren ? isOpen : undefined}\n aria-selected={isSelected}\n aria-level={level}\n aria-setsize={setSize}\n aria-posinset={posInSet}\n tabIndex={tabStopId === node.id ? 0 : -1}\n onFocus={(e) => {\n if (e.target === e.currentTarget) setFocusedId(node.id);\n }}\n onKeyDown={(e) => {\n if (e.target === e.currentTarget) handleKeyDown(e, flat);\n }}\n onClick={(e) => {\n e.stopPropagation();\n setFocusedId(node.id);\n select(node.id);\n if (hasChildren) setOpen(node.id, !isOpen);\n }}\n className=\"outline-none\"\n >\n <div\n style={{ paddingLeft: (level - 1) * 16 + 8 }}\n className={cn(\n 'flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-2 text-left text-sm',\n 'hover:bg-background-muted',\n '[li:focus-visible>&]:ring-ring [li:focus-visible>&]:ring-offset-background [li:focus-visible>&]:ring-2 [li:focus-visible>&]:ring-offset-1',\n isSelected && 'bg-accent-soft text-foreground',\n )}\n >\n {hasChildren ? (\n <ChevronRight\n className={cn(\n 'text-foreground-subtle size-3.5 shrink-0 transition-transform',\n isOpen && 'rotate-90',\n )}\n aria-hidden\n />\n ) : (\n <span className=\"w-3.5 shrink-0\" aria-hidden />\n )}\n {node.icon ?? (\n <DefaultIcon className=\"text-foreground-muted size-4 shrink-0\" aria-hidden />\n )}\n <span\n className=\"truncate\"\n title={typeof node.label === 'string' ? node.label : undefined}\n >\n {node.label}\n </span>\n </div>\n {hasChildren && isOpen && (\n <ul role=\"group\" className=\"mt-0.5\">\n {node.children!.map((c, i) =>\n renderNode(c, level + 1, node.children!.length, i + 1, node.id),\n )}\n </ul>\n )}\n </li>\n );\n }\n\n return (\n <ul\n role=\"tree\"\n aria-label={ariaLabel ?? (ariaLabelledby ? undefined : strings.tree.label)}\n aria-labelledby={ariaLabelledby}\n className={cn('flex flex-col gap-0.5', className)}\n >\n {data.map((n, i) => renderNode(n, 1, data.length, i + 1, null))}\n </ul>\n );\n}\nTree.displayName = 'Tree';\n"],"names":["Tree","data","defaultExpanded","expandedProp","onExpandedChange","selectedId","selectedProp","defaultSelected","onSelect","onSelectedChange","className","ariaLabel","ariaLabelledby","strings","useStrings","internalExpanded","setInternalExpanded","useState","expanded","useMemo","internalSelected","setInternalSelected","selected","focusedId","setFocusedId","itemRefs","useRef","setExpanded","useCallback","next","setOpen","id","open","select","visible","out","walk","nodes","level","parentId","node","i","tabStopId","v","_a","focusItem","handleKeyDown","flat","hasChildren","isOpen","index","_b","_c","_d","_e","renderNode","setSize","posInSet","isSelected","DefaultIcon","FolderOpen","Folder","FileIcon","jsxs","el","e","cn","jsx","ChevronRight","c","n"],"mappings":"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"}
1
+ {"version":3,"file":"Tree.js","sources":["../../../src/components/ui/Tree.tsx"],"sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useCallback,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { ChevronRight, File as FileIcon, Folder, FolderOpen } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\nexport interface TreeNode {\n id: string;\n label: ReactNode;\n /** If undefined the node is a leaf; if empty array it is an empty folder. */\n children?: TreeNode[];\n /** Optional custom icon (overrides default file/folder icons). */\n icon?: ReactNode;\n}\n\nexport interface TreeProps extends Omit<HTMLAttributes<HTMLUListElement>, 'onSelect'> {\n /** The root nodes. */\n data: TreeNode[];\n /** Ids to expand by default (uncontrolled). */\n defaultExpanded?: string[];\n /** Controlled set of expanded ids. */\n expanded?: string[];\n /** Called with the full next set of expanded ids. */\n onExpandedChange?: (next: string[]) => void;\n /**\n * @deprecated Use `selected` + `onSelectedChange`. Kept for backward\n * compatibility; ignored when `selected` is present.\n */\n selectedId?: string;\n /**\n * Controlled selected id. The component is controlled whenever this key is\n * present in props (even as `undefined`), mirroring React inputs.\n */\n selected?: string;\n /** Default selected id (uncontrolled). */\n defaultSelected?: string;\n /**\n * @deprecated Use `onSelectedChange`. Still called on every activation.\n */\n onSelect?: (id: string) => void;\n /** Called when a node is activated (click / Enter / Space). */\n onSelectedChange?: (id: string) => void;\n /** Accessible name for the tree. */\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n}\n\ninterface FlatNode {\n node: TreeNode;\n level: number;\n setSize: number;\n posInSet: number;\n parentId: string | null;\n}\n\n/**\n * Expandable file-tree style list following the WAI-ARIA tree pattern.\n * Single selection, roving tabindex (one tab stop), and full arrow-key\n * navigation: Up/Down move, Right expands or moves into a folder, Left\n * collapses or moves to the parent, Home/End jump, Enter/Space select.\n *\n * Expansion and selection are uncontrolled by default; pass\n * `expanded`/`onExpandedChange` and `selected`/`onSelectedChange` to control.\n */\nexport const Tree = forwardRef<HTMLUListElement, TreeProps>(function Tree(props, ref) {\n const {\n data,\n defaultExpanded = [],\n expanded: expandedProp,\n onExpandedChange,\n selectedId,\n selected: selectedProp,\n defaultSelected,\n onSelect,\n onSelectedChange,\n className,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n ...rest\n } = props;\n const isControlled = 'selected' in props || 'selectedId' in props;\n const strings = useStrings();\n const [internalExpanded, setInternalExpanded] = useState<Set<string>>(\n () => new Set(defaultExpanded),\n );\n const expanded = useMemo(\n () => (expandedProp ? new Set(expandedProp) : internalExpanded),\n [expandedProp, internalExpanded],\n );\n\n const [internalSelected, setInternalSelected] = useState<string | undefined>(defaultSelected);\n const selected = isControlled ? (selectedProp ?? selectedId) : internalSelected;\n\n const [focusedId, setFocusedId] = useState<string | undefined>(undefined);\n const itemRefs = useRef(new Map<string, HTMLLIElement>());\n\n const setExpanded = useCallback(\n (next: Set<string>) => {\n if (expandedProp === undefined) setInternalExpanded(next);\n onExpandedChange?.([...next]);\n },\n [expandedProp, onExpandedChange],\n );\n\n const setOpen = useCallback(\n (id: string, open: boolean) => {\n if (expanded.has(id) === open) return;\n const next = new Set(expanded);\n if (open) next.add(id);\n else next.delete(id);\n setExpanded(next);\n },\n [expanded, setExpanded],\n );\n\n const select = useCallback(\n (id: string) => {\n if (!isControlled) setInternalSelected(id);\n onSelect?.(id);\n onSelectedChange?.(id);\n },\n [onSelect, onSelectedChange, isControlled],\n );\n\n // Flatten the *visible* nodes in DOM order — this is what arrow keys walk.\n const visible = useMemo(() => {\n const out: FlatNode[] = [];\n const walk = (nodes: TreeNode[], level: number, parentId: string | null) => {\n nodes.forEach((node, i) => {\n out.push({ node, level, setSize: nodes.length, posInSet: i + 1, parentId });\n if (node.children && expanded.has(node.id)) walk(node.children, level + 1, node.id);\n });\n };\n walk(data, 1, null);\n return out;\n }, [data, expanded]);\n\n // The single tab stop: focused item if still visible, else selected, else first.\n const tabStopId =\n (focusedId && visible.some((v) => v.node.id === focusedId) && focusedId) ||\n (selected && visible.some((v) => v.node.id === selected) && selected) ||\n visible[0]?.node.id;\n\n const focusItem = (id: string | undefined) => {\n if (!id) return;\n setFocusedId(id);\n itemRefs.current.get(id)?.focus();\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLLIElement>, flat: FlatNode) => {\n const { node } = flat;\n const hasChildren = !!node.children;\n const isOpen = expanded.has(node.id);\n const index = visible.findIndex((v) => v.node.id === node.id);\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n focusItem(visible[index + 1]?.node.id);\n break;\n case 'ArrowUp':\n e.preventDefault();\n focusItem(visible[index - 1]?.node.id);\n break;\n case 'ArrowRight':\n e.preventDefault();\n if (hasChildren && !isOpen) setOpen(node.id, true);\n else if (hasChildren && isOpen) focusItem(node.children![0]?.id);\n break;\n case 'ArrowLeft':\n e.preventDefault();\n if (hasChildren && isOpen) setOpen(node.id, false);\n else if (flat.parentId) focusItem(flat.parentId);\n break;\n case 'Home':\n e.preventDefault();\n focusItem(visible[0]?.node.id);\n break;\n case 'End':\n e.preventDefault();\n focusItem(visible[visible.length - 1]?.node.id);\n break;\n case 'Enter':\n case ' ':\n e.preventDefault();\n select(node.id);\n if (hasChildren) setOpen(node.id, !isOpen);\n break;\n default:\n return;\n }\n e.stopPropagation();\n };\n\n function renderNode(\n node: TreeNode,\n level: number,\n setSize: number,\n posInSet: number,\n parentId: string | null,\n ): ReactNode {\n const hasChildren = !!node.children;\n const isOpen = expanded.has(node.id);\n const isSelected = selected === node.id;\n const DefaultIcon = hasChildren ? (isOpen ? FolderOpen : Folder) : FileIcon;\n const flat: FlatNode = { node, level, setSize, posInSet, parentId };\n\n return (\n <li\n key={node.id}\n ref={(el) => {\n if (el) itemRefs.current.set(node.id, el);\n else itemRefs.current.delete(node.id);\n }}\n role=\"treeitem\"\n aria-expanded={hasChildren ? isOpen : undefined}\n aria-selected={isSelected}\n aria-level={level}\n aria-setsize={setSize}\n aria-posinset={posInSet}\n tabIndex={tabStopId === node.id ? 0 : -1}\n onFocus={(e) => {\n if (e.target === e.currentTarget) setFocusedId(node.id);\n }}\n onKeyDown={(e) => {\n if (e.target === e.currentTarget) handleKeyDown(e, flat);\n }}\n onClick={(e) => {\n e.stopPropagation();\n setFocusedId(node.id);\n select(node.id);\n if (hasChildren) setOpen(node.id, !isOpen);\n }}\n className=\"outline-none\"\n >\n <div\n style={{ paddingLeft: `${level - 1 + 0.5}rem` }}\n className={cn(\n 'flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-2 text-left text-sm',\n 'hover:bg-background-muted',\n '[li:focus-visible>&]:ring-ring [li:focus-visible>&]:ring-offset-background [li:focus-visible>&]:ring-2 [li:focus-visible>&]:ring-offset-1',\n isSelected && 'bg-accent-soft text-foreground',\n )}\n >\n {hasChildren ? (\n <ChevronRight\n className={cn(\n 'text-foreground-subtle size-3.5 shrink-0 transition-transform',\n isOpen && 'rotate-90',\n )}\n aria-hidden\n />\n ) : (\n <span className=\"w-3.5 shrink-0\" aria-hidden />\n )}\n {node.icon ?? (\n <DefaultIcon className=\"text-foreground-muted size-4 shrink-0\" aria-hidden />\n )}\n <span\n className=\"truncate\"\n title={typeof node.label === 'string' ? node.label : undefined}\n >\n {node.label}\n </span>\n </div>\n {hasChildren && isOpen && (\n <ul role=\"group\" className=\"mt-0.5\">\n {node.children!.map((c, i) =>\n renderNode(c, level + 1, node.children!.length, i + 1, node.id),\n )}\n </ul>\n )}\n </li>\n );\n }\n\n return (\n <ul\n ref={ref}\n role=\"tree\"\n aria-label={ariaLabel ?? (ariaLabelledby ? undefined : strings.tree.label)}\n aria-labelledby={ariaLabelledby}\n className={cn('flex flex-col gap-0.5', className)}\n {...rest}\n >\n {data.map((n, i) => renderNode(n, 1, data.length, i + 1, null))}\n </ul>\n );\n});\nTree.displayName = 'Tree';\n"],"names":["Tree","forwardRef","props","ref","data","defaultExpanded","expandedProp","onExpandedChange","selectedId","selectedProp","defaultSelected","onSelect","onSelectedChange","className","ariaLabel","ariaLabelledby","rest","isControlled","strings","useStrings","internalExpanded","setInternalExpanded","useState","expanded","useMemo","internalSelected","setInternalSelected","selected","focusedId","setFocusedId","itemRefs","useRef","setExpanded","useCallback","next","setOpen","id","open","select","visible","out","walk","nodes","level","parentId","node","i","tabStopId","v","_a","focusItem","handleKeyDown","flat","hasChildren","isOpen","index","_b","_c","_d","_e","renderNode","setSize","posInSet","isSelected","DefaultIcon","FolderOpen","Folder","FileIcon","jsxs","el","e","cn","jsx","ChevronRight","c","n"],"mappings":";;;;;;AA2EO,MAAMA,KAAOC,EAAwC,SAAcC,GAAOC,GAAK;;AACpF,QAAM;AAAA,IACJ,MAAAC;AAAA,IACA,iBAAAC,IAAkB,CAAA;AAAA,IAClB,UAAUC;AAAA,IACV,kBAAAC;AAAA,IACA,YAAAC;AAAA,IACA,UAAUC;AAAA,IACV,iBAAAC;AAAA,IACA,UAAAC;AAAA,IACA,kBAAAC;AAAA,IACA,WAAAC;AAAA,IACA,cAAcC;AAAA,IACd,mBAAmBC;AAAA,IACnB,GAAGC;AAAA,EAAA,IACDd,GACEe,IAAe,cAAcf,KAAS,gBAAgBA,GACtDgB,IAAUC,GAAA,GACV,CAACC,GAAkBC,CAAmB,IAAIC;AAAA,IAC9C,MAAM,IAAI,IAAIjB,CAAe;AAAA,EAAA,GAEzBkB,IAAWC;AAAA,IACf,MAAOlB,IAAe,IAAI,IAAIA,CAAY,IAAIc;AAAA,IAC9C,CAACd,GAAcc,CAAgB;AAAA,EAAA,GAG3B,CAACK,GAAkBC,CAAmB,IAAIJ,EAA6BZ,CAAe,GACtFiB,IAAWV,IAAgBR,KAAgBD,IAAciB,GAEzD,CAACG,GAAWC,CAAY,IAAIP,EAA6B,MAAS,GAClEQ,IAAWC,GAAO,oBAAI,KAA4B,GAElDC,IAAcC;AAAA,IAClB,CAACC,MAAsB;AACrB,MAAI5B,MAAiB,UAAWe,EAAoBa,CAAI,GACxD3B,KAAA,QAAAA,EAAmB,CAAC,GAAG2B,CAAI;AAAA,IAC7B;AAAA,IACA,CAAC5B,GAAcC,CAAgB;AAAA,EAAA,GAG3B4B,IAAUF;AAAA,IACd,CAACG,GAAYC,MAAkB;AAC7B,UAAId,EAAS,IAAIa,CAAE,MAAMC,EAAM;AAC/B,YAAMH,IAAO,IAAI,IAAIX,CAAQ;AAC7B,MAAIc,IAAMH,EAAK,IAAIE,CAAE,IAChBF,EAAK,OAAOE,CAAE,GACnBJ,EAAYE,CAAI;AAAA,IAClB;AAAA,IACA,CAACX,GAAUS,CAAW;AAAA,EAAA,GAGlBM,IAASL;AAAA,IACb,CAACG,MAAe;AACd,MAAKnB,KAAcS,EAAoBU,CAAE,GACzCzB,KAAA,QAAAA,EAAWyB,IACXxB,KAAA,QAAAA,EAAmBwB;AAAA,IACrB;AAAA,IACA,CAACzB,GAAUC,GAAkBK,CAAY;AAAA,EAAA,GAIrCsB,IAAUf,EAAQ,MAAM;AAC5B,UAAMgB,IAAkB,CAAA,GAClBC,IAAO,CAACC,GAAmBC,GAAeC,MAA4B;AAC1E,MAAAF,EAAM,QAAQ,CAACG,GAAMC,MAAM;AACzB,QAAAN,EAAI,KAAK,EAAE,MAAAK,GAAM,OAAAF,GAAO,SAASD,EAAM,QAAQ,UAAUI,IAAI,GAAG,UAAAF,EAAA,CAAU,GACtEC,EAAK,YAAYtB,EAAS,IAAIsB,EAAK,EAAE,KAAGJ,EAAKI,EAAK,UAAUF,IAAQ,GAAGE,EAAK,EAAE;AAAA,MACpF,CAAC;AAAA,IACH;AACA,WAAAJ,EAAKrC,GAAM,GAAG,IAAI,GACXoC;AAAA,EACT,GAAG,CAACpC,GAAMmB,CAAQ,CAAC,GAGbwB,IACHnB,KAAaW,EAAQ,KAAK,CAACS,MAAMA,EAAE,KAAK,OAAOpB,CAAS,KAAKA,KAC7DD,KAAYY,EAAQ,KAAK,CAACS,MAAMA,EAAE,KAAK,OAAOrB,CAAQ,KAAKA,OAC5DsB,IAAAV,EAAQ,CAAC,MAAT,gBAAAU,EAAY,KAAK,KAEbC,IAAY,CAACd,MAA2B;;AAC5C,IAAKA,MACLP,EAAaO,CAAE,IACfa,IAAAnB,EAAS,QAAQ,IAAIM,CAAE,MAAvB,QAAAa,EAA0B;AAAA,EAC5B,GAEME,IAAgB,CAAC,GAAiCC,MAAmB;;AACzE,UAAM,EAAE,MAAAP,MAASO,GACXC,IAAc,CAAC,CAACR,EAAK,UACrBS,IAAS/B,EAAS,IAAIsB,EAAK,EAAE,GAC7BU,IAAQhB,EAAQ,UAAU,CAACS,MAAMA,EAAE,KAAK,OAAOH,EAAK,EAAE;AAE5D,YAAQ,EAAE,KAAA;AAAA,MACR,KAAK;AACH,UAAE,eAAA,GACFK,GAAUD,IAAAV,EAAQgB,IAAQ,CAAC,MAAjB,gBAAAN,EAAoB,KAAK,EAAE;AACrC;AAAA,MACF,KAAK;AACH,UAAE,eAAA,GACFC,GAAUM,IAAAjB,EAAQgB,IAAQ,CAAC,MAAjB,gBAAAC,EAAoB,KAAK,EAAE;AACrC;AAAA,MACF,KAAK;AACH,UAAE,eAAA,GACEH,KAAe,CAACC,IAAQnB,EAAQU,EAAK,IAAI,EAAI,IACxCQ,KAAeC,KAAQJ,GAAUO,IAAAZ,EAAK,SAAU,CAAC,MAAhB,gBAAAY,EAAmB,EAAE;AAC/D;AAAA,MACF,KAAK;AACH,UAAE,eAAA,GACEJ,KAAeC,IAAQnB,EAAQU,EAAK,IAAI,EAAK,IACxCO,EAAK,YAAUF,EAAUE,EAAK,QAAQ;AAC/C;AAAA,MACF,KAAK;AACH,UAAE,eAAA,GACFF,GAAUQ,IAAAnB,EAAQ,CAAC,MAAT,gBAAAmB,EAAY,KAAK,EAAE;AAC7B;AAAA,MACF,KAAK;AACH,UAAE,eAAA,GACFR,GAAUS,IAAApB,EAAQA,EAAQ,SAAS,CAAC,MAA1B,gBAAAoB,EAA6B,KAAK,EAAE;AAC9C;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,UAAE,eAAA,GACFrB,EAAOO,EAAK,EAAE,GACVQ,KAAalB,EAAQU,EAAK,IAAI,CAACS,CAAM;AACzC;AAAA,MACF;AACE;AAAA,IAAA;AAEJ,MAAE,gBAAA;AAAA,EACJ;AAEA,WAASM,EACPf,GACAF,GACAkB,GACAC,GACAlB,GACW;AACX,UAAMS,IAAc,CAAC,CAACR,EAAK,UACrBS,IAAS/B,EAAS,IAAIsB,EAAK,EAAE,GAC7BkB,IAAapC,MAAakB,EAAK,IAC/BmB,IAAcX,IAAeC,IAASW,KAAaC,KAAUC,IAC7Df,IAAiB,EAAE,MAAAP,GAAgC,UAAAD,EAAA;AAEzD,WACE,gBAAAwB;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,KAAK,CAACC,MAAO;AACX,UAAIA,IAAIvC,EAAS,QAAQ,IAAIe,EAAK,IAAIwB,CAAE,IACnCvC,EAAS,QAAQ,OAAOe,EAAK,EAAE;AAAA,QACtC;AAAA,QACA,MAAK;AAAA,QACL,iBAAeQ,IAAcC,IAAS;AAAA,QACtC,iBAAeS;AAAA,QACf,cAAYpB;AAAA,QACZ,gBAAckB;AAAA,QACd,iBAAeC;AAAA,QACf,UAAUf,MAAcF,EAAK,KAAK,IAAI;AAAA,QACtC,SAAS,CAACyB,MAAM;AACd,UAAIA,EAAE,WAAWA,EAAE,iBAAezC,EAAagB,EAAK,EAAE;AAAA,QACxD;AAAA,QACA,WAAW,CAACyB,MAAM;AAChB,UAAIA,EAAE,WAAWA,EAAE,iBAAenB,EAAcmB,GAAGlB,CAAI;AAAA,QACzD;AAAA,QACA,SAAS,CAACkB,MAAM;AACd,UAAAA,EAAE,gBAAA,GACFzC,EAAagB,EAAK,EAAE,GACpBP,EAAOO,EAAK,EAAE,GACVQ,KAAalB,EAAQU,EAAK,IAAI,CAACS,CAAM;AAAA,QAC3C;AAAA,QACA,WAAU;AAAA,QAEV,UAAA;AAAA,UAAA,gBAAAc;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,OAAO,EAAE,aAAa,GAAGzB,IAAQ,IAAI,GAAG,MAAA;AAAA,cACxC,WAAW4B;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA;AAAA,gBACAR,KAAc;AAAA,cAAA;AAAA,cAGf,UAAA;AAAA,gBAAAV,IACC,gBAAAmB;AAAA,kBAACC;AAAA,kBAAA;AAAA,oBACC,WAAWF;AAAA,sBACT;AAAA,sBACAjB,KAAU;AAAA,oBAAA;AAAA,oBAEZ,eAAW;AAAA,kBAAA;AAAA,gBAAA,IAGb,gBAAAkB,EAAC,QAAA,EAAK,WAAU,kBAAiB,eAAW,IAAC;AAAA,gBAE9C3B,EAAK,QACJ,gBAAA2B,EAACR,KAAY,WAAU,yCAAwC,eAAW,IAAC;AAAA,gBAE7E,gBAAAQ;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,WAAU;AAAA,oBACV,OAAO,OAAO3B,EAAK,SAAU,WAAWA,EAAK,QAAQ;AAAA,oBAEpD,UAAAA,EAAK;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACR;AAAA,YAAA;AAAA,UAAA;AAAA,UAEDQ,KAAeC,KACd,gBAAAkB,EAAC,MAAA,EAAG,MAAK,SAAQ,WAAU,UACxB,UAAA3B,EAAK,SAAU;AAAA,YAAI,CAAC6B,GAAG5B,MACtBc,EAAWc,GAAG/B,IAAQ,GAAGE,EAAK,SAAU,QAAQC,IAAI,GAAGD,EAAK,EAAE;AAAA,UAAA,EAChE,CACF;AAAA,QAAA;AAAA,MAAA;AAAA,MA7DGA,EAAK;AAAA,IAAA;AAAA,EAiEhB;AAEA,SACE,gBAAA2B;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAArE;AAAA,MACA,MAAK;AAAA,MACL,cAAYW,MAAcC,IAAiB,SAAYG,EAAQ,KAAK;AAAA,MACpE,mBAAiBH;AAAA,MACjB,WAAWwD,EAAG,yBAAyB1D,CAAS;AAAA,MAC/C,GAAGG;AAAA,MAEH,UAAAZ,EAAK,IAAI,CAACuE,GAAG7B,MAAMc,EAAWe,GAAG,GAAGvE,EAAK,QAAQ0C,IAAI,GAAG,IAAI,CAAC;AAAA,IAAA;AAAA,EAAA;AAGpE,CAAC;AACD9C,GAAK,cAAc;"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Returns `value` after it has stopped changing for `delay` ms. Use for
3
+ * search inputs that drive a query (URL or fetch) without firing per keystroke.
4
+ *
5
+ * @example
6
+ * const [q, setQ] = useState('');
7
+ * const debouncedQ = useDebounce(q); // 300ms
8
+ * useEffect(() => { fetchRows(debouncedQ); }, [debouncedQ]);
9
+ */
10
+ export declare function useDebounce<T>(value: T, delay?: number): T;
11
+ /**
12
+ * Stable callback that runs `fn` once `delay` ms have passed since the last
13
+ * call. Always invokes the latest `fn`; pending calls are dropped on unmount.
14
+ *
15
+ * @example
16
+ * const save = useDebouncedCallback((draft: string) => api.save(draft), 500);
17
+ * <Textarea onChange={(e) => save(e.target.value)} />
18
+ */
19
+ export declare function useDebouncedCallback<A extends unknown[]>(fn: (...args: A) => void, delay?: number): (...args: A) => void;
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ import { useState as i, useEffect as c, useRef as o, useCallback as s } from "react";
3
+ function w(t, n = 300) {
4
+ const [r, e] = i(t);
5
+ return c(() => {
6
+ if (n <= 0) {
7
+ e(t);
8
+ return;
9
+ }
10
+ const u = window.setTimeout(() => e(t), n);
11
+ return () => window.clearTimeout(u);
12
+ }, [t, n]), r;
13
+ }
14
+ function f(t, n = 300) {
15
+ const r = o(t), e = o(null);
16
+ return c(() => {
17
+ r.current = t;
18
+ }, [t]), c(
19
+ () => () => {
20
+ e.current !== null && window.clearTimeout(e.current);
21
+ },
22
+ []
23
+ ), s(
24
+ (...u) => {
25
+ e.current !== null && window.clearTimeout(e.current), e.current = window.setTimeout(() => {
26
+ e.current = null, r.current(...u);
27
+ }, n);
28
+ },
29
+ [n]
30
+ );
31
+ }
32
+ export {
33
+ w as useDebounce,
34
+ f as useDebouncedCallback
35
+ };
36
+ //# sourceMappingURL=use-debounce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-debounce.js","sources":["../../src/hooks/use-debounce.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\n/**\n * Returns `value` after it has stopped changing for `delay` ms. Use for\n * search inputs that drive a query (URL or fetch) without firing per keystroke.\n *\n * @example\n * const [q, setQ] = useState('');\n * const debouncedQ = useDebounce(q); // 300ms\n * useEffect(() => { fetchRows(debouncedQ); }, [debouncedQ]);\n */\nexport function useDebounce<T>(value: T, delay = 300): T {\n const [debounced, setDebounced] = useState(value);\n useEffect(() => {\n if (delay <= 0) {\n setDebounced(value);\n return;\n }\n const t = window.setTimeout(() => setDebounced(value), delay);\n return () => window.clearTimeout(t);\n }, [value, delay]);\n return debounced;\n}\n\n/**\n * Stable callback that runs `fn` once `delay` ms have passed since the last\n * call. Always invokes the latest `fn`; pending calls are dropped on unmount.\n *\n * @example\n * const save = useDebouncedCallback((draft: string) => api.save(draft), 500);\n * <Textarea onChange={(e) => save(e.target.value)} />\n */\nexport function useDebouncedCallback<A extends unknown[]>(\n fn: (...args: A) => void,\n delay = 300,\n): (...args: A) => void {\n const fnRef = useRef(fn);\n const timer = useRef<number | null>(null);\n useEffect(() => {\n fnRef.current = fn;\n }, [fn]);\n useEffect(\n () => () => {\n if (timer.current !== null) window.clearTimeout(timer.current);\n },\n [],\n );\n return useCallback(\n (...args: A) => {\n if (timer.current !== null) window.clearTimeout(timer.current);\n timer.current = window.setTimeout(() => {\n timer.current = null;\n fnRef.current(...args);\n }, delay);\n },\n [delay],\n );\n}\n"],"names":["useDebounce","value","delay","debounced","setDebounced","useState","useEffect","t","useDebouncedCallback","fn","fnRef","useRef","timer","useCallback","args"],"mappings":";;AAaO,SAASA,EAAeC,GAAUC,IAAQ,KAAQ;AACvD,QAAM,CAACC,GAAWC,CAAY,IAAIC,EAASJ,CAAK;AAChD,SAAAK,EAAU,MAAM;AACd,QAAIJ,KAAS,GAAG;AACd,MAAAE,EAAaH,CAAK;AAClB;AAAA,IACF;AACA,UAAMM,IAAI,OAAO,WAAW,MAAMH,EAAaH,CAAK,GAAGC,CAAK;AAC5D,WAAO,MAAM,OAAO,aAAaK,CAAC;AAAA,EACpC,GAAG,CAACN,GAAOC,CAAK,CAAC,GACVC;AACT;AAUO,SAASK,EACdC,GACAP,IAAQ,KACc;AACtB,QAAMQ,IAAQC,EAAOF,CAAE,GACjBG,IAAQD,EAAsB,IAAI;AACxC,SAAAL,EAAU,MAAM;AACd,IAAAI,EAAM,UAAUD;AAAA,EAClB,GAAG,CAACA,CAAE,CAAC,GACPH;AAAA,IACE,MAAM,MAAM;AACV,MAAIM,EAAM,YAAY,QAAM,OAAO,aAAaA,EAAM,OAAO;AAAA,IAC/D;AAAA,IACA,CAAA;AAAA,EAAC,GAEIC;AAAA,IACL,IAAIC,MAAY;AACd,MAAIF,EAAM,YAAY,QAAM,OAAO,aAAaA,EAAM,OAAO,GAC7DA,EAAM,UAAU,OAAO,WAAW,MAAM;AACtC,QAAAA,EAAM,UAAU,MAChBF,EAAM,QAAQ,GAAGI,CAAI;AAAA,MACvB,GAAGZ,CAAK;AAAA,IACV;AAAA,IACA,CAACA,CAAK;AAAA,EAAA;AAEV;"}
@@ -0,0 +1,21 @@
1
+ export interface DelayedLoadingOptions {
2
+ /**
3
+ * Once `true`, the value holds for at least this many ms even if `ms`
4
+ * changes, so a placeholder that did appear never flickers away. Default 500.
5
+ */
6
+ minVisible?: number;
7
+ }
8
+ /**
9
+ * Returns `false` until `ms` has elapsed, then `true`. Gate skeletons and
10
+ * spinners behind it so sub-300ms loads never flash a placeholder.
11
+ * `ms <= 0` returns `true` immediately (and synchronously on first render).
12
+ *
13
+ * Once shown, the value stays `true` for `minVisible` ms (default 500). Keep
14
+ * the gated component mounted for that window on the consumer side too —
15
+ * unmounting it early defeats the hold.
16
+ *
17
+ * @example
18
+ * const showSkeleton = useDelayedLoading(300);
19
+ * if (isLoading) return showSkeleton ? <Skeleton /> : null;
20
+ */
21
+ export declare function useDelayedLoading(ms: number, { minVisible }?: DelayedLoadingOptions): boolean;
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { useState as f, useRef as i, useEffect as l } from "react";
3
+ function d(t, { minVisible: u = 500 } = {}) {
4
+ const [a, n] = f(t <= 0), e = i(t <= 0 ? Date.now() : null);
5
+ return l(() => {
6
+ const c = () => {
7
+ e.current = Date.now(), n(!0);
8
+ };
9
+ if (t <= 0) {
10
+ e.current ?? (e.current = Date.now()), n(!0);
11
+ return;
12
+ }
13
+ const r = e.current === null ? 0 : Math.max(0, u - (Date.now() - e.current)), o = [];
14
+ return r > 0 ? o.push(window.setTimeout(() => n(!1), r)) : n(!1), o.push(window.setTimeout(c, r + t)), () => o.forEach((s) => window.clearTimeout(s));
15
+ }, [t, u]), a;
16
+ }
17
+ export {
18
+ d as useDelayedLoading
19
+ };
20
+ //# sourceMappingURL=use-delayed-loading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-delayed-loading.js","sources":["../../src/hooks/use-delayed-loading.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nexport interface DelayedLoadingOptions {\n /**\n * Once `true`, the value holds for at least this many ms even if `ms`\n * changes, so a placeholder that did appear never flickers away. Default 500.\n */\n minVisible?: number;\n}\n\n/**\n * Returns `false` until `ms` has elapsed, then `true`. Gate skeletons and\n * spinners behind it so sub-300ms loads never flash a placeholder.\n * `ms <= 0` returns `true` immediately (and synchronously on first render).\n *\n * Once shown, the value stays `true` for `minVisible` ms (default 500). Keep\n * the gated component mounted for that window on the consumer side too —\n * unmounting it early defeats the hold.\n *\n * @example\n * const showSkeleton = useDelayedLoading(300);\n * if (isLoading) return showSkeleton ? <Skeleton /> : null;\n */\nexport function useDelayedLoading(\n ms: number,\n { minVisible = 500 }: DelayedLoadingOptions = {},\n): boolean {\n const [ready, setReady] = useState(ms <= 0);\n const shownAt = useRef<number | null>(ms <= 0 ? Date.now() : null);\n\n useEffect(() => {\n const show = () => {\n shownAt.current = Date.now();\n setReady(true);\n };\n if (ms <= 0) {\n shownAt.current ??= Date.now();\n setReady(true);\n return;\n }\n // Respect the minimum-visible window before hiding again.\n const hold =\n shownAt.current === null ? 0 : Math.max(0, minVisible - (Date.now() - shownAt.current));\n const timers: number[] = [];\n if (hold > 0) {\n timers.push(window.setTimeout(() => setReady(false), hold));\n } else {\n setReady(false);\n }\n timers.push(window.setTimeout(show, hold + ms));\n return () => timers.forEach((t) => window.clearTimeout(t));\n }, [ms, minVisible]);\n\n return ready;\n}\n"],"names":["useDelayedLoading","ms","minVisible","ready","setReady","useState","shownAt","useRef","useEffect","show","hold","timers","t"],"mappings":";;AAyBO,SAASA,EACdC,GACA,EAAE,YAAAC,IAAa,IAAA,IAA+B,CAAA,GACrC;AACT,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAASJ,KAAM,CAAC,GACpCK,IAAUC,EAAsBN,KAAM,IAAI,KAAK,IAAA,IAAQ,IAAI;AAEjE,SAAAO,EAAU,MAAM;AACd,UAAMC,IAAO,MAAM;AACjB,MAAAH,EAAQ,UAAU,KAAK,IAAA,GACvBF,EAAS,EAAI;AAAA,IACf;AACA,QAAIH,KAAM,GAAG;AACX,MAAAK,EAAQ,YAARA,EAAQ,UAAY,KAAK,IAAA,IACzBF,EAAS,EAAI;AACb;AAAA,IACF;AAEA,UAAMM,IACJJ,EAAQ,YAAY,OAAO,IAAI,KAAK,IAAI,GAAGJ,KAAc,KAAK,IAAA,IAAQI,EAAQ,QAAQ,GAClFK,IAAmB,CAAA;AACzB,WAAID,IAAO,IACTC,EAAO,KAAK,OAAO,WAAW,MAAMP,EAAS,EAAK,GAAGM,CAAI,CAAC,IAE1DN,EAAS,EAAK,GAEhBO,EAAO,KAAK,OAAO,WAAWF,GAAMC,IAAOT,CAAE,CAAC,GACvC,MAAMU,EAAO,QAAQ,CAACC,MAAM,OAAO,aAAaA,CAAC,CAAC;AAAA,EAC3D,GAAG,CAACX,GAAIC,CAAU,CAAC,GAEZC;AACT;"}
@@ -1,7 +1,7 @@
1
1
  export interface FieldIds {
2
2
  /** Id for the control itself (`<input id>` / `<label htmlFor>`). */
3
3
  fieldId: string;
4
- /** Id for the description / helper text element. */
4
+ /** Id for the description / helper text element (`<fieldId>-desc`). */
5
5
  helperId: string;
6
6
  /** Id for the validation message element. */
7
7
  errorId: string;
@@ -25,5 +25,6 @@ export interface FieldIds {
25
25
  export declare function useFieldIds(id?: string, present?: {
26
26
  hasHelper?: boolean;
27
27
  hasError?: boolean;
28
+ /** Consumer-supplied `aria-describedby` ids, appended after the helper / error id. */
28
29
  extra?: string;
29
30
  }): FieldIds;
@@ -1,18 +1,18 @@
1
1
  "use client";
2
2
  import { useId as n } from "react";
3
- function c(s, r = {}) {
4
- const i = n(), e = s ?? i, o = `${e}-helper`, t = `${e}-error`, d = [
5
- r.hasError ? t : r.hasHelper ? o : void 0,
6
- r.extra
3
+ function l(s, o = {}) {
4
+ const i = n(), r = s ?? i, e = `${r}-desc`, d = `${r}-error`, t = [
5
+ o.hasError ? d : o.hasHelper ? e : void 0,
6
+ o.extra
7
7
  ].filter(Boolean);
8
8
  return {
9
- fieldId: e,
10
- helperId: o,
11
- errorId: t,
12
- describedBy: d.length ? d.join(" ") : void 0
9
+ fieldId: r,
10
+ helperId: e,
11
+ errorId: d,
12
+ describedBy: t.length ? t.join(" ") : void 0
13
13
  };
14
14
  }
15
15
  export {
16
- c as useFieldIds
16
+ l as useFieldIds
17
17
  };
18
18
  //# sourceMappingURL=use-field-ids.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-field-ids.js","sources":["../../src/hooks/use-field-ids.ts"],"sourcesContent":["'use client';\n\nimport { useId } from 'react';\n\nexport interface FieldIds {\n /** Id for the control itself (`<input id>` / `<label htmlFor>`). */\n fieldId: string;\n /** Id for the description / helper text element. */\n helperId: string;\n /** Id for the validation message element. */\n errorId: string;\n /**\n * `aria-describedby` value computed from what actually renders — the error\n * wins over the helper; `undefined` when neither is present.\n */\n describedBy: string | undefined;\n}\n\n/**\n * Shared id wiring for form fields: one stable id for the control plus\n * derived helper / error ids, and an `aria-describedby` that only references\n * elements that exist. Pass a consumer `id` to keep `<label htmlFor>` in sync.\n *\n * @example\n * const { fieldId, helperId, errorId, describedBy } = useFieldIds(id, {\n * hasHelper: Boolean(helperText),\n * hasError: Boolean(error),\n * });\n */\nexport function useFieldIds(\n id?: string,\n present: { hasHelper?: boolean; hasError?: boolean; extra?: string } = {},\n): FieldIds {\n const autoId = useId();\n const fieldId = id ?? autoId;\n const helperId = `${fieldId}-helper`;\n const errorId = `${fieldId}-error`;\n const parts = [\n present.hasError ? errorId : present.hasHelper ? helperId : undefined,\n present.extra,\n ].filter(Boolean);\n return {\n fieldId,\n helperId,\n errorId,\n describedBy: parts.length ? parts.join(' ') : undefined,\n };\n}\n"],"names":["useFieldIds","id","present","autoId","useId","fieldId","helperId","errorId","parts"],"mappings":";;AA6BO,SAASA,EACdC,GACAC,IAAuE,IAC7D;AACV,QAAMC,IAASC,EAAA,GACTC,IAAUJ,KAAME,GAChBG,IAAW,GAAGD,CAAO,WACrBE,IAAU,GAAGF,CAAO,UACpBG,IAAQ;AAAA,IACZN,EAAQ,WAAWK,IAAUL,EAAQ,YAAYI,IAAW;AAAA,IAC5DJ,EAAQ;AAAA,EAAA,EACR,OAAO,OAAO;AAChB,SAAO;AAAA,IACL,SAAAG;AAAA,IACA,UAAAC;AAAA,IACA,SAAAC;AAAA,IACA,aAAaC,EAAM,SAASA,EAAM,KAAK,GAAG,IAAI;AAAA,EAAA;AAElD;"}
1
+ {"version":3,"file":"use-field-ids.js","sources":["../../src/hooks/use-field-ids.ts"],"sourcesContent":["'use client';\n\nimport { useId } from 'react';\n\nexport interface FieldIds {\n /** Id for the control itself (`<input id>` / `<label htmlFor>`). */\n fieldId: string;\n /** Id for the description / helper text element (`<fieldId>-desc`). */\n helperId: string;\n /** Id for the validation message element. */\n errorId: string;\n /**\n * `aria-describedby` value computed from what actually renders — the error\n * wins over the helper; `undefined` when neither is present.\n */\n describedBy: string | undefined;\n}\n\n/**\n * Shared id wiring for form fields: one stable id for the control plus\n * derived helper / error ids, and an `aria-describedby` that only references\n * elements that exist. Pass a consumer `id` to keep `<label htmlFor>` in sync.\n *\n * @example\n * const { fieldId, helperId, errorId, describedBy } = useFieldIds(id, {\n * hasHelper: Boolean(helperText),\n * hasError: Boolean(error),\n * });\n */\nexport function useFieldIds(\n id?: string,\n present: {\n hasHelper?: boolean;\n hasError?: boolean;\n /** Consumer-supplied `aria-describedby` ids, appended after the helper / error id. */\n extra?: string;\n } = {},\n): FieldIds {\n const autoId = useId();\n const fieldId = id ?? autoId;\n const helperId = `${fieldId}-desc`;\n const errorId = `${fieldId}-error`;\n const parts = [\n present.hasError ? errorId : present.hasHelper ? helperId : undefined,\n present.extra,\n ].filter(Boolean);\n return {\n fieldId,\n helperId,\n errorId,\n describedBy: parts.length ? parts.join(' ') : undefined,\n };\n}\n"],"names":["useFieldIds","id","present","autoId","useId","fieldId","helperId","errorId","parts"],"mappings":";;AA6BO,SAASA,EACdC,GACAC,IAKI,IACM;AACV,QAAMC,IAASC,EAAA,GACTC,IAAUJ,KAAME,GAChBG,IAAW,GAAGD,CAAO,SACrBE,IAAU,GAAGF,CAAO,UACpBG,IAAQ;AAAA,IACZN,EAAQ,WAAWK,IAAUL,EAAQ,YAAYI,IAAW;AAAA,IAC5DJ,EAAQ;AAAA,EAAA,EACR,OAAO,OAAO;AAChB,SAAO;AAAA,IACL,SAAAG;AAAA,IACA,UAAAC;AAAA,IACA,SAAAC;AAAA,IACA,aAAaC,EAAM,SAASA,EAAM,KAAK,GAAG,IAAI;AAAA,EAAA;AAElD;"}
@@ -1,5 +1,7 @@
1
1
  /**
2
- * Subscribe to a CSS media query. SSR-safe returns `false` on the server.
2
+ * Subscribe to a CSS media query. SSR- and hydration-safe: the server snapshot
3
+ * is always `false`, and the client re-renders with the real value after
4
+ * hydration instead of producing a markup mismatch.
3
5
  *
4
6
  * @example
5
7
  * const isDesktop = useMediaQuery('(min-width: 1024px)');
@@ -1,18 +1,22 @@
1
1
  "use client";
2
- import { useState as s, useEffect as c } from "react";
3
- function d(e) {
4
- const [i, n] = s(() => typeof window > "u" ? !1 : window.matchMedia(e).matches);
5
- return c(() => {
6
- if (typeof window > "u") return;
7
- const t = window.matchMedia(e), r = (o) => n(o.matches);
8
- return n(t.matches), t.addEventListener("change", r), () => t.removeEventListener("change", r);
9
- }, [e]), i;
2
+ import { useCallback as o, useSyncExternalStore as d } from "react";
3
+ function r(e) {
4
+ const i = o(
5
+ (n) => {
6
+ if (typeof window > "u" || !window.matchMedia) return () => {
7
+ };
8
+ const t = window.matchMedia(e);
9
+ return t.addEventListener("change", n), () => t.removeEventListener("change", n);
10
+ },
11
+ [e]
12
+ );
13
+ return d(i, () => typeof window < "u" && window.matchMedia ? window.matchMedia(e).matches : !1, () => !1);
10
14
  }
11
- function a() {
12
- return d("(prefers-reduced-motion: reduce)");
15
+ function c() {
16
+ return r("(prefers-reduced-motion: reduce)");
13
17
  }
14
18
  export {
15
- d as useMediaQuery,
16
- a as usePrefersReducedMotion
19
+ r as useMediaQuery,
20
+ c as usePrefersReducedMotion
17
21
  };
18
22
  //# sourceMappingURL=use-media-query.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-media-query.js","sources":["../../src/hooks/use-media-query.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\n/**\n * Subscribe to a CSS media query. SSR-safe returns `false` on the server.\n *\n * @example\n * const isDesktop = useMediaQuery('(min-width: 1024px)');\n */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState(() => {\n if (typeof window === 'undefined') return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const list = window.matchMedia(query);\n const onChange = (event: MediaQueryListEvent) => setMatches(event.matches);\n setMatches(list.matches);\n list.addEventListener('change', onChange);\n return () => list.removeEventListener('change', onChange);\n }, [query]);\n\n return matches;\n}\n\n/** Convenience: respect `prefers-reduced-motion`. */\nexport function usePrefersReducedMotion(): boolean {\n return useMediaQuery('(prefers-reduced-motion: reduce)');\n}\n"],"names":["useMediaQuery","query","matches","setMatches","useState","useEffect","list","onChange","event","usePrefersReducedMotion"],"mappings":";;AAUO,SAASA,EAAcC,GAAwB;AACpD,QAAM,CAACC,GAASC,CAAU,IAAIC,EAAS,MACjC,OAAO,SAAW,MAAoB,KACnC,OAAO,WAAWH,CAAK,EAAE,OACjC;AAED,SAAAI,EAAU,MAAM;AACd,QAAI,OAAO,SAAW,IAAa;AACnC,UAAMC,IAAO,OAAO,WAAWL,CAAK,GAC9BM,IAAW,CAACC,MAA+BL,EAAWK,EAAM,OAAO;AACzE,WAAAL,EAAWG,EAAK,OAAO,GACvBA,EAAK,iBAAiB,UAAUC,CAAQ,GACjC,MAAMD,EAAK,oBAAoB,UAAUC,CAAQ;AAAA,EAC1D,GAAG,CAACN,CAAK,CAAC,GAEHC;AACT;AAGO,SAASO,IAAmC;AACjD,SAAOT,EAAc,kCAAkC;AACzD;"}
1
+ {"version":3,"file":"use-media-query.js","sources":["../../src/hooks/use-media-query.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useSyncExternalStore } from 'react';\n\n/**\n * Subscribe to a CSS media query. SSR- and hydration-safe: the server snapshot\n * is always `false`, and the client re-renders with the real value after\n * hydration instead of producing a markup mismatch.\n *\n * @example\n * const isDesktop = useMediaQuery('(min-width: 1024px)');\n */\nexport function useMediaQuery(query: string): boolean {\n const subscribe = useCallback(\n (onChange: () => void) => {\n if (typeof window === 'undefined' || !window.matchMedia) return () => {};\n const list = window.matchMedia(query);\n list.addEventListener('change', onChange);\n return () => list.removeEventListener('change', onChange);\n },\n [query],\n );\n const getSnapshot = () =>\n typeof window !== 'undefined' && window.matchMedia ? window.matchMedia(query).matches : false;\n return useSyncExternalStore(subscribe, getSnapshot, () => false);\n}\n\n/** Convenience: respect `prefers-reduced-motion`. */\nexport function usePrefersReducedMotion(): boolean {\n return useMediaQuery('(prefers-reduced-motion: reduce)');\n}\n"],"names":["useMediaQuery","query","subscribe","useCallback","onChange","list","useSyncExternalStore","usePrefersReducedMotion"],"mappings":";;AAYO,SAASA,EAAcC,GAAwB;AACpD,QAAMC,IAAYC;AAAA,IAChB,CAACC,MAAyB;AACxB,UAAI,OAAO,SAAW,OAAe,CAAC,OAAO,mBAAmB,MAAM;AAAA,MAAC;AACvE,YAAMC,IAAO,OAAO,WAAWJ,CAAK;AACpC,aAAAI,EAAK,iBAAiB,UAAUD,CAAQ,GACjC,MAAMC,EAAK,oBAAoB,UAAUD,CAAQ;AAAA,IAC1D;AAAA,IACA,CAACH,CAAK;AAAA,EAAA;AAIR,SAAOK,EAAqBJ,GAFR,MAClB,OAAO,SAAW,OAAe,OAAO,aAAa,OAAO,WAAWD,CAAK,EAAE,UAAU,IACtC,MAAM,EAAK;AACjE;AAGO,SAASM,IAAmC;AACjD,SAAOP,EAAc,kCAAkC;AACzD;"}
@@ -0,0 +1,13 @@
1
+ /** True on macOS / iOS / iPadOS, where the command modifier is ⌘. */
2
+ export declare function isApplePlatform(): boolean;
3
+ export interface ModifierKey {
4
+ /** Glyph for `<Kbd>`: `⌘` on Apple platforms, `Ctrl` elsewhere. */
5
+ symbol: '⌘' | 'Ctrl';
6
+ /** Spoken/long form for aria-labels and tooltips: `Cmd` / `Ctrl`. */
7
+ label: 'Cmd' | 'Ctrl';
8
+ }
9
+ /**
10
+ * Platform-correct command modifier for shortcut hints (⌘K vs Ctrl K).
11
+ * Defaults to Ctrl until mounted so server output is deterministic.
12
+ */
13
+ export declare function useModifierKey(): ModifierKey;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ import { useState as o, useEffect as r } from "react";
3
+ function n() {
4
+ var a;
5
+ if (typeof navigator > "u") return !1;
6
+ const e = navigator, t = ((a = e.userAgentData) == null ? void 0 : a.platform) ?? e.platform ?? "";
7
+ return /mac|iphone|ipad|ipod/i.test(t) || /Mac OS X/i.test(e.userAgent ?? "");
8
+ }
9
+ function s() {
10
+ const [e, t] = o(!1);
11
+ return r(() => {
12
+ t(n());
13
+ }, []), e ? { symbol: "⌘", label: "Cmd" } : { symbol: "Ctrl", label: "Ctrl" };
14
+ }
15
+ export {
16
+ n as isApplePlatform,
17
+ s as useModifierKey
18
+ };
19
+ //# sourceMappingURL=use-modifier-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-modifier-key.js","sources":["../../src/hooks/use-modifier-key.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\n/** True on macOS / iOS / iPadOS, where the command modifier is ⌘. */\nexport function isApplePlatform(): boolean {\n if (typeof navigator === 'undefined') return false;\n const nav = navigator as Navigator & { userAgentData?: { platform?: string } };\n const platform = nav.userAgentData?.platform ?? nav.platform ?? '';\n return /mac|iphone|ipad|ipod/i.test(platform) || /Mac OS X/i.test(nav.userAgent ?? '');\n}\n\nexport interface ModifierKey {\n /** Glyph for `<Kbd>`: `⌘` on Apple platforms, `Ctrl` elsewhere. */\n symbol: '⌘' | 'Ctrl';\n /** Spoken/long form for aria-labels and tooltips: `Cmd` / `Ctrl`. */\n label: 'Cmd' | 'Ctrl';\n}\n\n/**\n * Platform-correct command modifier for shortcut hints (⌘K vs Ctrl K).\n * Defaults to Ctrl until mounted so server output is deterministic.\n */\nexport function useModifierKey(): ModifierKey {\n const [apple, setApple] = useState(false);\n useEffect(() => {\n setApple(isApplePlatform());\n }, []);\n return apple ? { symbol: '⌘', label: 'Cmd' } : { symbol: 'Ctrl', label: 'Ctrl' };\n}\n"],"names":["isApplePlatform","nav","platform","_a","useModifierKey","apple","setApple","useState","useEffect"],"mappings":";;AAKO,SAASA,IAA2B;;AACzC,MAAI,OAAO,YAAc,IAAa,QAAO;AAC7C,QAAMC,IAAM,WACNC,MAAWC,IAAAF,EAAI,kBAAJ,gBAAAE,EAAmB,aAAYF,EAAI,YAAY;AAChE,SAAO,wBAAwB,KAAKC,CAAQ,KAAK,YAAY,KAAKD,EAAI,aAAa,EAAE;AACvF;AAaO,SAASG,IAA8B;AAC5C,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAAS,EAAK;AACxC,SAAAC,EAAU,MAAM;AACd,IAAAF,EAASN,GAAiB;AAAA,EAC5B,GAAG,CAAA,CAAE,GACEK,IAAQ,EAAE,QAAQ,KAAK,OAAO,MAAA,IAAU,EAAE,QAAQ,QAAQ,OAAO,OAAA;AAC1E;"}