@ansh-ui/react 0.2.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 (111) hide show
  1. package/LICENSE +21 -0
  2. package/dist/chunk-5CFQFMP5.js +178 -0
  3. package/dist/chunk-5CFQFMP5.js.map +1 -0
  4. package/dist/chunk-6VRF7X5C.cjs +164 -0
  5. package/dist/chunk-6VRF7X5C.cjs.map +1 -0
  6. package/dist/chunk-AVPEA5PB.cjs +718 -0
  7. package/dist/chunk-AVPEA5PB.cjs.map +1 -0
  8. package/dist/chunk-C32LKF23.cjs +198 -0
  9. package/dist/chunk-C32LKF23.cjs.map +1 -0
  10. package/dist/chunk-C5JG6TMM.cjs +1200 -0
  11. package/dist/chunk-C5JG6TMM.cjs.map +1 -0
  12. package/dist/chunk-DATBU46V.cjs +614 -0
  13. package/dist/chunk-DATBU46V.cjs.map +1 -0
  14. package/dist/chunk-EGG4CP7C.js +320 -0
  15. package/dist/chunk-EGG4CP7C.js.map +1 -0
  16. package/dist/chunk-H34OXXJU.js +55 -0
  17. package/dist/chunk-H34OXXJU.js.map +1 -0
  18. package/dist/chunk-HO2YSF5O.js +187 -0
  19. package/dist/chunk-HO2YSF5O.js.map +1 -0
  20. package/dist/chunk-HPGCILO4.js +760 -0
  21. package/dist/chunk-HPGCILO4.js.map +1 -0
  22. package/dist/chunk-IBXLC5IE.js +154 -0
  23. package/dist/chunk-IBXLC5IE.js.map +1 -0
  24. package/dist/chunk-LDCZQP3R.js +1185 -0
  25. package/dist/chunk-LDCZQP3R.js.map +1 -0
  26. package/dist/chunk-LXCIXR5T.js +27 -0
  27. package/dist/chunk-LXCIXR5T.js.map +1 -0
  28. package/dist/chunk-O2PX54KX.js +94 -0
  29. package/dist/chunk-O2PX54KX.js.map +1 -0
  30. package/dist/chunk-OHBWBXLO.cjs +101 -0
  31. package/dist/chunk-OHBWBXLO.cjs.map +1 -0
  32. package/dist/chunk-OJN5JBNP.cjs +776 -0
  33. package/dist/chunk-OJN5JBNP.cjs.map +1 -0
  34. package/dist/chunk-OLTS6G5M.js +606 -0
  35. package/dist/chunk-OLTS6G5M.js.map +1 -0
  36. package/dist/chunk-PO5DU3FU.cjs +29 -0
  37. package/dist/chunk-PO5DU3FU.cjs.map +1 -0
  38. package/dist/chunk-QO3E7TJS.js +2940 -0
  39. package/dist/chunk-QO3E7TJS.js.map +1 -0
  40. package/dist/chunk-RHTLNSVW.cjs +2976 -0
  41. package/dist/chunk-RHTLNSVW.cjs.map +1 -0
  42. package/dist/chunk-SFU5EPSN.cjs +61 -0
  43. package/dist/chunk-SFU5EPSN.cjs.map +1 -0
  44. package/dist/chunk-T4AK2KLM.js +699 -0
  45. package/dist/chunk-T4AK2KLM.js.map +1 -0
  46. package/dist/chunk-WGYNCSTP.cjs +333 -0
  47. package/dist/chunk-WGYNCSTP.cjs.map +1 -0
  48. package/dist/chunk-XEP6P7E6.js +279 -0
  49. package/dist/chunk-XEP6P7E6.js.map +1 -0
  50. package/dist/chunk-XMXECSQO.cjs +286 -0
  51. package/dist/chunk-XMXECSQO.cjs.map +1 -0
  52. package/dist/chunk-ZWAFLHC2.cjs +181 -0
  53. package/dist/chunk-ZWAFLHC2.cjs.map +1 -0
  54. package/dist/data.cjs +71 -0
  55. package/dist/data.cjs.map +1 -0
  56. package/dist/data.d.cts +316 -0
  57. package/dist/data.d.ts +316 -0
  58. package/dist/data.js +6 -0
  59. package/dist/data.js.map +1 -0
  60. package/dist/form.cjs +153 -0
  61. package/dist/form.cjs.map +1 -0
  62. package/dist/form.d.cts +660 -0
  63. package/dist/form.d.ts +660 -0
  64. package/dist/form.js +8 -0
  65. package/dist/form.js.map +1 -0
  66. package/dist/index.cjs +1641 -0
  67. package/dist/index.cjs.map +1 -0
  68. package/dist/index.d.cts +552 -0
  69. package/dist/index.d.ts +552 -0
  70. package/dist/index.js +956 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/layout.cjs +68 -0
  73. package/dist/layout.cjs.map +1 -0
  74. package/dist/layout.d.cts +169 -0
  75. package/dist/layout.d.ts +169 -0
  76. package/dist/layout.js +7 -0
  77. package/dist/layout.js.map +1 -0
  78. package/dist/motion-CQmCq6_k.d.ts +395 -0
  79. package/dist/motion-CSX4g2N8.d.cts +395 -0
  80. package/dist/motion.cjs +79 -0
  81. package/dist/motion.cjs.map +1 -0
  82. package/dist/motion.d.cts +4 -0
  83. package/dist/motion.d.ts +4 -0
  84. package/dist/motion.js +6 -0
  85. package/dist/motion.js.map +1 -0
  86. package/dist/navigation.cjs +48 -0
  87. package/dist/navigation.cjs.map +1 -0
  88. package/dist/navigation.d.cts +210 -0
  89. package/dist/navigation.d.ts +210 -0
  90. package/dist/navigation.js +7 -0
  91. package/dist/navigation.js.map +1 -0
  92. package/dist/overlay.cjs +69 -0
  93. package/dist/overlay.cjs.map +1 -0
  94. package/dist/overlay.d.cts +330 -0
  95. package/dist/overlay.d.ts +330 -0
  96. package/dist/overlay.js +8 -0
  97. package/dist/overlay.js.map +1 -0
  98. package/dist/position-DVScKcaa.d.cts +66 -0
  99. package/dist/position-DVScKcaa.d.ts +66 -0
  100. package/dist/primitives.cjs +162 -0
  101. package/dist/primitives.cjs.map +1 -0
  102. package/dist/primitives.d.cts +294 -0
  103. package/dist/primitives.d.ts +294 -0
  104. package/dist/primitives.js +9 -0
  105. package/dist/primitives.js.map +1 -0
  106. package/dist/sidebar-D-PafAXw.d.cts +71 -0
  107. package/dist/sidebar-D-PafAXw.d.ts +71 -0
  108. package/dist/styles.css +5575 -0
  109. package/dist/use-observers-B9qtimrk.d.cts +99 -0
  110. package/dist/use-observers-B9qtimrk.d.ts +99 -0
  111. package/package.json +92 -0
