@funstack/ui 0.0.0-stage → 0.1.0-alpha.1
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 +21 -0
- package/README.md +42 -2
- package/dist/accordion/accordion-context.d.mts +5 -0
- package/dist/accordion/accordion-context.d.mts.map +1 -0
- package/dist/accordion/accordion-context.mjs +9 -0
- package/dist/accordion/accordion-context.mjs.map +1 -0
- package/dist/accordion/accordion-item.d.mts +53 -0
- package/dist/accordion/accordion-item.d.mts.map +1 -0
- package/dist/accordion/accordion-item.mjs +85 -0
- package/dist/accordion/accordion-item.mjs.map +1 -0
- package/dist/accordion/accordion-panel.d.mts +40 -0
- package/dist/accordion/accordion-panel.d.mts.map +1 -0
- package/dist/accordion/accordion-panel.mjs +40 -0
- package/dist/accordion/accordion-panel.mjs.map +1 -0
- package/dist/accordion/accordion-root.d.mts +90 -0
- package/dist/accordion/accordion-root.d.mts.map +1 -0
- package/dist/accordion/accordion-root.mjs +229 -0
- package/dist/accordion/accordion-root.mjs.map +1 -0
- package/dist/accordion/accordion-trigger.d.mts +37 -0
- package/dist/accordion/accordion-trigger.d.mts.map +1 -0
- package/dist/accordion/accordion-trigger.mjs +81 -0
- package/dist/accordion/accordion-trigger.mjs.map +1 -0
- package/dist/accordion/index.d.mts +20 -0
- package/dist/accordion/index.d.mts.map +1 -0
- package/dist/accordion/index.mjs +20 -0
- package/dist/accordion/index.mjs.map +1 -0
- package/dist/checkbox/checkbox.d.mts +92 -0
- package/dist/checkbox/checkbox.d.mts.map +1 -0
- package/dist/checkbox/checkbox.mjs +82 -0
- package/dist/checkbox/checkbox.mjs.map +1 -0
- package/dist/checkbox/index.d.mts +3 -0
- package/dist/checkbox/index.mjs +1 -0
- package/dist/checkbox-group/checkbox-group-context.mjs +24 -0
- package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -0
- package/dist/checkbox-group/checkbox-group-item.d.mts +55 -0
- package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -0
- package/dist/checkbox-group/checkbox-group-item.mjs +90 -0
- package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -0
- package/dist/checkbox-group/checkbox-group-label.d.mts +21 -0
- package/dist/checkbox-group/checkbox-group-label.d.mts.map +1 -0
- package/dist/checkbox-group/checkbox-group-label.mjs +30 -0
- package/dist/checkbox-group/checkbox-group-label.mjs.map +1 -0
- package/dist/checkbox-group/checkbox-group-parent.d.mts +68 -0
- package/dist/checkbox-group/checkbox-group-parent.d.mts.map +1 -0
- package/dist/checkbox-group/checkbox-group-parent.mjs +145 -0
- package/dist/checkbox-group/checkbox-group-parent.mjs.map +1 -0
- package/dist/checkbox-group/checkbox-group-root.d.mts +100 -0
- package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -0
- package/dist/checkbox-group/checkbox-group-root.mjs +291 -0
- package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -0
- package/dist/checkbox-group/index.d.mts +23 -0
- package/dist/checkbox-group/index.d.mts.map +1 -0
- package/dist/checkbox-group/index.mjs +23 -0
- package/dist/checkbox-group/index.mjs.map +1 -0
- package/dist/dialog/dialog-close.d.mts +44 -0
- package/dist/dialog/dialog-close.d.mts.map +1 -0
- package/dist/dialog/dialog-close.mjs +76 -0
- package/dist/dialog/dialog-close.mjs.map +1 -0
- package/dist/dialog/dialog-context.mjs +13 -0
- package/dist/dialog/dialog-context.mjs.map +1 -0
- package/dist/dialog/dialog-description.d.mts +22 -0
- package/dist/dialog/dialog-description.d.mts.map +1 -0
- package/dist/dialog/dialog-description.mjs +31 -0
- package/dist/dialog/dialog-description.mjs.map +1 -0
- package/dist/dialog/dialog-form.d.mts +58 -0
- package/dist/dialog/dialog-form.d.mts.map +1 -0
- package/dist/dialog/dialog-form.mjs +108 -0
- package/dist/dialog/dialog-form.mjs.map +1 -0
- package/dist/dialog/dialog-popup.d.mts +81 -0
- package/dist/dialog/dialog-popup.d.mts.map +1 -0
- package/dist/dialog/dialog-popup.mjs +367 -0
- package/dist/dialog/dialog-popup.mjs.map +1 -0
- package/dist/dialog/dialog-root.d.mts +70 -0
- package/dist/dialog/dialog-root.d.mts.map +1 -0
- package/dist/dialog/dialog-root.mjs +113 -0
- package/dist/dialog/dialog-root.mjs.map +1 -0
- package/dist/dialog/dialog-title.d.mts +23 -0
- package/dist/dialog/dialog-title.d.mts.map +1 -0
- package/dist/dialog/dialog-title.mjs +31 -0
- package/dist/dialog/dialog-title.mjs.map +1 -0
- package/dist/dialog/dialog-trigger.d.mts +39 -0
- package/dist/dialog/dialog-trigger.d.mts.map +1 -0
- package/dist/dialog/dialog-trigger.mjs +80 -0
- package/dist/dialog/dialog-trigger.mjs.map +1 -0
- package/dist/dialog/index.d.mts +28 -0
- package/dist/dialog/index.d.mts.map +1 -0
- package/dist/dialog/index.mjs +28 -0
- package/dist/dialog/index.mjs.map +1 -0
- package/dist/index.d.mts +60 -0
- package/dist/index.mjs +12 -0
- package/dist/internal/action-details.mjs +47 -0
- package/dist/internal/action-details.mjs.map +1 -0
- package/dist/internal/anchor-position.d.mts +7 -0
- package/dist/internal/anchor-position.d.mts.map +1 -0
- package/dist/internal/anchor-position.mjs +16 -0
- package/dist/internal/anchor-position.mjs.map +1 -0
- package/dist/internal/data-attr.mjs +13 -0
- package/dist/internal/data-attr.mjs.map +1 -0
- package/dist/internal/merge-props.mjs +119 -0
- package/dist/internal/merge-props.mjs.map +1 -0
- package/dist/internal/platform-attributes.d.mts +55 -0
- package/dist/internal/platform-attributes.d.mts.map +1 -0
- package/dist/internal/sync-presence.mjs +19 -0
- package/dist/internal/sync-presence.mjs.map +1 -0
- package/dist/internal/transition-boundary.mjs +52 -0
- package/dist/internal/transition-boundary.mjs.map +1 -0
- package/dist/internal/transition-types.mjs +15 -0
- package/dist/internal/transition-types.mjs.map +1 -0
- package/dist/internal/types.d.mts +38 -0
- package/dist/internal/types.d.mts.map +1 -0
- package/dist/internal/use-action-trigger.mjs +58 -0
- package/dist/internal/use-action-trigger.mjs.map +1 -0
- package/dist/internal/use-controllable-action.mjs +115 -0
- package/dist/internal/use-controllable-action.mjs.map +1 -0
- package/dist/internal/use-delayed-pair.mjs +40 -0
- package/dist/internal/use-delayed-pair.mjs.map +1 -0
- package/dist/internal/use-dev-warning.mjs +21 -0
- package/dist/internal/use-dev-warning.mjs.map +1 -0
- package/dist/internal/use-exit-animation.mjs +55 -0
- package/dist/internal/use-exit-animation.mjs.map +1 -0
- package/dist/internal/use-form-reset.mjs +50 -0
- package/dist/internal/use-form-reset.mjs.map +1 -0
- package/dist/internal/use-native-checked.mjs +113 -0
- package/dist/internal/use-native-checked.mjs.map +1 -0
- package/dist/internal/use-native-group-reconciliation.mjs +64 -0
- package/dist/internal/use-native-group-reconciliation.mjs.map +1 -0
- package/dist/internal/use-popover-popup.mjs +274 -0
- package/dist/internal/use-popover-popup.mjs.map +1 -0
- package/dist/internal/use-popover-root.d.mts +2 -0
- package/dist/internal/use-popover-root.mjs +112 -0
- package/dist/internal/use-popover-root.mjs.map +1 -0
- package/dist/internal/use-popover-trigger.mjs +86 -0
- package/dist/internal/use-popover-trigger.mjs.map +1 -0
- package/dist/internal/use-render.mjs +51 -0
- package/dist/internal/use-render.mjs.map +1 -0
- package/dist/internal/use-until-found.mjs +40 -0
- package/dist/internal/use-until-found.mjs.map +1 -0
- package/dist/menu/index.d.mts +34 -0
- package/dist/menu/index.d.mts.map +1 -0
- package/dist/menu/index.mjs +34 -0
- package/dist/menu/index.mjs.map +1 -0
- package/dist/menu/menu-checkbox-item.d.mts +42 -0
- package/dist/menu/menu-checkbox-item.d.mts.map +1 -0
- package/dist/menu/menu-checkbox-item.mjs +71 -0
- package/dist/menu/menu-checkbox-item.mjs.map +1 -0
- package/dist/menu/menu-context.d.mts +1 -0
- package/dist/menu/menu-context.mjs +28 -0
- package/dist/menu/menu-context.mjs.map +1 -0
- package/dist/menu/menu-dom.mjs +37 -0
- package/dist/menu/menu-dom.mjs.map +1 -0
- package/dist/menu/menu-group.d.mts +48 -0
- package/dist/menu/menu-group.d.mts.map +1 -0
- package/dist/menu/menu-group.mjs +69 -0
- package/dist/menu/menu-group.mjs.map +1 -0
- package/dist/menu/menu-hover.mjs +29 -0
- package/dist/menu/menu-hover.mjs.map +1 -0
- package/dist/menu/menu-item.d.mts +45 -0
- package/dist/menu/menu-item.d.mts.map +1 -0
- package/dist/menu/menu-item.mjs +54 -0
- package/dist/menu/menu-item.mjs.map +1 -0
- package/dist/menu/menu-popup.d.mts +69 -0
- package/dist/menu/menu-popup.d.mts.map +1 -0
- package/dist/menu/menu-popup.mjs +207 -0
- package/dist/menu/menu-popup.mjs.map +1 -0
- package/dist/menu/menu-radio-group.d.mts +37 -0
- package/dist/menu/menu-radio-group.d.mts.map +1 -0
- package/dist/menu/menu-radio-group.mjs +72 -0
- package/dist/menu/menu-radio-group.mjs.map +1 -0
- package/dist/menu/menu-radio-item.d.mts +33 -0
- package/dist/menu/menu-radio-item.d.mts.map +1 -0
- package/dist/menu/menu-radio-item.mjs +62 -0
- package/dist/menu/menu-radio-item.mjs.map +1 -0
- package/dist/menu/menu-root.d.mts +54 -0
- package/dist/menu/menu-root.d.mts.map +1 -0
- package/dist/menu/menu-root.mjs +102 -0
- package/dist/menu/menu-root.mjs.map +1 -0
- package/dist/menu/menu-submenu-trigger.d.mts +31 -0
- package/dist/menu/menu-submenu-trigger.d.mts.map +1 -0
- package/dist/menu/menu-submenu-trigger.mjs +92 -0
- package/dist/menu/menu-submenu-trigger.mjs.map +1 -0
- package/dist/menu/menu-trigger.d.mts +36 -0
- package/dist/menu/menu-trigger.d.mts.map +1 -0
- package/dist/menu/menu-trigger.mjs +81 -0
- package/dist/menu/menu-trigger.mjs.map +1 -0
- package/dist/menu/use-menu-item.mjs +54 -0
- package/dist/menu/use-menu-item.mjs.map +1 -0
- package/dist/popover/index.d.mts +28 -0
- package/dist/popover/index.d.mts.map +1 -0
- package/dist/popover/index.mjs +28 -0
- package/dist/popover/index.mjs.map +1 -0
- package/dist/popover/popover-close.d.mts +41 -0
- package/dist/popover/popover-close.d.mts.map +1 -0
- package/dist/popover/popover-close.mjs +90 -0
- package/dist/popover/popover-close.mjs.map +1 -0
- package/dist/popover/popover-context.mjs +26 -0
- package/dist/popover/popover-context.mjs.map +1 -0
- package/dist/popover/popover-description.d.mts +22 -0
- package/dist/popover/popover-description.d.mts.map +1 -0
- package/dist/popover/popover-description.mjs +28 -0
- package/dist/popover/popover-description.mjs.map +1 -0
- package/dist/popover/popover-popup.d.mts +82 -0
- package/dist/popover/popover-popup.d.mts.map +1 -0
- package/dist/popover/popover-popup.mjs +93 -0
- package/dist/popover/popover-popup.mjs.map +1 -0
- package/dist/popover/popover-root.d.mts +64 -0
- package/dist/popover/popover-root.d.mts.map +1 -0
- package/dist/popover/popover-root.mjs +39 -0
- package/dist/popover/popover-root.mjs.map +1 -0
- package/dist/popover/popover-title.d.mts +23 -0
- package/dist/popover/popover-title.d.mts.map +1 -0
- package/dist/popover/popover-title.mjs +28 -0
- package/dist/popover/popover-title.mjs.map +1 -0
- package/dist/popover/popover-trigger.d.mts +37 -0
- package/dist/popover/popover-trigger.d.mts.map +1 -0
- package/dist/popover/popover-trigger.mjs +40 -0
- package/dist/popover/popover-trigger.mjs.map +1 -0
- package/dist/radio-group/index.d.mts +20 -0
- package/dist/radio-group/index.d.mts.map +1 -0
- package/dist/radio-group/index.mjs +20 -0
- package/dist/radio-group/index.mjs.map +1 -0
- package/dist/radio-group/radio-group-context.mjs +13 -0
- package/dist/radio-group/radio-group-context.mjs.map +1 -0
- package/dist/radio-group/radio-group-item.d.mts +54 -0
- package/dist/radio-group/radio-group-item.d.mts.map +1 -0
- package/dist/radio-group/radio-group-item.mjs +83 -0
- package/dist/radio-group/radio-group-item.mjs.map +1 -0
- package/dist/radio-group/radio-group-label.d.mts +21 -0
- package/dist/radio-group/radio-group-label.d.mts.map +1 -0
- package/dist/radio-group/radio-group-label.mjs +30 -0
- package/dist/radio-group/radio-group-label.mjs.map +1 -0
- package/dist/radio-group/radio-group-root.d.mts +92 -0
- package/dist/radio-group/radio-group-root.d.mts.map +1 -0
- package/dist/radio-group/radio-group-root.mjs +137 -0
- package/dist/radio-group/radio-group-root.mjs.map +1 -0
- package/dist/switch/index.d.mts +3 -0
- package/dist/switch/index.mjs +1 -0
- package/dist/switch/switch.d.mts +78 -0
- package/dist/switch/switch.d.mts.map +1 -0
- package/dist/switch/switch.mjs +77 -0
- package/dist/switch/switch.mjs.map +1 -0
- package/dist/tabs/index.d.mts +24 -0
- package/dist/tabs/index.d.mts.map +1 -0
- package/dist/tabs/index.mjs +23 -0
- package/dist/tabs/index.mjs.map +1 -0
- package/dist/tabs/tabs-context.d.mts +7 -0
- package/dist/tabs/tabs-context.d.mts.map +1 -0
- package/dist/tabs/tabs-context.mjs +14 -0
- package/dist/tabs/tabs-context.mjs.map +1 -0
- package/dist/tabs/tabs-ids.mjs +39 -0
- package/dist/tabs/tabs-ids.mjs.map +1 -0
- package/dist/tabs/tabs-indicator.d.mts +32 -0
- package/dist/tabs/tabs-indicator.d.mts.map +1 -0
- package/dist/tabs/tabs-indicator.mjs +49 -0
- package/dist/tabs/tabs-indicator.mjs.map +1 -0
- package/dist/tabs/tabs-list.d.mts +34 -0
- package/dist/tabs/tabs-list.d.mts.map +1 -0
- package/dist/tabs/tabs-list.mjs +130 -0
- package/dist/tabs/tabs-list.mjs.map +1 -0
- package/dist/tabs/tabs-panel.d.mts +50 -0
- package/dist/tabs/tabs-panel.d.mts.map +1 -0
- package/dist/tabs/tabs-panel.mjs +81 -0
- package/dist/tabs/tabs-panel.mjs.map +1 -0
- package/dist/tabs/tabs-root.d.mts +64 -0
- package/dist/tabs/tabs-root.d.mts.map +1 -0
- package/dist/tabs/tabs-root.mjs +90 -0
- package/dist/tabs/tabs-root.mjs.map +1 -0
- package/dist/tabs/tabs-tab.d.mts +49 -0
- package/dist/tabs/tabs-tab.d.mts.map +1 -0
- package/dist/tabs/tabs-tab.mjs +89 -0
- package/dist/tabs/tabs-tab.mjs.map +1 -0
- package/dist/toggle/index.d.mts +3 -0
- package/dist/toggle/index.mjs +1 -0
- package/dist/toggle/toggle.d.mts +57 -0
- package/dist/toggle/toggle.d.mts.map +1 -0
- package/dist/toggle/toggle.mjs +59 -0
- package/dist/toggle/toggle.mjs.map +1 -0
- package/dist/tooltip/index.d.mts +20 -0
- package/dist/tooltip/index.d.mts.map +1 -0
- package/dist/tooltip/index.mjs +19 -0
- package/dist/tooltip/index.mjs.map +1 -0
- package/dist/tooltip/tooltip-context.d.mts +5 -0
- package/dist/tooltip/tooltip-context.d.mts.map +1 -0
- package/dist/tooltip/tooltip-context.mjs +13 -0
- package/dist/tooltip/tooltip-context.mjs.map +1 -0
- package/dist/tooltip/tooltip-popup.d.mts +49 -0
- package/dist/tooltip/tooltip-popup.d.mts.map +1 -0
- package/dist/tooltip/tooltip-popup.mjs +52 -0
- package/dist/tooltip/tooltip-popup.mjs.map +1 -0
- package/dist/tooltip/tooltip-root.d.mts +41 -0
- package/dist/tooltip/tooltip-root.d.mts.map +1 -0
- package/dist/tooltip/tooltip-root.mjs +86 -0
- package/dist/tooltip/tooltip-root.mjs.map +1 -0
- package/dist/tooltip/tooltip-trigger.d.mts +38 -0
- package/dist/tooltip/tooltip-trigger.d.mts.map +1 -0
- package/dist/tooltip/tooltip-trigger.mjs +55 -0
- package/dist/tooltip/tooltip-trigger.mjs.map +1 -0
- package/dist/tooltip/use-tooltip-interaction.mjs +119 -0
- package/dist/tooltip/use-tooltip-interaction.mjs.map +1 -0
- package/package.json +73 -3
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
//#region src/menu/menu-dom.ts
|
|
3
|
+
const ITEM_SELECTOR = "[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]";
|
|
4
|
+
/** The items of `popup` in DOM order, without the items of its submenus. Disabled included. */
|
|
5
|
+
function menuItems(popup) {
|
|
6
|
+
return Array.from(popup.querySelectorAll(ITEM_SELECTOR)).filter((item) => item.closest("[role=\"menu\"]") === popup);
|
|
7
|
+
}
|
|
8
|
+
function isMenuItem(element, popup) {
|
|
9
|
+
return element.matches(ITEM_SELECTOR) && element.closest("[role=\"menu\"]") === popup;
|
|
10
|
+
}
|
|
11
|
+
function isDisabledItem(item) {
|
|
12
|
+
return item.getAttribute("aria-disabled") === "true";
|
|
13
|
+
}
|
|
14
|
+
/** The first (or last) enabled item; the first (or last) item if all are disabled. */
|
|
15
|
+
function edgeItem(popup, which) {
|
|
16
|
+
const items = menuItems(popup);
|
|
17
|
+
const ordered = which === "first" ? items : [...items].reverse();
|
|
18
|
+
return ordered.find((item) => !isDisabledItem(item)) ?? ordered[0] ?? null;
|
|
19
|
+
}
|
|
20
|
+
/** Keyboard focus: the ring shows even where the engine's heuristic would not (as Tabs). */
|
|
21
|
+
function focusItem(item) {
|
|
22
|
+
item.focus({ focusVisible: true });
|
|
23
|
+
}
|
|
24
|
+
/** The text typeahead matches: the item's `label` prop, else its text content. */
|
|
25
|
+
function typeaheadText(item) {
|
|
26
|
+
return (item.getAttribute("data-typeahead") ?? item.textContent ?? "").trim().toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
function isRtl(element) {
|
|
29
|
+
return getComputedStyle(element).direction === "rtl";
|
|
30
|
+
}
|
|
31
|
+
function hasModifier(event) {
|
|
32
|
+
return event.altKey || event.ctrlKey || event.metaKey;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { edgeItem, focusItem, hasModifier, isDisabledItem, isMenuItem, isRtl, menuItems, typeaheadText };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=menu-dom.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-dom.mjs","names":[],"sources":["../../src/menu/menu-dom.ts"],"sourcesContent":["\"use client\";\n// DOM helpers shared by the Menu parts. Items are read from the DOM when they are needed (a key\n// press, an open), never registered (design/components/menu.md 13 and 14).\n\nconst ITEM_SELECTOR = '[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]';\n\n/** The items of `popup` in DOM order, without the items of its submenus. Disabled included. */\nexport function menuItems(popup: Element): HTMLElement[] {\n return Array.from(popup.querySelectorAll<HTMLElement>(ITEM_SELECTOR)).filter(\n (item) => item.closest('[role=\"menu\"]') === popup,\n );\n}\n\nexport function isMenuItem(element: Element, popup: Element): boolean {\n return element.matches(ITEM_SELECTOR) && element.closest('[role=\"menu\"]') === popup;\n}\n\nexport function isDisabledItem(item: Element): boolean {\n return item.getAttribute(\"aria-disabled\") === \"true\";\n}\n\n/** The first (or last) enabled item; the first (or last) item if all are disabled. */\nexport function edgeItem(popup: Element, which: \"first\" | \"last\"): HTMLElement | null {\n const items = menuItems(popup);\n const ordered = which === \"first\" ? items : [...items].reverse();\n return ordered.find((item) => !isDisabledItem(item)) ?? ordered[0] ?? null;\n}\n\n/** Keyboard focus: the ring shows even where the engine's heuristic would not (as Tabs). */\nexport function focusItem(item: HTMLElement): void {\n item.focus({ focusVisible: true } as FocusOptions);\n}\n\n/** The text typeahead matches: the item's `label` prop, else its text content. */\nexport function typeaheadText(item: Element): string {\n return (item.getAttribute(\"data-typeahead\") ?? item.textContent ?? \"\").trim().toLowerCase();\n}\n\nexport function isRtl(element: Element): boolean {\n return getComputedStyle(element).direction === \"rtl\";\n}\n\nexport function hasModifier(event: { altKey: boolean; ctrlKey: boolean; metaKey: boolean }) {\n return event.altKey || event.ctrlKey || event.metaKey;\n}\n"],"mappings":";;AAIA,MAAM,gBAAgB;;AAGtB,SAAgB,UAAU,OAA+B;CACvD,OAAO,MAAM,KAAK,MAAM,iBAA8B,aAAa,CAAC,CAAC,CAAC,QACnE,SAAS,KAAK,QAAQ,iBAAe,MAAM,KAC9C;AACF;AAEA,SAAgB,WAAW,SAAkB,OAAyB;CACpE,OAAO,QAAQ,QAAQ,aAAa,KAAK,QAAQ,QAAQ,iBAAe,MAAM;AAChF;AAEA,SAAgB,eAAe,MAAwB;CACrD,OAAO,KAAK,aAAa,eAAe,MAAM;AAChD;;AAGA,SAAgB,SAAS,OAAgB,OAA6C;CACpF,MAAM,QAAQ,UAAU,KAAK;CAC7B,MAAM,UAAU,UAAU,UAAU,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ;CAC/D,OAAO,QAAQ,MAAM,SAAS,CAAC,eAAe,IAAI,CAAC,KAAK,QAAQ,MAAM;AACxE;;AAGA,SAAgB,UAAU,MAAyB;CACjD,KAAK,MAAM,EAAE,cAAc,KAAK,CAAiB;AACnD;;AAGA,SAAgB,cAAc,MAAuB;CACnD,QAAQ,KAAK,aAAa,gBAAgB,KAAK,KAAK,eAAe,GAAA,CAAI,KAAK,CAAC,CAAC,YAAY;AAC5F;AAEA,SAAgB,MAAM,SAA2B;CAC/C,OAAO,iBAAiB,OAAO,CAAC,CAAC,cAAc;AACjD;AAEA,SAAgB,YAAY,OAAgE;CAC1F,OAAO,MAAM,UAAU,MAAM,WAAW,MAAM;AAChD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
3
|
+
//#region src/menu/menu-group.d.ts
|
|
4
|
+
/** `Menu.Group`, `Menu.GroupLabel` and `Menu.Separator` have no state. */
|
|
5
|
+
export type MenuGroupState = Record<string, never>;
|
|
6
|
+
export type MenuGroupLabelState = Record<string, never>;
|
|
7
|
+
export type MenuSeparatorState = Record<string, never>;
|
|
8
|
+
export interface MenuGroupProps extends Omit<ComponentPropsWithRef<"div">, "role" | "className" | "style" | "children"> {
|
|
9
|
+
className?: StateValue<string | undefined, MenuGroupState>;
|
|
10
|
+
style?: StateValue<CSSProperties | undefined, MenuGroupState>;
|
|
11
|
+
children?: StateValue<ReactNode, MenuGroupState>;
|
|
12
|
+
render?: RenderProp<ComponentPropsWithRef<"div">, MenuGroupState>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A labelled set of items: `<div role="group" aria-labelledby>` named by the `Menu.GroupLabel`
|
|
16
|
+
* inside it. Owned keys: `role`, `aria-labelledby` (token-merged).
|
|
17
|
+
*/
|
|
18
|
+
export declare function MenuGroup({ className, style, children, render, ...rest }: MenuGroupProps): import("react").JSX.Element;
|
|
19
|
+
export declare namespace MenuGroup {
|
|
20
|
+
var displayName: string;
|
|
21
|
+
}
|
|
22
|
+
export interface MenuGroupLabelProps extends Omit<ComponentPropsWithRef<"div">, "id" | "className" | "style" | "children"> {
|
|
23
|
+
className?: StateValue<string | undefined, MenuGroupLabelState>;
|
|
24
|
+
style?: StateValue<CSSProperties | undefined, MenuGroupLabelState>;
|
|
25
|
+
children?: StateValue<ReactNode, MenuGroupLabelState>;
|
|
26
|
+
render?: RenderProp<ComponentPropsWithRef<"div">, MenuGroupLabelState>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The name of the enclosing `Menu.Group` or `Menu.RadioGroup`: a `<div>` with the id the group's
|
|
30
|
+
* `aria-labelledby` references. Owned key: `id`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function MenuGroupLabel({ className, style, children, render, ...rest }: MenuGroupLabelProps): ReactNode;
|
|
33
|
+
export declare namespace MenuGroupLabel {
|
|
34
|
+
var displayName: string;
|
|
35
|
+
}
|
|
36
|
+
export interface MenuSeparatorProps extends Omit<ComponentPropsWithRef<"hr">, "className" | "style" | "children"> {
|
|
37
|
+
className?: StateValue<string | undefined, MenuSeparatorState>;
|
|
38
|
+
style?: StateValue<CSSProperties | undefined, MenuSeparatorState>;
|
|
39
|
+
/** Another element must carry `role="separator"` itself. */
|
|
40
|
+
render?: RenderProp<ComponentPropsWithRef<"hr">, MenuSeparatorState>;
|
|
41
|
+
}
|
|
42
|
+
/** A divider between items: `<hr>` (implicit `role="separator"`). Owns no keys. */
|
|
43
|
+
export declare function MenuSeparator({ className, style, render, ...rest }: MenuSeparatorProps): ReactNode;
|
|
44
|
+
export declare namespace MenuSeparator {
|
|
45
|
+
var displayName: string;
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//# sourceMappingURL=menu-group.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-group.d.mts","names":[],"sources":["../../src/menu/menu-group.tsx"],"mappings":";;;;YASY,iBAAiB;YACjB,sBAAsB;YACtB,qBAAqB;iBAIhB,uBAAuB,KACtC;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;wBAOpC,YAAY,WAAW,OAAO,UAAU,WAAW,QAAQ,iCAAc,IAAA;yBAAzE;;;iBAiBC,4BAA4B,KAC3C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;wBAOpC,iBACd,WACA,OACA,UACA,WACG,QACF,sBAAmB;yBANN;;;iBA0BC,2BAA2B,KAC1C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,SAAS,WAAW,6BAA6B;;;wBAInC,gBAAgB,WAAW,OAAO,WAAW,QAAQ,qBAAkB;yBAAvE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
3
|
+
import { useDevWarning } from "../internal/use-dev-warning.mjs";
|
|
4
|
+
import { MenuGroupContext } from "./menu-context.mjs";
|
|
5
|
+
import { use, useId } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
//#region src/menu/menu-group.tsx
|
|
8
|
+
const EMPTY = {};
|
|
9
|
+
/**
|
|
10
|
+
* A labelled set of items: `<div role="group" aria-labelledby>` named by the `Menu.GroupLabel`
|
|
11
|
+
* inside it. Owned keys: `role`, `aria-labelledby` (token-merged).
|
|
12
|
+
*/
|
|
13
|
+
function MenuGroup({ className, style, children, render, ...rest }) {
|
|
14
|
+
const labelId = `${useId()}-label`;
|
|
15
|
+
const element = useRender({
|
|
16
|
+
defaultElement: "div",
|
|
17
|
+
render,
|
|
18
|
+
state: EMPTY,
|
|
19
|
+
className,
|
|
20
|
+
style,
|
|
21
|
+
children,
|
|
22
|
+
props: rest,
|
|
23
|
+
libraryProps: {
|
|
24
|
+
role: "group",
|
|
25
|
+
"aria-labelledby": labelId
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return /* @__PURE__ */ jsx(MenuGroupContext, {
|
|
29
|
+
value: { labelId },
|
|
30
|
+
children: element
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
MenuGroup.displayName = "MenuGroup";
|
|
34
|
+
/**
|
|
35
|
+
* The name of the enclosing `Menu.Group` or `Menu.RadioGroup`: a `<div>` with the id the group's
|
|
36
|
+
* `aria-labelledby` references. Owned key: `id`.
|
|
37
|
+
*/
|
|
38
|
+
function MenuGroupLabel({ className, style, children, render, ...rest }) {
|
|
39
|
+
const group = use(MenuGroupContext);
|
|
40
|
+
useDevWarning(group === null, "Menu.GroupLabel: rendered outside a Menu.Group or Menu.RadioGroup; it names nothing.");
|
|
41
|
+
return useRender({
|
|
42
|
+
defaultElement: "div",
|
|
43
|
+
render,
|
|
44
|
+
state: EMPTY,
|
|
45
|
+
className,
|
|
46
|
+
style,
|
|
47
|
+
children,
|
|
48
|
+
props: rest,
|
|
49
|
+
libraryProps: group === null ? {} : { id: group.labelId }
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
MenuGroupLabel.displayName = "MenuGroupLabel";
|
|
53
|
+
/** A divider between items: `<hr>` (implicit `role="separator"`). Owns no keys. */
|
|
54
|
+
function MenuSeparator({ className, style, render, ...rest }) {
|
|
55
|
+
return useRender({
|
|
56
|
+
defaultElement: "hr",
|
|
57
|
+
render,
|
|
58
|
+
state: EMPTY,
|
|
59
|
+
className,
|
|
60
|
+
style,
|
|
61
|
+
children: void 0,
|
|
62
|
+
props: rest
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
MenuSeparator.displayName = "MenuSeparator";
|
|
66
|
+
//#endregion
|
|
67
|
+
export { MenuGroup, MenuGroupLabel, MenuSeparator };
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=menu-group.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-group.mjs","names":[],"sources":["../../src/menu/menu-group.tsx"],"sourcesContent":["\"use client\";\nimport { use, useId } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { MenuGroupContext } from \"./menu-context.js\";\n\n/** `Menu.Group`, `Menu.GroupLabel` and `Menu.Separator` have no state. */\nexport type MenuGroupState = Record<string, never>;\nexport type MenuGroupLabelState = Record<string, never>;\nexport type MenuSeparatorState = Record<string, never>;\n\nconst EMPTY: Record<string, never> = {};\n\nexport interface MenuGroupProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"role\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, MenuGroupState>;\n style?: StateValue<CSSProperties | undefined, MenuGroupState>;\n children?: StateValue<ReactNode, MenuGroupState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, MenuGroupState>;\n}\n\n/**\n * A labelled set of items: `<div role=\"group\" aria-labelledby>` named by the `Menu.GroupLabel`\n * inside it. Owned keys: `role`, `aria-labelledby` (token-merged).\n */\nexport function MenuGroup({ className, style, children, render, ...rest }: MenuGroupProps) {\n const labelId = `${useId()}-label`;\n const element = useRender<ComponentPropsWithRef<\"div\">, MenuGroupState>({\n defaultElement: \"div\",\n render,\n state: EMPTY,\n className,\n style,\n children,\n props: rest,\n libraryProps: { role: \"group\", \"aria-labelledby\": labelId },\n });\n return <MenuGroupContext value={{ labelId }}>{element}</MenuGroupContext>;\n}\n\nMenuGroup.displayName = \"MenuGroup\";\n\nexport interface MenuGroupLabelProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"id\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, MenuGroupLabelState>;\n style?: StateValue<CSSProperties | undefined, MenuGroupLabelState>;\n children?: StateValue<ReactNode, MenuGroupLabelState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, MenuGroupLabelState>;\n}\n\n/**\n * The name of the enclosing `Menu.Group` or `Menu.RadioGroup`: a `<div>` with the id the group's\n * `aria-labelledby` references. Owned key: `id`.\n */\nexport function MenuGroupLabel({\n className,\n style,\n children,\n render,\n ...rest\n}: MenuGroupLabelProps) {\n const group = use(MenuGroupContext);\n useDevWarning(\n group === null,\n \"Menu.GroupLabel: rendered outside a Menu.Group or Menu.RadioGroup; it names nothing.\",\n );\n return useRender<ComponentPropsWithRef<\"div\">, MenuGroupLabelState>({\n defaultElement: \"div\",\n render,\n state: EMPTY,\n className,\n style,\n children,\n props: rest,\n libraryProps: group === null ? {} : { id: group.labelId },\n });\n}\n\nMenuGroupLabel.displayName = \"MenuGroupLabel\";\n\nexport interface MenuSeparatorProps extends Omit<\n ComponentPropsWithRef<\"hr\">,\n \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, MenuSeparatorState>;\n style?: StateValue<CSSProperties | undefined, MenuSeparatorState>;\n /** Another element must carry `role=\"separator\"` itself. */\n render?: RenderProp<ComponentPropsWithRef<\"hr\">, MenuSeparatorState>;\n}\n\n/** A divider between items: `<hr>` (implicit `role=\"separator\"`). Owns no keys. */\nexport function MenuSeparator({ className, style, render, ...rest }: MenuSeparatorProps) {\n return useRender<ComponentPropsWithRef<\"hr\">, MenuSeparatorState>({\n defaultElement: \"hr\",\n render,\n state: EMPTY,\n className,\n style,\n children: undefined,\n props: rest,\n });\n}\n\nMenuSeparator.displayName = \"MenuSeparator\";\n"],"mappings":";;;;;;;AAaA,MAAM,QAA+B,CAAC;;;;;AAgBtC,SAAgB,UAAU,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAAwB;CACzF,MAAM,UAAU,GAAG,MAAM,EAAE;CAC3B,MAAM,UAAU,UAAwD;EACtE,gBAAgB;EAChB;EACA,OAAO;EACP;EACA;EACA;EACA,OAAO;EACP,cAAc;GAAE,MAAM;GAAS,mBAAmB;EAAQ;CAC5D,CAAC;CACD,OAAO,oBAAC,kBAAD;EAAkB,OAAO,EAAE,QAAQ;EAAI,UAAA;CAA0B,CAAA;AAC1E;AAEA,UAAU,cAAc;;;;;AAgBxB,SAAgB,eAAe,EAC7B,WACA,OACA,UACA,QACA,GAAG,QACmB;CACtB,MAAM,QAAQ,IAAI,gBAAgB;CAClC,cACE,UAAU,MACV,sFACF;CACA,OAAO,UAA6D;EAClE,gBAAgB;EAChB;EACA,OAAO;EACP;EACA;EACA;EACA,OAAO;EACP,cAAc,UAAU,OAAO,CAAC,IAAI,EAAE,IAAI,MAAM,QAAQ;CAC1D,CAAC;AACH;AAEA,eAAe,cAAc;;AAa7B,SAAgB,cAAc,EAAE,WAAW,OAAO,QAAQ,GAAG,QAA4B;CACvF,OAAO,UAA2D;EAChE,gBAAgB;EAChB;EACA,OAAO;EACP;EACA;EACA,UAAU,KAAA;EACV,OAAO;CACT,CAAC;AACH;AAEA,cAAc,cAAc"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
//#region src/menu/menu-hover.ts
|
|
3
|
+
function hovered(element) {
|
|
4
|
+
if (element === null) return false;
|
|
5
|
+
try {
|
|
6
|
+
return element.matches(":hover");
|
|
7
|
+
} catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Starts the submenu's `closeDelay` (design/components/menu.md 7): on expiry the submenu closes
|
|
13
|
+
* with `"dismiss"` unless the pointer is back over its SubmenuTrigger or Popup, or keyboard focus
|
|
14
|
+
* is inside it (a user who arrowed in and nudged the mouse keeps the submenu).
|
|
15
|
+
*/
|
|
16
|
+
function scheduleHoverClose(menu, event) {
|
|
17
|
+
const { popover, timers, closeDelay } = menu;
|
|
18
|
+
timers.close(closeDelay, () => {
|
|
19
|
+
const popup = popover.popupRef.current;
|
|
20
|
+
if (hovered(popover.triggerRef.current) || hovered(popup)) return;
|
|
21
|
+
const active = popup?.ownerDocument.activeElement ?? null;
|
|
22
|
+
if (popup !== null && active !== null && popup.contains(active)) return;
|
|
23
|
+
popover.requestClose(event, "dismiss");
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { scheduleHoverClose };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=menu-hover.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-hover.mjs","names":[],"sources":["../../src/menu/menu-hover.ts"],"sourcesContent":["\"use client\";\nimport type { MenuContextValue } from \"./menu-context.js\";\n\nfunction hovered(element: Element | null): boolean {\n if (element === null) return false;\n try {\n return element.matches(\":hover\");\n } catch {\n return false;\n }\n}\n\n/**\n * Starts the submenu's `closeDelay` (design/components/menu.md 7): on expiry the submenu closes\n * with `\"dismiss\"` unless the pointer is back over its SubmenuTrigger or Popup, or keyboard focus\n * is inside it (a user who arrowed in and nudged the mouse keeps the submenu).\n */\nexport function scheduleHoverClose(menu: MenuContextValue, event: Event): void {\n const { popover, timers, closeDelay } = menu;\n timers.close(closeDelay, () => {\n const popup = popover.popupRef.current;\n if (hovered(popover.triggerRef.current) || hovered(popup)) return;\n const active = popup?.ownerDocument.activeElement ?? null;\n if (popup !== null && active !== null && popup.contains(active)) return;\n popover.requestClose(event, \"dismiss\");\n });\n}\n"],"mappings":";;AAGA,SAAS,QAAQ,SAAkC;CACjD,IAAI,YAAY,MAAM,OAAO;CAC7B,IAAI;EACF,OAAO,QAAQ,QAAQ,QAAQ;CACjC,QAAQ;EACN,OAAO;CACT;AACF;;;;;;AAOA,SAAgB,mBAAmB,MAAwB,OAAoB;CAC7E,MAAM,EAAE,SAAS,QAAQ,eAAe;CACxC,OAAO,MAAM,kBAAkB;EAC7B,MAAM,QAAQ,QAAQ,SAAS;EAC/B,IAAI,QAAQ,QAAQ,WAAW,OAAO,KAAK,QAAQ,KAAK,GAAG;EAC3D,MAAM,SAAS,OAAO,cAAc,iBAAiB;EACrD,IAAI,UAAU,QAAQ,WAAW,QAAQ,MAAM,SAAS,MAAM,GAAG;EACjE,QAAQ,aAAa,OAAO,SAAS;CACvC,CAAC;AACH"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Action, ActionDetails, RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
3
|
+
//#region src/menu/menu-item.d.ts
|
|
4
|
+
/** Details passed to `Menu.Item`'s `action`. */
|
|
5
|
+
export interface MenuItemDetails extends ActionDetails {}
|
|
6
|
+
export interface MenuItemState {
|
|
7
|
+
/** This item's `action` is in flight. */
|
|
8
|
+
readonly pending: boolean;
|
|
9
|
+
readonly disabled: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface MenuItemProps extends Omit<ComponentPropsWithRef<"button">, "onClick" | "onKeyDown" | "onPointerMove" | "disabled" | "className" | "style" | "children"> {
|
|
12
|
+
/**
|
|
13
|
+
* Runs inside this item's own transition (`data-pending` on the item); the menu stays open
|
|
14
|
+
* until it settles, then the whole menu closes (`closeOnActivate`). May be a Server Function
|
|
15
|
+
* (`details.event` is virtual). A rejection leaves the menu open and reaches the nearest Error
|
|
16
|
+
* Boundary above the item. Without `action`, activation only closes.
|
|
17
|
+
*/
|
|
18
|
+
action?: Action<[details: MenuItemDetails]>;
|
|
19
|
+
/** Close the whole menu once `action` settles. Default `true`. */
|
|
20
|
+
closeOnActivate?: boolean;
|
|
21
|
+
/** Renders `aria-disabled`: the item stays focusable and never activates. */
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
/** Text typeahead matches when the item's text content is not representative (an icon). */
|
|
24
|
+
label?: string;
|
|
25
|
+
className?: StateValue<string | undefined, MenuItemState>;
|
|
26
|
+
style?: StateValue<CSSProperties | undefined, MenuItemState>;
|
|
27
|
+
children?: StateValue<ReactNode, MenuItemState>;
|
|
28
|
+
render?: RenderProp<ComponentPropsWithRef<"button">, MenuItemState>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A menu command: `<button type="button" role="menuitem" tabindex="-1">`. Click, Enter or Space
|
|
32
|
+
* runs `action` in the item's own transition; once it resolves the whole menu stack closes
|
|
33
|
+
* through the top-level Root (`openChangeAction(false, { reason: "activate" })` awaited, view
|
|
34
|
+
* transition if set). Hovering the item focuses it.
|
|
35
|
+
*
|
|
36
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>`), `role`, `tabIndex`, `disabled`
|
|
37
|
+
* (always absent), `aria-disabled`, `onClick`, `onKeyDown`, `onPointerMove`, `data-pending`,
|
|
38
|
+
* `data-disabled`, `data-typeahead`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function MenuItem({ action, closeOnActivate, disabled, label, className, style, children, render, ...rest }: MenuItemProps): ReactNode;
|
|
41
|
+
export declare namespace MenuItem {
|
|
42
|
+
var displayName: string;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//# sourceMappingURL=menu-item.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-item.d.mts","names":[],"sources":["../../src/menu/menu-item.tsx"],"mappings":";;;;iBAWiB,wBAAwB;iBAExB;;WAEN;WACA;;iBAGM,sBAAsB,KACrC;;;;;;;EASA,SAAS,QAAQ,SAAS;;EAE1B;;EAEA;;EAEA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;wBAavC,WACd,QACA,iBACA,UACA,OACA,WACA,OACA,UACA,WACG,QACF,gBAAa;yBAVA"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createActionDetails } from "../internal/action-details.mjs";
|
|
3
|
+
import { dataAttr } from "../internal/data-attr.mjs";
|
|
4
|
+
import { rendersButton, useRender } from "../internal/use-render.mjs";
|
|
5
|
+
import { useActionTrigger } from "../internal/use-action-trigger.mjs";
|
|
6
|
+
import { useMenuContext } from "./menu-context.mjs";
|
|
7
|
+
import { useMenuItem } from "./use-menu-item.mjs";
|
|
8
|
+
//#region src/menu/menu-item.tsx
|
|
9
|
+
/**
|
|
10
|
+
* A menu command: `<button type="button" role="menuitem" tabindex="-1">`. Click, Enter or Space
|
|
11
|
+
* runs `action` in the item's own transition; once it resolves the whole menu stack closes
|
|
12
|
+
* through the top-level Root (`openChangeAction(false, { reason: "activate" })` awaited, view
|
|
13
|
+
* transition if set). Hovering the item focuses it.
|
|
14
|
+
*
|
|
15
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>`), `role`, `tabIndex`, `disabled`
|
|
16
|
+
* (always absent), `aria-disabled`, `onClick`, `onKeyDown`, `onPointerMove`, `data-pending`,
|
|
17
|
+
* `data-disabled`, `data-typeahead`.
|
|
18
|
+
*/
|
|
19
|
+
function MenuItem({ action, closeOnActivate = true, disabled = false, label, className, style, children, render, ...rest }) {
|
|
20
|
+
const menu = useMenuContext("Menu.Item");
|
|
21
|
+
const { pending, run } = useActionTrigger(menu.root.popover.openGeneration);
|
|
22
|
+
const { libraryProps } = useMenuItem({
|
|
23
|
+
part: "Menu.Item",
|
|
24
|
+
role: "menuitem",
|
|
25
|
+
disabled,
|
|
26
|
+
label,
|
|
27
|
+
activate: (event) => {
|
|
28
|
+
const details = createActionDetails(event, {});
|
|
29
|
+
run(() => action?.(details), closeOnActivate ? () => menu.root.popover.requestClose(event, "activate") : void 0);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return useRender({
|
|
33
|
+
defaultElement: "button",
|
|
34
|
+
render,
|
|
35
|
+
state: {
|
|
36
|
+
pending,
|
|
37
|
+
disabled
|
|
38
|
+
},
|
|
39
|
+
className,
|
|
40
|
+
style,
|
|
41
|
+
children,
|
|
42
|
+
props: rest,
|
|
43
|
+
libraryProps: {
|
|
44
|
+
...rendersButton(render) && { type: "button" },
|
|
45
|
+
...libraryProps,
|
|
46
|
+
"data-pending": dataAttr(pending)
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
MenuItem.displayName = "MenuItem";
|
|
51
|
+
//#endregion
|
|
52
|
+
export { MenuItem };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=menu-item.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-item.mjs","names":[],"sources":["../../src/menu/menu-item.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { Action, ActionDetails, RenderProp, StateValue } from \"../internal/types.js\";\nimport { useActionTrigger } from \"../internal/use-action-trigger.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\nimport { useMenuContext } from \"./menu-context.js\";\nimport { useMenuItem } from \"./use-menu-item.js\";\n\n/** Details passed to `Menu.Item`'s `action`. */\nexport interface MenuItemDetails extends ActionDetails {}\n\nexport interface MenuItemState {\n /** This item's `action` is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface MenuItemProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"onKeyDown\" | \"onPointerMove\" | \"disabled\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * Runs inside this item's own transition (`data-pending` on the item); the menu stays open\n * until it settles, then the whole menu closes (`closeOnActivate`). May be a Server Function\n * (`details.event` is virtual). A rejection leaves the menu open and reaches the nearest Error\n * Boundary above the item. Without `action`, activation only closes.\n */\n action?: Action<[details: MenuItemDetails]>;\n /** Close the whole menu once `action` settles. Default `true`. */\n closeOnActivate?: boolean;\n /** Renders `aria-disabled`: the item stays focusable and never activates. */\n disabled?: boolean;\n /** Text typeahead matches when the item's text content is not representative (an icon). */\n label?: string;\n className?: StateValue<string | undefined, MenuItemState>;\n style?: StateValue<CSSProperties | undefined, MenuItemState>;\n children?: StateValue<ReactNode, MenuItemState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, MenuItemState>;\n}\n\n/**\n * A menu command: `<button type=\"button\" role=\"menuitem\" tabindex=\"-1\">`. Click, Enter or Space\n * runs `action` in the item's own transition; once it resolves the whole menu stack closes\n * through the top-level Root (`openChangeAction(false, { reason: \"activate\" })` awaited, view\n * transition if set). Hovering the item focuses it.\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>`), `role`, `tabIndex`, `disabled`\n * (always absent), `aria-disabled`, `onClick`, `onKeyDown`, `onPointerMove`, `data-pending`,\n * `data-disabled`, `data-typeahead`.\n */\nexport function MenuItem({\n action,\n closeOnActivate = true,\n disabled = false,\n label,\n className,\n style,\n children,\n render,\n ...rest\n}: MenuItemProps) {\n const menu = useMenuContext(\"Menu.Item\");\n // The close belongs to the top-level menu, so its open generation guards it.\n const { pending, run } = useActionTrigger(menu.root.popover.openGeneration);\n\n const { libraryProps } = useMenuItem({\n part: \"Menu.Item\",\n role: \"menuitem\",\n disabled,\n label,\n activate: (event) => {\n const details: MenuItemDetails = createActionDetails(event, {});\n // Repeated activations re-run the action; the first close wins (design 2.3).\n run(\n () => action?.(details),\n closeOnActivate ? () => menu.root.popover.requestClose(event, \"activate\") : undefined,\n );\n },\n });\n\n const state: MenuItemState = { pending, disabled };\n\n return useRender<ComponentPropsWithRef<\"button\">, MenuItemState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n ...(rendersButton(render) && { type: \"button\" as const }),\n ...libraryProps,\n \"data-pending\": dataAttr(pending),\n },\n });\n}\n\nMenuItem.displayName = \"MenuItem\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoDA,SAAgB,SAAS,EACvB,QACA,kBAAkB,MAClB,WAAW,OACX,OACA,WACA,OACA,UACA,QACA,GAAG,QACa;CAChB,MAAM,OAAO,eAAe,WAAW;CAEvC,MAAM,EAAE,SAAS,QAAQ,iBAAiB,KAAK,KAAK,QAAQ,cAAc;CAE1E,MAAM,EAAE,iBAAiB,YAAY;EACnC,MAAM;EACN,MAAM;EACN;EACA;EACA,WAAW,UAAU;GACnB,MAAM,UAA2B,oBAAoB,OAAO,CAAC,CAAC;GAE9D,UACQ,SAAS,OAAO,GACtB,wBAAwB,KAAK,KAAK,QAAQ,aAAa,OAAO,UAAU,IAAI,KAAA,CAC9E;EACF;CACF,CAAC;CAID,OAAO,UAA0D;EAC/D,gBAAgB;EAChB;EACA,OAAA;GAL6B;GAAS;EAKlC;EACJ;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;GACvD,GAAG;GACH,gBAAgB,SAAS,OAAO;EAClC;CACF,CAAC;AACH;AAEA,SAAS,cAAc"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { AnchorAlign, AnchorSide } from "../internal/anchor-position.mjs";
|
|
3
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode, ViewTransitionClass } from "react";
|
|
4
|
+
//#region src/menu/menu-popup.d.ts
|
|
5
|
+
export type MenuSide = AnchorSide;
|
|
6
|
+
export type MenuAlign = AnchorAlign;
|
|
7
|
+
export interface MenuPopupState {
|
|
8
|
+
/** Committed open state. Read `:popover-open` for the DOM truth. */
|
|
9
|
+
readonly open: boolean;
|
|
10
|
+
readonly side: MenuSide | null;
|
|
11
|
+
readonly align: MenuAlign;
|
|
12
|
+
/** This popup belongs to a submenu. */
|
|
13
|
+
readonly submenu: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface MenuPopupProps extends Omit<ComponentPropsWithRef<"div">, "id" | "popover" | "role" | "aria-labelledby" | "onKeyDown" | "onFocus" | "onBlur" | "onMouseDown" | "onPointerEnter" | "onPointerLeave" | "onToggle" | "onBeforeToggle" | "className" | "style" | "children"> {
|
|
16
|
+
/**
|
|
17
|
+
* Placement against the Trigger through inline `position-area`, with `position-try-fallbacks`
|
|
18
|
+
* flipping it when it does not fit. Default `"bottom"` for a top-level menu, `"right"` for a
|
|
19
|
+
* submenu (whose fallback is `flip-inline` only). `null` emits no inline positioning (and no
|
|
20
|
+
* `data-side` / `data-align`): the stylesheet owns placement.
|
|
21
|
+
*/
|
|
22
|
+
side?: MenuSide | null;
|
|
23
|
+
/** Alignment along the side. Default `"start"`. Ignored when `side` is `null`. */
|
|
24
|
+
align?: MenuAlign;
|
|
25
|
+
/**
|
|
26
|
+
* `false` (default): the items are not rendered while the menu is closed; mounting them is part
|
|
27
|
+
* of the opening transition, so items that suspend hold the open (and the Trigger pending)
|
|
28
|
+
* until they are ready. `true`: they stay mounted inside `<Activity mode="hidden">`.
|
|
29
|
+
*/
|
|
30
|
+
keepMounted?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Experimental (Chromium 150+). Renders `focusgroup="menu wrap|nowrap"` and, where the engine
|
|
33
|
+
* implements `focusGroup`, lets the browser handle ArrowUp / ArrowDown / Home / End: items then
|
|
34
|
+
* render no `tabindex` and the library's arrow-key handling steps aside. Typeahead, activation,
|
|
35
|
+
* submenu keys and Tab stay in JS. No effect in other engines. Default `false`.
|
|
36
|
+
*/
|
|
37
|
+
experimental_focusgroup?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Forwarded to the `<ViewTransition default>` around the popup element. Animates the opening
|
|
40
|
+
* commit and every library-driven close (an item's activation, the Trigger, ArrowLeft, hover
|
|
41
|
+
* leaving a submenu, controlled `open={false}`) in all engines; dismissals (Escape, a click
|
|
42
|
+
* outside, Tab) animate with CSS only. Without it the part renders no `<ViewTransition>`.
|
|
43
|
+
*/
|
|
44
|
+
transition?: ViewTransitionClass;
|
|
45
|
+
className?: StateValue<string | undefined, MenuPopupState>;
|
|
46
|
+
style?: StateValue<CSSProperties | undefined, MenuPopupState>;
|
|
47
|
+
children?: StateValue<ReactNode, MenuPopupState>;
|
|
48
|
+
/** Should render a `<div>`; a `<dialog>` would run the dialog focusing steps. */
|
|
49
|
+
render?: RenderProp<ComponentPropsWithRef<"div">, MenuPopupState>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The menu itself: `<div popover="auto" role="menu" aria-labelledby={trigger} tabindex="-1">`,
|
|
53
|
+
* placed against its Trigger with CSS Anchor Positioning. Owns the keyboard model (design 5):
|
|
54
|
+
* ArrowUp / ArrowDown / Home / End move focus between items (or the browser does, with
|
|
55
|
+
* `experimental_focusgroup` where supported), printable characters run typeahead, ArrowLeft
|
|
56
|
+
* closes a submenu. The top-level popup closes the whole menu at once when focus leaves it (Tab).
|
|
57
|
+
* Style it with `:popover-open`; it emits `data-side` / `data-align` but no `data-open`.
|
|
58
|
+
*
|
|
59
|
+
* Owned keys (conventions 1.2): `id`, `popover`, `role`, `aria-labelledby` (token-merged),
|
|
60
|
+
* `tabIndex`, `focusgroup` (with the flag), `onKeyDown`, `onBlur`, `onMouseDown` (a press on
|
|
61
|
+
* non-item space keeps focus on the current item), `onPointerEnter` / `onPointerLeave`, and
|
|
62
|
+
* `data-side` / `data-align` unless `side` is `null`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function MenuPopup({ side: sideProp, align, keepMounted, experimental_focusgroup, transition, className, style, children, render, ...rest }: MenuPopupProps): ReactNode;
|
|
65
|
+
export declare namespace MenuPopup {
|
|
66
|
+
var displayName: string;
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
//# sourceMappingURL=menu-popup.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-popup.d.mts","names":[],"sources":["../../src/menu/menu-popup.tsx"],"mappings":";;;;YAiCY,WAAW;YACX,YAAY;iBAEP;;WAEN;WACA,MAAM;WACN,OAAO;;WAEP;;iBAGM,uBAAuB,KACtC;;;;;;;EAuBA,OAAO;;EAEP,QAAQ;;;;;;EAMR;;;;;;;EAOA;;;;;;;EAOA,aAAa;EACb,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,8BAA8B;;;;;;;;;;;;;;;wBA4BpC,YACd,MAAM,UACN,OACA,aACA,yBACA,YACA,WACA,OACA,UACA,WACG,QACF,iBAAc;yBAXD"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useTransitionBoundary } from "../internal/transition-boundary.mjs";
|
|
3
|
+
import { mergeRefs } from "../internal/merge-props.mjs";
|
|
4
|
+
import { resolveStateValue, useRender } from "../internal/use-render.mjs";
|
|
5
|
+
import { POSITION_TRY_FALLBACKS, positionArea } from "../internal/anchor-position.mjs";
|
|
6
|
+
import { usePopoverPopup } from "../internal/use-popover-popup.mjs";
|
|
7
|
+
import { MenuListContext, useMenuContext } from "./menu-context.mjs";
|
|
8
|
+
import { edgeItem, focusItem, hasModifier, isMenuItem, isRtl, menuItems, typeaheadText } from "./menu-dom.mjs";
|
|
9
|
+
import { scheduleHoverClose } from "./menu-hover.mjs";
|
|
10
|
+
import { Activity, useCallback, useMemo, useRef, useSyncExternalStore } from "react";
|
|
11
|
+
import { jsx } from "react/jsx-runtime";
|
|
12
|
+
//#region src/menu/menu-popup.tsx
|
|
13
|
+
/** Engine capabilities never change while the page lives. */
|
|
14
|
+
const subscribeNever = () => () => {};
|
|
15
|
+
/** Typeahead buffer lifetime after the last keystroke (design 5.1). */
|
|
16
|
+
const TYPEAHEAD_RESET_MS = 500;
|
|
17
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
18
|
+
/** What a press may focus inside a popup: items, the popup itself, the consumer's controls. */
|
|
19
|
+
const FOCUSABLE_SELECTOR = "a[href], button, input, select, textarea, summary, [tabindex], [contenteditable]:not([contenteditable=\"false\"])";
|
|
20
|
+
/**
|
|
21
|
+
* The menu itself: `<div popover="auto" role="menu" aria-labelledby={trigger} tabindex="-1">`,
|
|
22
|
+
* placed against its Trigger with CSS Anchor Positioning. Owns the keyboard model (design 5):
|
|
23
|
+
* ArrowUp / ArrowDown / Home / End move focus between items (or the browser does, with
|
|
24
|
+
* `experimental_focusgroup` where supported), printable characters run typeahead, ArrowLeft
|
|
25
|
+
* closes a submenu. The top-level popup closes the whole menu at once when focus leaves it (Tab).
|
|
26
|
+
* Style it with `:popover-open`; it emits `data-side` / `data-align` but no `data-open`.
|
|
27
|
+
*
|
|
28
|
+
* Owned keys (conventions 1.2): `id`, `popover`, `role`, `aria-labelledby` (token-merged),
|
|
29
|
+
* `tabIndex`, `focusgroup` (with the flag), `onKeyDown`, `onBlur`, `onMouseDown` (a press on
|
|
30
|
+
* non-item space keeps focus on the current item), `onPointerEnter` / `onPointerLeave`, and
|
|
31
|
+
* `data-side` / `data-align` unless `side` is `null`.
|
|
32
|
+
*/
|
|
33
|
+
function MenuPopup({ side: sideProp, align = "start", keepMounted, experimental_focusgroup = false, transition, className, style, children, render, ...rest }) {
|
|
34
|
+
const menu = useMenuContext("Menu.Popup");
|
|
35
|
+
const { popover, submenu, loop } = menu;
|
|
36
|
+
const side = sideProp === void 0 ? submenu ? "right" : "bottom" : sideProp;
|
|
37
|
+
const nativeFocusgroup = useSyncExternalStore(subscribeNever, () => experimental_focusgroup && "focusGroup" in HTMLElement.prototype, () => false);
|
|
38
|
+
const typeahead = useRef({
|
|
39
|
+
text: "",
|
|
40
|
+
at: 0
|
|
41
|
+
});
|
|
42
|
+
const typingAhead = useCallback(() => {
|
|
43
|
+
const { text, at } = typeahead.current;
|
|
44
|
+
return text !== "" && Date.now() - at <= TYPEAHEAD_RESET_MS;
|
|
45
|
+
}, []);
|
|
46
|
+
const { setElement, visible, wrapContent } = usePopoverPopup(popover, {
|
|
47
|
+
part: "Menu.Popup",
|
|
48
|
+
initialFocus: void 0,
|
|
49
|
+
resolveInitialFocus: (popup) => {
|
|
50
|
+
const { cause, pointer } = menu.takeOpenCause();
|
|
51
|
+
if (cause === "none") return null;
|
|
52
|
+
const element = edgeItem(popup, cause) ?? popup;
|
|
53
|
+
return pointer ? {
|
|
54
|
+
element,
|
|
55
|
+
focusVisible: false
|
|
56
|
+
} : element;
|
|
57
|
+
},
|
|
58
|
+
keepMounted,
|
|
59
|
+
transition
|
|
60
|
+
});
|
|
61
|
+
const devRef = useCallback((element) => {
|
|
62
|
+
if (isDev && element.tagName === "DIALOG") console.error("Menu.Popup: rendered a <dialog>. Render a <div>: a <dialog popover> runs the dialog focusing steps, which fight the menu's item focus.");
|
|
63
|
+
return () => {};
|
|
64
|
+
}, []);
|
|
65
|
+
const ref = useMemo(() => mergeRefs(setElement, devRef), [setElement, devRef]);
|
|
66
|
+
const listContext = useMemo(() => ({
|
|
67
|
+
menu,
|
|
68
|
+
nativeFocusgroup,
|
|
69
|
+
typingAhead
|
|
70
|
+
}), [
|
|
71
|
+
menu,
|
|
72
|
+
nativeFocusgroup,
|
|
73
|
+
typingAhead
|
|
74
|
+
]);
|
|
75
|
+
const state = {
|
|
76
|
+
open: popover.committed,
|
|
77
|
+
side,
|
|
78
|
+
align,
|
|
79
|
+
submenu
|
|
80
|
+
};
|
|
81
|
+
const runTypeahead = (popup, current, key) => {
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const buffer = typeahead.current;
|
|
84
|
+
if (buffer.text === "" || now - buffer.at > TYPEAHEAD_RESET_MS) buffer.text = "";
|
|
85
|
+
if (key === " " && buffer.text === "") return false;
|
|
86
|
+
buffer.text += key.toLowerCase();
|
|
87
|
+
buffer.at = now;
|
|
88
|
+
const items = menuItems(popup);
|
|
89
|
+
if (items.length === 0) return true;
|
|
90
|
+
const repeated = [...buffer.text].every((c) => c === buffer.text[0]);
|
|
91
|
+
const search = repeated ? buffer.text[0] : buffer.text;
|
|
92
|
+
const index = current === null ? -1 : items.indexOf(current);
|
|
93
|
+
const start = repeated || index < 0 ? index + 1 : index;
|
|
94
|
+
for (let i = 0; i < items.length; i++) {
|
|
95
|
+
const item = items[(start + i) % items.length];
|
|
96
|
+
if (typeaheadText(item).startsWith(search)) {
|
|
97
|
+
if (item !== current) focusItem(item);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
};
|
|
103
|
+
const onKeyDown = (event) => {
|
|
104
|
+
if (event.defaultPrevented || hasModifier(event)) return;
|
|
105
|
+
const popup = event.currentTarget;
|
|
106
|
+
const target = event.target;
|
|
107
|
+
const onItem = isMenuItem(target, popup);
|
|
108
|
+
if (!onItem && target !== popup) return;
|
|
109
|
+
const key = event.key;
|
|
110
|
+
if (submenu && key === (isRtl(popup) ? "ArrowRight" : "ArrowLeft")) {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
popover.requestClose(event.nativeEvent, "activate");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (key === "ArrowDown" || key === "ArrowUp" || key === "Home" || key === "End") {
|
|
116
|
+
if (!onItem) {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
const items = menuItems(popup);
|
|
119
|
+
const item = key === "ArrowDown" || key === "Home" ? items[0] : items.at(-1);
|
|
120
|
+
if (item !== void 0) focusItem(item);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (nativeFocusgroup) return;
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
const items = menuItems(popup);
|
|
126
|
+
const index = items.indexOf(target);
|
|
127
|
+
const last = items.length - 1;
|
|
128
|
+
let next;
|
|
129
|
+
if (key === "Home") next = 0;
|
|
130
|
+
else if (key === "End") next = last;
|
|
131
|
+
else if (key === "ArrowDown") next = index >= last ? loop ? 0 : last : index + 1;
|
|
132
|
+
else next = index <= 0 ? loop ? last : 0 : index - 1;
|
|
133
|
+
const item = items[next];
|
|
134
|
+
if (item !== void 0 && item !== target) focusItem(item);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (key.length === 1 && runTypeahead(popup, onItem ? target : null, key)) event.preventDefault();
|
|
138
|
+
};
|
|
139
|
+
const onBlur = (event) => {
|
|
140
|
+
if (submenu) return;
|
|
141
|
+
const popup = event.currentTarget;
|
|
142
|
+
const next = event.relatedTarget;
|
|
143
|
+
if (!(next instanceof Node) || popup.contains(next)) return;
|
|
144
|
+
if (popup.matches(":popover-open")) popup.hidePopover();
|
|
145
|
+
};
|
|
146
|
+
const onMouseDown = (event) => {
|
|
147
|
+
if (event.button !== 0) return;
|
|
148
|
+
const focusable = event.target.closest(FOCUSABLE_SELECTOR);
|
|
149
|
+
if (focusable === null || focusable.getAttribute("role") === "menu") event.preventDefault();
|
|
150
|
+
};
|
|
151
|
+
const libraryProps = {
|
|
152
|
+
id: popover.ids.popup,
|
|
153
|
+
popover: "auto",
|
|
154
|
+
role: "menu",
|
|
155
|
+
"aria-labelledby": menu.triggerId,
|
|
156
|
+
tabIndex: -1,
|
|
157
|
+
...experimental_focusgroup && { focusgroup: `menu ${loop ? "wrap" : "nowrap"}` },
|
|
158
|
+
onKeyDown,
|
|
159
|
+
onBlur,
|
|
160
|
+
onMouseDown,
|
|
161
|
+
...submenu && {
|
|
162
|
+
onPointerEnter: (event) => {
|
|
163
|
+
if (event.pointerType !== "touch") menu.timers.cancelClose();
|
|
164
|
+
},
|
|
165
|
+
onPointerLeave: (event) => {
|
|
166
|
+
if (event.pointerType !== "touch") scheduleHoverClose(menu, event.nativeEvent);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
...side !== null && {
|
|
170
|
+
"data-side": side,
|
|
171
|
+
"data-align": align
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const content = (s) => {
|
|
175
|
+
let node;
|
|
176
|
+
if (keepMounted === true) node = /* @__PURE__ */ jsx(Activity, {
|
|
177
|
+
mode: visible ? "visible" : "hidden",
|
|
178
|
+
children: wrapContent(resolveStateValue(children, s))
|
|
179
|
+
});
|
|
180
|
+
else node = visible ? wrapContent(resolveStateValue(children, s)) : null;
|
|
181
|
+
return /* @__PURE__ */ jsx(MenuListContext, {
|
|
182
|
+
value: listContext,
|
|
183
|
+
children: node
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
const element = useRender({
|
|
187
|
+
defaultElement: "div",
|
|
188
|
+
render,
|
|
189
|
+
state,
|
|
190
|
+
className,
|
|
191
|
+
style: side === null ? style : (s) => ({
|
|
192
|
+
positionArea: positionArea(side, align),
|
|
193
|
+
positionTryFallbacks: submenu ? "flip-inline" : POSITION_TRY_FALLBACKS,
|
|
194
|
+
...resolveStateValue(style, s)
|
|
195
|
+
}),
|
|
196
|
+
children: content,
|
|
197
|
+
props: rest,
|
|
198
|
+
libraryProps,
|
|
199
|
+
ref
|
|
200
|
+
});
|
|
201
|
+
return useTransitionBoundary("Menu.Popup", transition, element);
|
|
202
|
+
}
|
|
203
|
+
MenuPopup.displayName = "MenuPopup";
|
|
204
|
+
//#endregion
|
|
205
|
+
export { MenuPopup };
|
|
206
|
+
|
|
207
|
+
//# sourceMappingURL=menu-popup.mjs.map
|