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.
- package/LICENSE +15 -0
- package/README.md +126 -0
- package/dist/accordion.js +130 -0
- package/dist/avatar.js +54 -0
- package/dist/calendar.js +2 -0
- package/dist/carousel.js +239 -0
- package/dist/chart.js +790 -0
- package/dist/checkbox-group.js +70 -0
- package/dist/checkbox.js +77 -0
- package/dist/chunks/bar-CVafh6C1.js +99 -0
- package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
- package/dist/chunks/collection-DtRB63U4.js +111 -0
- package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
- package/dist/chunks/menu-B45IyHHC.js +704 -0
- package/dist/chunks/native-BJdhd9XJ.js +20 -0
- package/dist/chunks/popup-C8Bb3l_g.js +459 -0
- package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
- package/dist/chunks/position-D6_i_SRn.js +434 -0
- package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
- package/dist/collapsible.js +106 -0
- package/dist/command.js +263 -0
- package/dist/context-menu.js +112 -0
- package/dist/data-table.js +5 -0
- package/dist/dialog.js +207 -0
- package/dist/direction.js +22 -0
- package/dist/drawer.js +139 -0
- package/dist/field.js +26 -0
- package/dist/index.js +38 -0
- package/dist/input-date.js +994 -0
- package/dist/input-group.js +52 -0
- package/dist/input-otp.js +179 -0
- package/dist/menu.js +2 -0
- package/dist/menubar.js +236 -0
- package/dist/message-scroller.js +446 -0
- package/dist/navigation-menu.js +330 -0
- package/dist/popover.js +307 -0
- package/dist/progress.js +39 -0
- package/dist/radio-group.js +107 -0
- package/dist/resizable.js +172 -0
- package/dist/select.js +961 -0
- package/dist/sidebar.js +343 -0
- package/dist/slider.js +259 -0
- package/dist/switch.js +53 -0
- package/dist/tabs.js +182 -0
- package/dist/toast.js +492 -0
- package/dist/toggle-group.js +111 -0
- package/dist/toggle.js +52 -0
- package/dist/toolbar.js +127 -0
- package/dist/tooltip.js +196 -0
- package/dist/utils.js +104 -0
- package/dist/virtual-list.js +2 -0
- package/package.json +250 -0
- package/src/accordion.tsx +261 -0
- package/src/availability.ts +261 -0
- package/src/avatar.tsx +99 -0
- package/src/bar.ts +156 -0
- package/src/calendar.tsx +1441 -0
- package/src/carousel.tsx +424 -0
- package/src/chart.tsx +1194 -0
- package/src/checkbox-group.tsx +132 -0
- package/src/checkbox.tsx +130 -0
- package/src/collapsible.tsx +188 -0
- package/src/collection.ts +154 -0
- package/src/command.tsx +511 -0
- package/src/context-menu.tsx +233 -0
- package/src/data-table-contract.ts +143 -0
- package/src/data-table-model.ts +760 -0
- package/src/data-table.tsx +475 -0
- package/src/dialog.tsx +393 -0
- package/src/direction.tsx +45 -0
- package/src/drawer.tsx +251 -0
- package/src/field.tsx +61 -0
- package/src/index.ts +37 -0
- package/src/input-date.tsx +1539 -0
- package/src/input-group.tsx +142 -0
- package/src/input-otp.tsx +324 -0
- package/src/menu-cluster.ts +124 -0
- package/src/menu.tsx +1095 -0
- package/src/menubar.tsx +459 -0
- package/src/message-scroller.tsx +732 -0
- package/src/native.ts +26 -0
- package/src/navigation-menu.tsx +578 -0
- package/src/popover.tsx +519 -0
- package/src/popup-surface.tsx +31 -0
- package/src/popup.ts +569 -0
- package/src/position.ts +523 -0
- package/src/progress.tsx +70 -0
- package/src/radio-group.tsx +186 -0
- package/src/resizable.tsx +310 -0
- package/src/segments.ts +922 -0
- package/src/select.tsx +1501 -0
- package/src/sidebar.tsx +683 -0
- package/src/slider.tsx +424 -0
- package/src/switch.tsx +104 -0
- package/src/tabs.tsx +314 -0
- package/src/toast.tsx +923 -0
- package/src/toggle-group.tsx +249 -0
- package/src/toggle.tsx +91 -0
- package/src/toolbar.tsx +212 -0
- package/src/tooltip.tsx +359 -0
- package/src/utils.ts +204 -0
- package/src/virtual-list.tsx +205 -0
- 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 };
|