gridsmith-ui 0.29.0 → 0.30.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 (103) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +5 -1
  3. package/dist/DropdownMenu-DqT7ZQ0D.d.ts +39 -0
  4. package/dist/charts.d.ts +5 -0
  5. package/dist/charts.js +5 -3
  6. package/dist/charts.js.map +1 -1
  7. package/dist/chat.js +5 -5
  8. package/dist/{chunk-EIWXVICU.js → chunk-6A5RHW2W.js} +3 -3
  9. package/dist/{chunk-EIWXVICU.js.map → chunk-6A5RHW2W.js.map} +1 -1
  10. package/dist/{chunk-XVB55M3N.js → chunk-72SDAX54.js} +2 -2
  11. package/dist/chunk-72SDAX54.js.map +1 -0
  12. package/dist/{chunk-OYNUU346.js → chunk-7QLXYRPD.js} +3 -12
  13. package/dist/chunk-7QLXYRPD.js.map +1 -0
  14. package/dist/chunk-F2BNJSON.js +1348 -0
  15. package/dist/chunk-F2BNJSON.js.map +1 -0
  16. package/dist/{chunk-GXMTS3YD.js → chunk-GQVBZ4GW.js} +54 -16
  17. package/dist/chunk-GQVBZ4GW.js.map +1 -0
  18. package/dist/{chunk-2ROVDVLZ.js → chunk-INGF2CPW.js} +4 -23
  19. package/dist/chunk-INGF2CPW.js.map +1 -0
  20. package/dist/{chunk-66JJCQU3.js → chunk-JTU7OBYJ.js} +5 -3
  21. package/dist/chunk-JTU7OBYJ.js.map +1 -0
  22. package/dist/{chunk-DQCHCLEJ.js → chunk-KTPGMJUK.js} +6 -6
  23. package/dist/{chunk-DQCHCLEJ.js.map → chunk-KTPGMJUK.js.map} +1 -1
  24. package/dist/{chunk-TJEV7YAY.js → chunk-L6HWACK7.js} +7 -7
  25. package/dist/chunk-L6HWACK7.js.map +1 -0
  26. package/dist/chunk-LJYSVZMI.js +25 -0
  27. package/dist/chunk-LJYSVZMI.js.map +1 -0
  28. package/dist/{chunk-YUTF2X6B.js → chunk-NI5542ZM.js} +42 -38
  29. package/dist/chunk-NI5542ZM.js.map +1 -0
  30. package/dist/chunk-NKUCS67T.js +13 -0
  31. package/dist/chunk-NKUCS67T.js.map +1 -0
  32. package/dist/{chunk-TRDXWHD2.js → chunk-NLXQ323J.js} +6 -6
  33. package/dist/chunk-NLXQ323J.js.map +1 -0
  34. package/dist/{chunk-ZI3OQDET.js → chunk-OIOII5L6.js} +93 -4
  35. package/dist/chunk-OIOII5L6.js.map +1 -0
  36. package/dist/{chunk-R4GXKH4U.js → chunk-Q36JVJX4.js} +52 -93
  37. package/dist/chunk-Q36JVJX4.js.map +1 -0
  38. package/dist/{chunk-FD4YED7O.js → chunk-RHWTMTU5.js} +3 -3
  39. package/dist/{chunk-FD4YED7O.js.map → chunk-RHWTMTU5.js.map} +1 -1
  40. package/dist/chunk-TYNDXMBV.js +105 -0
  41. package/dist/chunk-TYNDXMBV.js.map +1 -0
  42. package/dist/{chunk-OPFJEL3Z.js → chunk-UNS4TGGK.js} +110 -20
  43. package/dist/chunk-UNS4TGGK.js.map +1 -0
  44. package/dist/chunk-Z4MCD73B.js +316 -0
  45. package/dist/chunk-Z4MCD73B.js.map +1 -0
  46. package/dist/{chunk-CRQ6KCRD.js → chunk-ZHUVLBCC.js} +7 -4
  47. package/dist/chunk-ZHUVLBCC.js.map +1 -0
  48. package/dist/data-grid.d.ts +26 -7
  49. package/dist/data-grid.js +179 -57
  50. package/dist/data-grid.js.map +1 -1
  51. package/dist/editor-mentions.js +3 -3
  52. package/dist/editor.js +5 -5
  53. package/dist/{feather-FNBQ74RR.js → feather-XFFEFLTV.js} +5 -3
  54. package/dist/feather-XFFEFLTV.js.map +1 -0
  55. package/dist/{heroicons-DODCSYWD.js → heroicons-L6DOHBEA.js} +5 -3
  56. package/dist/heroicons-L6DOHBEA.js.map +1 -0
  57. package/dist/icons.d.ts +10 -1
  58. package/dist/icons.js +1 -1
  59. package/dist/index.d.ts +174 -50
  60. package/dist/index.js +810 -1731
  61. package/dist/index.js.map +1 -1
  62. package/dist/kanban.d.ts +26 -8
  63. package/dist/kanban.js +203 -79
  64. package/dist/kanban.js.map +1 -1
  65. package/dist/{lucide-ZWE3EPGP.js → lucide-JOORDTO6.js} +5 -3
  66. package/dist/lucide-JOORDTO6.js.map +1 -0
  67. package/dist/{phosphor-DIHPA4AJ.js → phosphor-TKGDYNGR.js} +5 -3
  68. package/dist/phosphor-TKGDYNGR.js.map +1 -0
  69. package/dist/resizable.js +12 -11
  70. package/dist/resizable.js.map +1 -1
  71. package/dist/roots.css +14 -8
  72. package/dist/{table-utils-BkjNEWts.d.ts → table-utils-BXkP-2Hx.d.ts} +8 -5
  73. package/dist/{tabler-D3VYTSGI.js → tabler-YGETLYRU.js} +5 -3
  74. package/dist/tabler-YGETLYRU.js.map +1 -0
  75. package/dist/tokens.css +14 -0
  76. package/dist/{use-pagination-BxcRlNQG.d.ts → use-pagination-QykGSREv.d.ts} +7 -0
  77. package/dist/utils.js.map +1 -1
  78. package/dist/virtualized.d.ts +1 -1
  79. package/dist/virtualized.js +10 -9
  80. package/dist/virtualized.js.map +1 -1
  81. package/dist-static/tokens.css +14 -0
  82. package/package.json +1 -1
  83. package/dist/chunk-2ROVDVLZ.js.map +0 -1
  84. package/dist/chunk-66JJCQU3.js.map +0 -1
  85. package/dist/chunk-BRAXJEEP.js +0 -117
  86. package/dist/chunk-BRAXJEEP.js.map +0 -1
  87. package/dist/chunk-CRQ6KCRD.js.map +0 -1
  88. package/dist/chunk-GXMTS3YD.js.map +0 -1
  89. package/dist/chunk-OPFJEL3Z.js.map +0 -1
  90. package/dist/chunk-OYNUU346.js.map +0 -1
  91. package/dist/chunk-QM4SJKWV.js +0 -51
  92. package/dist/chunk-QM4SJKWV.js.map +0 -1
  93. package/dist/chunk-R4GXKH4U.js.map +0 -1
  94. package/dist/chunk-TJEV7YAY.js.map +0 -1
  95. package/dist/chunk-TRDXWHD2.js.map +0 -1
  96. package/dist/chunk-XVB55M3N.js.map +0 -1
  97. package/dist/chunk-YUTF2X6B.js.map +0 -1
  98. package/dist/chunk-ZI3OQDET.js.map +0 -1
  99. package/dist/feather-FNBQ74RR.js.map +0 -1
  100. package/dist/heroicons-DODCSYWD.js.map +0 -1
  101. package/dist/lucide-ZWE3EPGP.js.map +0 -1
  102. package/dist/phosphor-DIHPA4AJ.js.map +0 -1
  103. package/dist/tabler-D3VYTSGI.js.map +0 -1
