@arun-dev/headless 4.14.0 → 4.16.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/dist/chunk-ETNICT5M.js +8 -0
- package/dist/combobox/index.cjs +3 -1
- package/dist/combobox/index.js +7 -7
- package/dist/menu/index.cjs +509 -33
- package/dist/menu/index.d.cts +216 -20
- package/dist/menu/index.d.ts +216 -20
- package/dist/menu/index.js +509 -37
- package/package.json +1 -1
package/dist/menu/index.cjs
CHANGED
|
@@ -27,9 +27,17 @@ module.exports = __toCommonJS(menu_exports);
|
|
|
27
27
|
// src/menu/index.parts.ts
|
|
28
28
|
var index_parts_exports = {};
|
|
29
29
|
__export(index_parts_exports, {
|
|
30
|
+
CheckboxItem: () => MenuCheckboxItem,
|
|
31
|
+
Group: () => MenuGroup,
|
|
32
|
+
GroupLabel: () => MenuGroupLabel,
|
|
30
33
|
Item: () => MenuItem,
|
|
34
|
+
ItemIndicator: () => MenuItemIndicator,
|
|
31
35
|
Popup: () => MenuPopup,
|
|
36
|
+
RadioGroup: () => MenuRadioGroup,
|
|
37
|
+
RadioItem: () => MenuRadioItem,
|
|
32
38
|
Root: () => MenuRoot,
|
|
39
|
+
SubmenuRoot: () => MenuSubmenuRoot,
|
|
40
|
+
SubmenuTrigger: () => MenuSubmenuTrigger,
|
|
33
41
|
Trigger: () => MenuTrigger
|
|
34
42
|
});
|
|
35
43
|
|
|
@@ -133,17 +141,20 @@ function useMenuRootContext(part) {
|
|
|
133
141
|
|
|
134
142
|
// src/menu/MenuRoot.tsx
|
|
135
143
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
136
|
-
function MenuRoot({
|
|
144
|
+
function MenuRoot({ children, ...props }) {
|
|
145
|
+
const context = useMenuRootValue("Menu.Root", props, null);
|
|
146
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuRootContext.Provider, { value: context, children });
|
|
147
|
+
}
|
|
148
|
+
function useMenuRootValue(name, {
|
|
137
149
|
open: openProp,
|
|
138
150
|
defaultOpen,
|
|
139
151
|
onOpenChange,
|
|
140
|
-
focusableWhenDisabled = false
|
|
141
|
-
|
|
142
|
-
}) {
|
|
152
|
+
focusableWhenDisabled = false
|
|
153
|
+
}, parent) {
|
|
143
154
|
const [open, setOpenState] = useControlled({
|
|
144
155
|
controlled: openProp,
|
|
145
156
|
default: defaultOpen ?? false,
|
|
146
|
-
name
|
|
157
|
+
name,
|
|
147
158
|
state: "open"
|
|
148
159
|
});
|
|
149
160
|
const setOpen = (0, import_react3.useCallback)(
|
|
@@ -154,6 +165,10 @@ function MenuRoot({
|
|
|
154
165
|
},
|
|
155
166
|
[open, setOpenState, onOpenChange]
|
|
156
167
|
);
|
|
168
|
+
const closeAll = (0, import_react3.useCallback)(() => {
|
|
169
|
+
setOpen(false);
|
|
170
|
+
parent?.closeAll();
|
|
171
|
+
}, [setOpen, parent]);
|
|
157
172
|
const [items, setItems] = (0, import_react3.useState)([]);
|
|
158
173
|
const register = (0, import_react3.useCallback)((entry) => {
|
|
159
174
|
setItems((current) => [...current, entry].sort(byDocumentOrder));
|
|
@@ -166,10 +181,14 @@ function MenuRoot({
|
|
|
166
181
|
const id = (0, import_react3.useId)();
|
|
167
182
|
const triggerRef = (0, import_react3.useRef)(null);
|
|
168
183
|
const focusOnOpenRef = (0, import_react3.useRef)("first");
|
|
169
|
-
const
|
|
184
|
+
const popupRef = (0, import_react3.useRef)(null);
|
|
185
|
+
const openSubmenuRef = (0, import_react3.useRef)(null);
|
|
186
|
+
const graceRef = (0, import_react3.useRef)(null);
|
|
187
|
+
return (0, import_react3.useMemo)(
|
|
170
188
|
() => ({
|
|
171
189
|
open,
|
|
172
190
|
setOpen,
|
|
191
|
+
closeAll,
|
|
173
192
|
navigableItems,
|
|
174
193
|
focusableWhenDisabled,
|
|
175
194
|
register,
|
|
@@ -177,11 +196,14 @@ function MenuRoot({
|
|
|
177
196
|
triggerId: `${id}-trigger`,
|
|
178
197
|
popupId: `${id}-popup`,
|
|
179
198
|
anchorName: anchorNameFor(id),
|
|
180
|
-
triggerRef
|
|
199
|
+
triggerRef,
|
|
200
|
+
parent,
|
|
201
|
+
popupRef,
|
|
202
|
+
openSubmenuRef,
|
|
203
|
+
graceRef
|
|
181
204
|
}),
|
|
182
|
-
[open, setOpen, navigableItems, focusableWhenDisabled, register, id]
|
|
205
|
+
[open, setOpen, closeAll, navigableItems, focusableWhenDisabled, register, id, parent]
|
|
183
206
|
);
|
|
184
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuRootContext.Provider, { value: context, children });
|
|
185
207
|
}
|
|
186
208
|
|
|
187
209
|
// src/core/useRender.ts
|
|
@@ -280,9 +302,6 @@ function menuDataAttributes({ open }) {
|
|
|
280
302
|
"data-closed": open ? void 0 : ""
|
|
281
303
|
};
|
|
282
304
|
}
|
|
283
|
-
function menuItemDataAttributes(disabled) {
|
|
284
|
-
return { "data-disabled": disabled ? "" : void 0 };
|
|
285
|
-
}
|
|
286
305
|
|
|
287
306
|
// src/menu/MenuTrigger.tsx
|
|
288
307
|
function MenuTrigger({ className, children, render, ...rest }) {
|
|
@@ -317,9 +336,60 @@ function MenuTrigger({ className, children, render, ...rest }) {
|
|
|
317
336
|
}
|
|
318
337
|
|
|
319
338
|
// src/menu/MenuPopup.tsx
|
|
339
|
+
var import_react6 = require("react");
|
|
340
|
+
|
|
341
|
+
// src/core/typeahead.ts
|
|
320
342
|
var import_react5 = require("react");
|
|
343
|
+
|
|
344
|
+
// src/core/fold.ts
|
|
345
|
+
function fold(text) {
|
|
346
|
+
return text.normalize("NFD").replace(/\p{M}/gu, "").toLocaleLowerCase();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/core/typeahead.ts
|
|
350
|
+
var TYPEAHEAD_TIMEOUT = 500;
|
|
351
|
+
function textOf(node) {
|
|
352
|
+
let text = "";
|
|
353
|
+
import_react5.Children.forEach(node, (child) => {
|
|
354
|
+
if (typeof child === "string" || typeof child === "number") text += child;
|
|
355
|
+
else if ((0, import_react5.isValidElement)(child)) text += textOf(child.props.children);
|
|
356
|
+
});
|
|
357
|
+
return text;
|
|
358
|
+
}
|
|
359
|
+
function typeaheadIndex(labels, current, search) {
|
|
360
|
+
const count = labels.length;
|
|
361
|
+
if (count === 0 || search === "") return null;
|
|
362
|
+
const [first = "", ...others] = [...search];
|
|
363
|
+
const repeated = others.every((char) => char === first);
|
|
364
|
+
const needle = fold(repeated ? first : search);
|
|
365
|
+
const start = current < 0 ? 0 : repeated ? current + 1 : current;
|
|
366
|
+
for (let step = 0; step < count; step += 1) {
|
|
367
|
+
const index = (start + step) % count;
|
|
368
|
+
if (fold(labels[index] ?? "").startsWith(needle)) return index;
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
function useTypeahead() {
|
|
373
|
+
const searchRef = (0, import_react5.useRef)("");
|
|
374
|
+
const timerRef = (0, import_react5.useRef)(void 0);
|
|
375
|
+
(0, import_react5.useEffect)(() => () => clearTimeout(timerRef.current), []);
|
|
376
|
+
return (0, import_react5.useCallback)((event) => {
|
|
377
|
+
const { key } = event;
|
|
378
|
+
if (event.ctrlKey || event.metaKey || event.altKey) return null;
|
|
379
|
+
if ([...key].length !== 1) return null;
|
|
380
|
+
if (key === " " && searchRef.current === "") return null;
|
|
381
|
+
searchRef.current += key;
|
|
382
|
+
clearTimeout(timerRef.current);
|
|
383
|
+
timerRef.current = setTimeout(() => {
|
|
384
|
+
searchRef.current = "";
|
|
385
|
+
}, TYPEAHEAD_TIMEOUT);
|
|
386
|
+
return searchRef.current;
|
|
387
|
+
}, []);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// src/menu/MenuPopup.tsx
|
|
321
391
|
function MenuPopup({
|
|
322
|
-
side
|
|
392
|
+
side: sideProp,
|
|
323
393
|
align = "start",
|
|
324
394
|
className,
|
|
325
395
|
children,
|
|
@@ -334,12 +404,16 @@ function MenuPopup({
|
|
|
334
404
|
triggerId,
|
|
335
405
|
popupId,
|
|
336
406
|
anchorName,
|
|
337
|
-
triggerRef
|
|
407
|
+
triggerRef,
|
|
408
|
+
parent,
|
|
409
|
+
popupRef: elementRef,
|
|
410
|
+
openSubmenuRef
|
|
338
411
|
} = useMenuRootContext("Popup");
|
|
339
|
-
const
|
|
340
|
-
const focusPendingRef = (0,
|
|
341
|
-
const [, resync] = (0,
|
|
342
|
-
|
|
412
|
+
const side = sideProp ?? (parent ? "right" : "bottom");
|
|
413
|
+
const focusPendingRef = (0, import_react6.useRef)(false);
|
|
414
|
+
const [, resync] = (0, import_react6.useReducer)((n) => n + 1, 0);
|
|
415
|
+
const typeahead = useTypeahead();
|
|
416
|
+
(0, import_react6.useEffect)(() => {
|
|
343
417
|
const popup = elementRef.current;
|
|
344
418
|
if (!popup || typeof popup.showPopover !== "function") return;
|
|
345
419
|
const shown = popup.matches(":popover-open");
|
|
@@ -353,7 +427,8 @@ function MenuPopup({
|
|
|
353
427
|
}
|
|
354
428
|
if (focusPendingRef.current && navigableItems.length > 0) {
|
|
355
429
|
focusPendingRef.current = false;
|
|
356
|
-
const
|
|
430
|
+
const focus = focusOnOpenRef.current;
|
|
431
|
+
const target = focus === "last" ? navigableItems.at(-1) : focus === "first" ? navigableItems[0] : null;
|
|
357
432
|
target?.ref.current?.focus();
|
|
358
433
|
}
|
|
359
434
|
});
|
|
@@ -378,15 +453,35 @@ function MenuPopup({
|
|
|
378
453
|
}
|
|
379
454
|
const current = navigableItems.findIndex((i) => i.ref.current === event.target);
|
|
380
455
|
if (current < 0 && event.target !== event.currentTarget) return;
|
|
381
|
-
const
|
|
456
|
+
const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
|
|
457
|
+
if (parent && event.key === (rtl ? "ArrowRight" : "ArrowLeft")) {
|
|
458
|
+
event.preventDefault();
|
|
459
|
+
setOpen(false);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const search = typeahead(event);
|
|
463
|
+
const next = search === null ? rovingIndex(event.key, {
|
|
382
464
|
count: navigableItems.length,
|
|
383
465
|
current,
|
|
384
466
|
orientation: "vertical",
|
|
385
467
|
rtl: false
|
|
386
|
-
})
|
|
468
|
+
}) : typeaheadIndex(
|
|
469
|
+
navigableItems.map((i) => i.textValue),
|
|
470
|
+
current,
|
|
471
|
+
search
|
|
472
|
+
);
|
|
473
|
+
if (search !== null) event.preventDefault();
|
|
387
474
|
if (next === null) return;
|
|
388
475
|
event.preventDefault();
|
|
389
|
-
navigableItems[next]?.ref.current
|
|
476
|
+
const target = navigableItems[next]?.ref.current;
|
|
477
|
+
const submenu = openSubmenuRef.current;
|
|
478
|
+
if (submenu && submenu.triggerRef.current !== target) submenu.close();
|
|
479
|
+
target?.focus();
|
|
480
|
+
},
|
|
481
|
+
onBeforeToggle(event) {
|
|
482
|
+
if (event.target !== event.currentTarget || !parent) return;
|
|
483
|
+
if (event.nativeEvent.newState !== "closed") return;
|
|
484
|
+
if (event.currentTarget.contains(document.activeElement)) triggerRef.current?.focus();
|
|
390
485
|
},
|
|
391
486
|
onToggle(event) {
|
|
392
487
|
if (event.target !== event.currentTarget) return;
|
|
@@ -399,34 +494,415 @@ function MenuPopup({
|
|
|
399
494
|
});
|
|
400
495
|
}
|
|
401
496
|
|
|
497
|
+
// src/menu/useMenuItem.ts
|
|
498
|
+
var import_react7 = require("react");
|
|
499
|
+
|
|
500
|
+
// src/core/pointerIntent.ts
|
|
501
|
+
function graceArea(exit, popup) {
|
|
502
|
+
const toRight = popup.left >= exit.x;
|
|
503
|
+
const edge = toRight ? popup.left : popup.right;
|
|
504
|
+
const back = toRight ? -5 : 5;
|
|
505
|
+
return [
|
|
506
|
+
{ x: exit.x + back, y: exit.y - 5 },
|
|
507
|
+
{ x: edge, y: popup.top },
|
|
508
|
+
{ x: edge, y: popup.bottom },
|
|
509
|
+
{ x: exit.x + back, y: exit.y + 5 }
|
|
510
|
+
];
|
|
511
|
+
}
|
|
512
|
+
function isPointInPolygon({ x, y }, polygon) {
|
|
513
|
+
let inside = false;
|
|
514
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i, i += 1) {
|
|
515
|
+
const a = polygon[i];
|
|
516
|
+
const b = polygon[j];
|
|
517
|
+
if (!a || !b) continue;
|
|
518
|
+
if (a.y > y !== b.y > y && x < (b.x - a.x) * (y - a.y) / (b.y - a.y) + a.x) {
|
|
519
|
+
inside = !inside;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return inside;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/menu/useMenuItem.ts
|
|
526
|
+
function useMenuItem(part, { disabled, textValue, children }, menu) {
|
|
527
|
+
const own = useMenuRootContext(part);
|
|
528
|
+
const { closeAll, focusableWhenDisabled, register, openSubmenuRef, graceRef } = menu ?? own;
|
|
529
|
+
const ref = (0, import_react7.useRef)(null);
|
|
530
|
+
const text = textValue ?? textOf(children);
|
|
531
|
+
(0, import_react7.useLayoutEffect)(() => register({ disabled, textValue: text, ref }), [register, disabled, text]);
|
|
532
|
+
return {
|
|
533
|
+
closeAll,
|
|
534
|
+
itemProps: {
|
|
535
|
+
type: "button",
|
|
536
|
+
tabIndex: -1,
|
|
537
|
+
disabled: disabled && !focusableWhenDisabled || void 0,
|
|
538
|
+
"aria-disabled": disabled && focusableWhenDisabled || void 0,
|
|
539
|
+
"data-disabled": disabled ? "" : void 0,
|
|
540
|
+
ref,
|
|
541
|
+
onPointerMove(event) {
|
|
542
|
+
if (event.pointerType !== "mouse" || !openSubmenuRef.current) return;
|
|
543
|
+
if (isInGrace(graceRef.current, event)) return;
|
|
544
|
+
openSubmenuRef.current.close();
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function isInGrace(grace, event) {
|
|
550
|
+
if (!grace || performance.now() > grace.until) return false;
|
|
551
|
+
return isPointInPolygon({ x: event.clientX, y: event.clientY }, grace.area);
|
|
552
|
+
}
|
|
553
|
+
var MenuItemIndicatorContext = (0, import_react7.createContext)(null);
|
|
554
|
+
function useMenuItemIndicatorContext() {
|
|
555
|
+
const context = (0, import_react7.useContext)(MenuItemIndicatorContext);
|
|
556
|
+
if (context === null) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
"<Menu.ItemIndicator> must be rendered inside <Menu.CheckboxItem> or <Menu.RadioItem>."
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
return context;
|
|
562
|
+
}
|
|
563
|
+
function menuCheckableDataAttributes({ checked, disabled }) {
|
|
564
|
+
return {
|
|
565
|
+
"data-checked": checked ? "" : void 0,
|
|
566
|
+
"data-unchecked": checked ? void 0 : "",
|
|
567
|
+
"data-disabled": disabled ? "" : void 0
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
402
571
|
// src/menu/MenuItem.tsx
|
|
403
|
-
var import_react6 = require("react");
|
|
404
572
|
function MenuItem({
|
|
405
573
|
disabled = false,
|
|
574
|
+
textValue,
|
|
406
575
|
onClick,
|
|
407
576
|
className,
|
|
408
577
|
children,
|
|
409
578
|
render,
|
|
410
579
|
...rest
|
|
411
580
|
}) {
|
|
412
|
-
const {
|
|
413
|
-
const elementRef = (0, import_react6.useRef)(null);
|
|
414
|
-
(0, import_react6.useLayoutEffect)(() => register({ disabled, ref: elementRef }), [register, disabled]);
|
|
581
|
+
const { closeAll, itemProps } = useMenuItem("Item", { disabled, textValue, children });
|
|
415
582
|
return useRender({
|
|
416
583
|
render,
|
|
417
584
|
defaultTagName: "button",
|
|
418
585
|
props: {
|
|
419
|
-
|
|
586
|
+
...itemProps,
|
|
420
587
|
role: "menuitem",
|
|
421
|
-
tabIndex: -1,
|
|
422
|
-
disabled: disabled && !focusableWhenDisabled || void 0,
|
|
423
|
-
"aria-disabled": disabled && focusableWhenDisabled || void 0,
|
|
424
|
-
...menuItemDataAttributes(disabled),
|
|
425
588
|
className,
|
|
426
589
|
children,
|
|
427
|
-
ref: elementRef,
|
|
428
590
|
onClick() {
|
|
429
|
-
if (!disabled)
|
|
591
|
+
if (!disabled) closeAll();
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
consumerProps: disabled ? rest : { ...rest, onClick }
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// src/menu/MenuCheckboxItem.tsx
|
|
599
|
+
var import_react8 = require("react");
|
|
600
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
601
|
+
function MenuCheckboxItem({
|
|
602
|
+
checked: checkedProp,
|
|
603
|
+
defaultChecked,
|
|
604
|
+
onCheckedChange,
|
|
605
|
+
closeOnClick = false,
|
|
606
|
+
disabled = false,
|
|
607
|
+
textValue,
|
|
608
|
+
onClick,
|
|
609
|
+
className,
|
|
610
|
+
children,
|
|
611
|
+
render,
|
|
612
|
+
...rest
|
|
613
|
+
}) {
|
|
614
|
+
const { closeAll, itemProps } = useMenuItem("CheckboxItem", { disabled, textValue, children });
|
|
615
|
+
const [checked, setChecked] = useControlled({
|
|
616
|
+
controlled: checkedProp,
|
|
617
|
+
default: defaultChecked ?? false,
|
|
618
|
+
name: "Menu.CheckboxItem",
|
|
619
|
+
state: "checked"
|
|
620
|
+
});
|
|
621
|
+
const state = (0, import_react8.useMemo)(() => ({ checked, disabled }), [checked, disabled]);
|
|
622
|
+
const element = useRender({
|
|
623
|
+
render,
|
|
624
|
+
defaultTagName: "button",
|
|
625
|
+
props: {
|
|
626
|
+
...itemProps,
|
|
627
|
+
role: "menuitemcheckbox",
|
|
628
|
+
"aria-checked": checked,
|
|
629
|
+
...menuCheckableDataAttributes(state),
|
|
630
|
+
className,
|
|
631
|
+
children,
|
|
632
|
+
onClick() {
|
|
633
|
+
if (disabled) return;
|
|
634
|
+
setChecked(!checked);
|
|
635
|
+
onCheckedChange?.(!checked);
|
|
636
|
+
if (closeOnClick) closeAll();
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
consumerProps: disabled ? rest : { ...rest, onClick }
|
|
640
|
+
});
|
|
641
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MenuItemIndicatorContext.Provider, { value: state, children: element });
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/menu/MenuRadioGroup.tsx
|
|
645
|
+
var import_react10 = require("react");
|
|
646
|
+
|
|
647
|
+
// src/menu/MenuGroup.tsx
|
|
648
|
+
var import_react9 = require("react");
|
|
649
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
650
|
+
var MenuGroupContext = (0, import_react9.createContext)(null);
|
|
651
|
+
function useMenuGroupContext() {
|
|
652
|
+
return (0, import_react9.useContext)(MenuGroupContext);
|
|
653
|
+
}
|
|
654
|
+
function useMenuGroupElement(part, {
|
|
655
|
+
render,
|
|
656
|
+
props,
|
|
657
|
+
consumerProps
|
|
658
|
+
}) {
|
|
659
|
+
useMenuRootContext(part);
|
|
660
|
+
const labelId = `${(0, import_react9.useId)()}-label`;
|
|
661
|
+
const [hasLabel, setHasLabel] = (0, import_react9.useState)(false);
|
|
662
|
+
const registerLabel = (0, import_react9.useCallback)(() => {
|
|
663
|
+
setHasLabel(true);
|
|
664
|
+
return () => setHasLabel(false);
|
|
665
|
+
}, []);
|
|
666
|
+
const context = (0, import_react9.useMemo)(() => ({ labelId, registerLabel }), [labelId, registerLabel]);
|
|
667
|
+
const element = useRender({
|
|
668
|
+
render,
|
|
669
|
+
defaultTagName: "div",
|
|
670
|
+
props: { role: "group", "aria-labelledby": hasLabel ? labelId : void 0, ...props },
|
|
671
|
+
consumerProps
|
|
672
|
+
});
|
|
673
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MenuGroupContext.Provider, { value: context, children: element });
|
|
674
|
+
}
|
|
675
|
+
function MenuGroup({ className, children, render, ...rest }) {
|
|
676
|
+
return useMenuGroupElement("Group", {
|
|
677
|
+
render,
|
|
678
|
+
props: { className, children },
|
|
679
|
+
consumerProps: rest
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// src/menu/MenuRadioGroup.tsx
|
|
684
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
685
|
+
var MenuRadioGroupContext = (0, import_react10.createContext)(null);
|
|
686
|
+
function useMenuRadioGroupContext() {
|
|
687
|
+
const context = (0, import_react10.useContext)(MenuRadioGroupContext);
|
|
688
|
+
if (context === null) {
|
|
689
|
+
throw new Error("<Menu.RadioItem> must be rendered inside <Menu.RadioGroup>.");
|
|
690
|
+
}
|
|
691
|
+
return context;
|
|
692
|
+
}
|
|
693
|
+
function MenuRadioGroup({
|
|
694
|
+
value: valueProp,
|
|
695
|
+
defaultValue,
|
|
696
|
+
onValueChange,
|
|
697
|
+
disabled = false,
|
|
698
|
+
className,
|
|
699
|
+
children,
|
|
700
|
+
render,
|
|
701
|
+
...rest
|
|
702
|
+
}) {
|
|
703
|
+
const [value, setValue] = useControlled({
|
|
704
|
+
controlled: valueProp,
|
|
705
|
+
default: defaultValue ?? null,
|
|
706
|
+
name: "Menu.RadioGroup",
|
|
707
|
+
state: "value"
|
|
708
|
+
});
|
|
709
|
+
const select = (0, import_react10.useCallback)(
|
|
710
|
+
(next) => {
|
|
711
|
+
if (next === value) return;
|
|
712
|
+
setValue(next);
|
|
713
|
+
onValueChange?.(next);
|
|
714
|
+
},
|
|
715
|
+
[value, setValue, onValueChange]
|
|
716
|
+
);
|
|
717
|
+
const context = (0, import_react10.useMemo)(() => ({ value, disabled, select }), [value, disabled, select]);
|
|
718
|
+
const element = useMenuGroupElement("RadioGroup", {
|
|
719
|
+
render,
|
|
720
|
+
props: { "data-disabled": disabled ? "" : void 0, className, children },
|
|
721
|
+
consumerProps: rest
|
|
722
|
+
});
|
|
723
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MenuRadioGroupContext.Provider, { value: context, children: element });
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// src/menu/MenuRadioItem.tsx
|
|
727
|
+
var import_react11 = require("react");
|
|
728
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
729
|
+
function MenuRadioItem({
|
|
730
|
+
value,
|
|
731
|
+
closeOnClick = false,
|
|
732
|
+
disabled: disabledProp = false,
|
|
733
|
+
textValue,
|
|
734
|
+
onClick,
|
|
735
|
+
className,
|
|
736
|
+
children,
|
|
737
|
+
render,
|
|
738
|
+
...rest
|
|
739
|
+
}) {
|
|
740
|
+
const group = useMenuRadioGroupContext();
|
|
741
|
+
const disabled = disabledProp || group.disabled;
|
|
742
|
+
const { closeAll, itemProps } = useMenuItem("RadioItem", { disabled, textValue, children });
|
|
743
|
+
const checked = group.value === value;
|
|
744
|
+
const state = (0, import_react11.useMemo)(() => ({ checked, disabled }), [checked, disabled]);
|
|
745
|
+
const element = useRender({
|
|
746
|
+
render,
|
|
747
|
+
defaultTagName: "button",
|
|
748
|
+
props: {
|
|
749
|
+
...itemProps,
|
|
750
|
+
role: "menuitemradio",
|
|
751
|
+
"aria-checked": checked,
|
|
752
|
+
...menuCheckableDataAttributes(state),
|
|
753
|
+
className,
|
|
754
|
+
children,
|
|
755
|
+
onClick() {
|
|
756
|
+
if (disabled) return;
|
|
757
|
+
group.select(value);
|
|
758
|
+
if (closeOnClick) closeAll();
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
consumerProps: disabled ? rest : { ...rest, onClick }
|
|
762
|
+
});
|
|
763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MenuItemIndicatorContext.Provider, { value: state, children: element });
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// src/menu/MenuItemIndicator.tsx
|
|
767
|
+
function MenuItemIndicator({
|
|
768
|
+
className,
|
|
769
|
+
children,
|
|
770
|
+
render,
|
|
771
|
+
...rest
|
|
772
|
+
}) {
|
|
773
|
+
const state = useMenuItemIndicatorContext();
|
|
774
|
+
return useRender({
|
|
775
|
+
render,
|
|
776
|
+
defaultTagName: "span",
|
|
777
|
+
props: { "aria-hidden": true, ...menuCheckableDataAttributes(state), className, children },
|
|
778
|
+
consumerProps: rest
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// src/menu/MenuGroupLabel.tsx
|
|
783
|
+
var import_react12 = require("react");
|
|
784
|
+
function MenuGroupLabel({ className, children, render, ...rest }) {
|
|
785
|
+
const group = useMenuGroupContext();
|
|
786
|
+
if (group === null) {
|
|
787
|
+
throw new Error("<Menu.GroupLabel> must be rendered inside <Menu.Group> or <Menu.RadioGroup>.");
|
|
788
|
+
}
|
|
789
|
+
const { labelId, registerLabel } = group;
|
|
790
|
+
(0, import_react12.useLayoutEffect)(registerLabel, [registerLabel]);
|
|
791
|
+
return useRender({
|
|
792
|
+
render,
|
|
793
|
+
defaultTagName: "div",
|
|
794
|
+
props: { id: labelId, className, children },
|
|
795
|
+
consumerProps: rest
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// src/menu/MenuSubmenuRoot.tsx
|
|
800
|
+
var import_react13 = require("react");
|
|
801
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
802
|
+
function MenuSubmenuRoot({
|
|
803
|
+
focusableWhenDisabled,
|
|
804
|
+
children,
|
|
805
|
+
...props
|
|
806
|
+
}) {
|
|
807
|
+
const parent = useMenuRootContext("SubmenuRoot");
|
|
808
|
+
const context = useMenuRootValue(
|
|
809
|
+
"Menu.SubmenuRoot",
|
|
810
|
+
{ ...props, focusableWhenDisabled: focusableWhenDisabled ?? parent.focusableWhenDisabled },
|
|
811
|
+
parent
|
|
812
|
+
);
|
|
813
|
+
const { open, setOpen, triggerRef } = context;
|
|
814
|
+
(0, import_react13.useEffect)(() => {
|
|
815
|
+
if (!open) return;
|
|
816
|
+
const entry = { close: () => setOpen(false), triggerRef };
|
|
817
|
+
parent.openSubmenuRef.current = entry;
|
|
818
|
+
return () => {
|
|
819
|
+
if (parent.openSubmenuRef.current === entry) parent.openSubmenuRef.current = null;
|
|
820
|
+
};
|
|
821
|
+
}, [open, setOpen, triggerRef, parent]);
|
|
822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MenuRootContext.Provider, { value: context, children });
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// src/menu/MenuSubmenuTrigger.tsx
|
|
826
|
+
var import_react14 = require("react");
|
|
827
|
+
var SUBMENU_OPEN_DELAY = 100;
|
|
828
|
+
var SUBMENU_GRACE_PERIOD = 300;
|
|
829
|
+
function MenuSubmenuTrigger({
|
|
830
|
+
disabled = false,
|
|
831
|
+
textValue,
|
|
832
|
+
onClick,
|
|
833
|
+
className,
|
|
834
|
+
children,
|
|
835
|
+
render,
|
|
836
|
+
...rest
|
|
837
|
+
}) {
|
|
838
|
+
const submenu = useMenuRootContext("SubmenuTrigger");
|
|
839
|
+
const { parent } = submenu;
|
|
840
|
+
if (parent === null) {
|
|
841
|
+
throw new Error("<Menu.SubmenuTrigger> must be rendered inside <Menu.SubmenuRoot>.");
|
|
842
|
+
}
|
|
843
|
+
const { itemProps } = useMenuItem("SubmenuTrigger", { disabled, textValue, children }, parent);
|
|
844
|
+
const { open, setOpen, focusOnOpenRef, navigableItems, triggerRef, popupRef } = submenu;
|
|
845
|
+
const timerRef = (0, import_react14.useRef)(void 0);
|
|
846
|
+
(0, import_react14.useEffect)(() => () => clearTimeout(timerRef.current), []);
|
|
847
|
+
const openAt = (focus) => {
|
|
848
|
+
if (disabled) return;
|
|
849
|
+
focusOnOpenRef.current = focus;
|
|
850
|
+
if (!open) setOpen(true);
|
|
851
|
+
else if (focus === "first") navigableItems[0]?.ref.current?.focus();
|
|
852
|
+
};
|
|
853
|
+
const openAtRef = (0, import_react14.useRef)(openAt);
|
|
854
|
+
openAtRef.current = openAt;
|
|
855
|
+
const itemRef = itemProps.ref;
|
|
856
|
+
const ref = (0, import_react14.useCallback)(
|
|
857
|
+
(element) => {
|
|
858
|
+
itemRef.current = element;
|
|
859
|
+
triggerRef.current = element;
|
|
860
|
+
},
|
|
861
|
+
[itemRef, triggerRef]
|
|
862
|
+
);
|
|
863
|
+
return useRender({
|
|
864
|
+
render,
|
|
865
|
+
defaultTagName: "button",
|
|
866
|
+
props: {
|
|
867
|
+
...itemProps,
|
|
868
|
+
id: submenu.triggerId,
|
|
869
|
+
role: "menuitem",
|
|
870
|
+
"aria-haspopup": "menu",
|
|
871
|
+
"aria-expanded": open,
|
|
872
|
+
"aria-controls": submenu.popupId,
|
|
873
|
+
...menuDataAttributes({ open }),
|
|
874
|
+
style: { anchorName: submenu.anchorName },
|
|
875
|
+
className,
|
|
876
|
+
children,
|
|
877
|
+
ref,
|
|
878
|
+
onClick(event) {
|
|
879
|
+
openAt(event.detail === 0 ? "first" : "none");
|
|
880
|
+
},
|
|
881
|
+
onKeyDown(event) {
|
|
882
|
+
const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
|
|
883
|
+
if (event.key !== (rtl ? "ArrowLeft" : "ArrowRight")) return;
|
|
884
|
+
event.preventDefault();
|
|
885
|
+
openAt("first");
|
|
886
|
+
},
|
|
887
|
+
onPointerMove(event) {
|
|
888
|
+
if (event.pointerType !== "mouse" || disabled) return;
|
|
889
|
+
const sibling = parent.openSubmenuRef.current;
|
|
890
|
+
if (sibling && sibling.triggerRef !== triggerRef) sibling.close();
|
|
891
|
+
if (open || timerRef.current !== void 0) return;
|
|
892
|
+
timerRef.current = setTimeout(() => {
|
|
893
|
+
timerRef.current = void 0;
|
|
894
|
+
openAtRef.current("none");
|
|
895
|
+
}, SUBMENU_OPEN_DELAY);
|
|
896
|
+
},
|
|
897
|
+
onPointerLeave(event) {
|
|
898
|
+
clearTimeout(timerRef.current);
|
|
899
|
+
timerRef.current = void 0;
|
|
900
|
+
const popup = popupRef.current;
|
|
901
|
+
if (!open || !popup) return;
|
|
902
|
+
parent.graceRef.current = {
|
|
903
|
+
area: graceArea({ x: event.clientX, y: event.clientY }, popup.getBoundingClientRect()),
|
|
904
|
+
until: performance.now() + SUBMENU_GRACE_PERIOD
|
|
905
|
+
};
|
|
430
906
|
}
|
|
431
907
|
},
|
|
432
908
|
consumerProps: disabled ? rest : { ...rest, onClick }
|