@@ -0,0 +1,1185 @@
1
+ import { Slot, usePresence, useFocusTrap, useAutoFocus, useFocusRestore } from './chunk-HO2YSF5O.js';
2
+ import { useAnchoredPosition, useDismiss, Portal, useScrollLock } from './chunk-XEP6P7E6.js';
3
+ import { useControllableState } from './chunk-LXCIXR5T.js';
4
+ import { cx } from './chunk-H34OXXJU.js';
5
+ import { createContext, useState, useRef, useId, useMemo, useEffect, useCallback, useContext } from 'react';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+
8
+ function score(query, text) {
9
+ if (query.length === 0) return 0;
10
+ const q = query.toLowerCase();
11
+ const t = text.toLowerCase();
12
+ let queryIndex = 0;
13
+ let total = 0;
14
+ let run = 0;
15
+ for (let i = 0; i < t.length && queryIndex < q.length; i++) {
16
+ if (t[i] === q[queryIndex]) {
17
+ run++;
18
+ total += run * 2 + (i === 0 ? 8 : 0);
19
+ queryIndex++;
20
+ } else {
21
+ run = 0;
22
+ }
23
+ }
24
+ return queryIndex === q.length ? total : -1;
25
+ }
26
+ function CommandPalette({
27
+ items,
28
+ open,
29
+ onOpenChange,
30
+ placeholder = "Type a command or search\u2026",
31
+ emptyMessage = "No results found."
32
+ }) {
33
+ const [query, setQuery] = useState("");
34
+ const [active, setActive] = useState(0);
35
+ const inputRef = useRef(null);
36
+ const dialogRef = useRef(null);
37
+ const restoreFocusRef = useRef(null);
38
+ const listId = useId();
39
+ const results = useMemo(() => {
40
+ if (query.trim() === "") return [...items];
41
+ return items.map((item) => ({
42
+ item,
43
+ score: Math.max(score(query, item.label), score(query, item.keywords ?? ""))
44
+ })).filter((entry) => entry.score >= 0).sort((a, b) => b.score - a.score).map((entry) => entry.item);
45
+ }, [items, query]);
46
+ useEffect(() => setActive(0), [query, items]);
47
+ const close = useCallback(() => {
48
+ onOpenChange(false);
49
+ setQuery("");
50
+ }, [onOpenChange]);
51
+ const select = useCallback(
52
+ (item) => {
53
+ if (!item) return;
54
+ item.onSelect?.();
55
+ close();
56
+ },
57
+ [close]
58
+ );
59
+ useEffect(() => {
60
+ if (!open) return;
61
+ restoreFocusRef.current = document.activeElement;
62
+ const raf = requestAnimationFrame(() => inputRef.current?.focus());
63
+ const previousOverflow = document.body.style.overflow;
64
+ document.body.style.overflow = "hidden";
65
+ return () => {
66
+ cancelAnimationFrame(raf);
67
+ document.body.style.overflow = previousOverflow;
68
+ restoreFocusRef.current?.focus?.();
69
+ };
70
+ }, [open]);
71
+ const onKeyDown = (event) => {
72
+ switch (event.key) {
73
+ case "ArrowDown":
74
+ event.preventDefault();
75
+ setActive((i) => results.length === 0 ? 0 : (i + 1) % results.length);
76
+ break;
77
+ case "ArrowUp":
78
+ event.preventDefault();
79
+ setActive((i) => results.length === 0 ? 0 : (i - 1 + results.length) % results.length);
80
+ break;
81
+ case "Home":
82
+ event.preventDefault();
83
+ setActive(0);
84
+ break;
85
+ case "End":
86
+ event.preventDefault();
87
+ setActive(Math.max(0, results.length - 1));
88
+ break;
89
+ case "Enter":
90
+ event.preventDefault();
91
+ select(results[active]);
92
+ break;
93
+ case "Escape":
94
+ event.preventDefault();
95
+ close();
96
+ break;
97
+ case "Tab": {
98
+ event.preventDefault();
99
+ break;
100
+ }
101
+ }
102
+ };
103
+ useEffect(() => {
104
+ if (!open) return;
105
+ const node = document.getElementById(`${listId}-option-${active}`);
106
+ node?.scrollIntoView?.({ block: "nearest" });
107
+ }, [active, listId, open]);
108
+ if (!open) return null;
109
+ let lastGroup;
110
+ return /* @__PURE__ */ jsx(
111
+ "div",
112
+ {
113
+ className: "ansh-cmdk__backdrop",
114
+ onMouseDown: (event) => {
115
+ if (event.target === event.currentTarget) close();
116
+ },
117
+ children: /* @__PURE__ */ jsxs(
118
+ "div",
119
+ {
120
+ ref: dialogRef,
121
+ className: "ansh-cmdk",
122
+ role: "dialog",
123
+ "aria-modal": "true",
124
+ "aria-label": "Command palette",
125
+ children: [
126
+ /* @__PURE__ */ jsx(
127
+ "input",
128
+ {
129
+ ref: inputRef,
130
+ className: "ansh-cmdk__input",
131
+ value: query,
132
+ onChange: (event) => setQuery(event.target.value),
133
+ onKeyDown,
134
+ placeholder,
135
+ role: "combobox",
136
+ "aria-expanded": "true",
137
+ "aria-controls": listId,
138
+ "aria-autocomplete": "list",
139
+ "aria-activedescendant": results.length > 0 ? `${listId}-option-${active}` : void 0,
140
+ autoComplete: "off",
141
+ spellCheck: false
142
+ }
143
+ ),
144
+ results.length === 0 ? /* @__PURE__ */ jsx("div", { className: "ansh-cmdk__empty", children: emptyMessage }) : /* @__PURE__ */ jsx("ul", { className: "ansh-cmdk__list", id: listId, role: "listbox", "aria-label": "Commands", children: results.map((item, index) => {
145
+ const heading = item.group && item.group !== lastGroup ? item.group : null;
146
+ lastGroup = item.group;
147
+ return /* @__PURE__ */ jsxs("li", { role: "presentation", children: [
148
+ heading ? /* @__PURE__ */ jsx("div", { className: "ansh-cmdk__group", role: "presentation", children: heading }) : null,
149
+ /* @__PURE__ */ jsxs(
150
+ "div",
151
+ {
152
+ id: `${listId}-option-${index}`,
153
+ className: "ansh-cmdk__item",
154
+ role: "option",
155
+ "aria-selected": index === active,
156
+ onMouseEnter: () => setActive(index),
157
+ onClick: () => select(item),
158
+ children: [
159
+ item.icon,
160
+ /* @__PURE__ */ jsx("span", { children: item.label }),
161
+ item.shortcut ? /* @__PURE__ */ jsx("kbd", { className: "ansh-cmdk__kbd", children: item.shortcut }) : null
162
+ ]
163
+ }
164
+ )
165
+ ] }, item.id);
166
+ }) })
167
+ ]
168
+ }
169
+ )
170
+ }
171
+ );
172
+ }
173
+ function useCommandShortcut(toggle, key = "k") {
174
+ const toggleRef = useRef(toggle);
175
+ toggleRef.current = toggle;
176
+ useEffect(() => {
177
+ const onKeyDown = (event) => {
178
+ if (event.key.toLowerCase() === key && (event.metaKey || event.ctrlKey)) {
179
+ event.preventDefault();
180
+ toggleRef.current();
181
+ }
182
+ };
183
+ window.addEventListener("keydown", onKeyDown);
184
+ return () => window.removeEventListener("keydown", onKeyDown);
185
+ }, [key]);
186
+ }
187
+ var PopoverContext = createContext(null);
188
+ function usePopover(component) {
189
+ const context = useContext(PopoverContext);
190
+ if (!context) {
191
+ throw new Error(`<${component}> must be rendered inside <Popover>.`);
192
+ }
193
+ return context;
194
+ }
195
+ function Popover({
196
+ open,
197
+ defaultOpen = false,
198
+ onOpenChange,
199
+ placement = "bottom",
200
+ offset = 8,
201
+ modal = false,
202
+ children
203
+ }) {
204
+ const [isOpen, setOpen] = useControllableState({
205
+ value: open,
206
+ defaultValue: defaultOpen,
207
+ onChange: onOpenChange
208
+ });
209
+ const triggerRef = useRef(null);
210
+ const contentId = useId();
211
+ const context = useMemo(
212
+ () => ({ open: isOpen, setOpen, triggerRef, contentId, placement, offset, modal }),
213
+ [isOpen, setOpen, contentId, placement, offset, modal]
214
+ );
215
+ return /* @__PURE__ */ jsx(PopoverContext.Provider, { value: context, children });
216
+ }
217
+ function PopoverTrigger({ asChild = false, children, ...rest }) {
218
+ const { open, setOpen, triggerRef, contentId } = usePopover("PopoverTrigger");
219
+ const Component = asChild ? Slot : "button";
220
+ return /* @__PURE__ */ jsx(
221
+ Component,
222
+ {
223
+ ref: triggerRef,
224
+ ...asChild ? null : { type: "button" },
225
+ "aria-haspopup": "dialog",
226
+ "aria-expanded": open,
227
+ "aria-controls": open ? contentId : void 0,
228
+ ...rest,
229
+ onClick: (event) => {
230
+ rest.onClick?.(event);
231
+ if (event.defaultPrevented) return;
232
+ setOpen(!open);
233
+ },
234
+ children
235
+ }
236
+ );
237
+ }
238
+ function PopoverContent({
239
+ arrow = false,
240
+ matchWidth = false,
241
+ label,
242
+ className,
243
+ style,
244
+ children,
245
+ ...rest
246
+ }) {
247
+ const { open, setOpen, triggerRef, contentId, placement, offset, modal } = usePopover("PopoverContent");
248
+ const contentRef = useRef(null);
249
+ const { mounted, state } = usePresence(open, contentRef);
250
+ const position = useAnchoredPosition(triggerRef, contentRef, mounted, {
251
+ placement,
252
+ offset,
253
+ matchWidth,
254
+ arrowSize: arrow ? 10 : 0
255
+ });
256
+ useDismiss(contentRef, open, () => setOpen(false), { ignore: [triggerRef] });
257
+ useFocusTrap(contentRef, open && modal);
258
+ useAutoFocus(contentRef, open);
259
+ useFocusRestore(open);
260
+ if (!mounted) return null;
261
+ return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(
262
+ "div",
263
+ {
264
+ ref: contentRef,
265
+ id: contentId,
266
+ role: "dialog",
267
+ "aria-modal": modal || void 0,
268
+ ...label ? { "aria-label": label } : null,
269
+ tabIndex: -1,
270
+ "data-state": state,
271
+ "data-side": position.side,
272
+ className: cx("ansh-popover", className),
273
+ style: { ...position.style, ...style },
274
+ ...rest,
275
+ children: [
276
+ children,
277
+ arrow && position.arrowStyle ? /* @__PURE__ */ jsx("span", { className: "ansh-popover__arrow", style: position.arrowStyle, "aria-hidden": "true" }) : null
278
+ ]
279
+ }
280
+ ) });
281
+ }
282
+ function HoverCard({
283
+ content,
284
+ openDelay = 500,
285
+ closeDelay = 200,
286
+ placement = "bottom",
287
+ className,
288
+ children
289
+ }) {
290
+ const [open, setOpen] = useState(false);
291
+ const triggerRef = useRef(null);
292
+ const cardRef = useRef(null);
293
+ const timerRef = useRef(void 0);
294
+ const id = useId();
295
+ const { mounted, state } = usePresence(open, cardRef);
296
+ const position = useAnchoredPosition(triggerRef, cardRef, mounted, { placement, offset: 8 });
297
+ const schedule = (next) => {
298
+ clearTimeout(timerRef.current);
299
+ timerRef.current = setTimeout(() => setOpen(next), next ? openDelay : closeDelay);
300
+ };
301
+ useEffect(() => () => clearTimeout(timerRef.current), []);
302
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
303
+ /* @__PURE__ */ jsx(
304
+ "span",
305
+ {
306
+ ref: triggerRef,
307
+ className: "ansh-hovercard__trigger",
308
+ "aria-describedby": open ? id : void 0,
309
+ onMouseEnter: () => schedule(true),
310
+ onMouseLeave: () => schedule(false),
311
+ onFocus: () => setOpen(true),
312
+ onBlur: () => setOpen(false),
313
+ children
314
+ }
315
+ ),
316
+ mounted ? /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
317
+ "div",
318
+ {
319
+ ref: cardRef,
320
+ id,
321
+ role: "tooltip",
322
+ "data-state": state,
323
+ className: cx("ansh-hovercard", className),
324
+ style: position.style,
325
+ onMouseEnter: () => schedule(true),
326
+ onMouseLeave: () => schedule(false),
327
+ children: content
328
+ }
329
+ ) }) : null
330
+ ] });
331
+ }
332
+ var TooltipGroupContext = createContext(null);
333
+ function TooltipProvider({ delay = 600, children }) {
334
+ const [active, setActive] = useState(false);
335
+ const value = useMemo(() => ({ active, setActive, delay }), [active, delay]);
336
+ return /* @__PURE__ */ jsx(TooltipGroupContext.Provider, { value, children });
337
+ }
338
+ function Tooltip({
339
+ content,
340
+ placement = "top",
341
+ delay,
342
+ arrow = true,
343
+ disabled = false,
344
+ className,
345
+ children
346
+ }) {
347
+ const group = useContext(TooltipGroupContext);
348
+ const [open, setOpen] = useState(false);
349
+ const triggerRef = useRef(null);
350
+ const tipRef = useRef(null);
351
+ const timerRef = useRef(void 0);
352
+ const id = useId();
353
+ const { mounted, state } = usePresence(open, tipRef);
354
+ const position = useAnchoredPosition(triggerRef, tipRef, mounted, {
355
+ placement,
356
+ offset: 6,
357
+ arrowSize: arrow ? 8 : 0
358
+ });
359
+ const effectiveDelay = delay ?? group?.delay ?? 600;
360
+ const show = (immediate = false) => {
361
+ if (disabled) return;
362
+ clearTimeout(timerRef.current);
363
+ if (immediate || group?.active) {
364
+ setOpen(true);
365
+ group?.setActive(true);
366
+ return;
367
+ }
368
+ timerRef.current = setTimeout(() => {
369
+ setOpen(true);
370
+ group?.setActive(true);
371
+ }, effectiveDelay);
372
+ };
373
+ const hide = () => {
374
+ clearTimeout(timerRef.current);
375
+ setOpen(false);
376
+ timerRef.current = setTimeout(() => group?.setActive(false), 200);
377
+ };
378
+ useEffect(() => () => clearTimeout(timerRef.current), []);
379
+ useEffect(() => {
380
+ if (!open) return;
381
+ const onKeyDown = (event) => {
382
+ if (event.key === "Escape") {
383
+ setOpen(false);
384
+ group?.setActive(false);
385
+ }
386
+ };
387
+ document.addEventListener("keydown", onKeyDown);
388
+ return () => document.removeEventListener("keydown", onKeyDown);
389
+ }, [open, group]);
390
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
391
+ /* @__PURE__ */ jsx(
392
+ Slot,
393
+ {
394
+ ref: triggerRef,
395
+ "aria-describedby": open ? id : void 0,
396
+ onMouseEnter: () => show(),
397
+ onMouseLeave: hide,
398
+ onFocus: () => show(true),
399
+ onBlur: hide,
400
+ children
401
+ }
402
+ ),
403
+ mounted && !disabled ? /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(
404
+ "div",
405
+ {
406
+ ref: tipRef,
407
+ id,
408
+ role: "tooltip",
409
+ "data-state": state,
410
+ "data-side": position.side,
411
+ className: cx("ansh-tooltip", className),
412
+ style: position.style,
413
+ children: [
414
+ content,
415
+ arrow && position.arrowStyle ? /* @__PURE__ */ jsx(
416
+ "span",
417
+ {
418
+ className: "ansh-tooltip__arrow",
419
+ style: position.arrowStyle,
420
+ "aria-hidden": "true"
421
+ }
422
+ ) : null
423
+ ]
424
+ }
425
+ ) }) : null
426
+ ] });
427
+ }
428
+ function Dialog({
429
+ open,
430
+ onOpenChange,
431
+ title,
432
+ description,
433
+ size = "md",
434
+ footer,
435
+ showClose = true,
436
+ dismissable = true,
437
+ initialFocus,
438
+ className,
439
+ children
440
+ }) {
441
+ const panelRef = useRef(null);
442
+ const id = useId();
443
+ const titleId = `${id}-title`;
444
+ const descriptionId = `${id}-description`;
445
+ const { mounted, state } = usePresence(open, panelRef);
446
+ const close = useCallback(() => {
447
+ if (dismissable) onOpenChange(false);
448
+ }, [dismissable, onOpenChange]);
449
+ useDismiss(panelRef, open, close, { outsidePress: dismissable, escapeKey: dismissable });
450
+ useFocusTrap(panelRef, open);
451
+ useAutoFocus(panelRef, open, initialFocus);
452
+ useFocusRestore(open);
453
+ useScrollLock(open);
454
+ if (!mounted) return null;
455
+ return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs("div", { className: "ansh-dialog__layer", "data-state": state, children: [
456
+ /* @__PURE__ */ jsx("div", { className: "ansh-dialog__scrim", "data-state": state, "aria-hidden": "true" }),
457
+ /* @__PURE__ */ jsxs(
458
+ "div",
459
+ {
460
+ ref: panelRef,
461
+ role: "dialog",
462
+ "aria-modal": "true",
463
+ "aria-labelledby": titleId,
464
+ ...description ? { "aria-describedby": descriptionId } : null,
465
+ tabIndex: -1,
466
+ "data-state": state,
467
+ className: cx("ansh-dialog", `ansh-dialog--${size}`, className),
468
+ children: [
469
+ /* @__PURE__ */ jsxs("header", { className: "ansh-dialog__header", children: [
470
+ /* @__PURE__ */ jsxs("div", { className: "ansh-dialog__heading", children: [
471
+ /* @__PURE__ */ jsx("h2", { className: "ansh-dialog__title", id: titleId, children: title }),
472
+ description ? /* @__PURE__ */ jsx("p", { className: "ansh-dialog__description", id: descriptionId, children: description }) : null
473
+ ] }),
474
+ showClose ? /* @__PURE__ */ jsx(
475
+ "button",
476
+ {
477
+ type: "button",
478
+ className: "ansh-dialog__close ansh-focusable",
479
+ "aria-label": "Close dialog",
480
+ onClick: () => onOpenChange(false),
481
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: "14", height: "14", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
482
+ "path",
483
+ {
484
+ d: "M4 4l8 8M12 4l-8 8",
485
+ stroke: "currentColor",
486
+ strokeWidth: "1.75",
487
+ strokeLinecap: "round",
488
+ fill: "none"
489
+ }
490
+ ) })
491
+ }
492
+ ) : null
493
+ ] }),
494
+ children ? /* @__PURE__ */ jsx("div", { className: "ansh-dialog__body", children }) : null,
495
+ footer ? /* @__PURE__ */ jsx("footer", { className: "ansh-dialog__footer", children: footer }) : null
496
+ ]
497
+ }
498
+ )
499
+ ] }) });
500
+ }
501
+ function AlertDialog({
502
+ open,
503
+ onOpenChange,
504
+ title,
505
+ description,
506
+ confirmLabel = "Confirm",
507
+ cancelLabel = "Cancel",
508
+ destructive = false,
509
+ onConfirm,
510
+ onCancel,
511
+ busy = false,
512
+ className
513
+ }) {
514
+ const panelRef = useRef(null);
515
+ const cancelRef = useRef(null);
516
+ const id = useId();
517
+ const { mounted, state } = usePresence(open, panelRef);
518
+ const cancel = useCallback(() => {
519
+ onCancel?.();
520
+ onOpenChange(false);
521
+ }, [onCancel, onOpenChange]);
522
+ useDismiss(panelRef, open, cancel, { outsidePress: false, escapeKey: true });
523
+ useFocusTrap(panelRef, open);
524
+ useAutoFocus(panelRef, open, cancelRef);
525
+ useFocusRestore(open);
526
+ useScrollLock(open);
527
+ if (!mounted) return null;
528
+ return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs("div", { className: "ansh-dialog__layer", "data-state": state, children: [
529
+ /* @__PURE__ */ jsx("div", { className: "ansh-dialog__scrim", "data-state": state, "aria-hidden": "true" }),
530
+ /* @__PURE__ */ jsxs(
531
+ "div",
532
+ {
533
+ ref: panelRef,
534
+ role: "alertdialog",
535
+ "aria-modal": "true",
536
+ "aria-labelledby": `${id}-title`,
537
+ "aria-describedby": `${id}-description`,
538
+ tabIndex: -1,
539
+ "data-state": state,
540
+ className: cx("ansh-dialog", "ansh-dialog--sm", "ansh-dialog--alert", className),
541
+ children: [
542
+ /* @__PURE__ */ jsxs("div", { className: "ansh-dialog__body", children: [
543
+ destructive ? /* @__PURE__ */ jsx(
544
+ "span",
545
+ {
546
+ className: "ansh-dialog__alert-icon ansh-dialog__alert-icon--danger",
547
+ "aria-hidden": "true",
548
+ children: /* @__PURE__ */ jsxs(
549
+ "svg",
550
+ {
551
+ viewBox: "0 0 24 24",
552
+ width: "20",
553
+ height: "20",
554
+ fill: "none",
555
+ stroke: "currentColor",
556
+ strokeWidth: "1.75",
557
+ children: [
558
+ /* @__PURE__ */ jsx("path", { d: "M12 8v5", strokeLinecap: "round" }),
559
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "16.5", r: "1", fill: "currentColor", stroke: "none" }),
560
+ /* @__PURE__ */ jsx(
561
+ "path",
562
+ {
563
+ d: "M10.3 3.9L2.6 17.4A2 2 0 004.3 20.4h15.4a2 2 0 001.7-3L13.7 3.9a2 2 0 00-3.4 0z",
564
+ strokeLinejoin: "round"
565
+ }
566
+ )
567
+ ]
568
+ }
569
+ )
570
+ }
571
+ ) : null,
572
+ /* @__PURE__ */ jsx("h2", { className: "ansh-dialog__title", id: `${id}-title`, children: title }),
573
+ /* @__PURE__ */ jsx("p", { className: "ansh-dialog__description", id: `${id}-description`, children: description })
574
+ ] }),
575
+ /* @__PURE__ */ jsxs("footer", { className: "ansh-dialog__footer", children: [
576
+ /* @__PURE__ */ jsx(
577
+ "button",
578
+ {
579
+ ref: cancelRef,
580
+ type: "button",
581
+ className: "ansh-btn ansh-btn--ghost ansh-btn--sm ansh-focusable",
582
+ disabled: busy,
583
+ onClick: cancel,
584
+ children: cancelLabel
585
+ }
586
+ ),
587
+ /* @__PURE__ */ jsxs(
588
+ "button",
589
+ {
590
+ type: "button",
591
+ className: cx(
592
+ "ansh-btn",
593
+ "ansh-btn--sm",
594
+ destructive ? "ansh-btn--danger" : "ansh-btn--solid",
595
+ "ansh-focusable"
596
+ ),
597
+ disabled: busy,
598
+ onClick: () => void onConfirm(),
599
+ children: [
600
+ busy ? /* @__PURE__ */ jsx("span", { className: "ansh-spinner ansh-spinner--sm", "aria-hidden": "true" }) : null,
601
+ confirmLabel
602
+ ]
603
+ }
604
+ )
605
+ ] })
606
+ ]
607
+ }
608
+ )
609
+ ] }) });
610
+ }
611
+ function DialogFooter({ children, className }) {
612
+ return /* @__PURE__ */ jsx("div", { className: cx("ansh-dialog__actions", className), children });
613
+ }
614
+ function Drawer({
615
+ open,
616
+ onOpenChange,
617
+ side = "right",
618
+ title,
619
+ description,
620
+ size = "22rem",
621
+ footer,
622
+ showClose = true,
623
+ modal = true,
624
+ dismissable = true,
625
+ className,
626
+ children
627
+ }) {
628
+ const panelRef = useRef(null);
629
+ const id = useId();
630
+ const { mounted, state } = usePresence(open, panelRef);
631
+ const close = useCallback(() => {
632
+ if (dismissable) onOpenChange(false);
633
+ }, [dismissable, onOpenChange]);
634
+ useDismiss(panelRef, open, close, { outsidePress: dismissable && modal, escapeKey: dismissable });
635
+ useFocusTrap(panelRef, open && modal);
636
+ useAutoFocus(panelRef, open);
637
+ useFocusRestore(open);
638
+ useScrollLock(open && modal);
639
+ if (!mounted) return null;
640
+ const horizontal = side === "left" || side === "right";
641
+ return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(
642
+ "div",
643
+ {
644
+ className: cx("ansh-drawer__layer", !modal && "ansh-drawer__layer--inline"),
645
+ "data-state": state,
646
+ children: [
647
+ modal ? /* @__PURE__ */ jsx("div", { className: "ansh-dialog__scrim", "data-state": state, "aria-hidden": "true" }) : null,
648
+ /* @__PURE__ */ jsxs(
649
+ "div",
650
+ {
651
+ ref: panelRef,
652
+ role: "dialog",
653
+ "aria-modal": modal || void 0,
654
+ "aria-labelledby": `${id}-title`,
655
+ ...description ? { "aria-describedby": `${id}-description` } : null,
656
+ tabIndex: -1,
657
+ "data-state": state,
658
+ "data-side": side,
659
+ className: cx("ansh-drawer", `ansh-drawer--${side}`, className),
660
+ style: {
661
+ [horizontal ? "width" : "height"]: size,
662
+ ["--ansh-drawer-size"]: size
663
+ },
664
+ children: [
665
+ /* @__PURE__ */ jsxs("header", { className: "ansh-drawer__header", children: [
666
+ /* @__PURE__ */ jsxs("div", { className: "ansh-dialog__heading", children: [
667
+ /* @__PURE__ */ jsx("h2", { className: "ansh-dialog__title", id: `${id}-title`, children: title }),
668
+ description ? /* @__PURE__ */ jsx("p", { className: "ansh-dialog__description", id: `${id}-description`, children: description }) : null
669
+ ] }),
670
+ showClose ? /* @__PURE__ */ jsx(
671
+ "button",
672
+ {
673
+ type: "button",
674
+ className: "ansh-dialog__close ansh-focusable",
675
+ "aria-label": "Close panel",
676
+ onClick: () => onOpenChange(false),
677
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: "14", height: "14", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
678
+ "path",
679
+ {
680
+ d: "M4 4l8 8M12 4l-8 8",
681
+ stroke: "currentColor",
682
+ strokeWidth: "1.75",
683
+ strokeLinecap: "round",
684
+ fill: "none"
685
+ }
686
+ ) })
687
+ }
688
+ ) : null
689
+ ] }),
690
+ /* @__PURE__ */ jsx("div", { className: "ansh-drawer__body", children }),
691
+ footer ? /* @__PURE__ */ jsx("footer", { className: "ansh-drawer__footer", children: footer }) : null
692
+ ]
693
+ }
694
+ )
695
+ ]
696
+ }
697
+ ) });
698
+ }
699
+ function flatten(items) {
700
+ const rows = [];
701
+ for (const entry of items) {
702
+ if ("type" in entry && (entry.type === "separator" || entry.type === "label")) continue;
703
+ if ("type" in entry && entry.type === "radio") {
704
+ entry.options.forEach((option, optionIndex) => {
705
+ rows.push({ entry, optionIndex, disabled: option.disabled ?? false });
706
+ });
707
+ continue;
708
+ }
709
+ rows.push({ entry, disabled: entry.disabled ?? false });
710
+ }
711
+ return rows;
712
+ }
713
+ function DropdownMenu({
714
+ items,
715
+ placement = "bottom-start",
716
+ label = "Menu",
717
+ className,
718
+ children,
719
+ open: controlledOpen,
720
+ onOpenChange
721
+ }) {
722
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
723
+ const open = controlledOpen ?? uncontrolledOpen;
724
+ const setOpen = useCallback(
725
+ (next) => {
726
+ setUncontrolledOpen(next);
727
+ onOpenChange?.(next);
728
+ },
729
+ [onOpenChange]
730
+ );
731
+ const triggerRef = useRef(null);
732
+ const menuRef = useRef(null);
733
+ const menuId = useId();
734
+ const { mounted, state } = usePresence(open, menuRef);
735
+ const position = useAnchoredPosition(triggerRef, menuRef, mounted, { placement, offset: 6 });
736
+ useDismiss(menuRef, open, () => setOpen(false), { ignore: [triggerRef] });
737
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
738
+ /* @__PURE__ */ jsx(
739
+ Slot,
740
+ {
741
+ ref: triggerRef,
742
+ "aria-haspopup": "menu",
743
+ "aria-expanded": open,
744
+ "aria-controls": open ? menuId : void 0,
745
+ onClick: () => setOpen(!open),
746
+ onKeyDown: (event) => {
747
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
748
+ event.preventDefault();
749
+ setOpen(true);
750
+ }
751
+ },
752
+ children
753
+ }
754
+ ),
755
+ mounted ? /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
756
+ MenuSurface,
757
+ {
758
+ ref: menuRef,
759
+ id: menuId,
760
+ items,
761
+ label,
762
+ state,
763
+ style: position.style,
764
+ className,
765
+ onClose: () => {
766
+ setOpen(false);
767
+ triggerRef.current?.focus?.();
768
+ }
769
+ }
770
+ ) }) : null
771
+ ] });
772
+ }
773
+ function ContextMenu({
774
+ items,
775
+ label = "Context menu",
776
+ className,
777
+ children
778
+ }) {
779
+ const [point, setPoint] = useState(null);
780
+ const menuRef = useRef(null);
781
+ const anchorRef = useRef(null);
782
+ const menuId = useId();
783
+ const open = point !== null;
784
+ const { mounted, state } = usePresence(open, menuRef);
785
+ const position = useAnchoredPosition(anchorRef, menuRef, mounted, {
786
+ placement: "bottom-start",
787
+ offset: 2
788
+ });
789
+ useDismiss(menuRef, open, () => setPoint(null));
790
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
791
+ /* @__PURE__ */ jsx(
792
+ "span",
793
+ {
794
+ className: "ansh-contextmenu__area",
795
+ onContextMenu: (event) => {
796
+ event.preventDefault();
797
+ setPoint({ x: event.clientX, y: event.clientY });
798
+ },
799
+ onKeyDown: (event) => {
800
+ if (event.key === "ContextMenu" || event.shiftKey && event.key === "F10") {
801
+ event.preventDefault();
802
+ const rect = event.currentTarget.getBoundingClientRect();
803
+ setPoint({ x: rect.left + 8, y: rect.top + 8 });
804
+ }
805
+ },
806
+ children
807
+ }
808
+ ),
809
+ point ? /* @__PURE__ */ jsx(
810
+ "span",
811
+ {
812
+ ref: anchorRef,
813
+ "aria-hidden": "true",
814
+ style: { position: "fixed", left: point.x, top: point.y, width: 0, height: 0 }
815
+ }
816
+ ) : null,
817
+ mounted ? /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
818
+ MenuSurface,
819
+ {
820
+ ref: menuRef,
821
+ id: menuId,
822
+ items,
823
+ label,
824
+ state,
825
+ style: position.style,
826
+ className,
827
+ onClose: () => setPoint(null)
828
+ }
829
+ ) }) : null
830
+ ] });
831
+ }
832
+ var MenuSurface = function MenuSurface2({
833
+ ref,
834
+ id,
835
+ items,
836
+ label,
837
+ state,
838
+ style,
839
+ className,
840
+ onClose
841
+ }) {
842
+ const rows = flatten(items);
843
+ const itemRefs = useRef([]);
844
+ const [active, setActive] = useState(() => rows.findIndex((row) => !row.disabled));
845
+ const [openSubmenu, setOpenSubmenu] = useState(null);
846
+ const typeaheadRef = useRef({
847
+ buffer: "",
848
+ timer: void 0
849
+ });
850
+ useEffect(() => {
851
+ itemRefs.current[active]?.focus();
852
+ }, []);
853
+ const move = (delta) => {
854
+ if (rows.length === 0) return;
855
+ let next = active;
856
+ for (let attempts = 0; attempts < rows.length; attempts++) {
857
+ next = (next + delta + rows.length) % rows.length;
858
+ if (!rows[next]?.disabled) break;
859
+ }
860
+ setActive(next);
861
+ itemRefs.current[next]?.focus();
862
+ };
863
+ const activate = (index) => {
864
+ const row = rows[index];
865
+ if (!row || row.disabled) return;
866
+ const { entry } = row;
867
+ if ("type" in entry && entry.type === "submenu") {
868
+ setOpenSubmenu(index);
869
+ return;
870
+ }
871
+ if ("type" in entry && entry.type === "checkbox") {
872
+ entry.onChange(!entry.checked);
873
+ return;
874
+ }
875
+ if ("type" in entry && entry.type === "radio") {
876
+ entry.onChange(entry.options[row.optionIndex].value);
877
+ onClose();
878
+ return;
879
+ }
880
+ entry.onSelect?.();
881
+ onClose();
882
+ };
883
+ const onKeyDown = (event) => {
884
+ switch (event.key) {
885
+ case "ArrowDown":
886
+ event.preventDefault();
887
+ move(1);
888
+ break;
889
+ case "ArrowUp":
890
+ event.preventDefault();
891
+ move(-1);
892
+ break;
893
+ case "Home":
894
+ event.preventDefault();
895
+ setActive(0);
896
+ itemRefs.current[0]?.focus();
897
+ break;
898
+ case "End":
899
+ event.preventDefault();
900
+ setActive(rows.length - 1);
901
+ itemRefs.current[rows.length - 1]?.focus();
902
+ break;
903
+ case "Enter":
904
+ case " ":
905
+ event.preventDefault();
906
+ activate(active);
907
+ break;
908
+ case "ArrowRight": {
909
+ const row = rows[active];
910
+ if (row && "type" in row.entry && row.entry.type === "submenu") {
911
+ event.preventDefault();
912
+ setOpenSubmenu(active);
913
+ }
914
+ break;
915
+ }
916
+ case "Escape":
917
+ event.preventDefault();
918
+ onClose();
919
+ break;
920
+ case "Tab":
921
+ event.preventDefault();
922
+ onClose();
923
+ break;
924
+ default: {
925
+ if (event.key.length !== 1) return;
926
+ const state2 = typeaheadRef.current;
927
+ clearTimeout(state2.timer);
928
+ state2.buffer += event.key.toLowerCase();
929
+ state2.timer = setTimeout(() => {
930
+ state2.buffer = "";
931
+ }, 600);
932
+ const match = rows.findIndex((row, index) => {
933
+ if (row.disabled || index === active) return false;
934
+ const text = typeof row.entry.label === "string" ? row.entry.label : "";
935
+ return text.toLowerCase().startsWith(state2.buffer);
936
+ });
937
+ if (match >= 0) {
938
+ setActive(match);
939
+ itemRefs.current[match]?.focus();
940
+ }
941
+ }
942
+ }
943
+ };
944
+ let rowIndex = -1;
945
+ return /* @__PURE__ */ jsx(
946
+ "div",
947
+ {
948
+ ref,
949
+ id,
950
+ role: "menu",
951
+ "aria-label": label,
952
+ "data-state": state,
953
+ className: cx("ansh-menu", className),
954
+ style,
955
+ onKeyDown,
956
+ children: items.map((entry, entryIndex) => {
957
+ if ("type" in entry && entry.type === "separator") {
958
+ return /* @__PURE__ */ jsx("div", { role: "separator", className: "ansh-menu__separator" }, `sep-${entryIndex}`);
959
+ }
960
+ if ("type" in entry && entry.type === "label") {
961
+ return /* @__PURE__ */ jsx("div", { role: "presentation", className: "ansh-menu__label", children: entry.label }, `label-${entryIndex}`);
962
+ }
963
+ if ("type" in entry && entry.type === "radio") {
964
+ return /* @__PURE__ */ jsx("div", { role: "group", className: "ansh-menu__group", children: entry.options.map((option) => {
965
+ rowIndex++;
966
+ const index2 = rowIndex;
967
+ return /* @__PURE__ */ jsxs(
968
+ "div",
969
+ {
970
+ ref: (node) => {
971
+ itemRefs.current[index2] = node;
972
+ },
973
+ role: "menuitemradio",
974
+ "aria-checked": entry.value === option.value,
975
+ "aria-disabled": option.disabled || void 0,
976
+ tabIndex: index2 === active ? 0 : -1,
977
+ className: cx(
978
+ "ansh-menu__item",
979
+ option.disabled && "ansh-menu__item--disabled"
980
+ ),
981
+ onClick: () => activate(index2),
982
+ onMouseEnter: () => !option.disabled && setActive(index2),
983
+ children: [
984
+ /* @__PURE__ */ jsx(
985
+ "span",
986
+ {
987
+ className: cx(
988
+ "ansh-menu__indicator",
989
+ entry.value === option.value && "ansh-menu__indicator--on"
990
+ ),
991
+ "aria-hidden": "true"
992
+ }
993
+ ),
994
+ /* @__PURE__ */ jsx("span", { className: "ansh-menu__text", children: option.label })
995
+ ]
996
+ },
997
+ option.value
998
+ );
999
+ }) }, `radio-${entryIndex}`);
1000
+ }
1001
+ rowIndex++;
1002
+ const index = rowIndex;
1003
+ if ("type" in entry && entry.type === "checkbox") {
1004
+ return /* @__PURE__ */ jsxs(
1005
+ "div",
1006
+ {
1007
+ ref: (node) => {
1008
+ itemRefs.current[index] = node;
1009
+ },
1010
+ role: "menuitemcheckbox",
1011
+ "aria-checked": entry.checked,
1012
+ "aria-disabled": entry.disabled || void 0,
1013
+ tabIndex: index === active ? 0 : -1,
1014
+ className: cx("ansh-menu__item", entry.disabled && "ansh-menu__item--disabled"),
1015
+ onClick: () => activate(index),
1016
+ onMouseEnter: () => !entry.disabled && setActive(index),
1017
+ children: [
1018
+ /* @__PURE__ */ jsx(
1019
+ "span",
1020
+ {
1021
+ className: cx(
1022
+ "ansh-menu__indicator",
1023
+ "ansh-menu__indicator--check",
1024
+ entry.checked && "ansh-menu__indicator--on"
1025
+ ),
1026
+ "aria-hidden": "true",
1027
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 14 14", width: "10", height: "10", children: /* @__PURE__ */ jsx(
1028
+ "path",
1029
+ {
1030
+ d: "M2.5 7.5l3 3 6-6.5",
1031
+ stroke: "currentColor",
1032
+ strokeWidth: "2",
1033
+ strokeLinecap: "round",
1034
+ strokeLinejoin: "round",
1035
+ fill: "none"
1036
+ }
1037
+ ) })
1038
+ }
1039
+ ),
1040
+ /* @__PURE__ */ jsx("span", { className: "ansh-menu__text", children: entry.label }),
1041
+ entry.shortcut ? /* @__PURE__ */ jsx("kbd", { className: "ansh-menu__shortcut", children: entry.shortcut }) : null
1042
+ ]
1043
+ },
1044
+ entry.id ?? `check-${entryIndex}`
1045
+ );
1046
+ }
1047
+ if ("type" in entry && entry.type === "submenu") {
1048
+ return /* @__PURE__ */ jsx(
1049
+ Submenu,
1050
+ {
1051
+ ref: (node) => {
1052
+ itemRefs.current[index] = node;
1053
+ },
1054
+ entry,
1055
+ active: index === active,
1056
+ open: openSubmenu === index,
1057
+ onOpenChange: (next) => setOpenSubmenu(next ? index : null),
1058
+ onHover: () => setActive(index),
1059
+ onCloseAll: onClose
1060
+ },
1061
+ entry.id ?? `sub-${entryIndex}`
1062
+ );
1063
+ }
1064
+ const action = entry;
1065
+ return /* @__PURE__ */ jsxs(
1066
+ "div",
1067
+ {
1068
+ ref: (node) => {
1069
+ itemRefs.current[index] = node;
1070
+ },
1071
+ role: "menuitem",
1072
+ "aria-disabled": action.disabled || void 0,
1073
+ tabIndex: index === active ? 0 : -1,
1074
+ className: cx(
1075
+ "ansh-menu__item",
1076
+ action.danger && "ansh-menu__item--danger",
1077
+ action.disabled && "ansh-menu__item--disabled"
1078
+ ),
1079
+ onClick: () => activate(index),
1080
+ onMouseEnter: () => !action.disabled && setActive(index),
1081
+ children: [
1082
+ action.icon ? /* @__PURE__ */ jsx("span", { className: "ansh-menu__icon", children: action.icon }) : null,
1083
+ /* @__PURE__ */ jsx("span", { className: "ansh-menu__text", children: action.label }),
1084
+ action.shortcut ? /* @__PURE__ */ jsx("kbd", { className: "ansh-menu__shortcut", children: action.shortcut }) : null
1085
+ ]
1086
+ },
1087
+ action.id ?? `item-${entryIndex}`
1088
+ );
1089
+ })
1090
+ }
1091
+ );
1092
+ };
1093
+ function Submenu({ ref, entry, active, open, onOpenChange, onHover, onCloseAll }) {
1094
+ const triggerRef = useRef(null);
1095
+ const panelRef = useRef(null);
1096
+ const timerRef = useRef(void 0);
1097
+ const id = useId();
1098
+ const { mounted, state } = usePresence(open, panelRef);
1099
+ const position = useAnchoredPosition(triggerRef, panelRef, mounted, {
1100
+ placement: "right-start",
1101
+ offset: 2
1102
+ });
1103
+ useEffect(() => () => clearTimeout(timerRef.current), []);
1104
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1105
+ /* @__PURE__ */ jsxs(
1106
+ "div",
1107
+ {
1108
+ ref: (node) => {
1109
+ triggerRef.current = node;
1110
+ if (typeof ref === "function") ref(node);
1111
+ else if (ref) ref.current = node;
1112
+ },
1113
+ role: "menuitem",
1114
+ "aria-haspopup": "menu",
1115
+ "aria-expanded": open,
1116
+ "aria-controls": open ? id : void 0,
1117
+ "aria-disabled": entry.disabled || void 0,
1118
+ tabIndex: active ? 0 : -1,
1119
+ className: cx(
1120
+ "ansh-menu__item",
1121
+ "ansh-menu__item--submenu",
1122
+ entry.disabled && "ansh-menu__item--disabled"
1123
+ ),
1124
+ onMouseEnter: () => {
1125
+ onHover();
1126
+ clearTimeout(timerRef.current);
1127
+ timerRef.current = setTimeout(() => onOpenChange(true), 120);
1128
+ },
1129
+ onMouseLeave: () => {
1130
+ clearTimeout(timerRef.current);
1131
+ timerRef.current = setTimeout(() => onOpenChange(false), 200);
1132
+ },
1133
+ onClick: () => onOpenChange(!open),
1134
+ children: [
1135
+ entry.icon ? /* @__PURE__ */ jsx("span", { className: "ansh-menu__icon", children: entry.icon }) : null,
1136
+ /* @__PURE__ */ jsx("span", { className: "ansh-menu__text", children: entry.label }),
1137
+ /* @__PURE__ */ jsx("span", { className: "ansh-menu__chevron", "aria-hidden": "true", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: "12", height: "12", children: /* @__PURE__ */ jsx(
1138
+ "path",
1139
+ {
1140
+ d: "M4.5 2.5L8 6l-3.5 3.5",
1141
+ stroke: "currentColor",
1142
+ strokeWidth: "1.5",
1143
+ fill: "none",
1144
+ strokeLinecap: "round",
1145
+ strokeLinejoin: "round"
1146
+ }
1147
+ ) }) })
1148
+ ]
1149
+ }
1150
+ ),
1151
+ mounted ? /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
1152
+ "div",
1153
+ {
1154
+ onMouseEnter: () => clearTimeout(timerRef.current),
1155
+ onMouseLeave: () => {
1156
+ timerRef.current = setTimeout(() => onOpenChange(false), 200);
1157
+ },
1158
+ onKeyDown: (event) => {
1159
+ if (event.key === "ArrowLeft") {
1160
+ event.preventDefault();
1161
+ event.stopPropagation();
1162
+ onOpenChange(false);
1163
+ triggerRef.current?.focus();
1164
+ }
1165
+ },
1166
+ children: /* @__PURE__ */ jsx(
1167
+ MenuSurface,
1168
+ {
1169
+ ref: panelRef,
1170
+ id,
1171
+ items: entry.items,
1172
+ label: typeof entry.label === "string" ? entry.label : "Submenu",
1173
+ state,
1174
+ style: position.style,
1175
+ onClose: onCloseAll
1176
+ }
1177
+ )
1178
+ }
1179
+ ) }) : null
1180
+ ] });
1181
+ }
1182
+
1183
+ export { AlertDialog, CommandPalette, ContextMenu, Dialog, DialogFooter, Drawer, DropdownMenu, HoverCard, Popover, PopoverContent, PopoverTrigger, Tooltip, TooltipProvider, useCommandShortcut };
1184
+ //# sourceMappingURL=chunk-LDCZQP3R.js.map
1185
+ //# sourceMappingURL=chunk-LDCZQP3R.js.map