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
package/src/menubar.tsx
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import type { IntrinsicElements, Stateful, Stateless, WithChildren } from 'ajo'
|
|
2
|
+
import { callHandler, callRef, id, listen, statefulRootAttrs as rootAttrs } from 'ajo-cloves'
|
|
3
|
+
import { context } from 'ajo/context'
|
|
4
|
+
import { bar } from './bar'
|
|
5
|
+
import type { ReservedPositionArg } from './position'
|
|
6
|
+
import type { FixedArgs, OmitArg, PopupPosition } from './utils'
|
|
7
|
+
import {
|
|
8
|
+
Menu,
|
|
9
|
+
MenuCheckboxItem,
|
|
10
|
+
MenuContent,
|
|
11
|
+
MenuGroup,
|
|
12
|
+
MenuItem,
|
|
13
|
+
MenuLabel,
|
|
14
|
+
MenuRadioGroup,
|
|
15
|
+
MenuRadioItem,
|
|
16
|
+
MenuSeparator,
|
|
17
|
+
MenuShortcut,
|
|
18
|
+
MenuSub,
|
|
19
|
+
MenuSubContent,
|
|
20
|
+
MenuSubTrigger,
|
|
21
|
+
MenuTrigger,
|
|
22
|
+
} from './menu'
|
|
23
|
+
import type {
|
|
24
|
+
MenuCheckboxItemArgs,
|
|
25
|
+
MenuContentArgs,
|
|
26
|
+
MenuGroupArgs,
|
|
27
|
+
MenuItemArgs,
|
|
28
|
+
MenuLabelArgs,
|
|
29
|
+
MenuRadioGroupArgs,
|
|
30
|
+
MenuRadioItemArgs,
|
|
31
|
+
MenuSeparatorArgs,
|
|
32
|
+
MenuShortcutArgs,
|
|
33
|
+
MenuSubContentArgs,
|
|
34
|
+
MenuSubArgs,
|
|
35
|
+
MenuSubTriggerArgs,
|
|
36
|
+
MenuTriggerArgs,
|
|
37
|
+
} from './menu'
|
|
38
|
+
import { provideMenubarComposition } from './menu-cluster'
|
|
39
|
+
import { text, withSlot } from './utils'
|
|
40
|
+
export type { PopupPlacement, PopupPosition } from './utils'
|
|
41
|
+
|
|
42
|
+
/** Arguments for a horizontal Menubar and its controlled open menu. */
|
|
43
|
+
export type MenubarArgs = WithChildren<OmitArg<IntrinsicElements['div'], 'onchange' | ReservedPositionArg> & PopupPosition & {
|
|
44
|
+
/** Controlled open top-level menu value. */
|
|
45
|
+
value?: string
|
|
46
|
+
/** Initial open top-level menu value for uncontrolled usage. */
|
|
47
|
+
defaultValue?: string
|
|
48
|
+
/** Disable every menubar trigger and menu item. */
|
|
49
|
+
disabled?: boolean
|
|
50
|
+
/** Wrap arrow-key navigation at the ends. */
|
|
51
|
+
loop?: boolean
|
|
52
|
+
/** Called whenever the open top-level menu changes. Empty string means closed. */
|
|
53
|
+
onValueChange?: (value: string, event?: Event) => void
|
|
54
|
+
/** Additional UnoCSS classes. */
|
|
55
|
+
class?: string
|
|
56
|
+
}> & FixedArgs<'onchange' | ReservedPositionArg>
|
|
57
|
+
|
|
58
|
+
/** Arguments for one value-bearing top-level menu in a Menubar. */
|
|
59
|
+
export type MenubarMenuArgs = WithChildren<OmitArg<IntrinsicElements['div'], 'gap' | 'placement' | ReservedPositionArg> & {
|
|
60
|
+
/** Top-level menu value used by controlled Menubar state. */
|
|
61
|
+
value?: string
|
|
62
|
+
/** Disable this top-level menu. */
|
|
63
|
+
disabled?: boolean
|
|
64
|
+
/** Additional UnoCSS classes. */
|
|
65
|
+
class?: string
|
|
66
|
+
}> & FixedArgs<'gap' | 'placement' | ReservedPositionArg>
|
|
67
|
+
|
|
68
|
+
/** Arguments for the trigger of a top-level Menubar menu. */
|
|
69
|
+
export type MenubarTriggerArgs = WithChildren<MenuTriggerArgs & {
|
|
70
|
+
/** Plain-text label used for menubar typeahead. */
|
|
71
|
+
textValue?: string
|
|
72
|
+
}>
|
|
73
|
+
|
|
74
|
+
/** Arguments for positioned content belonging to a top-level Menubar menu. */
|
|
75
|
+
export type MenubarContentArgs = MenuContentArgs
|
|
76
|
+
|
|
77
|
+
/** Arguments for a standard actionable Menubar item. */
|
|
78
|
+
export type MenubarItemArgs = MenuItemArgs
|
|
79
|
+
/** Arguments for a checkable Menubar item. */
|
|
80
|
+
export type MenubarCheckboxItemArgs = MenuCheckboxItemArgs
|
|
81
|
+
/** Arguments for a single-selection group inside a Menubar menu. */
|
|
82
|
+
export type MenubarRadioGroupArgs = MenuRadioGroupArgs
|
|
83
|
+
/** Arguments for one value-bearing Menubar radio item. */
|
|
84
|
+
export type MenubarRadioItemArgs = MenuRadioItemArgs
|
|
85
|
+
/** Arguments for a non-interactive label inside a Menubar menu. */
|
|
86
|
+
export type MenubarLabelArgs = MenuLabelArgs
|
|
87
|
+
/** Arguments for a semantic group of Menubar items. */
|
|
88
|
+
export type MenubarGroupArgs = MenuGroupArgs
|
|
89
|
+
/** Arguments for a visual separator between Menubar groups. */
|
|
90
|
+
export type MenubarSeparatorArgs = MenuSeparatorArgs
|
|
91
|
+
/** Arguments for a shortcut hint beside a Menubar item. */
|
|
92
|
+
export type MenubarShortcutArgs = MenuShortcutArgs
|
|
93
|
+
/** Arguments for a nested Menubar submenu provider. */
|
|
94
|
+
export type MenubarSubArgs = MenuSubArgs
|
|
95
|
+
/** Arguments for the item that opens a nested Menubar submenu. */
|
|
96
|
+
export type MenubarSubTriggerArgs = MenuSubTriggerArgs
|
|
97
|
+
/** Arguments for positioned content belonging to a Menubar submenu. */
|
|
98
|
+
export type MenubarSubContentArgs = MenuSubContentArgs
|
|
99
|
+
|
|
100
|
+
type MenubarContextValue = {
|
|
101
|
+
ackFocus: (value: string) => boolean
|
|
102
|
+
close: (event?: Event) => void
|
|
103
|
+
disabled: boolean
|
|
104
|
+
focus: (value: string, event?: Event) => void
|
|
105
|
+
follow: (value: string, event?: Event) => void
|
|
106
|
+
gap: PopupPosition['gap']
|
|
107
|
+
isTabbable: (value: string) => boolean
|
|
108
|
+
open: (value: string, event?: Event) => void
|
|
109
|
+
placement: PopupPosition['placement']
|
|
110
|
+
register: (value: string, element: HTMLButtonElement | null) => void
|
|
111
|
+
value: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
type MenubarMenuContextValue = {
|
|
115
|
+
disabled: boolean
|
|
116
|
+
value: string
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const MenubarContext = context<MenubarContextValue | null>(null)
|
|
120
|
+
const MenubarMenuContext = context<MenubarMenuContextValue | null>(null)
|
|
121
|
+
|
|
122
|
+
const order = (root: HTMLElement) =>
|
|
123
|
+
Array.from(root.querySelectorAll<HTMLButtonElement>('[data-menubar-trigger="true"]'))
|
|
124
|
+
.filter(trigger =>
|
|
125
|
+
!trigger.disabled
|
|
126
|
+
&& trigger.offsetParent !== null
|
|
127
|
+
&& trigger.closest('[data-slot="menubar"]') === root)
|
|
128
|
+
|
|
129
|
+
const MenubarRoot: Stateful<MenubarArgs> = function* ({ defaultValue, value }) {
|
|
130
|
+
const triggers = new Map<string, HTMLButtonElement>()
|
|
131
|
+
let disabled = false
|
|
132
|
+
let entering = false
|
|
133
|
+
let loop = true
|
|
134
|
+
let onValueChange: MenubarArgs['onValueChange']
|
|
135
|
+
let pendingFocus = ''
|
|
136
|
+
let queued = false
|
|
137
|
+
|
|
138
|
+
const state = bar(this, {
|
|
139
|
+
triggers: () => order(this),
|
|
140
|
+
initialValue: String(value ?? defaultValue ?? ''),
|
|
141
|
+
disabled: () => disabled,
|
|
142
|
+
loop: () => loop,
|
|
143
|
+
onValueChange: (next, event) => onValueChange?.(next, event),
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Trigger mount/unmount changes the tab-stop row after the render that
|
|
147
|
+
// caused it; the microtask queue coalesces the follow-up pass (ajo render
|
|
148
|
+
// semantics: refs run during render, no reentrant render).
|
|
149
|
+
const rerender = () => {
|
|
150
|
+
if (queued) return
|
|
151
|
+
queued = true
|
|
152
|
+
queueMicrotask(() => {
|
|
153
|
+
queued = false
|
|
154
|
+
this.next()
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const register = (itemValue: string, element: HTMLButtonElement | null) => {
|
|
159
|
+
if (element) {
|
|
160
|
+
if (triggers.get(itemValue) === element) return
|
|
161
|
+
triggers.set(itemValue, element)
|
|
162
|
+
rerender()
|
|
163
|
+
} else if (triggers.delete(itemValue)) {
|
|
164
|
+
// Tab-stop repair falls back to DOM order through the row query.
|
|
165
|
+
if (state.focused === itemValue) state.adopt('')
|
|
166
|
+
rerender()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const close = (event?: Event) => {
|
|
171
|
+
pendingFocus = ''
|
|
172
|
+
state.close(event)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const focus = (next: string, event?: Event) => {
|
|
176
|
+
if (!entering) pendingFocus = ''
|
|
177
|
+
state.focus(next, event)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const follow = (next: string, event?: Event) => {
|
|
181
|
+
if (!entering) pendingFocus = ''
|
|
182
|
+
state.follow(next, event)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const open = (next: string, event?: Event) => {
|
|
186
|
+
pendingFocus = ''
|
|
187
|
+
state.setValue(next, event)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const enter = (next: string, event: Event) => {
|
|
191
|
+
pendingFocus = next
|
|
192
|
+
entering = true
|
|
193
|
+
try {
|
|
194
|
+
state.follow(next, event)
|
|
195
|
+
} finally {
|
|
196
|
+
entering = false
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const ackFocus = (next: string) => {
|
|
201
|
+
if (!next || pendingFocus !== next) return false
|
|
202
|
+
pendingFocus = ''
|
|
203
|
+
return true
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
listen(this, 'keydown', (event: KeyboardEvent) => {
|
|
207
|
+
if (event.defaultPrevented) return
|
|
208
|
+
const target = event.target as HTMLElement | null
|
|
209
|
+
if (target?.closest('[data-slot="menubar"]') !== this) return
|
|
210
|
+
|
|
211
|
+
if (event.key === 'Tab') {
|
|
212
|
+
// Tab leaves the bar (from a trigger or from inside an open menu):
|
|
213
|
+
// close and let focus proceed.
|
|
214
|
+
close(event)
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (target?.closest('[data-menubar-trigger="true"]')) {
|
|
219
|
+
if (event.key === 'Escape') pendingFocus = ''
|
|
220
|
+
const transfer = Boolean(pendingFocus)
|
|
221
|
+
if (transfer) entering = true
|
|
222
|
+
try {
|
|
223
|
+
if (state.handle(event) && transfer) pendingFocus = state.value
|
|
224
|
+
} finally {
|
|
225
|
+
if (transfer) entering = false
|
|
226
|
+
}
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ArrowLeft/Right from inside an open menu move to the adjacent
|
|
231
|
+
// top-level menu with its first item focused (APG). Submenu-owned
|
|
232
|
+
// arrows stay with the submenu machinery: ArrowRight enters a submenu
|
|
233
|
+
// from its trigger, ArrowLeft closes one from inside.
|
|
234
|
+
if (state.value && (event.key === 'ArrowLeft' || event.key === 'ArrowRight') && target?.closest('[data-menu-content="true"]')) {
|
|
235
|
+
if (event.key === 'ArrowRight' && target.closest('[data-menu-sub-trigger="true"]')) return
|
|
236
|
+
if (target.closest('[data-menu-sub-content="true"]')) return
|
|
237
|
+
const row = order(this)
|
|
238
|
+
const index = row.findIndex(trigger => trigger.dataset.value === state.value)
|
|
239
|
+
if (index < 0) return
|
|
240
|
+
const step = event.key === 'ArrowRight' ? 1 : -1
|
|
241
|
+
const next = row[index + step] ?? (loop ? row[(index + step + row.length) % row.length] : undefined)
|
|
242
|
+
if (!next || next === row[index]) return
|
|
243
|
+
event.preventDefault()
|
|
244
|
+
enter(next.dataset.value ?? '', event)
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
for (const args of this) {
|
|
249
|
+
disabled = Boolean(args.disabled)
|
|
250
|
+
loop = args.loop !== false
|
|
251
|
+
onValueChange = args.onValueChange
|
|
252
|
+
state.sync(args.value != null ? String(args.value ?? '') : undefined)
|
|
253
|
+
|
|
254
|
+
MenubarContext({
|
|
255
|
+
ackFocus,
|
|
256
|
+
close,
|
|
257
|
+
disabled,
|
|
258
|
+
focus,
|
|
259
|
+
follow,
|
|
260
|
+
gap: args.gap,
|
|
261
|
+
isTabbable: state.isTabbable,
|
|
262
|
+
open,
|
|
263
|
+
placement: args.placement,
|
|
264
|
+
register,
|
|
265
|
+
value: state.value,
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
yield <>{args.children}</>
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
/** Persistent horizontal menu bar. */
|
|
274
|
+
const Menubar: Stateless<MenubarArgs> = ({
|
|
275
|
+
children,
|
|
276
|
+
class: classes,
|
|
277
|
+
defaultValue,
|
|
278
|
+
disabled,
|
|
279
|
+
gap,
|
|
280
|
+
loop,
|
|
281
|
+
onValueChange,
|
|
282
|
+
placement,
|
|
283
|
+
value,
|
|
284
|
+
...attrs
|
|
285
|
+
}) => (
|
|
286
|
+
<MenubarRoot
|
|
287
|
+
{...rootAttrs(attrs)}
|
|
288
|
+
defaultValue={defaultValue}
|
|
289
|
+
disabled={disabled}
|
|
290
|
+
gap={gap}
|
|
291
|
+
loop={loop}
|
|
292
|
+
onValueChange={onValueChange}
|
|
293
|
+
placement={placement}
|
|
294
|
+
value={value}
|
|
295
|
+
attr:aria-orientation="horizontal"
|
|
296
|
+
attr:class={classes}
|
|
297
|
+
attr:data-slot="menubar"
|
|
298
|
+
attr:role="menubar"
|
|
299
|
+
>
|
|
300
|
+
{children}
|
|
301
|
+
</MenubarRoot>
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
const MenubarMenuRoot: Stateful<MenubarMenuArgs> = function* ({ value }) {
|
|
305
|
+
const fallback = id('menubar-menu')
|
|
306
|
+
let bar: MenubarContextValue | null = null
|
|
307
|
+
let itemValue = String(value ?? fallback)
|
|
308
|
+
const ackFocus = () => bar?.ackFocus(itemValue) ?? false
|
|
309
|
+
|
|
310
|
+
for (const args of this) {
|
|
311
|
+
bar = MenubarContext()
|
|
312
|
+
itemValue = String(args.value ?? value ?? fallback)
|
|
313
|
+
const disabled = Boolean(args.disabled ?? bar?.disabled)
|
|
314
|
+
|
|
315
|
+
MenubarMenuContext({ disabled, value: itemValue })
|
|
316
|
+
if (bar) provideMenubarComposition(ackFocus)
|
|
317
|
+
|
|
318
|
+
yield (
|
|
319
|
+
// Without a Menubar ancestor the menu degrades to a standalone
|
|
320
|
+
// uncontrolled Menu instead of a permanently-closed one.
|
|
321
|
+
<Menu
|
|
322
|
+
disabled={disabled}
|
|
323
|
+
gap={bar?.gap}
|
|
324
|
+
onOpenChange={(open, event) => open ? bar?.open(itemValue, event) : bar?.close(event)}
|
|
325
|
+
open={bar ? bar.value === itemValue : undefined}
|
|
326
|
+
placement={bar?.placement}
|
|
327
|
+
>
|
|
328
|
+
{args.children}
|
|
329
|
+
</Menu>
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
/** Top-level Menubar menu. */
|
|
336
|
+
const MenubarMenu: Stateless<MenubarMenuArgs> = ({
|
|
337
|
+
children,
|
|
338
|
+
class: classes,
|
|
339
|
+
disabled,
|
|
340
|
+
value,
|
|
341
|
+
...attrs
|
|
342
|
+
}) => (
|
|
343
|
+
<MenubarMenuRoot
|
|
344
|
+
{...rootAttrs(attrs)}
|
|
345
|
+
disabled={disabled}
|
|
346
|
+
value={value}
|
|
347
|
+
attr:class={classes}
|
|
348
|
+
attr:data-slot="menubar-menu"
|
|
349
|
+
>
|
|
350
|
+
{children}
|
|
351
|
+
</MenubarMenuRoot>
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
/** Top-level trigger inside a Menubar. */
|
|
355
|
+
const MenubarTrigger: Stateless<MenubarTriggerArgs> = ({
|
|
356
|
+
children,
|
|
357
|
+
class: classes,
|
|
358
|
+
disabled,
|
|
359
|
+
ref,
|
|
360
|
+
textValue,
|
|
361
|
+
'set:onfocus': onFocus,
|
|
362
|
+
'set:onmouseenter': onMouseEnter,
|
|
363
|
+
...attrs
|
|
364
|
+
}) => {
|
|
365
|
+
const bar = MenubarContext()
|
|
366
|
+
const menu = MenubarMenuContext()
|
|
367
|
+
const itemValue = menu?.value ?? ''
|
|
368
|
+
const disabledFlag = Boolean(disabled ?? menu?.disabled ?? bar?.disabled)
|
|
369
|
+
const label = textValue ?? text(children)
|
|
370
|
+
const reference = (element: HTMLButtonElement | null) => {
|
|
371
|
+
if (itemValue) bar?.register(itemValue, element)
|
|
372
|
+
callRef(ref, element)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return (
|
|
376
|
+
<MenuTrigger
|
|
377
|
+
{...attrs}
|
|
378
|
+
class={classes}
|
|
379
|
+
data-label={bar ? label : undefined}
|
|
380
|
+
data-menubar-trigger={bar ? 'true' : undefined}
|
|
381
|
+
data-slot="menubar-trigger"
|
|
382
|
+
data-value={bar ? itemValue : undefined}
|
|
383
|
+
disabled={disabledFlag}
|
|
384
|
+
ref={reference}
|
|
385
|
+
role={bar ? 'menuitem' : undefined}
|
|
386
|
+
set:onfocus={(event: FocusEvent) => {
|
|
387
|
+
callHandler(onFocus, event)
|
|
388
|
+
if (event.defaultPrevented || disabledFlag) return
|
|
389
|
+
// Single follow path: roving/typeahead only focus, this follows.
|
|
390
|
+
bar?.focus(itemValue, event)
|
|
391
|
+
}}
|
|
392
|
+
set:onmouseenter={(event: MouseEvent) => {
|
|
393
|
+
callHandler(onMouseEnter, event)
|
|
394
|
+
if (event.defaultPrevented || disabledFlag) return
|
|
395
|
+
bar?.follow(itemValue, event)
|
|
396
|
+
}}
|
|
397
|
+
tabindex={bar ? (bar.isTabbable(itemValue) ? 0 : -1) : undefined}
|
|
398
|
+
>
|
|
399
|
+
{children}
|
|
400
|
+
</MenuTrigger>
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** Popover content for a top-level Menubar menu. */
|
|
405
|
+
const MenubarContent: Stateless<MenubarContentArgs> = withSlot<MenubarContentArgs>(
|
|
406
|
+
MenuContent,
|
|
407
|
+
'menubar-content',
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
/** Standard menubar action item. */
|
|
411
|
+
const MenubarItem: Stateless<MenubarItemArgs> = withSlot<MenubarItemArgs>(MenuItem, 'menubar-item')
|
|
412
|
+
|
|
413
|
+
/** Checkable menubar item. */
|
|
414
|
+
const MenubarCheckboxItem: Stateless<MenubarCheckboxItemArgs> = withSlot<MenubarCheckboxItemArgs>(MenuCheckboxItem, 'menubar-checkbox-item')
|
|
415
|
+
|
|
416
|
+
/** Radio group inside a menubar menu. */
|
|
417
|
+
const MenubarRadioGroup: Stateless<MenubarRadioGroupArgs> = withSlot<MenubarRadioGroupArgs>(MenuRadioGroup, 'menubar-radio-group')
|
|
418
|
+
|
|
419
|
+
/** Radio item inside a menubar radio group. */
|
|
420
|
+
const MenubarRadioItem: Stateless<MenubarRadioItemArgs> = withSlot<MenubarRadioItemArgs>(MenuRadioItem, 'menubar-radio-item')
|
|
421
|
+
|
|
422
|
+
/** Group of menubar items. */
|
|
423
|
+
const MenubarGroup: Stateless<MenubarGroupArgs> = withSlot<MenubarGroupArgs>(MenuGroup, 'menubar-group')
|
|
424
|
+
|
|
425
|
+
/** Non-interactive label inside a menubar menu. */
|
|
426
|
+
const MenubarLabel: Stateless<MenubarLabelArgs> = withSlot<MenubarLabelArgs>(MenuLabel, 'menubar-label')
|
|
427
|
+
|
|
428
|
+
/** Visual separator between menubar groups. */
|
|
429
|
+
const MenubarSeparator: Stateless<MenubarSeparatorArgs> = withSlot<MenubarSeparatorArgs>(MenuSeparator, 'menubar-separator')
|
|
430
|
+
|
|
431
|
+
/** Right-aligned shortcut hint inside a menubar item. */
|
|
432
|
+
const MenubarShortcut: Stateless<MenubarShortcutArgs> = withSlot<MenubarShortcutArgs>(MenuShortcut, 'menubar-shortcut')
|
|
433
|
+
|
|
434
|
+
/** Root provider for a menubar submenu. */
|
|
435
|
+
const MenubarSub: Stateless<MenubarSubArgs> = withSlot<MenubarSubArgs>(MenuSub, 'menubar-sub')
|
|
436
|
+
|
|
437
|
+
/** Trigger item that opens a menubar submenu. */
|
|
438
|
+
const MenubarSubTrigger: Stateless<MenubarSubTriggerArgs> = withSlot<MenubarSubTriggerArgs>(MenuSubTrigger, 'menubar-sub-trigger')
|
|
439
|
+
|
|
440
|
+
/** Content for a menubar submenu. */
|
|
441
|
+
const MenubarSubContent: Stateless<MenubarSubContentArgs> = withSlot<MenubarSubContentArgs>(MenuSubContent, 'menubar-sub-content')
|
|
442
|
+
|
|
443
|
+
export {
|
|
444
|
+
Menubar,
|
|
445
|
+
MenubarCheckboxItem,
|
|
446
|
+
MenubarContent,
|
|
447
|
+
MenubarGroup,
|
|
448
|
+
MenubarItem,
|
|
449
|
+
MenubarLabel,
|
|
450
|
+
MenubarMenu,
|
|
451
|
+
MenubarRadioGroup,
|
|
452
|
+
MenubarRadioItem,
|
|
453
|
+
MenubarSeparator,
|
|
454
|
+
MenubarShortcut,
|
|
455
|
+
MenubarSub,
|
|
456
|
+
MenubarSubContent,
|
|
457
|
+
MenubarSubTrigger,
|
|
458
|
+
MenubarTrigger,
|
|
459
|
+
}
|