@@ -0,0 +1,316 @@
1
+ "use client";
2
+ import { triggerType } from './chunk-Q36JVJX4.js';
3
+ import { useBreakpoint } from './chunk-ZHUVLBCC.js';
4
+ import { usePopupFocus } from './chunk-OIOII5L6.js';
5
+ import { Slot } from './chunk-7Q46JXSK.js';
6
+ import { Icon } from './chunk-L6HWACK7.js';
7
+ import { usePosition } from './chunk-HP7WUZPK.js';
8
+ import { cn, GLASS_DROPDOWN, FOCUS_RING } from './chunk-JTU7OBYJ.js';
9
+ import { useState, useRef, useEffect, useLayoutEffect } from 'react';
10
+ import { createPortal } from 'react-dom';
11
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
12
+
13
+ function isDivider(entry) {
14
+ return "type" in entry && entry.type === "divider";
15
+ }
16
+ function isSubmenu(entry) {
17
+ return "items" in entry;
18
+ }
19
+ var stopsOf = (entries) => entries.reduce((acc, e, i) => {
20
+ if (!isDivider(e) && !isUnavailable(e)) acc.push(i);
21
+ return acc;
22
+ }, []);
23
+ function isUnavailable(entry) {
24
+ return !!entry.disabled || isSubmenu(entry) && stopsOf(entry.items).length === 0;
25
+ }
26
+ var SUBMENU_GRACE_MS = 300;
27
+ var ITEM = "flex items-center gap-ds-2 w-full text-left px-ds-3 py-ds-2 text-sm cursor-pointer transition-colors rounded-[var(--ds-radius-sm)]";
28
+ var itemLook = (entry, open = false) => cn(
29
+ ITEM,
30
+ FOCUS_RING,
31
+ isUnavailable(entry) ? "opacity-disabled cursor-not-allowed" : "variant" in entry && entry.variant === "destructive" ? "text-[var(--ds-status-error-text)] hover:bg-[var(--ds-status-error-bg)] focus-visible:bg-[var(--ds-status-error-bg)]" : cn("text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-emphasis)] focus-visible:bg-[var(--ds-bg-emphasis)]", open && "bg-[var(--ds-bg-emphasis)]")
32
+ );
33
+ var PANEL = cn(
34
+ // the items' outline has its 4 px of room at any density (Sprint 30, P3)
35
+ "fixed z-[var(--ds-z-popover)] min-w-[180px] p-[max(var(--ds-space-1),4px)]",
36
+ "bg-[var(--ds-bg-surface-elevated)]",
37
+ GLASS_DROPDOWN,
38
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
39
+ "rounded-[var(--ds-radius-md)] overflow-hidden"
40
+ );
41
+ var Divider = () => /* @__PURE__ */ jsx("div", { role: "separator", className: "my-ds-1 h-px bg-[var(--ds-border-default)]" });
42
+ function DropdownMenu({ trigger, items, align = "end", className, container }) {
43
+ const [open, setOpen] = useState(false);
44
+ const [focusedIdx, setFocusedIdx] = useState(-1);
45
+ const [sub, setSub] = useState(null);
46
+ const [subFocusedIdx, setSubFocusedIdx] = useState(-1);
47
+ const triggerRef = useRef(null);
48
+ const panelsRef = useRef(null);
49
+ const menuRef = useRef(null);
50
+ const subRef = useRef(null);
51
+ const subItemRef = useRef(null);
52
+ const isPhone = useBreakpoint("sm");
53
+ const graceTimer = useRef(null);
54
+ const cancelGrace = () => {
55
+ if (graceTimer.current) {
56
+ clearTimeout(graceTimer.current);
57
+ graceTimer.current = null;
58
+ }
59
+ };
60
+ const afterGrace = (then) => {
61
+ cancelGrace();
62
+ graceTimer.current = setTimeout(() => {
63
+ graceTimer.current = null;
64
+ then();
65
+ }, SUBMENU_GRACE_MS);
66
+ };
67
+ useEffect(() => {
68
+ if (!open) cancelGrace();
69
+ }, [open]);
70
+ useEffect(() => cancelGrace, []);
71
+ const pos = usePosition(triggerRef, menuRef, open, { side: "bottom", align, offset: 4, flip: true });
72
+ const [subPos, setSubPos] = useState({ top: 0, left: 0 });
73
+ useLayoutEffect(() => {
74
+ if (!open || sub === null || isPhone) return;
75
+ const place = () => {
76
+ const menu = menuRef.current;
77
+ const entry = subItemRef.current;
78
+ const panel = subRef.current;
79
+ if (!menu || !entry || !panel) return;
80
+ const m = menu.getBoundingClientRect();
81
+ const e = entry.getBoundingClientRect();
82
+ const p = panel.getBoundingClientRect();
83
+ const first = panel.querySelector("[data-sub-index]")?.getBoundingClientRect();
84
+ let left = m.right + 4;
85
+ if (left + p.width > window.innerWidth - 8) left = Math.max(8, m.left - 4 - p.width);
86
+ const top = Math.max(8, Math.min(e.top - (first ? first.top - p.top : 0), window.innerHeight - p.height - 8));
87
+ setSubPos((cur) => cur.top === top && cur.left === left ? cur : { top, left });
88
+ };
89
+ place();
90
+ window.addEventListener("resize", place);
91
+ window.addEventListener("scroll", place, true);
92
+ return () => {
93
+ window.removeEventListener("resize", place);
94
+ window.removeEventListener("scroll", place, true);
95
+ };
96
+ }, [open, sub, isPhone]);
97
+ const enabledIndices = stopsOf(items);
98
+ const subEntry = sub !== null && isSubmenu(items[sub]) ? items[sub] : null;
99
+ const subIndices = subEntry ? stopsOf(subEntry.items) : [];
100
+ const [prevOpen, setPrevOpen] = useState(open);
101
+ if (open !== prevOpen) {
102
+ setPrevOpen(open);
103
+ if (!open) {
104
+ setFocusedIdx(-1);
105
+ setSub(null);
106
+ setSubFocusedIdx(-1);
107
+ }
108
+ }
109
+ const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: panelsRef, close: () => setOpen(false), tabExits: "any" });
110
+ const closeSub = () => {
111
+ if (sub === null) return;
112
+ setFocusedIdx(sub);
113
+ setSub(null);
114
+ setSubFocusedIdx(-1);
115
+ };
116
+ const openSub = (i, focusFirst) => {
117
+ const entry = items[i];
118
+ if (!isSubmenu(entry) || isUnavailable(entry)) return;
119
+ cancelGrace();
120
+ setSub(i);
121
+ setFocusedIdx(i);
122
+ setSubFocusedIdx(focusFirst ? stopsOf(entry.items)[0] ?? -1 : -1);
123
+ };
124
+ useEffect(() => {
125
+ if (!open) return;
126
+ const onKeyDown = (e) => {
127
+ if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
128
+ e.preventDefault();
129
+ if (sub !== null) {
130
+ closeSub();
131
+ return;
132
+ }
133
+ returnFocus();
134
+ setOpen(false);
135
+ };
136
+ const onClick = (e) => {
137
+ const target = e.target;
138
+ if (triggerRef.current?.contains(target) || panelsRef.current?.contains(target)) return;
139
+ setOpen(false);
140
+ };
141
+ document.addEventListener("keydown", onKeyDown);
142
+ document.addEventListener("mousedown", onClick);
143
+ return () => {
144
+ document.removeEventListener("keydown", onKeyDown);
145
+ document.removeEventListener("mousedown", onClick);
146
+ };
147
+ });
148
+ useEffect(() => {
149
+ if (!open) return;
150
+ if (sub !== null && subFocusedIdx >= 0) panelsRef.current?.querySelector(`[data-sub-index="${subFocusedIdx}"]`)?.focus();
151
+ else if (sub !== null && isPhone) panelsRef.current?.querySelector("[data-sub-back]")?.focus();
152
+ else if (focusedIdx >= 0) menuRef.current?.querySelector(`[data-index="${focusedIdx}"]`)?.focus();
153
+ }, [open, focusedIdx, sub, subFocusedIdx, isPhone]);
154
+ const step = (stops, at, by) => stops[((stops.indexOf(at) + by) % stops.length + stops.length) % stops.length];
155
+ function handleMenuKeyDown(e) {
156
+ const inSub = sub !== null;
157
+ const stops = inSub ? subIndices : enabledIndices;
158
+ const at = inSub ? subFocusedIdx : focusedIdx;
159
+ const go = (i) => {
160
+ if (i === void 0) return;
161
+ if (inSub) setSubFocusedIdx(i);
162
+ else setFocusedIdx(i);
163
+ };
164
+ if (e.key === "ArrowDown") {
165
+ e.preventDefault();
166
+ go(at < 0 ? stops[0] : step(stops, at, 1));
167
+ } else if (e.key === "ArrowUp") {
168
+ e.preventDefault();
169
+ go(at < 0 ? stops[stops.length - 1] : step(stops, at, -1));
170
+ } else if (e.key === "Home") {
171
+ e.preventDefault();
172
+ go(stops[0]);
173
+ } else if (e.key === "End") {
174
+ e.preventDefault();
175
+ go(stops[stops.length - 1]);
176
+ } else if (e.key === "ArrowRight" && !inSub && focusedIdx >= 0 && isSubmenu(items[focusedIdx])) {
177
+ e.preventDefault();
178
+ openSub(focusedIdx, true);
179
+ } else if (e.key === "ArrowLeft" && inSub) {
180
+ e.preventDefault();
181
+ closeSub();
182
+ }
183
+ }
184
+ const choose = (item) => {
185
+ if (item.disabled) return;
186
+ returnFocus();
187
+ item.onClick();
188
+ setOpen(false);
189
+ };
190
+ const subItems = (entries) => entries.map((item, i) => isDivider(item) ? /* @__PURE__ */ jsx(Divider, {}, i) : /* @__PURE__ */ jsxs("button", { type: "button", "data-sub-index": i, role: "menuitem", disabled: item.disabled, tabIndex: subFocusedIdx === i ? 0 : -1, onFocus: () => setSubFocusedIdx(i), onClick: () => choose(item), className: itemLook(item), children: [
191
+ item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 16, className: "shrink-0" }),
192
+ /* @__PURE__ */ jsx("span", { children: item.label })
193
+ ] }, i));
194
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
195
+ /* @__PURE__ */ jsx(
196
+ Slot,
197
+ {
198
+ ...triggerType(trigger),
199
+ "aria-haspopup": "menu",
200
+ "aria-expanded": open,
201
+ ref: triggerRef,
202
+ onClick: () => {
203
+ setOpen(!open);
204
+ if (!open && enabledIndices.length) setFocusedIdx(enabledIndices[0]);
205
+ },
206
+ children: trigger
207
+ }
208
+ ),
209
+ open && createPortal(
210
+ /* @__PURE__ */ jsxs("div", { ref: panelsRef, className: "contents", children: [
211
+ /* @__PURE__ */ jsx(
212
+ "div",
213
+ {
214
+ ref: menuRef,
215
+ role: "menu",
216
+ "data-gs": "menu",
217
+ "aria-label": isPhone && subEntry ? subEntry.label : void 0,
218
+ onKeyDown: handleMenuKeyDown,
219
+ className: cn(PANEL, className),
220
+ style: { top: pos.top, left: pos.left },
221
+ onClick: (e) => e.stopPropagation(),
222
+ children: isPhone && subEntry ? (
223
+ // on a phone the submenu takes the menu's place: Back first, its entry's name on it
224
+ /* @__PURE__ */ jsxs(Fragment, { children: [
225
+ /* @__PURE__ */ jsxs("button", { type: "button", "data-sub-back": "", role: "menuitem", "aria-label": `Back from ${subEntry.label}`, tabIndex: -1, onClick: closeSub, className: cn(itemLook(subEntry), "text-[var(--ds-text-secondary)] font-[var(--ds-weight-heading)]"), children: [
226
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-left", size: 16, className: "shrink-0" }),
227
+ /* @__PURE__ */ jsx("span", { children: subEntry.label })
228
+ ] }),
229
+ /* @__PURE__ */ jsx(Divider, {}),
230
+ subItems(subEntry.items)
231
+ ] })
232
+ ) : items.map((item, i) => {
233
+ if (isDivider(item)) return /* @__PURE__ */ jsx(Divider, {}, i);
234
+ if (isSubmenu(item)) {
235
+ const isOpen = sub === i;
236
+ return /* @__PURE__ */ jsxs(
237
+ "button",
238
+ {
239
+ type: "button",
240
+ ref: isOpen ? (el) => {
241
+ subItemRef.current = el;
242
+ } : void 0,
243
+ "data-index": i,
244
+ role: "menuitem",
245
+ "aria-haspopup": "menu",
246
+ "aria-expanded": isOpen,
247
+ disabled: isUnavailable(item),
248
+ tabIndex: focusedIdx === i ? 0 : -1,
249
+ onFocus: () => setFocusedIdx(i),
250
+ onClick: () => openSub(i, true),
251
+ onMouseEnter: (e) => {
252
+ if (isPhone || e.buttons !== 0) return;
253
+ if (isOpen) cancelGrace();
254
+ else if (sub === null) openSub(i, false);
255
+ else afterGrace(() => openSub(i, false));
256
+ },
257
+ className: itemLook(item, isOpen),
258
+ children: [
259
+ item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 16, className: "shrink-0" }),
260
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: item.label }),
261
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14, className: "shrink-0 text-[var(--ds-text-muted)]" })
262
+ ]
263
+ },
264
+ i
265
+ );
266
+ }
267
+ return /* @__PURE__ */ jsxs(
268
+ "button",
269
+ {
270
+ type: "button",
271
+ "data-index": i,
272
+ role: "menuitem",
273
+ disabled: item.disabled,
274
+ tabIndex: focusedIdx === i ? 0 : -1,
275
+ onFocus: () => setFocusedIdx(i),
276
+ onClick: () => choose(item),
277
+ onMouseEnter: () => {
278
+ if (!isPhone && sub !== null) afterGrace(() => {
279
+ setSub(null);
280
+ setSubFocusedIdx(-1);
281
+ });
282
+ },
283
+ className: itemLook(item),
284
+ children: [
285
+ item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 16, className: "shrink-0" }),
286
+ /* @__PURE__ */ jsx("span", { children: item.label })
287
+ ]
288
+ },
289
+ i
290
+ );
291
+ })
292
+ }
293
+ ),
294
+ !isPhone && subEntry && /* @__PURE__ */ jsx(
295
+ "div",
296
+ {
297
+ ref: subRef,
298
+ role: "menu",
299
+ "aria-label": subEntry.label,
300
+ onKeyDown: handleMenuKeyDown,
301
+ className: PANEL,
302
+ style: { top: subPos.top, left: subPos.left },
303
+ onClick: (e) => e.stopPropagation(),
304
+ onMouseEnter: cancelGrace,
305
+ children: subItems(subEntry.items)
306
+ }
307
+ )
308
+ ] }),
309
+ container ?? document.body
310
+ )
311
+ ] });
312
+ }
313
+
314
+ export { DropdownMenu };
315
+ //# sourceMappingURL=chunk-Z4MCD73B.js.map
316
+ //# sourceMappingURL=chunk-Z4MCD73B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/components/DropdownMenu.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAoCA,SAAS,UAAU,KAAA,EAAwD;AACzE,EAAA,OAAO,MAAA,IAAU,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,SAAA;AAC3C;AACA,SAAS,UAAU,KAAA,EAAwD;AACzE,EAAA,OAAO,OAAA,IAAW,KAAA;AACpB;AAEA,IAAM,OAAA,GAAU,CAAC,OAAA,KAA2C,OAAA,CAAQ,OAAiB,CAAC,GAAA,EAAK,GAAG,CAAA,KAAM;AAClG,EAAA,IAAI,CAAC,SAAA,CAAU,CAAC,CAAA,IAAK,CAAC,cAAc,CAAC,CAAA,EAAG,GAAA,CAAI,IAAA,CAAK,CAAC,CAAA;AAClD,EAAA,OAAO,GAAA;AACT,CAAA,EAAG,EAAE,CAAA;AAEL,SAAS,cAAc,KAAA,EAAwD;AAC7E,EAAA,OAAO,CAAC,CAAC,KAAA,CAAM,QAAA,IAAa,SAAA,CAAU,KAAK,CAAA,IAAK,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAA,CAAE,MAAA,KAAW,CAAA;AAClF;AAKA,IAAM,gBAAA,GAAmB,GAAA;AAEzB,IAAM,IAAA,GAAO,oIAAA;AACb,IAAM,QAAA,GAAW,CAAC,KAAA,EAA+C,IAAA,GAAO,KAAA,KAAU,EAAA;AAAA,EAChF,IAAA;AAAA,EACA,UAAA;AAAA,EACA,aAAA,CAAc,KAAK,CAAA,GACf,qCAAA,GACA,SAAA,IAAa,KAAA,IAAS,KAAA,CAAM,OAAA,KAAY,aAAA,GACtC,sHAAA,GACA,EAAA,CAAG,yGAAA,EAA2G,QAAQ,4BAA4B;AAC1J,CAAA;AACA,IAAM,KAAA,GAAQ,EAAA;AAAA;AAAA,EAEZ,4EAAA;AAAA,EACA,oCAAA;AAAA,EACA,cAAA;AAAA,EACA,0EAAA;AAAA,EACA;AACF,CAAA;AACA,IAAM,UAAU,sBAAM,GAAA,CAAC,SAAI,IAAA,EAAK,WAAA,EAAY,WAAU,4CAAA,EAA6C,CAAA;AAU5F,SAAS,YAAA,CAAa,EAAE,OAAA,EAAS,KAAA,EAAO,QAAQ,KAAA,EAAO,SAAA,EAAW,WAAU,EAAsB;AACvG,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,KAAK,CAAA;AACtC,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAS,EAAE,CAAA;AAE/C,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAI,SAAwB,IAAI,CAAA;AAClD,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,EAAE,CAAA;AACrD,EAAA,MAAM,UAAA,GAAa,OAAoB,IAAI,CAAA;AAE3C,EAAA,MAAM,SAAA,GAAY,OAAuB,IAAI,CAAA;AAC7C,EAAA,MAAM,OAAA,GAAU,OAAuB,IAAI,CAAA;AAC3C,EAAA,MAAM,MAAA,GAAS,OAAuB,IAAI,CAAA;AAC1C,EAAA,MAAM,UAAA,GAAa,OAAoB,IAAI,CAAA;AAC3C,EAAA,MAAM,OAAA,GAAU,cAAc,IAAI,CAAA;AAElC,EAAA,MAAM,UAAA,GAAa,OAA6C,IAAI,CAAA;AACpE,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,IAAI,WAAW,OAAA,EAAS;AAAE,MAAA,YAAA,CAAa,WAAW,OAAO,CAAA;AAAG,MAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AAAA,IAAM;AAAA,EACzF,CAAA;AACA,EAAA,MAAM,UAAA,GAAa,CAAC,IAAA,KAAqB;AACvC,IAAA,WAAA,EAAY;AACZ,IAAA,UAAA,CAAW,OAAA,GAAU,WAAW,MAAM;AAAE,MAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AAAM,MAAA,IAAA,EAAK;AAAA,IAAG,GAAG,gBAAgB,CAAA;AAAA,EAChG,CAAA;AAEA,EAAA,SAAA,CAAU,MAAM;AAAE,IAAA,IAAI,CAAC,MAAM,WAAA,EAAY;AAAA,EAAG,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AACrD,EAAA,SAAA,CAAU,MAAM,WAAA,EAAa,EAAE,CAAA;AAE/B,EAAA,MAAM,GAAA,GAAM,WAAA,CAAY,UAAA,EAAY,OAAA,EAAS,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA,EAAU,KAAA,EAAO,MAAA,EAAQ,CAAA,EAAG,IAAA,EAAM,MAAM,CAAA;AAGnG,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,QAAA,CAAS,EAAE,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,CAAA,EAAG,CAAA;AACxD,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,IAAA,IAAQ,GAAA,KAAQ,IAAA,IAAQ,OAAA,EAAS;AACtC,IAAA,MAAM,QAAQ,MAAM;AAClB,MAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,MAAA,MAAM,QAAQ,UAAA,CAAW,OAAA;AACzB,MAAA,MAAM,QAAQ,MAAA,CAAO,OAAA;AACrB,MAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,KAAA,IAAS,CAAC,KAAA,EAAO;AAC/B,MAAA,MAAM,CAAA,GAAI,KAAK,qBAAA,EAAsB;AACrC,MAAA,MAAM,CAAA,GAAI,MAAM,qBAAA,EAAsB;AACtC,MAAA,MAAM,CAAA,GAAI,MAAM,qBAAA,EAAsB;AACtC,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,aAAA,CAAc,kBAAkB,GAAG,qBAAA,EAAsB;AAC7E,MAAA,IAAI,IAAA,GAAO,EAAE,KAAA,GAAQ,CAAA;AACrB,MAAA,IAAI,IAAA,GAAO,CAAA,CAAE,KAAA,GAAQ,MAAA,CAAO,aAAa,CAAA,EAAG,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,CAAA,CAAE,IAAA,GAAO,CAAA,GAAI,EAAE,KAAK,CAAA;AACnF,MAAA,MAAM,MAAM,IAAA,CAAK,GAAA,CAAI,GAAG,IAAA,CAAK,GAAA,CAAI,EAAE,GAAA,IAAO,KAAA,GAAQ,MAAM,GAAA,GAAM,CAAA,CAAE,MAAM,CAAA,CAAA,EAAI,MAAA,CAAO,cAAc,CAAA,CAAE,MAAA,GAAS,CAAC,CAAC,CAAA;AAC5G,MAAA,SAAA,CAAU,CAAC,GAAA,KAAS,GAAA,CAAI,GAAA,KAAQ,GAAA,IAAO,GAAA,CAAI,IAAA,KAAS,IAAA,GAAO,GAAA,GAAM,EAAE,GAAA,EAAK,IAAA,EAAO,CAAA;AAAA,IACjF,CAAA;AACA,IAAA,KAAA,EAAM;AACN,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,KAAK,CAAA;AACvC,IAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,KAAA,EAAO,IAAI,CAAA;AAC7C,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,mBAAA,CAAoB,UAAU,KAAK,CAAA;AAC1C,MAAA,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,KAAA,EAAO,IAAI,CAAA;AAAA,IAClD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,IAAA,EAAM,GAAA,EAAK,OAAO,CAAC,CAAA;AAEvB,EAAA,MAAM,cAAA,GAAiB,QAAQ,KAAK,CAAA;AACpC,EAAA,MAAM,QAAA,GAAW,GAAA,KAAQ,IAAA,IAAQ,SAAA,CAAU,KAAA,CAAM,GAAG,CAAC,CAAA,GAAK,KAAA,CAAM,GAAG,CAAA,GAA4B,IAAA;AAC/F,EAAA,MAAM,aAAa,QAAA,GAAW,OAAA,CAAQ,QAAA,CAAS,KAAK,IAAI,EAAC;AAGzD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,IAAI,CAAA;AAC7C,EAAA,IAAI,SAAS,QAAA,EAAU;AACrB,IAAA,WAAA,CAAY,IAAI,CAAA;AAChB,IAAA,IAAI,CAAC,IAAA,EAAM;AAAE,MAAA,aAAA,CAAc,EAAE,CAAA;AAAG,MAAA,MAAA,CAAO,IAAI,CAAA;AAAG,MAAA,gBAAA,CAAiB,EAAE,CAAA;AAAA,IAAG;AAAA,EACtE;AAGA,EAAA,MAAM,EAAE,KAAA,EAAO,UAAA,EAAY,aAAY,GAAI,aAAA,CAAc,MAAM,EAAE,OAAA,EAAS,YAAY,KAAA,EAAO,SAAA,EAAW,OAAO,MAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,QAAA,EAAU,OAAO,CAAA;AAGtJ,EAAA,MAAM,WAAW,MAAM;AACrB,IAAA,IAAI,QAAQ,IAAA,EAAM;AAClB,IAAA,aAAA,CAAc,GAAG,CAAA;AACjB,IAAA,MAAA,CAAO,IAAI,CAAA;AACX,IAAA,gBAAA,CAAiB,EAAE,CAAA;AAAA,EACrB,CAAA;AACA,EAAA,MAAM,OAAA,GAAU,CAAC,CAAA,EAAW,UAAA,KAAwB;AAClD,IAAA,MAAM,KAAA,GAAQ,MAAM,CAAC,CAAA;AACrB,IAAA,IAAI,CAAC,SAAA,CAAU,KAAK,CAAA,IAAK,aAAA,CAAc,KAAK,CAAA,EAAG;AAC/C,IAAA,WAAA,EAAY;AACZ,IAAA,MAAA,CAAO,CAAC,CAAA;AACR,IAAA,aAAA,CAAc,CAAC,CAAA;AACf,IAAA,gBAAA,CAAiB,UAAA,GAAa,QAAQ,KAAA,CAAM,KAAK,EAAE,CAAC,CAAA,IAAK,KAAK,EAAE,CAAA;AAAA,EAClE,CAAA;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,EAAM;AAIX,IAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAgC;AACjD,MAAA,IAAI,EAAE,GAAA,KAAQ,QAAA,IAAY,EAAE,gBAAA,IAAoB,CAAC,YAAW,EAAG;AAC/D,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,IAAI,QAAQ,IAAA,EAAM;AAAE,QAAA,QAAA,EAAS;AAAG,QAAA;AAAA,MAAQ;AACxC,MAAA,WAAA,EAAY;AACZ,MAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,IACf,CAAA;AAEA,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAkB;AACjC,MAAA,MAAM,SAAS,CAAA,CAAE,MAAA;AACjB,MAAA,IAAI,UAAA,CAAW,SAAS,QAAA,CAAS,MAAM,KAAK,SAAA,CAAU,OAAA,EAAS,QAAA,CAAS,MAAM,CAAA,EAAG;AACjF,MAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,IACf,CAAA;AAEA,IAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC9C,IAAA,QAAA,CAAS,gBAAA,CAAiB,aAAa,OAAO,CAAA;AAC9C,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,mBAAA,CAAoB,WAAW,SAAS,CAAA;AACjD,MAAA,QAAA,CAAS,mBAAA,CAAoB,aAAa,OAAO,CAAA;AAAA,IACnD,CAAA;AAAA,EACF,CAAC,CAAA;AAED,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,EAAM;AAGX,IAAA,IAAI,GAAA,KAAQ,IAAA,IAAQ,aAAA,IAAiB,CAAA,EAAG,SAAA,CAAU,OAAA,EAAS,aAAA,CAA2B,CAAA,iBAAA,EAAoB,aAAa,CAAA,EAAA,CAAI,CAAA,EAAG,KAAA,EAAM;AAAA,SAAA,IAC3H,GAAA,KAAQ,QAAQ,OAAA,EAAS,SAAA,CAAU,SAAS,aAAA,CAA2B,iBAAiB,GAAG,KAAA,EAAM;AAAA,SAAA,IACjG,UAAA,IAAc,GAAG,OAAA,CAAQ,OAAA,EAAS,cAA2B,CAAA,aAAA,EAAgB,UAAU,CAAA,EAAA,CAAI,CAAA,EAAG,KAAA,EAAM;AAAA,EAC/G,GAAG,CAAC,IAAA,EAAM,YAAY,GAAA,EAAK,aAAA,EAAe,OAAO,CAAC,CAAA;AAElD,EAAA,MAAM,OAAO,CAAC,KAAA,EAAiB,EAAA,EAAY,EAAA,KAAe,QAAQ,KAAA,CAAM,OAAA,CAAQ,EAAE,CAAA,GAAI,MAAM,KAAA,CAAM,MAAA,GAAS,KAAA,CAAM,MAAA,IAAU,MAAM,MAAM,CAAA;AACvI,EAAA,SAAS,kBAAkB,CAAA,EAAuB;AAChD,IAAA,MAAM,QAAQ,GAAA,KAAQ,IAAA;AACtB,IAAA,MAAM,KAAA,GAAQ,QAAQ,UAAA,GAAa,cAAA;AACnC,IAAA,MAAM,EAAA,GAAK,QAAQ,aAAA,GAAgB,UAAA;AACnC,IAAA,MAAM,EAAA,GAAK,CAAC,CAAA,KAA0B;AAAE,MAAA,IAAI,MAAM,MAAA,EAAW;AAAQ,MAAA,IAAI,KAAA,mBAAwB,CAAC,CAAA;AAAA,yBAAsB,CAAC,CAAA;AAAA,IAAG,CAAA;AAC5H,IAAA,IAAI,CAAA,CAAE,QAAQ,WAAA,EAAa;AAAE,MAAA,CAAA,CAAE,cAAA,EAAe;AAAG,MAAA,EAAA,CAAG,EAAA,GAAK,IAAI,KAAA,CAAM,CAAC,IAAI,IAAA,CAAK,KAAA,EAAO,EAAA,EAAI,CAAC,CAAC,CAAA;AAAA,IAAG,CAAA,MAAA,IACpF,CAAA,CAAE,GAAA,KAAQ,SAAA,EAAW;AAAE,MAAA,CAAA,CAAE,cAAA,EAAe;AAAG,MAAA,EAAA,CAAG,EAAA,GAAK,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,EAAO,EAAA,EAAI,EAAE,CAAC,CAAA;AAAA,IAAG,CAAA,MAAA,IACvG,CAAA,CAAE,GAAA,KAAQ,MAAA,EAAQ;AAAE,MAAA,CAAA,CAAE,cAAA,EAAe;AAAG,MAAA,EAAA,CAAG,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,IAAG,CAAA,MAAA,IACtD,CAAA,CAAE,GAAA,KAAQ,KAAA,EAAO;AAAE,MAAA,CAAA,CAAE,cAAA,EAAe;AAAG,MAAA,EAAA,CAAG,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAC,CAAA;AAAA,IAAG,CAAA,MAAA,IACpE,CAAA,CAAE,GAAA,KAAQ,YAAA,IAAgB,CAAC,KAAA,IAAS,UAAA,IAAc,CAAA,IAAK,SAAA,CAAU,KAAA,CAAM,UAAU,CAAC,CAAA,EAAG;AAAE,MAAA,CAAA,CAAE,cAAA,EAAe;AAAG,MAAA,OAAA,CAAQ,YAAY,IAAI,CAAA;AAAA,IAAG,CAAA,MAAA,IACtI,CAAA,CAAE,GAAA,KAAQ,WAAA,IAAe,KAAA,EAAO;AAAE,MAAA,CAAA,CAAE,cAAA,EAAe;AAAG,MAAA,QAAA,EAAS;AAAA,IAAG;AAAA,EAC7E;AAGA,EAAA,MAAM,MAAA,GAAS,CAAC,IAAA,KAA2B;AACzC,IAAA,IAAI,KAAK,QAAA,EAAU;AACnB,IAAA,WAAA,EAAY;AACZ,IAAA,IAAA,CAAK,OAAA,EAAQ;AACb,IAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,MAAM,QAAA,GAAW,CAAC,OAAA,KAAwD,OAAA,CAAQ,IAAI,CAAC,IAAA,EAAM,MAAO,SAAA,CAAU,IAAI,oBAAI,GAAA,CAAC,OAAA,EAAA,EAAA,EAAa,CAAG,CAAA,mBACrI,IAAA,CAAC,YAAO,IAAA,EAAK,QAAA,EAAiB,kBAAgB,CAAA,EAAG,IAAA,EAAK,YAAW,QAAA,EAAU,IAAA,CAAK,UAAU,QAAA,EAAU,aAAA,KAAkB,IAAI,CAAA,GAAI,EAAA,EAAI,SAAS,MAAM,gBAAA,CAAiB,CAAC,CAAA,EAAG,OAAA,EAAS,MAAM,MAAA,CAAO,IAAI,GAAG,SAAA,EAAW,QAAA,CAAS,IAAI,CAAA,EACxN,QAAA,EAAA;AAAA,IAAA,IAAA,CAAK,IAAA,wBAAS,IAAA,EAAA,EAAK,IAAA,EAAM,KAAK,IAAA,EAAM,IAAA,EAAM,EAAA,EAAI,SAAA,EAAU,UAAA,EAAW,CAAA;AAAA,oBACpE,GAAA,CAAC,MAAA,EAAA,EAAM,QAAA,EAAA,IAAA,CAAK,KAAA,EAAM;AAAA,GAAA,EAAA,EAFO,CAG3B,CACA,CAAA;AAEF,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,IAAA;AAAA,MAAA;AAAA,QACE,GAAG,YAAY,OAAO,CAAA;AAAA,QAEvB,eAAA,EAAc,MAAA;AAAA,QACd,eAAA,EAAe,IAAA;AAAA,QACf,GAAA,EAAK,UAAA;AAAA,QACL,SAAS,MAAM;AACb,UAAA,OAAA,CAAQ,CAAC,IAAI,CAAA;AACb,UAAA,IAAI,CAAC,IAAA,IAAQ,cAAA,CAAe,QAAQ,aAAA,CAAc,cAAA,CAAe,CAAC,CAAC,CAAA;AAAA,QACrE,CAAA;AAAA,QAEC,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,IACC,IAAA,IAAQ,YAAA;AAAA,sBACP,IAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,SAAA,EAAW,WAAU,UAAA,EAC7B,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,OAAA;AAAA,YACL,IAAA,EAAK,MAAA;AAAA,YACL,SAAA,EAAQ,MAAA;AAAA,YACR,YAAA,EAAY,OAAA,IAAW,QAAA,GAAW,QAAA,CAAS,KAAA,GAAQ,MAAA;AAAA,YACnD,SAAA,EAAW,iBAAA;AAAA,YACX,SAAA,EAAW,EAAA,CAAG,KAAA,EAAO,SAAS,CAAA;AAAA,YAC9B,OAAO,EAAE,GAAA,EAAK,IAAI,GAAA,EAAK,IAAA,EAAM,IAAI,IAAA,EAAK;AAAA,YAGtC,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,EAAgB;AAAA,YAEjC,QAAA,EAAA,OAAA,IAAW,QAAA;AAAA;AAAA,8BAEV,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,gCAAA,IAAA,CAAC,QAAA,EAAA,EAAO,MAAK,QAAA,EAAS,eAAA,EAAc,IAAG,IAAA,EAAK,UAAA,EAAW,YAAA,EAAY,CAAA,UAAA,EAAa,QAAA,CAAS,KAAK,IAAI,QAAA,EAAU,EAAA,EAAI,SAAS,QAAA,EAAU,SAAA,EAAW,GAAG,QAAA,CAAS,QAAQ,CAAA,EAAG,iEAAiE,CAAA,EACpO,QAAA,EAAA;AAAA,kCAAA,GAAA,CAAC,QAAK,IAAA,EAAK,cAAA,EAAe,IAAA,EAAM,EAAA,EAAI,WAAU,UAAA,EAAW,CAAA;AAAA,kCACzD,GAAA,CAAC,MAAA,EAAA,EAAM,QAAA,EAAA,QAAA,CAAS,KAAA,EAAM;AAAA,iBAAA,EACxB,CAAA;AAAA,oCACC,OAAA,EAAA,EAAQ,CAAA;AAAA,gBACR,QAAA,CAAS,SAAS,KAAK;AAAA,eAAA,EAC1B;AAAA,gBACE,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,EAAM,CAAA,KAAM;AACzB,cAAA,IAAI,UAAU,IAAI,CAAA,EAAG,uBAAO,GAAA,CAAC,aAAa,CAAG,CAAA;AAC7C,cAAA,IAAI,SAAA,CAAU,IAAI,CAAA,EAAG;AACnB,gBAAA,MAAM,SAAS,GAAA,KAAQ,CAAA;AACvB,gBAAA,uBACE,IAAA;AAAA,kBAAC,QAAA;AAAA,kBAAA;AAAA,oBAAO,IAAA,EAAK,QAAA;AAAA,oBAEX,GAAA,EAAK,MAAA,GAAS,CAAC,EAAA,KAAO;AAAE,sBAAA,UAAA,CAAW,OAAA,GAAU,EAAA;AAAA,oBAAI,CAAA,GAAI,MAAA;AAAA,oBACrD,YAAA,EAAY,CAAA;AAAA,oBACZ,IAAA,EAAK,UAAA;AAAA,oBACL,eAAA,EAAc,MAAA;AAAA,oBACd,eAAA,EAAe,MAAA;AAAA,oBACf,QAAA,EAAU,cAAc,IAAI,CAAA;AAAA,oBAC5B,QAAA,EAAU,UAAA,KAAe,CAAA,GAAI,CAAA,GAAI,EAAA;AAAA,oBAEjC,OAAA,EAAS,MAAM,aAAA,CAAc,CAAC,CAAA;AAAA,oBAI9B,OAAA,EAAS,MAAM,OAAA,CAAQ,CAAA,EAAG,IAAI,CAAA;AAAA,oBAG9B,YAAA,EAAc,CAAC,CAAA,KAAM;AACnB,sBAAA,IAAI,OAAA,IAAW,CAAA,CAAE,OAAA,KAAY,CAAA,EAAG;AAChC,sBAAA,IAAI,QAAQ,WAAA,EAAY;AAAA,2BAAA,IACf,GAAA,KAAQ,IAAA,EAAM,OAAA,CAAQ,CAAA,EAAG,KAAK,CAAA;AAAA,2BAClC,UAAA,CAAW,MAAM,OAAA,CAAQ,CAAA,EAAG,KAAK,CAAC,CAAA;AAAA,oBACzC,CAAA;AAAA,oBACA,SAAA,EAAW,QAAA,CAAS,IAAA,EAAM,MAAM,CAAA;AAAA,oBAE/B,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAK,IAAA,wBAAS,IAAA,EAAA,EAAK,IAAA,EAAM,KAAK,IAAA,EAAM,IAAA,EAAM,EAAA,EAAI,SAAA,EAAU,UAAA,EAAW,CAAA;AAAA,sCACpE,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,QAAA,EAAU,eAAK,KAAA,EAAM,CAAA;AAAA,0CACpC,IAAA,EAAA,EAAK,IAAA,EAAK,iBAAgB,IAAA,EAAM,EAAA,EAAI,WAAU,sCAAA,EAAuC;AAAA;AAAA,mBAAA;AAAA,kBA1BjF;AAAA,iBA2BP;AAAA,cAEJ;AACA,cAAA,uBACE,IAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBAAO,IAAA,EAAK,QAAA;AAAA,kBAEX,YAAA,EAAY,CAAA;AAAA,kBACZ,IAAA,EAAK,UAAA;AAAA,kBACL,UAAU,IAAA,CAAK,QAAA;AAAA,kBACf,QAAA,EAAU,UAAA,KAAe,CAAA,GAAI,CAAA,GAAI,EAAA;AAAA,kBACjC,OAAA,EAAS,MAAM,aAAA,CAAc,CAAC,CAAA;AAAA,kBAC9B,OAAA,EAAS,MAAM,MAAA,CAAO,IAAI,CAAA;AAAA,kBAG1B,cAAc,MAAM;AAAE,oBAAA,IAAI,CAAC,OAAA,IAAW,GAAA,KAAQ,IAAA,aAAiB,MAAM;AAAE,sBAAA,MAAA,CAAO,IAAI,CAAA;AAAG,sBAAA,gBAAA,CAAiB,EAAE,CAAA;AAAA,oBAAG,CAAC,CAAA;AAAA,kBAAG,CAAA;AAAA,kBAC/G,SAAA,EAAW,SAAS,IAAI,CAAA;AAAA,kBAEvB,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAK,IAAA,wBAAS,IAAA,EAAA,EAAK,IAAA,EAAM,KAAK,IAAA,EAAM,IAAA,EAAM,EAAA,EAAI,SAAA,EAAU,UAAA,EAAW,CAAA;AAAA,oCACpE,GAAA,CAAC,MAAA,EAAA,EAAM,QAAA,EAAA,IAAA,CAAK,KAAA,EAAM;AAAA;AAAA,iBAAA;AAAA,gBAbb;AAAA,eAcP;AAAA,YAEJ,CAAC;AAAA;AAAA,SACH;AAAA,QACC,CAAC,WAAW,QAAA,oBACX,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,MAAA;AAAA,YACL,IAAA,EAAK,MAAA;AAAA,YACL,cAAY,QAAA,CAAS,KAAA;AAAA,YACrB,SAAA,EAAW,iBAAA;AAAA,YACX,SAAA,EAAW,KAAA;AAAA,YACX,OAAO,EAAE,GAAA,EAAK,OAAO,GAAA,EAAK,IAAA,EAAM,OAAO,IAAA,EAAK;AAAA,YAC5C,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,EAAgB;AAAA,YAElC,YAAA,EAAc,WAAA;AAAA,YAEb,QAAA,EAAA,QAAA,CAAS,SAAS,KAAK;AAAA;AAAA;AAC1B,OAAA,EAEJ,CAAA;AAAA,MACA,aAAa,QAAA,CAAS;AAAA;AACxB,GAAA,EACF,CAAA;AAEJ","file":"chunk-Z4MCD73B.js","sourcesContent":["import { useRef, useEffect, useLayoutEffect, useState, type ReactNode, type KeyboardEvent as ReactKeyboardEvent } from \"react\";\nimport { useBreakpoint } from \"../lib/use-breakpoint\";\nimport { createPortal } from \"react-dom\";\nimport { cn, FOCUS_RING, GLASS_DROPDOWN } from \"../lib/utils\";\nimport { Icon, type IconName } from \"../lib/icons\";\nimport { Slot } from \"../lib/slot\";\nimport { triggerType } from \"./Button\";\nimport { usePosition, type Align } from \"../lib/use-position\";\nimport { usePopupFocus } from \"../lib/focus\";\n\nexport interface DropdownMenuItem {\n label: string;\n icon?: IconName;\n onClick: () => void;\n variant?: \"default\" | \"destructive\";\n disabled?: boolean;\n}\n\nexport interface DropdownMenuDivider {\n type: \"divider\";\n}\n\n/**\n * An entry that opens a submenu of its own (Sprint 25.8.7's look check: a card's Move among its other actions), one\n * level deep. On a desktop it opens beside the menu: → or Enter opens it, its first item focused; ← or Escape closes it,\n * focus back on this entry; a hover opens it too. On a phone its items take the menu's place, under a Back item.\n */\nexport interface DropdownMenuSubmenu {\n label: string;\n icon?: IconName;\n items: (DropdownMenuItem | DropdownMenuDivider)[];\n disabled?: boolean;\n}\n\nexport type DropdownMenuEntry = DropdownMenuItem | DropdownMenuDivider | DropdownMenuSubmenu;\n\nfunction isDivider(entry: DropdownMenuEntry): entry is DropdownMenuDivider {\n return \"type\" in entry && entry.type === \"divider\";\n}\nfunction isSubmenu(entry: DropdownMenuEntry): entry is DropdownMenuSubmenu {\n return \"items\" in entry;\n}\n/** The indices of a list's items the arrows stop at: no divider, nothing unavailable. */\nconst stopsOf = (entries: DropdownMenuEntry[]): number[] => entries.reduce<number[]>((acc, e, i) => {\n if (!isDivider(e) && !isUnavailable(e)) acc.push(i);\n return acc;\n}, []);\n/** An entry that can't be chosen: disabled, or a submenu with nothing in it to choose, which never opens empty. */\nfunction isUnavailable(entry: DropdownMenuItem | DropdownMenuSubmenu): boolean {\n return !!entry.disabled || (isSubmenu(entry) && stopsOf(entry.items).length === 0);\n}\n/**\n * How long a hover on another entry waits before it closes the open submenu (or opens the other's): a pointer on its way\n * to the submenu crosses the entries below its own, and reaching the submenu in that time keeps it open.\n */\nconst SUBMENU_GRACE_MS = 300;\n\nconst ITEM = \"flex items-center gap-ds-2 w-full text-left px-ds-3 py-ds-2 text-sm cursor-pointer transition-colors rounded-[var(--ds-radius-sm)]\";\nconst itemLook = (entry: DropdownMenuItem | DropdownMenuSubmenu, open = false) => cn(\n ITEM,\n FOCUS_RING,\n isUnavailable(entry)\n ? \"opacity-disabled cursor-not-allowed\"\n : \"variant\" in entry && entry.variant === \"destructive\"\n ? \"text-[var(--ds-status-error-text)] hover:bg-[var(--ds-status-error-bg)] focus-visible:bg-[var(--ds-status-error-bg)]\"\n : cn(\"text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-emphasis)] focus-visible:bg-[var(--ds-bg-emphasis)]\", open && \"bg-[var(--ds-bg-emphasis)]\"),\n);\nconst PANEL = cn(\n // the items' outline has its 4 px of room at any density (Sprint 30, P3)\n \"fixed z-[var(--ds-z-popover)] min-w-[180px] p-[max(var(--ds-space-1),4px)]\",\n \"bg-[var(--ds-bg-surface-elevated)]\",\n GLASS_DROPDOWN,\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]\",\n \"rounded-[var(--ds-radius-md)] overflow-hidden\",\n);\nconst Divider = () => <div role=\"separator\" className=\"my-ds-1 h-px bg-[var(--ds-border-default)]\" />;\n\ninterface DropdownMenuProps {\n trigger: ReactNode;\n items: DropdownMenuEntry[];\n align?: Align;\n className?: string;\n container?: Element | DocumentFragment;\n}\n\nexport function DropdownMenu({ trigger, items, align = \"end\", className, container }: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const [focusedIdx, setFocusedIdx] = useState(-1);\n // the submenu open, by its entry's index, and the focus inside it\n const [sub, setSub] = useState<number | null>(null);\n const [subFocusedIdx, setSubFocusedIdx] = useState(-1);\n const triggerRef = useRef<HTMLElement>(null);\n // both panels, the menu and its submenu, sit in one box (display: contents) that is the popup's for focus and presses\n const panelsRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const subRef = useRef<HTMLDivElement>(null);\n const subItemRef = useRef<HTMLElement>(null);\n const isPhone = useBreakpoint(\"sm\");\n // a hover elsewhere, waiting before it closes the open submenu (SUBMENU_GRACE_MS)\n const graceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const cancelGrace = () => {\n if (graceTimer.current) { clearTimeout(graceTimer.current); graceTimer.current = null; }\n };\n const afterGrace = (then: () => void) => {\n cancelGrace();\n graceTimer.current = setTimeout(() => { graceTimer.current = null; then(); }, SUBMENU_GRACE_MS);\n };\n // closing the menu, or leaving the page, drops a hover's wait\n useEffect(() => { if (!open) cancelGrace(); }, [open]);\n useEffect(() => cancelGrace, []);\n\n const pos = usePosition(triggerRef, menuRef, open, { side: \"bottom\", align, offset: 4, flip: true });\n // beside the menu, 4 px from its edge, its first item level with its entry; on the other side where the screen has no\n // room, and inside the screen (measured against the menu, not the entry: the menu's padding and edge sit between them)\n const [subPos, setSubPos] = useState({ top: 0, left: 0 });\n useLayoutEffect(() => {\n if (!open || sub === null || isPhone) return;\n const place = () => {\n const menu = menuRef.current;\n const entry = subItemRef.current;\n const panel = subRef.current;\n if (!menu || !entry || !panel) return;\n const m = menu.getBoundingClientRect();\n const e = entry.getBoundingClientRect();\n const p = panel.getBoundingClientRect();\n const first = panel.querySelector(\"[data-sub-index]\")?.getBoundingClientRect();\n let left = m.right + 4;\n if (left + p.width > window.innerWidth - 8) left = Math.max(8, m.left - 4 - p.width);\n const top = Math.max(8, Math.min(e.top - (first ? first.top - p.top : 0), window.innerHeight - p.height - 8));\n setSubPos((cur) => (cur.top === top && cur.left === left ? cur : { top, left }));\n };\n place();\n window.addEventListener(\"resize\", place);\n window.addEventListener(\"scroll\", place, true);\n return () => {\n window.removeEventListener(\"resize\", place);\n window.removeEventListener(\"scroll\", place, true);\n };\n }, [open, sub, isPhone]);\n\n const enabledIndices = stopsOf(items);\n const subEntry = sub !== null && isSubmenu(items[sub]) ? (items[sub] as DropdownMenuSubmenu) : null;\n const subIndices = subEntry ? stopsOf(subEntry.items) : [];\n\n // Closing clears the keyboard focus index and the submenu (render-time state adjustment).\n const [prevOpen, setPrevOpen] = useState(open);\n if (open !== prevOpen) {\n setPrevOpen(open);\n if (!open) { setFocusedIdx(-1); setSub(null); setSubFocusedIdx(-1); }\n }\n\n // Sprint 25.7: focus hands back on close, and Tab out carries on from the trigger (lib/focus usePopupFocus).\n const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: panelsRef, close: () => setOpen(false), tabExits: \"any\" });\n\n /** The submenu closes, and focus goes back to the entry that opened it. */\n const closeSub = () => {\n if (sub === null) return;\n setFocusedIdx(sub);\n setSub(null);\n setSubFocusedIdx(-1);\n };\n const openSub = (i: number, focusFirst: boolean) => {\n const entry = items[i];\n if (!isSubmenu(entry) || isUnavailable(entry)) return;\n cancelGrace();\n setSub(i);\n setFocusedIdx(i);\n setSubFocusedIdx(focusFirst ? stopsOf(entry.items)[0] ?? -1 : -1);\n };\n\n useEffect(() => {\n if (!open) return;\n\n // An Escape a control inside already handled is its own; the one this acts on is marked for overlays around it.\n // In a submenu it closes the submenu alone.\n const onKeyDown = (e: globalThis.KeyboardEvent) => {\n if (e.key !== \"Escape\" || e.defaultPrevented || !isTopLayer()) return;\n e.preventDefault();\n if (sub !== null) { closeSub(); return; }\n returnFocus();\n setOpen(false);\n };\n\n const onClick = (e: MouseEvent) => {\n const target = e.target as Node;\n if (triggerRef.current?.contains(target) || panelsRef.current?.contains(target)) return;\n setOpen(false);\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n document.addEventListener(\"mousedown\", onClick);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.removeEventListener(\"mousedown\", onClick);\n };\n });\n\n useEffect(() => {\n if (!open) return;\n // By the item's own index: items and dividers share one numbering, so indexing the buttons alone left every\n // item after a divider unreachable by the arrows (the shell's user menu: Settings, divider, Log out).\n if (sub !== null && subFocusedIdx >= 0) panelsRef.current?.querySelector<HTMLElement>(`[data-sub-index=\"${subFocusedIdx}\"]`)?.focus();\n else if (sub !== null && isPhone) panelsRef.current?.querySelector<HTMLElement>(\"[data-sub-back]\")?.focus();\n else if (focusedIdx >= 0) menuRef.current?.querySelector<HTMLElement>(`[data-index=\"${focusedIdx}\"]`)?.focus();\n }, [open, focusedIdx, sub, subFocusedIdx, isPhone]);\n\n const step = (stops: number[], at: number, by: number) => stops[((stops.indexOf(at) + by) % stops.length + stops.length) % stops.length];\n function handleMenuKeyDown(e: ReactKeyboardEvent) {\n const inSub = sub !== null;\n const stops = inSub ? subIndices : enabledIndices;\n const at = inSub ? subFocusedIdx : focusedIdx;\n const go = (i: number | undefined) => { if (i === undefined) return; if (inSub) setSubFocusedIdx(i); else setFocusedIdx(i); };\n if (e.key === \"ArrowDown\") { e.preventDefault(); go(at < 0 ? stops[0] : step(stops, at, 1)); }\n else if (e.key === \"ArrowUp\") { e.preventDefault(); go(at < 0 ? stops[stops.length - 1] : step(stops, at, -1)); }\n else if (e.key === \"Home\") { e.preventDefault(); go(stops[0]); }\n else if (e.key === \"End\") { e.preventDefault(); go(stops[stops.length - 1]); }\n else if (e.key === \"ArrowRight\" && !inSub && focusedIdx >= 0 && isSubmenu(items[focusedIdx])) { e.preventDefault(); openSub(focusedIdx, true); }\n else if (e.key === \"ArrowLeft\" && inSub) { e.preventDefault(); closeSub(); }\n }\n\n /** A choice: focus back on the trigger first, so a dialog the item opens returns there (Sprint 25.7). */\n const choose = (item: DropdownMenuItem) => {\n if (item.disabled) return;\n returnFocus();\n item.onClick();\n setOpen(false);\n };\n\n const subItems = (entries: (DropdownMenuItem | DropdownMenuDivider)[]) => entries.map((item, i) => (isDivider(item) ? <Divider key={i} /> : (\n <button type=\"button\" key={i} data-sub-index={i} role=\"menuitem\" disabled={item.disabled} tabIndex={subFocusedIdx === i ? 0 : -1} onFocus={() => setSubFocusedIdx(i)} onClick={() => choose(item)} className={itemLook(item)}>\n {item.icon && <Icon name={item.icon} size={16} className=\"shrink-0\" />}\n <span>{item.label}</span>\n </button>\n )));\n\n return (\n <>\n <Slot\n {...triggerType(trigger)}\n // the APG's menu button: the trigger says it opens a menu, and whether it is open\n aria-haspopup=\"menu\"\n aria-expanded={open}\n ref={triggerRef}\n onClick={() => {\n setOpen(!open);\n if (!open && enabledIndices.length) setFocusedIdx(enabledIndices[0]);\n }}\n >\n {trigger}\n </Slot>\n {open && createPortal(\n <div ref={panelsRef} className=\"contents\">\n <div\n ref={menuRef}\n role=\"menu\"\n data-gs=\"menu\"\n aria-label={isPhone && subEntry ? subEntry.label : undefined}\n onKeyDown={handleMenuKeyDown}\n className={cn(PANEL, className)}\n style={{ top: pos.top, left: pos.left }}\n // A choice is the menu's own: the click must not also reach a clickable row or card the menu sits in\n // (React carries events from a portal to its React parents).\n onClick={(e) => e.stopPropagation()}\n >\n {isPhone && subEntry ? (\n // on a phone the submenu takes the menu's place: Back first, its entry's name on it\n <>\n <button type=\"button\" data-sub-back=\"\" role=\"menuitem\" aria-label={`Back from ${subEntry.label}`} tabIndex={-1} onClick={closeSub} className={cn(itemLook(subEntry), \"text-[var(--ds-text-secondary)] font-[var(--ds-weight-heading)]\")}>\n <Icon name=\"chevron-left\" size={16} className=\"shrink-0\" />\n <span>{subEntry.label}</span>\n </button>\n <Divider />\n {subItems(subEntry.items)}\n </>\n ) : items.map((item, i) => {\n if (isDivider(item)) return <Divider key={i} />;\n if (isSubmenu(item)) {\n const isOpen = sub === i;\n return (\n <button type=\"button\"\n key={i}\n ref={isOpen ? (el) => { subItemRef.current = el; } : undefined}\n data-index={i}\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n disabled={isUnavailable(item)}\n tabIndex={focusedIdx === i ? 0 : -1}\n // the arrows go on from wherever focus is, however it got there (a click, a screen reader's command)\n onFocus={() => setFocusedIdx(i)}\n // Enter (its click) or a press goes into the submenu, its first item focused, whether a hover had\n // opened it or not (the APG's menu: Enter opens it and focuses its first item); ←, Escape or Back\n // closes it\n onClick={() => openSub(i, true)}\n // a hover opens it at once where no other is open; with another open it waits, as the pointer may be\n // on its way to that one (SUBMENU_GRACE_MS); back on its own entry, the wait for a close is dropped\n onMouseEnter={(e) => {\n if (isPhone || e.buttons !== 0) return;\n if (isOpen) cancelGrace();\n else if (sub === null) openSub(i, false);\n else afterGrace(() => openSub(i, false));\n }}\n className={itemLook(item, isOpen)}\n >\n {item.icon && <Icon name={item.icon} size={16} className=\"shrink-0\" />}\n <span className=\"flex-1\">{item.label}</span>\n <Icon name=\"chevron-right\" size={14} className=\"shrink-0 text-[var(--ds-text-muted)]\" />\n </button>\n );\n }\n return (\n <button type=\"button\"\n key={i}\n data-index={i}\n role=\"menuitem\"\n disabled={item.disabled}\n tabIndex={focusedIdx === i ? 0 : -1}\n onFocus={() => setFocusedIdx(i)}\n onClick={() => choose(item)}\n // a hover on another entry closes an open submenu, as moving on from it does, after a moment: the\n // pointer may only be crossing it on its way to the submenu (SUBMENU_GRACE_MS)\n onMouseEnter={() => { if (!isPhone && sub !== null) afterGrace(() => { setSub(null); setSubFocusedIdx(-1); }); }}\n className={itemLook(item)}\n >\n {item.icon && <Icon name={item.icon} size={16} className=\"shrink-0\" />}\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>\n {!isPhone && subEntry && (\n <div\n ref={subRef}\n role=\"menu\"\n aria-label={subEntry.label}\n onKeyDown={handleMenuKeyDown}\n className={PANEL}\n style={{ top: subPos.top, left: subPos.left }}\n onClick={(e) => e.stopPropagation()}\n // the pointer has reached the submenu: a hover it crossed on the way closes nothing\n onMouseEnter={cancelGrace}\n >\n {subItems(subEntry.items)}\n </div>\n )}\n </div>,\n container ?? document.body\n )}\n </>\n );\n}\n"]}
@@ -13,13 +13,16 @@ function readBreakpointPx(key) {
13
13
  const px = parseInt(raw, 10);
14
14
  return isNaN(px) ? DEFAULT_BREAKPOINTS[key] : px;
15
15
  }
