ajo-ui 0.1.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/LICENSE +15 -0
  2. package/README.md +126 -0
  3. package/dist/accordion.js +130 -0
  4. package/dist/avatar.js +54 -0
  5. package/dist/calendar.js +2 -0
  6. package/dist/carousel.js +239 -0
  7. package/dist/chart.js +790 -0
  8. package/dist/checkbox-group.js +70 -0
  9. package/dist/checkbox.js +77 -0
  10. package/dist/chunks/bar-CVafh6C1.js +99 -0
  11. package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
  12. package/dist/chunks/collection-DtRB63U4.js +111 -0
  13. package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
  14. package/dist/chunks/menu-B45IyHHC.js +704 -0
  15. package/dist/chunks/native-BJdhd9XJ.js +20 -0
  16. package/dist/chunks/popup-C8Bb3l_g.js +459 -0
  17. package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
  18. package/dist/chunks/position-D6_i_SRn.js +434 -0
  19. package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
  20. package/dist/collapsible.js +106 -0
  21. package/dist/command.js +263 -0
  22. package/dist/context-menu.js +112 -0
  23. package/dist/data-table.js +5 -0
  24. package/dist/dialog.js +207 -0
  25. package/dist/direction.js +22 -0
  26. package/dist/drawer.js +139 -0
  27. package/dist/field.js +26 -0
  28. package/dist/index.js +38 -0
  29. package/dist/input-date.js +994 -0
  30. package/dist/input-group.js +52 -0
  31. package/dist/input-otp.js +179 -0
  32. package/dist/menu.js +2 -0
  33. package/dist/menubar.js +236 -0
  34. package/dist/message-scroller.js +446 -0
  35. package/dist/navigation-menu.js +330 -0
  36. package/dist/popover.js +307 -0
  37. package/dist/progress.js +39 -0
  38. package/dist/radio-group.js +107 -0
  39. package/dist/resizable.js +172 -0
  40. package/dist/select.js +961 -0
  41. package/dist/sidebar.js +343 -0
  42. package/dist/slider.js +259 -0
  43. package/dist/switch.js +53 -0
  44. package/dist/tabs.js +182 -0
  45. package/dist/toast.js +492 -0
  46. package/dist/toggle-group.js +111 -0
  47. package/dist/toggle.js +52 -0
  48. package/dist/toolbar.js +127 -0
  49. package/dist/tooltip.js +196 -0
  50. package/dist/utils.js +104 -0
  51. package/dist/virtual-list.js +2 -0
  52. package/package.json +250 -0
  53. package/src/accordion.tsx +261 -0
  54. package/src/availability.ts +261 -0
  55. package/src/avatar.tsx +99 -0
  56. package/src/bar.ts +156 -0
  57. package/src/calendar.tsx +1441 -0
  58. package/src/carousel.tsx +424 -0
  59. package/src/chart.tsx +1194 -0
  60. package/src/checkbox-group.tsx +132 -0
  61. package/src/checkbox.tsx +130 -0
  62. package/src/collapsible.tsx +188 -0
  63. package/src/collection.ts +154 -0
  64. package/src/command.tsx +511 -0
  65. package/src/context-menu.tsx +233 -0
  66. package/src/data-table-contract.ts +143 -0
  67. package/src/data-table-model.ts +760 -0
  68. package/src/data-table.tsx +475 -0
  69. package/src/dialog.tsx +393 -0
  70. package/src/direction.tsx +45 -0
  71. package/src/drawer.tsx +251 -0
  72. package/src/field.tsx +61 -0
  73. package/src/index.ts +37 -0
  74. package/src/input-date.tsx +1539 -0
  75. package/src/input-group.tsx +142 -0
  76. package/src/input-otp.tsx +324 -0
  77. package/src/menu-cluster.ts +124 -0
  78. package/src/menu.tsx +1095 -0
  79. package/src/menubar.tsx +459 -0
  80. package/src/message-scroller.tsx +732 -0
  81. package/src/native.ts +26 -0
  82. package/src/navigation-menu.tsx +578 -0
  83. package/src/popover.tsx +519 -0
  84. package/src/popup-surface.tsx +31 -0
  85. package/src/popup.ts +569 -0
  86. package/src/position.ts +523 -0
  87. package/src/progress.tsx +70 -0
  88. package/src/radio-group.tsx +186 -0
  89. package/src/resizable.tsx +310 -0
  90. package/src/segments.ts +922 -0
  91. package/src/select.tsx +1501 -0
  92. package/src/sidebar.tsx +683 -0
  93. package/src/slider.tsx +424 -0
  94. package/src/switch.tsx +104 -0
  95. package/src/tabs.tsx +314 -0
  96. package/src/toast.tsx +923 -0
  97. package/src/toggle-group.tsx +249 -0
  98. package/src/toggle.tsx +91 -0
  99. package/src/toolbar.tsx +212 -0
  100. package/src/tooltip.tsx +359 -0
  101. package/src/utils.ts +204 -0
  102. package/src/virtual-list.tsx +205 -0
  103. package/src/virtual.ts +385 -0