16
+ function belowQuery(px) {
17
+ return `(width < ${px / 16}rem)`;
18
+ }
16
19
  var noopSubscribe = () => () => {
17
20
  };
18
21
  var serverSnapshot = () => false;
19
22
  function useBreakpoint(key) {
20
23
  const overrideWidth = useContext(BreakpointWidthCtx);
21
24
  const px = useMemo(() => readBreakpointPx(key), [key]);
22
- const mediaQuery = `(max-width: ${px}px)`;
25
+ const mediaQuery = belowQuery(px);
23
26
  const subscribe = useCallback((onChange) => {
24
27
  if (overrideWidth !== null || typeof window === "undefined") return noopSubscribe();
25
28
  const mql = window.matchMedia(mediaQuery);
@@ -31,10 +34,10 @@ function useBreakpoint(key) {
31
34
  () => typeof window !== "undefined" ? window.matchMedia(mediaQuery).matches : false,
32
35
  serverSnapshot
33
36
  );
34
- if (overrideWidth !== null) return overrideWidth <= px;
37
+ if (overrideWidth !== null) return overrideWidth < px;
35
38
  return matches;
36
39
  }
37
40
 
38
41
  export { BreakpointWidthProvider, readBreakpointPx, useBreakpoint };
39
- //# sourceMappingURL=chunk-CRQ6KCRD.js.map
40
- //# sourceMappingURL=chunk-CRQ6KCRD.js.map
42
+ //# sourceMappingURL=chunk-ZHUVLBCC.js.map
43
+ //# sourceMappingURL=chunk-ZHUVLBCC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/use-breakpoint.ts"],"names":[],"mappings":";;;;AAGO,IAAM,mBAAA,GAAsB,EAAE,EAAA,EAAI,GAAA,EAAK,IAAI,GAAA,EAAK,EAAA,EAAI,IAAA,EAAM,EAAA,EAAI,IAAA,EAAK;AAI1E,IAAM,kBAAA,GAAqB,cAA6B,IAAI,CAAA;AAOrD,SAAS,uBAAA,CAAwB,EAAE,KAAA,EAAO,QAAA,EAAS,EAAkD;AAC1G,EAAA,OAAO,cAAc,kBAAA,EAAoB,EAAE,KAAA,EAAO,KAAA,IAAS,QAAQ,CAAA;AACrE;AAGO,SAAS,iBAAiB,GAAA,EAA4B;AAC3D,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,oBAAoB,GAAG,CAAA;AACjE,EAAA,MAAM,GAAA,GAAM,gBAAA,CAAiB,QAAA,CAAS,eAAe,CAAA,CAClD,iBAAiB,CAAA,QAAA,EAAW,GAAG,CAAA,CAAE,CAAA,CACjC,IAAA,EAAK;AACR,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,GAAA,EAAK,EAAE,CAAA;AAC3B,EAAA,OAAO,KAAA,CAAM,EAAE,CAAA,GAAI,mBAAA,CAAoB,GAAG,CAAA,GAAI,EAAA;AAChD;AAOO,SAAS,WAAW,EAAA,EAAoB;AAC7C,EAAA,OAAO,CAAA,SAAA,EAAY,KAAK,EAAE,CAAA,IAAA,CAAA;AAC5B;AAEA,IAAM,aAAA,GAAgB,MAAM,MAAM;AAAC,CAAA;AACnC,IAAM,iBAAiB,MAAM,KAAA;AAatB,SAAS,cAAc,GAAA,EAA6B;AACzD,EAAA,MAAM,aAAA,GAAgB,WAAW,kBAAkB,CAAA;AACnD,EAAA,MAAM,EAAA,GAAK,QAAQ,MAAM,gBAAA,CAAiB,GAAG,CAAA,EAAG,CAAC,GAAG,CAAC,CAAA;AACrD,EAAA,MAAM,UAAA,GAAa,WAAW,EAAE,CAAA;AAEhC,EAAA,MAAM,SAAA,GAAY,WAAA,CAAY,CAAC,QAAA,KAAyB;AACtD,IAAA,IAAI,kBAAkB,IAAA,IAAQ,OAAO,MAAA,KAAW,WAAA,SAAoB,aAAA,EAAc;AAClF,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,UAAU,CAAA;AACxC,IAAA,GAAA,CAAI,gBAAA,CAAiB,UAAU,QAAQ,CAAA;AACvC,IAAA,OAAO,MAAM,GAAA,CAAI,mBAAA,CAAoB,QAAA,EAAU,QAAQ,CAAA;AAAA,EACzD,CAAA,EAAG,CAAC,UAAA,EAAY,aAAa,CAAC,CAAA;AAE9B,EAAA,MAAM,OAAA,GAAU,oBAAA;AAAA,IACd,SAAA;AAAA,IACA,MAAO,OAAO,MAAA,KAAW,WAAA,GAAc,OAAO,UAAA,CAAW,UAAU,EAAE,OAAA,GAAU,KAAA;AAAA,IAC/E;AAAA,GACF;AAGA,EAAA,IAAI,aAAA,KAAkB,IAAA,EAAM,OAAO,aAAA,GAAgB,EAAA;AACnD,EAAA,OAAO,OAAA;AACT","file":"chunk-ZHUVLBCC.js","sourcesContent":["import { useCallback, useMemo, useSyncExternalStore, createContext, useContext, createElement, type ReactNode } from \"react\";\n\n/** The breakpoints a server and jsdom fall back to: the `--ds-bp-*` tokens' own (a test holds them to roots.css). */\nexport const DEFAULT_BREAKPOINTS = { sm: 640, md: 768, lg: 1024, xl: 1280 } as const;\n\nexport type BreakpointKey = keyof typeof DEFAULT_BREAKPOINTS;\n\nconst BreakpointWidthCtx = createContext<number | null>(null);\n\n/**\n * Provides an override container width for all `useBreakpoint` calls within\n * this subtree. Used by the builder's viewport preview to make components\n * respond to the constrained canvas width instead of the browser viewport.\n */\nexport function BreakpointWidthProvider({ width, children }: { width: number | null; children: ReactNode }) {\n return createElement(BreakpointWidthCtx, { value: width }, children);\n}\n\n/** The `--ds-bp-*` breakpoint in px (the token, else the default). */\nexport function readBreakpointPx(key: BreakpointKey): number {\n if (typeof window === \"undefined\") return DEFAULT_BREAKPOINTS[key];\n const raw = getComputedStyle(document.documentElement)\n .getPropertyValue(`--ds-bp-${key}`)\n .trim();\n const px = parseInt(raw, 10);\n return isNaN(px) ? DEFAULT_BREAKPOINTS[key] : px;\n}\n\n/**\n * The media query for \"below a breakpoint of `px`\": the one Tailwind's own compiler writes for `max-sm:` and the rest,\n * `(width < 40rem)` for 640 px (Sprint 25.28). In rem, so it moves with the browser's text size as the styles' `sm:`\n * and `md:` do, and never claims the width they start at. Internal: the agreement test reads it.\n */\nexport function belowQuery(px: number): string {\n return `(width < ${px / 16}rem)`;\n}\n\nconst noopSubscribe = () => () => {};\nconst serverSnapshot = () => false;\n\n/**\n * Reactive breakpoint check that reads the `--ds-bp-*` breakpoints. Returns true when the viewport (or the override\n * container's width) is below the named breakpoint; Tailwind's `sm:` and `md:` start at it, so the code and the\n * styles switch at the same width, at any browser text size.\n *\n * Implemented with useSyncExternalStore so the first render already reflects\n * the media query and a change between render and subscribe cannot be missed.\n *\n * @example\n * const isMobile = useBreakpoint(\"md\"); // true below 768px, where md: starts\n */\nexport function useBreakpoint(key: BreakpointKey): boolean {\n const overrideWidth = useContext(BreakpointWidthCtx);\n const px = useMemo(() => readBreakpointPx(key), [key]);\n const mediaQuery = belowQuery(px);\n\n const subscribe = useCallback((onChange: () => void) => {\n if (overrideWidth !== null || typeof window === \"undefined\") return noopSubscribe();\n const mql = window.matchMedia(mediaQuery);\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n }, [mediaQuery, overrideWidth]);\n\n const matches = useSyncExternalStore(\n subscribe,\n () => (typeof window !== \"undefined\" ? window.matchMedia(mediaQuery).matches : false),\n serverSnapshot,\n );\n\n // a preview's width is a device's at the default text size: below the breakpoint, as the query\n if (overrideWidth !== null) return overrideWidth < px;\n return matches;\n}\n"]}
@@ -1,9 +1,14 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ColumnDef } from '@tanstack/react-table';
3
- import { B as BreakpointKey, C as ColumnKind } from './table-utils-BkjNEWts.js';
4
- import { P as Pager } from './use-pagination-BxcRlNQG.js';
3
+ import { B as BreakpointKey, C as ColumnKind } from './table-utils-BXkP-2Hx.js';
4
+ import { P as Pager } from './use-pagination-QykGSREv.js';
5
5
  import 'react';
6
6
 
7
+ /** One of a choice column's options: the value its cells hold, and the words shown for it. */
8
+ interface DataGridOption {
9
+ value: string | number;
10
+ label: string;
11
+ }
7
12
  /** Column meta for opt-in truncation. Set on ColumnDef.meta. */
8
13
  interface DataGridColumnMeta {
9
14
  /** true = single-line, or { lines: N } for multi-line clamp. */
@@ -16,15 +21,29 @@ interface DataGridColumnMeta {
16
21
  width?: string;
17
22
  /** CSS min-width for the column. */
18
23
  minWidth?: string;
19
- /** Explicit column kind. When omitted the DataGrid infers it from data values. */
24
+ /**
25
+ * Explicit column kind. When omitted the DataGrid infers it from data values (a yes or no too; never a choice). In an
26
+ * editable grid it chooses the editor, where the control can hold the cell's value: "number" a NumberInput for a
27
+ * number, "date" a DatePicker for a Date or an ISO date ("2026-10-14"), "choice" a Dropdown of `options`, "boolean" a
28
+ * Checkbox; any other kind, and a number or a date written as text ("18 KB", "12 Jan 2026"), a text Input.
29
+ */
20
30
  kind?: ColumnKind;
21
31
  /** Horizontal alignment of the header and the cells; a block element (a Checkbox, a Button) is laid out with flex so it follows too (round 14). Default: numbers right, everything else left. */
22
32
  align?: "left" | "center" | "right";
23
- /** In an editable grid, whether this column's cells edit (default true); false for a column that shows (a status, actions). */
33
+ /**
34
+ * In an editable grid, whether this column's cells edit (default true); false for a column that shows (a status, a
35
+ * calculated value, actions): its cells show no edit cue, and assistive tech hears them as read-only.
36
+ */
24
37
  editable?: boolean;
25
38
  /**
26
- * In an editable grid, the value onCellEdit gets from the text typed (default the text itself): a number column's
27
- * `(text) => Number(text)`, so a salary stays a number (the review of 30 September).
39
+ * A choice column's options (with `kind: "choice"`): its cells edit with a Dropdown of them, and a save gives the
40
+ * option's `value`. Give the column a `cell` that shows the option's label.
41
+ */
42
+ options?: readonly DataGridOption[];
43
+ /**
44
+ * In an editable grid, the value onCellEdit gets from what was typed in a text cell, or in a number cell as its text
45
+ * (default the text itself, or the number): a price written as text, `(text) => Number(text.replace(/[$,]/g, ""))`.
46
+ * A date, a choice and a yes or no are given as their own values.
28
47
  */
29
48
  parse?: (text: string) => unknown;
30
49
  }
@@ -66,4 +85,4 @@ interface DataGridProps<T> {
66
85
  }
67
86
  declare function DataGrid<T>({ data, columns, pageSize, editable, onCellEdit, mobileBreakpoint: mobileBreakpointProp, cardsBelow, pager: pagerProp, "aria-label": ariaLabel, className, }: DataGridProps<T>): react_jsx_runtime.JSX.Element;
68
87
 
69
- export { DataGrid, type DataGridColumnMeta };
88
+ export { DataGrid, type DataGridColumnMeta, type DataGridOption };