@@ -0,0 +1,704 @@
1
+ import { flag, popupStyle, text, triggerAttrs } from "../utils.js";
2
+ import { t as collection } from "./collection-DtRB63U4.js";
3
+ import { n as popup, t as contentAttrs } from "./popup-C8Bb3l_g.js";
4
+ import { callHandler, controlled, dom, frame, listen, roving, statefulRootAttrs, typeahead } from "ajo-cloves";
5
+ import { context } from "ajo/context";
6
+ import { Fragment, jsx, jsxs } from "ajo/jsx-runtime";
7
+ //#region packages/ajo-ui/src/menu-cluster.ts
8
+ var CompositionContext = context(null);
9
+ var InvocationContext = context(null);
10
+ /** Selects ContextMenu policy for exactly one descendant Menu root. */
11
+ var provideContextMenuComposition = (invoke, restoreFocus) => CompositionContext({
12
+ invoke,
13
+ profile: "context",
14
+ restoreFocus
15
+ });
16
+ /** Selects Menubar policy and its commit-time focus acknowledgement. */
17
+ var provideMenubarComposition = (ackFocus) => CompositionContext({
18
+ ackFocus,
19
+ profile: "menubar"
20
+ });
21
+ /** Returns the private family policy selected by a composing Menu root. */
22
+ var menuComposition = () => CompositionContext();
23
+ /** Prevents a composing family policy from leaking into nested Menu roots. */
24
+ var isolateMenuComposition = () => CompositionContext(null);
25
+ /** Publishes Menu's private context-invocation controller to its trigger. */
26
+ var provideMenuInvocation = (value) => InvocationContext(value);
27
+ /** Prevents a context invocation controller from leaking through a nested Menu. */
28
+ var isolateMenuInvocation = () => InvocationContext(null);
29
+ /** Returns the nearest private context-invocation controller. */
30
+ var menuInvocation = () => InvocationContext();
31
+ /** Collection registry for menu items; disabled items are skipped in the focus order. */
32
+ var menuItems = collection("menu");
33
+ /** Matches root and submenu content surfaces. */
34
+ var SURFACE_SELECTOR = "[data-menu-sub-content=\"true\"],[data-menu-content=\"true\"]";
35
+ /** Items owned directly by one menu surface, excluding nested submenu items. */
36
+ var surfaceItems = (surface) => menuItems.items(surface).filter((item) => item.closest(SURFACE_SELECTOR) === surface);
37
+ /** Focuses the first or last direct item of one menu surface. */
38
+ var focusEdge = (surface, which) => {
39
+ const list = surfaceItems(surface);
40
+ menuItems.focusItem(surface, which === "first" ? list[0] : list[list.length - 1]);
41
+ };
42
+ /** Private tree seam: each menu level owns only its direct submenu branches. */
43
+ var cluster = () => {
44
+ const branches = /* @__PURE__ */ new Set();
45
+ const contains = (branch, target) => Boolean(branch.trigger()?.contains(target) || branch.content()?.contains(target));
46
+ return {
47
+ /** Registers one direct branch for exactly its mounted lifetime. */
48
+ register(branch) {
49
+ branches.add(branch);
50
+ return () => branches.delete(branch);
51
+ },
52
+ /** Closes every direct branch except an optional active sibling. */
53
+ close(event, except) {
54
+ for (const branch of [...branches]) if (branch !== except) branch.close(event);
55
+ },
56
+ /** Retains the branch containing target, closes siblings, and recurses. */
57
+ prune(target, event) {
58
+ for (const branch of [...branches]) if (contains(branch, target)) branch.prune(target, event);
59
+ else branch.close(event);
60
+ }
61
+ };
62
+ };
63
+ //#endregion
64
+ //#region packages/ajo-ui/src/menu.tsx
65
+ var MenuContext = context(null);
66
+ var RadioContext = context(null);
67
+ var SubContext = context(null);
68
+ var LevelContext = context(null);
69
+ var pointerHighlight = (disabled, closeSubmenus, keep) => (event) => {
70
+ if (disabled) return;
71
+ const content = event.currentTarget.closest(SURFACE_SELECTOR);
72
+ menuItems.focusItem(content, event.currentTarget);
73
+ closeSubmenus?.(event, keep);
74
+ };
75
+ var MenuRoot = function* ({ defaultOpen, open }) {
76
+ const composition = menuComposition();
77
+ const contextComposition = composition?.profile === "context" ? composition : null;
78
+ const ownerDocument = dom(this) ? this.ownerDocument : null;
79
+ const node = (value) => {
80
+ const view = ownerDocument?.defaultView;
81
+ return Boolean(view && value instanceof view.Node);
82
+ };
83
+ const submenus = cluster();
84
+ let contextSource = null;
85
+ let disabled = false;
86
+ let focusRestore = 0;
87
+ let geometryReady = false;
88
+ let onOpenChange;
89
+ let pendingFocus;
90
+ let menu;
91
+ const commitMenubarFocus = frame(() => {
92
+ if (composition?.profile === "menubar" && composition.ackFocus()) focusEdge(menu.content, "first");
93
+ });
94
+ this.signal.addEventListener("abort", commitMenubarFocus.cancel);
95
+ menu = popup(this, {
96
+ prefix: "menu",
97
+ profile: composition?.profile ?? "menu",
98
+ initialOpen: Boolean(open ?? defaultOpen),
99
+ disabled: () => disabled,
100
+ onOpenChange: (next, event) => onOpenChange?.(next, event),
101
+ reference: (view) => view.reference ?? view.trigger,
102
+ source: (view) => contextComposition ? contextSource : view.trigger ?? (dom(view.reference) ? view.reference : null),
103
+ reopenOnReferenceChange: Boolean(contextComposition),
104
+ referenceHidden: "close",
105
+ dismiss: {
106
+ prevent: true,
107
+ outside: true,
108
+ onDismiss: (event) => {
109
+ if (event.type === "keydown") close(event);
110
+ else setOpen(false, event);
111
+ }
112
+ },
113
+ onPosition: () => {
114
+ geometryReady = true;
115
+ if (pendingFocus === "content") menu.content?.focus();
116
+ else if (pendingFocus) focusEdge(menu.content, pendingFocus);
117
+ else if (composition?.profile === "menubar") commitMenubarFocus();
118
+ pendingFocus = void 0;
119
+ },
120
+ onSync: (opened) => {
121
+ if (!opened) {
122
+ commitMenubarFocus.cancel();
123
+ geometryReady = false;
124
+ menuItems.clearHighlight(menu.content);
125
+ submenus.close();
126
+ pendingFocus = void 0;
127
+ }
128
+ }
129
+ });
130
+ const focusWhenReady = (focus) => {
131
+ if (geometryReady) if (focus === "content") menu.content?.focus();
132
+ else focusEdge(menu.content, focus);
133
+ else pendingFocus = focus;
134
+ };
135
+ const setOpen = (next, event, focus) => {
136
+ if (disabled && next) return;
137
+ if (next) focusRestore++;
138
+ if (next === menu.open) {
139
+ if (next && focus) focusWhenReady(focus);
140
+ return;
141
+ }
142
+ if (!next) {
143
+ submenus.close(event);
144
+ pendingFocus = void 0;
145
+ } else geometryReady = false;
146
+ if (next && focus) pendingFocus = focus;
147
+ menu.setOpen(next, event);
148
+ };
149
+ const invoke = (reference, source, event, focus) => {
150
+ geometryReady = false;
151
+ pendingFocus = focus;
152
+ contextSource = source;
153
+ const changed = menu.reference !== reference;
154
+ submenus.close(event);
155
+ menu.setReference(reference);
156
+ if (menu.open) {
157
+ if (!changed) menu.update();
158
+ } else menu.setOpen(true, event);
159
+ };
160
+ const close = (event) => {
161
+ const wasOpen = menu.open;
162
+ const restore = ++focusRestore;
163
+ setOpen(false, event);
164
+ if (contextComposition) {
165
+ if (wasOpen && !menu.open) contextComposition.restoreFocus();
166
+ } else queueMicrotask(() => {
167
+ if (restore === focusRestore && wasOpen && !menu.open) menu.trigger?.focus();
168
+ });
169
+ };
170
+ const dismiss = (event) => {
171
+ const target = event.target;
172
+ const reference = menu.reference;
173
+ if (node(target) && Boolean(this.contains(target) || menu.trigger?.contains(target) || menu.content?.contains(target) || dom(reference) && reference.contains(target))) submenus.prune(target, event);
174
+ else setOpen(false, event);
175
+ };
176
+ const focusedSurface = () => {
177
+ const active = ownerDocument?.activeElement;
178
+ return (dom(active) ? active.closest("[data-menu-sub-content=\"true\"],[data-menu-content=\"true\"]") : null) ?? menu.content;
179
+ };
180
+ const nav = roving(this, {
181
+ items: () => surfaceItems(focusedSurface()),
182
+ onMove: (target) => menuItems.focusItem(focusedSurface(), target)
183
+ });
184
+ const ta = typeahead(this, {
185
+ items: () => surfaceItems(focusedSurface()),
186
+ onMatch: (target) => menuItems.focusItem(focusedSurface(), target)
187
+ });
188
+ listen(this, "keydown", (event) => {
189
+ if (event.defaultPrevented) return;
190
+ const target = event.target;
191
+ if (!target?.closest("[data-menu-trigger=\"true\"],[data-menu-content=\"true\"]")) return;
192
+ if (target.closest("[data-menu-trigger=\"true\"]")) {
193
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
194
+ event.preventDefault();
195
+ setOpen(true, event, "first");
196
+ } else if (event.key === "ArrowUp") {
197
+ event.preventDefault();
198
+ setOpen(true, event, "last");
199
+ }
200
+ return;
201
+ }
202
+ if (nav.handle(event)) return;
203
+ else if (event.key === "Enter" || event.key === " ") {
204
+ const item = menuItems.item(event);
205
+ if (!item) return;
206
+ event.preventDefault();
207
+ item.click();
208
+ } else if (ta.handle(event)) event.preventDefault();
209
+ });
210
+ for (const args of this) {
211
+ disabled = Boolean(args.disabled);
212
+ onOpenChange = args.onOpenChange;
213
+ const wasOpen = menu.open;
214
+ const opened = menu.sync(args.open != null ? Boolean(args.open) : null, {
215
+ placement: args.placement,
216
+ gap: args.gap
217
+ });
218
+ if (!wasOpen && opened) focusRestore++;
219
+ if (wasOpen && !opened) {
220
+ geometryReady = false;
221
+ commitMenubarFocus.cancel();
222
+ pendingFocus = void 0;
223
+ submenus.close();
224
+ }
225
+ isolateMenuComposition();
226
+ isolateMenuInvocation();
227
+ MenuContext({
228
+ adoptTriggerId: menu.adoptTriggerId,
229
+ close,
230
+ contentId: menu.contentId,
231
+ contentStyle: menu.contentStyle,
232
+ disabled,
233
+ dismiss,
234
+ open: opened,
235
+ setContent: menu.setContent,
236
+ setOpen,
237
+ setTrigger: menu.setTrigger,
238
+ get triggerId() {
239
+ return menu.triggerId;
240
+ }
241
+ });
242
+ if (contextComposition) provideMenuInvocation({
243
+ adoptTriggerId: menu.adoptTriggerId,
244
+ contentId: menu.contentId,
245
+ disabled,
246
+ invoke: (x, y, event, source, focus) => contextComposition.invoke(invoke, x, y, event, source, focus),
247
+ open: opened
248
+ });
249
+ LevelContext({
250
+ cluster: submenus,
251
+ content: () => menu.content,
252
+ open: () => menu.open
253
+ });
254
+ yield /* @__PURE__ */ jsx(Fragment, { children: args.children });
255
+ }
256
+ };
257
+ /** Root provider for a menu. */
258
+ var Menu = ({ children, class: classes, defaultOpen, disabled, gap, onOpenChange, open, placement, ...attrs }) => /* @__PURE__ */ jsx(MenuRoot, {
259
+ ...statefulRootAttrs(attrs),
260
+ defaultOpen,
261
+ disabled,
262
+ gap,
263
+ onOpenChange,
264
+ open,
265
+ placement,
266
+ "attr:class": classes,
267
+ "attr:data-slot": "menu",
268
+ children
269
+ });
270
+ /** Button that opens a Menu. */
271
+ var MenuTrigger = ({ children, "data-slot": slot = "menu-trigger", disabled, id, ref, type = "button", "set:onclick": onClick, ...attrs }) => {
272
+ const menu = MenuContext();
273
+ const disabledFlag = Boolean(disabled ?? menu?.disabled);
274
+ const adoptedId = menu?.adoptTriggerId(id);
275
+ return /* @__PURE__ */ jsx("button", {
276
+ ...attrs,
277
+ ...triggerAttrs({
278
+ controls: menu?.contentId,
279
+ expanded: Boolean(menu?.open),
280
+ haspopup: "menu",
281
+ id: adoptedId ?? id,
282
+ open: Boolean(menu?.open),
283
+ ref,
284
+ setTrigger: menu?.setTrigger,
285
+ triggerId: menu?.triggerId
286
+ }),
287
+ "data-menu-trigger": "true",
288
+ "data-slot": slot,
289
+ disabled: disabledFlag,
290
+ "set:onclick": (event) => {
291
+ callHandler(onClick, event);
292
+ if (event.defaultPrevented) return;
293
+ menu?.setOpen(!menu.open, event, event.detail === 0 ? "first" : void 0);
294
+ },
295
+ type,
296
+ children
297
+ });
298
+ };
299
+ /** Popover menu content. */
300
+ var MenuContent = ({ children, class: classes, "data-slot": slot = "menu-content", ref, style, ...attrs }) => {
301
+ const menu = MenuContext();
302
+ return /* @__PURE__ */ jsx("div", {
303
+ ...attrs,
304
+ ...contentAttrs({
305
+ id: menu?.contentId,
306
+ open: Boolean(menu?.open),
307
+ ref,
308
+ setContent: menu?.setContent,
309
+ style: menu?.contentStyle(style) ?? popupStyle(style),
310
+ tabindex: "-1"
311
+ }),
312
+ "aria-labelledby": menu?.triggerId,
313
+ class: classes,
314
+ "data-menu-content": "true",
315
+ "data-slot": slot,
316
+ hidden: void 0,
317
+ role: "menu",
318
+ children
319
+ });
320
+ };
321
+ /** Group of menu items. */
322
+ var MenuGroup = ({ children, class: classes, "data-slot": slot = "menu-group", ...attrs }) => /* @__PURE__ */ jsx("div", {
323
+ ...attrs,
324
+ class: classes,
325
+ "data-slot": slot,
326
+ role: "group",
327
+ children
328
+ });
329
+ /** Non-interactive label inside a menu. */
330
+ var MenuLabel = ({ children, class: classes, "data-slot": slot = "menu-label", inset, ...attrs }) => /* @__PURE__ */ jsx("div", {
331
+ ...attrs,
332
+ class: classes,
333
+ "data-inset": flag(inset),
334
+ "data-slot": slot,
335
+ children
336
+ });
337
+ /** Single activation guard for every item kind: disabled check, composed onclick, then the kind's action. */
338
+ var activate = (disabled, onClick, action) => (event) => {
339
+ if (disabled) return;
340
+ callHandler(onClick, event);
341
+ if (event.defaultPrevented) return;
342
+ action(event);
343
+ };
344
+ /** Standard menu action item. */
345
+ var MenuItem = ({ children, class: classes, "data-slot": slot = "menu-item", disabled, inset, onSelect, textValue, variant = "default", "set:onclick": onClick, ...attrs }) => {
346
+ const menu = MenuContext();
347
+ const level = LevelContext();
348
+ const disabledFlag = Boolean(disabled ?? menu?.disabled);
349
+ const label = textValue ?? text(children);
350
+ const highlight = pointerHighlight(disabledFlag, level?.cluster.close);
351
+ return /* @__PURE__ */ jsx("div", {
352
+ ...attrs,
353
+ ...menuItems.attrs({
354
+ disabled: disabledFlag,
355
+ label
356
+ }),
357
+ "aria-disabled": flag(disabledFlag),
358
+ class: classes,
359
+ "data-inset": flag(inset),
360
+ "data-slot": slot,
361
+ "data-variant": variant,
362
+ role: "menuitem",
363
+ "set:onclick": activate(disabledFlag, onClick, (event) => {
364
+ onSelect?.(event);
365
+ if (!event.defaultPrevented) menu?.close(event);
366
+ }),
367
+ "set:onpointerenter": highlight,
368
+ "set:onpointermove": highlight,
369
+ tabindex: "-1",
370
+ children
371
+ });
372
+ };
373
+ /** Shared row shell for checkbox/radio choice items; only role, checked source, and action differ. */
374
+ var choiceItem = (opts) => {
375
+ const highlight = pointerHighlight(opts.disabled, LevelContext()?.cluster.close);
376
+ return /* @__PURE__ */ jsxs("div", {
377
+ ...opts.attrs,
378
+ ...menuItems.attrs({
379
+ disabled: opts.disabled,
380
+ label: opts.label,
381
+ value: opts.value
382
+ }),
383
+ "aria-checked": opts.checked ? "true" : "false",
384
+ "aria-disabled": flag(opts.disabled),
385
+ class: opts.class,
386
+ "data-checked": flag(opts.checked),
387
+ "data-slot": opts.slot,
388
+ role: opts.role,
389
+ "set:onclick": activate(opts.disabled, opts.onClick, opts.action),
390
+ "set:onpointerenter": highlight,
391
+ "set:onpointermove": highlight,
392
+ tabindex: "-1",
393
+ children: [/* @__PURE__ */ jsx("span", {
394
+ class: opts.indicatorClass,
395
+ children: opts.checked ? /* @__PURE__ */ jsx("span", {
396
+ "aria-hidden": "true",
397
+ class: opts.indicatorIconClass
398
+ }) : null
399
+ }), opts.children]
400
+ });
401
+ };
402
+ /** Checkable menu item. */
403
+ var MenuCheckboxItem = ({ checked, children, class: classes, "data-slot": slot = "menu-checkbox-item", disabled, indicatorClass, indicatorIconClass, onCheckedChange, textValue, "set:onclick": onClick, ...attrs }) => {
404
+ const menu = MenuContext();
405
+ const checkedFlag = Boolean(checked);
406
+ return choiceItem({
407
+ attrs,
408
+ checked: checkedFlag,
409
+ children,
410
+ class: classes,
411
+ disabled: Boolean(disabled ?? menu?.disabled),
412
+ indicatorClass,
413
+ indicatorIconClass,
414
+ label: textValue ?? text(children),
415
+ menu,
416
+ action: (event) => onCheckedChange?.(!checkedFlag, event),
417
+ onClick,
418
+ role: "menuitemcheckbox",
419
+ slot
420
+ });
421
+ };
422
+ var MenuRadioGroupRoot = function* ({ defaultValue, value }) {
423
+ let onValueChange;
424
+ const state = controlled(this, {
425
+ fallback: String(value ?? defaultValue ?? ""),
426
+ onChange: (next, event) => onValueChange?.(next, event)
427
+ });
428
+ const change = (next, event) => {
429
+ state.set(next, event);
430
+ };
431
+ for (const args of this) {
432
+ onValueChange = args.onValueChange;
433
+ state.sync(args.value != null ? String(args.value ?? "") : void 0);
434
+ RadioContext({
435
+ change,
436
+ value: state.value
437
+ });
438
+ yield /* @__PURE__ */ jsx(Fragment, { children: args.children });
439
+ }
440
+ };
441
+ /** Radio group inside a menu. */
442
+ var MenuRadioGroup = ({ children, class: classes, "data-slot": slot = "menu-radio-group", defaultValue, onValueChange, value, ...attrs }) => /* @__PURE__ */ jsx(MenuRadioGroupRoot, {
443
+ ...statefulRootAttrs(attrs),
444
+ defaultValue,
445
+ onValueChange,
446
+ value,
447
+ "attr:class": classes,
448
+ "attr:data-slot": slot,
449
+ "attr:role": "group",
450
+ children
451
+ });
452
+ /** Radio item inside a menu radio group. */
453
+ var MenuRadioItem = ({ children, class: classes, "data-slot": slot = "menu-radio-item", disabled, indicatorClass, indicatorIconClass, textValue, value, "set:onclick": onClick, ...attrs }) => {
454
+ const menu = MenuContext();
455
+ const group = RadioContext();
456
+ const itemValue = String(value);
457
+ return choiceItem({
458
+ attrs,
459
+ checked: group?.value === itemValue,
460
+ children,
461
+ class: classes,
462
+ disabled: Boolean(disabled ?? menu?.disabled),
463
+ indicatorClass,
464
+ indicatorIconClass,
465
+ label: textValue ?? text(children),
466
+ menu,
467
+ action: (event) => group?.change(itemValue, event),
468
+ onClick,
469
+ role: "menuitemradio",
470
+ slot,
471
+ value: itemValue
472
+ });
473
+ };
474
+ /** Visual separator between menu groups. */
475
+ var MenuSeparator = ({ class: classes, "data-slot": slot = "menu-separator", ...attrs }) => /* @__PURE__ */ jsx("div", {
476
+ ...attrs,
477
+ class: classes,
478
+ "data-slot": slot,
479
+ role: "separator"
480
+ });
481
+ /** Right-aligned shortcut hint inside a menu item. */
482
+ var MenuShortcut = ({ children, class: classes, "data-slot": slot = "menu-shortcut", ...attrs }) => /* @__PURE__ */ jsx("span", {
483
+ ...attrs,
484
+ class: classes,
485
+ "data-slot": slot,
486
+ children
487
+ });
488
+ var MenuSubRoot = function* ({ defaultOpen, open }) {
489
+ const children = cluster();
490
+ const parent = LevelContext();
491
+ let focusRestore = 0;
492
+ let geometryReady = false;
493
+ let menu = null;
494
+ let onOpenChange;
495
+ const parentCluster = parent?.cluster ?? null;
496
+ let pendingFocus = false;
497
+ let unregister;
498
+ let branch;
499
+ let submenu;
500
+ submenu = popup(this, {
501
+ prefix: "menu-sub",
502
+ profile: "submenu",
503
+ initialOpen: Boolean(open ?? defaultOpen),
504
+ onOpenChange: (next, event) => onOpenChange?.(next, event),
505
+ reference: (view) => view.trigger,
506
+ source: (view) => view.trigger,
507
+ referenceBoundary: () => parent?.content() ?? null,
508
+ referenceHidden: "close",
509
+ dismiss: {
510
+ prevent: true,
511
+ outside: true,
512
+ onDismiss: (event) => {
513
+ if (event.type === "keydown") close(event);
514
+ else if (menu) menu.dismiss(event);
515
+ else setOpen(false, event);
516
+ }
517
+ },
518
+ onSync: (opened) => {
519
+ if (opened) {
520
+ geometryReady = true;
521
+ if (pendingFocus) focusEdge(submenu.content, "first");
522
+ } else {
523
+ geometryReady = false;
524
+ menuItems.clearHighlight(submenu.content);
525
+ children.close();
526
+ }
527
+ pendingFocus = false;
528
+ }
529
+ });
530
+ const focusWhenReady = () => {
531
+ if (geometryReady) focusEdge(submenu.content, "first");
532
+ else pendingFocus = true;
533
+ };
534
+ const setOpen = (next, event, focus = false) => {
535
+ if (next) focusRestore++;
536
+ if (next === submenu.open) {
537
+ if (next && focus) focusWhenReady();
538
+ return;
539
+ }
540
+ if (next) {
541
+ geometryReady = false;
542
+ parentCluster?.close(event, branch);
543
+ } else children.close(event);
544
+ pendingFocus = next && focus;
545
+ submenu.setOpen(next, event);
546
+ };
547
+ branch = {
548
+ close: (event) => setOpen(false, event),
549
+ content: () => submenu.content,
550
+ prune: (target, event) => children.prune(target, event),
551
+ trigger: () => submenu.trigger
552
+ };
553
+ unregister = parentCluster?.register(branch);
554
+ const close = (event) => {
555
+ const wasOpen = submenu.open;
556
+ const restore = ++focusRestore;
557
+ setOpen(false, event);
558
+ queueMicrotask(() => {
559
+ if (restore === focusRestore && wasOpen && !submenu.open) submenu.trigger?.focus();
560
+ });
561
+ };
562
+ this.signal.addEventListener("abort", () => unregister?.());
563
+ listen(this, "keydown", (event) => {
564
+ if (event.defaultPrevented) return;
565
+ const target = event.target;
566
+ if (!target?.closest("[data-menu-sub-trigger=\"true\"],[data-menu-sub-content=\"true\"]")) return;
567
+ if (event.key === "ArrowRight" && target.matches("[data-menu-sub-trigger=\"true\"]")) {
568
+ event.preventDefault();
569
+ setOpen(true, event, true);
570
+ } else if (event.key === "ArrowLeft") {
571
+ if (!submenu.open) return;
572
+ event.preventDefault();
573
+ close(event);
574
+ }
575
+ });
576
+ for (const args of this) {
577
+ menu = MenuContext();
578
+ onOpenChange = args.onOpenChange;
579
+ const parentOpen = parent?.open() ?? menu?.open ?? true;
580
+ if (!parentOpen) {
581
+ children.close();
582
+ submenu.init(false);
583
+ }
584
+ const wasOpen = submenu.open;
585
+ const opened = submenu.sync(parentOpen ? args.open != null ? Boolean(args.open) : null : false);
586
+ if (!wasOpen && opened) focusRestore++;
587
+ if (wasOpen && !opened) {
588
+ geometryReady = false;
589
+ pendingFocus = false;
590
+ children.close();
591
+ }
592
+ SubContext({
593
+ adoptTriggerId: submenu.adoptTriggerId,
594
+ branch,
595
+ close,
596
+ contentId: submenu.contentId,
597
+ contentStyle: submenu.contentStyle,
598
+ open: opened,
599
+ setContent: submenu.setContent,
600
+ setOpen,
601
+ setTrigger: submenu.setTrigger,
602
+ get triggerId() {
603
+ return submenu.triggerId;
604
+ }
605
+ });
606
+ LevelContext({
607
+ cluster: children,
608
+ content: () => submenu.content,
609
+ open: () => submenu.open
610
+ });
611
+ yield /* @__PURE__ */ jsx(Fragment, { children: args.children });
612
+ }
613
+ };
614
+ /** Root provider for a nested menu. */
615
+ var MenuSub = ({ children, "data-slot": slot = "menu-sub", defaultOpen, onOpenChange, open, ...attrs }) => /* @__PURE__ */ jsx(MenuSubRoot, {
616
+ ...statefulRootAttrs(attrs),
617
+ defaultOpen,
618
+ onOpenChange,
619
+ open,
620
+ "attr:data-slot": slot,
621
+ children
622
+ });
623
+ /** Trigger item that opens a nested menu. */
624
+ var MenuSubTrigger = ({ children, class: classes, "data-slot": slot = "menu-sub-trigger", disabled, iconClass, id, inset, ref, textValue, "set:onclick": onClick, "set:onmouseenter": onMouseEnter, "set:onpointerenter": onPointerEnter, "set:onpointermove": onPointerMove, ...attrs }) => {
625
+ const menu = MenuContext();
626
+ const parent = LevelContext();
627
+ const sub = SubContext();
628
+ const disabledFlag = Boolean(disabled ?? menu?.disabled);
629
+ const label = textValue ?? text(children);
630
+ const highlight = pointerHighlight(disabledFlag, parent?.cluster.close, sub?.branch);
631
+ const adoptedId = sub?.adoptTriggerId(id);
632
+ return /* @__PURE__ */ jsxs("div", {
633
+ ...attrs,
634
+ ...menuItems.attrs({
635
+ disabled: disabledFlag,
636
+ label
637
+ }),
638
+ ...triggerAttrs({
639
+ controls: sub?.contentId,
640
+ expanded: Boolean(sub?.open),
641
+ haspopup: "menu",
642
+ id: adoptedId ?? id,
643
+ open: Boolean(sub?.open),
644
+ ref,
645
+ setTrigger: sub?.setTrigger,
646
+ triggerId: sub?.triggerId
647
+ }),
648
+ "aria-disabled": flag(disabledFlag),
649
+ class: classes,
650
+ "data-inset": flag(inset),
651
+ "data-menu-sub-trigger": "true",
652
+ "data-slot": slot,
653
+ role: "menuitem",
654
+ "set:onclick": (event) => {
655
+ callHandler(onClick, event);
656
+ if (event.defaultPrevented) return;
657
+ if (disabledFlag) return;
658
+ sub?.setOpen(!sub.open, event, true);
659
+ },
660
+ "set:onpointerenter": (event) => {
661
+ callHandler(onPointerEnter, event);
662
+ if (!event.defaultPrevented) highlight(event);
663
+ },
664
+ "set:onpointermove": (event) => {
665
+ callHandler(onPointerMove, event);
666
+ if (!event.defaultPrevented) highlight(event);
667
+ },
668
+ "set:onmouseenter": (event) => {
669
+ callHandler(onMouseEnter, event);
670
+ if (event.defaultPrevented) return;
671
+ if (disabledFlag) return;
672
+ sub?.setOpen(true, event);
673
+ },
674
+ tabindex: "-1",
675
+ children: [children, /* @__PURE__ */ jsx("span", {
676
+ "aria-hidden": "true",
677
+ class: iconClass
678
+ })]
679
+ });
680
+ };
681
+ /** Content for a nested menu. */
682
+ var MenuSubContent = ({ children, class: classes, "data-slot": slot = "menu-sub-content", ref, style, ...attrs }) => {
683
+ const sub = SubContext();
684
+ return /* @__PURE__ */ jsx("div", {
685
+ ...attrs,
686
+ ...contentAttrs({
687
+ id: sub?.contentId,
688
+ open: Boolean(sub?.open),
689
+ ref,
690
+ setContent: sub?.setContent,
691
+ style: sub?.contentStyle(style) ?? popupStyle(style),
692
+ tabindex: "-1"
693
+ }),
694
+ "aria-labelledby": sub?.triggerId,
695
+ class: classes,
696
+ "data-menu-sub-content": "true",
697
+ "data-slot": slot,
698
+ hidden: void 0,
699
+ role: "menu",
700
+ children
701
+ });
702
+ };
703
+ //#endregion
704
+ export { provideContextMenuComposition as _, MenuItem as a, MenuRadioItem as c, MenuSub as d, MenuSubContent as f, menuInvocation as g, SURFACE_SELECTOR as h, MenuGroup as i, MenuSeparator as l, MenuTrigger as m, MenuCheckboxItem as n, MenuLabel as o, MenuSubTrigger as p, MenuContent as r, MenuRadioGroup as s, Menu as t, MenuShortcut as u, provideMenubarComposition as v };