@funstack/ui 0.1.0-alpha.0 → 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/README.md +1 -1
- 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.map +1 -1
- package/dist/dialog/dialog-close.mjs +4 -4
- package/dist/dialog/dialog-close.mjs.map +1 -1
- package/dist/dialog/dialog-context.mjs.map +1 -1
- package/dist/dialog/dialog-popup.mjs +1 -0
- package/dist/dialog/dialog-popup.mjs.map +1 -1
- package/dist/dialog/dialog-root.mjs +9 -1
- package/dist/dialog/dialog-root.mjs.map +1 -1
- package/dist/index.d.mts +23 -1
- package/dist/index.mjs +4 -1
- 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/platform-attributes.d.mts +6 -0
- package/dist/internal/platform-attributes.d.mts.map +1 -1
- package/dist/internal/sync-presence.mjs +19 -0
- package/dist/internal/sync-presence.mjs.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-delayed-pair.mjs +40 -0
- package/dist/internal/use-delayed-pair.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-native-group-reconciliation.mjs +2 -2
- package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
- 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/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/radio-group-item.d.mts.map +1 -1
- package/dist/radio-group/radio-group-item.mjs +1 -6
- package/dist/radio-group/radio-group-item.mjs.map +1 -1
- package/dist/tabs/tabs-context.mjs +2 -1
- package/dist/tabs/tabs-context.mjs.map +1 -1
- package/dist/tabs/tabs-list.d.mts +1 -1
- package/dist/tabs/tabs-list.d.mts.map +1 -1
- package/dist/tabs/tabs-list.mjs +17 -9
- package/dist/tabs/tabs-list.mjs.map +1 -1
- package/dist/tabs/tabs-tab.d.mts +1 -1
- package/dist/tabs/tabs-tab.d.mts.map +1 -1
- package/dist/tabs/tabs-tab.mjs +6 -3
- package/dist/tabs/tabs-tab.mjs.map +1 -1
- package/dist/tooltip/tooltip-popup.d.mts.map +1 -1
- package/dist/tooltip/tooltip-popup.mjs +2 -7
- package/dist/tooltip/tooltip-popup.mjs.map +1 -1
- package/dist/tooltip/use-tooltip-interaction.mjs +5 -17
- package/dist/tooltip/use-tooltip-interaction.mjs.map +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import "../internal/platform-attributes.mjs";
|
|
2
|
+
import { MenuItem, MenuItemDetails, MenuItemProps, MenuItemState } from "./menu-item.mjs";
|
|
3
|
+
import { MenuCheckboxItem, MenuCheckboxItemProps, MenuCheckboxItemState } from "./menu-checkbox-item.mjs";
|
|
4
|
+
import { MenuGroup, MenuGroupLabel, MenuGroupLabelProps, MenuGroupLabelState, MenuGroupProps, MenuGroupState, MenuSeparator, MenuSeparatorProps, MenuSeparatorState } from "./menu-group.mjs";
|
|
5
|
+
import { MenuAlign, MenuPopup, MenuPopupProps, MenuPopupState, MenuSide } from "./menu-popup.mjs";
|
|
6
|
+
import { MenuRadioGroup, MenuRadioGroupProps, MenuRadioGroupState } from "./menu-radio-group.mjs";
|
|
7
|
+
import { MenuRadioItem, MenuRadioItemProps, MenuRadioItemState } from "./menu-radio-item.mjs";
|
|
8
|
+
import { MenuOpenChangeDetails, MenuRoot, MenuRootProps, MenuRootState } from "./menu-root.mjs";
|
|
9
|
+
import { MenuTrigger, MenuTriggerProps, MenuTriggerState } from "./menu-trigger.mjs";
|
|
10
|
+
import { MenuSubmenuTrigger, MenuSubmenuTriggerProps, MenuSubmenuTriggerState } from "./menu-submenu-trigger.mjs";
|
|
11
|
+
//#region src/menu/index.d.ts
|
|
12
|
+
/**
|
|
13
|
+
* Menu button (WAI-ARIA APG Menu Button + Menu) on `popover="auto"`: `Root` (no DOM) > `Trigger`
|
|
14
|
+
* (`button[aria-haspopup=menu]`) + `Popup` (`div[popover=auto][role=menu]`) > `Item`,
|
|
15
|
+
* `CheckboxItem`, `RadioGroup` > `RadioItem`, `Group` > `GroupLabel`, `Separator`, and submenus
|
|
16
|
+
* (a `Root` inside a `Popup`, opened by its `SubmenuTrigger`). An item's `action` runs in its own
|
|
17
|
+
* transition with `data-pending` on the item, and the whole menu closes when it settles.
|
|
18
|
+
*/
|
|
19
|
+
export declare const Menu: {
|
|
20
|
+
readonly Root: typeof MenuRoot;
|
|
21
|
+
readonly Trigger: typeof MenuTrigger;
|
|
22
|
+
readonly Popup: typeof MenuPopup;
|
|
23
|
+
readonly Item: typeof MenuItem;
|
|
24
|
+
readonly CheckboxItem: typeof MenuCheckboxItem;
|
|
25
|
+
readonly RadioGroup: typeof MenuRadioGroup;
|
|
26
|
+
readonly RadioItem: typeof MenuRadioItem;
|
|
27
|
+
readonly Group: typeof MenuGroup;
|
|
28
|
+
readonly GroupLabel: typeof MenuGroupLabel;
|
|
29
|
+
readonly Separator: typeof MenuSeparator;
|
|
30
|
+
readonly SubmenuTrigger: typeof MenuSubmenuTrigger;
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export type { MenuAlign, MenuCheckboxItemProps, MenuCheckboxItemState, MenuGroupLabelProps, MenuGroupLabelState, MenuGroupProps, MenuGroupState, MenuItemDetails, MenuItemProps, MenuItemState, MenuOpenChangeDetails, MenuPopupProps, MenuPopupState, MenuRadioGroupProps, MenuRadioGroupState, MenuRadioItemProps, MenuRadioItemState, MenuRootProps, MenuRootState, MenuSeparatorProps, MenuSeparatorState, MenuSide, MenuSubmenuTriggerProps, MenuSubmenuTriggerState, MenuTriggerProps, MenuTriggerState };
|
|
34
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/menu/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;qBAoBa;WACX,aAAI;WACJ,gBAAO;WACP,cAAK;WACL,aAAI;WACJ,qBAAY;WACZ,mBAAU;WACV,kBAAS;WACT,cAAK;WACL,mBAAU;WACV,kBAAS;WACT,uBAAc"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { MenuCheckboxItem } from "./menu-checkbox-item.mjs";
|
|
2
|
+
import { MenuGroup, MenuGroupLabel, MenuSeparator } from "./menu-group.mjs";
|
|
3
|
+
import { MenuItem } from "./menu-item.mjs";
|
|
4
|
+
import { MenuPopup } from "./menu-popup.mjs";
|
|
5
|
+
import { MenuRadioGroup } from "./menu-radio-group.mjs";
|
|
6
|
+
import { MenuRadioItem } from "./menu-radio-item.mjs";
|
|
7
|
+
import { MenuRoot } from "./menu-root.mjs";
|
|
8
|
+
import { MenuTrigger } from "./menu-trigger.mjs";
|
|
9
|
+
import { MenuSubmenuTrigger } from "./menu-submenu-trigger.mjs";
|
|
10
|
+
//#region src/menu/index.ts
|
|
11
|
+
/**
|
|
12
|
+
* Menu button (WAI-ARIA APG Menu Button + Menu) on `popover="auto"`: `Root` (no DOM) > `Trigger`
|
|
13
|
+
* (`button[aria-haspopup=menu]`) + `Popup` (`div[popover=auto][role=menu]`) > `Item`,
|
|
14
|
+
* `CheckboxItem`, `RadioGroup` > `RadioItem`, `Group` > `GroupLabel`, `Separator`, and submenus
|
|
15
|
+
* (a `Root` inside a `Popup`, opened by its `SubmenuTrigger`). An item's `action` runs in its own
|
|
16
|
+
* transition with `data-pending` on the item, and the whole menu closes when it settles.
|
|
17
|
+
*/
|
|
18
|
+
const Menu = {
|
|
19
|
+
Root: MenuRoot,
|
|
20
|
+
Trigger: MenuTrigger,
|
|
21
|
+
Popup: MenuPopup,
|
|
22
|
+
Item: MenuItem,
|
|
23
|
+
CheckboxItem: MenuCheckboxItem,
|
|
24
|
+
RadioGroup: MenuRadioGroup,
|
|
25
|
+
RadioItem: MenuRadioItem,
|
|
26
|
+
Group: MenuGroup,
|
|
27
|
+
GroupLabel: MenuGroupLabel,
|
|
28
|
+
Separator: MenuSeparator,
|
|
29
|
+
SubmenuTrigger: MenuSubmenuTrigger
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { Menu };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/menu/index.ts"],"sourcesContent":["// Ships the platform attribute typings (`command`, `commandfor`, `focusgroup`) with this subpath\n// (conventions 7.4). Type-only: the built JS has no import of it.\nimport \"../internal/platform-attributes.js\";\nimport { MenuCheckboxItem } from \"./menu-checkbox-item.js\";\nimport { MenuGroup, MenuGroupLabel, MenuSeparator } from \"./menu-group.js\";\nimport { MenuItem } from \"./menu-item.js\";\nimport { MenuPopup } from \"./menu-popup.js\";\nimport { MenuRadioGroup } from \"./menu-radio-group.js\";\nimport { MenuRadioItem } from \"./menu-radio-item.js\";\nimport { MenuRoot } from \"./menu-root.js\";\nimport { MenuSubmenuTrigger } from \"./menu-submenu-trigger.js\";\nimport { MenuTrigger } from \"./menu-trigger.js\";\n\n/**\n * Menu button (WAI-ARIA APG Menu Button + Menu) on `popover=\"auto\"`: `Root` (no DOM) > `Trigger`\n * (`button[aria-haspopup=menu]`) + `Popup` (`div[popover=auto][role=menu]`) > `Item`,\n * `CheckboxItem`, `RadioGroup` > `RadioItem`, `Group` > `GroupLabel`, `Separator`, and submenus\n * (a `Root` inside a `Popup`, opened by its `SubmenuTrigger`). An item's `action` runs in its own\n * transition with `data-pending` on the item, and the whole menu closes when it settles.\n */\nexport const Menu = {\n Root: MenuRoot,\n Trigger: MenuTrigger,\n Popup: MenuPopup,\n Item: MenuItem,\n CheckboxItem: MenuCheckboxItem,\n RadioGroup: MenuRadioGroup,\n RadioItem: MenuRadioItem,\n Group: MenuGroup,\n GroupLabel: MenuGroupLabel,\n Separator: MenuSeparator,\n SubmenuTrigger: MenuSubmenuTrigger,\n} as const;\n\nexport type { MenuRootProps, MenuRootState, MenuOpenChangeDetails } from \"./menu-root.js\";\nexport type { MenuTriggerProps, MenuTriggerState } from \"./menu-trigger.js\";\nexport type { MenuPopupProps, MenuPopupState, MenuSide, MenuAlign } from \"./menu-popup.js\";\nexport type { MenuItemProps, MenuItemState, MenuItemDetails } from \"./menu-item.js\";\nexport type { MenuCheckboxItemProps, MenuCheckboxItemState } from \"./menu-checkbox-item.js\";\nexport type { MenuRadioGroupProps, MenuRadioGroupState } from \"./menu-radio-group.js\";\nexport type { MenuRadioItemProps, MenuRadioItemState } from \"./menu-radio-item.js\";\nexport type {\n MenuGroupProps,\n MenuGroupState,\n MenuGroupLabelProps,\n MenuGroupLabelState,\n MenuSeparatorProps,\n MenuSeparatorState,\n} from \"./menu-group.js\";\nexport type { MenuSubmenuTriggerProps, MenuSubmenuTriggerState } from \"./menu-submenu-trigger.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,MAAa,OAAO;CAClB,MAAM;CACN,SAAS;CACT,OAAO;CACP,MAAM;CACN,cAAc;CACd,YAAY;CACZ,WAAW;CACX,OAAO;CACP,YAAY;CACZ,WAAW;CACX,gBAAgB;AAClB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ChangeAction, RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { MenuItemProps } from "./menu-item.mjs";
|
|
3
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
4
|
+
//#region src/menu/menu-checkbox-item.d.ts
|
|
5
|
+
export interface MenuCheckboxItemState {
|
|
6
|
+
/** Optimistic checked state (`aria-checked`). */
|
|
7
|
+
readonly checked: boolean;
|
|
8
|
+
/** `checkedChangeAction` is in flight. */
|
|
9
|
+
readonly pending: boolean;
|
|
10
|
+
readonly disabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface MenuCheckboxItemProps extends Omit<MenuItemProps, "action" | "closeOnActivate" | "className" | "style" | "children" | "render"> {
|
|
13
|
+
/** Controlled checked state. `undefined` means uncontrolled. */
|
|
14
|
+
checked?: boolean;
|
|
15
|
+
/** Initial checked state when uncontrolled. Default `false`. */
|
|
16
|
+
defaultChecked?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Awaited inside the item's transition; `aria-checked` flips at once and rolls back on
|
|
19
|
+
* rejection (which reaches the nearest Error Boundary above the item). May be a Server
|
|
20
|
+
* Function.
|
|
21
|
+
*/
|
|
22
|
+
checkedChangeAction?: ChangeAction<boolean>;
|
|
23
|
+
/** Close the whole menu once the change settles. Default `false`: the menu stays open. */
|
|
24
|
+
closeOnActivate?: boolean;
|
|
25
|
+
className?: StateValue<string | undefined, MenuCheckboxItemState>;
|
|
26
|
+
style?: StateValue<CSSProperties | undefined, MenuCheckboxItemState>;
|
|
27
|
+
children?: StateValue<ReactNode, MenuCheckboxItemState>;
|
|
28
|
+
render?: RenderProp<ComponentPropsWithRef<"button">, MenuCheckboxItemState>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A checkable menu item: `<button type="button" role="menuitemcheckbox" aria-checked>`. Click,
|
|
32
|
+
* Enter or Space toggles it through its own transition with optimistic `aria-checked` /
|
|
33
|
+
* `data-checked` and `data-pending`; the menu stays open unless `closeOnActivate`.
|
|
34
|
+
*
|
|
35
|
+
* Owned keys (conventions 1.2): those of `Menu.Item`, plus `aria-checked` and `data-checked`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function MenuCheckboxItem({ checked, defaultChecked, checkedChangeAction, closeOnActivate, disabled, label, className, style, children, render, ...rest }: MenuCheckboxItemProps): ReactNode;
|
|
38
|
+
export declare namespace MenuCheckboxItem {
|
|
39
|
+
var displayName: string;
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//# sourceMappingURL=menu-checkbox-item.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-checkbox-item.d.mts","names":[],"sources":["../../src/menu/menu-checkbox-item.tsx"],"mappings":";;;;iBAaiB;;WAEN;;WAEA;WACA;;iBAGM,8BAA8B,KAC7C;;EAIA;;EAEA;;;;;;EAMA,sBAAsB;;EAEtB;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,iCAAiC;;;;;;;;;wBAUvC,mBACd,SACA,gBACA,qBACA,iBACA,UACA,OACA,WACA,OACA,UACA,WACG,QACF,wBAAqB;yBAZR"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createActionDetails } from "../internal/action-details.mjs";
|
|
3
|
+
import { dataAttr } from "../internal/data-attr.mjs";
|
|
4
|
+
import { transitionTypes } from "../internal/transition-types.mjs";
|
|
5
|
+
import { useControllableAction } from "../internal/use-controllable-action.mjs";
|
|
6
|
+
import { rendersButton, useRender } from "../internal/use-render.mjs";
|
|
7
|
+
import { closeStackLater, useMenuContext } from "./menu-context.mjs";
|
|
8
|
+
import { useMenuItem } from "./use-menu-item.mjs";
|
|
9
|
+
import { useRef } from "react";
|
|
10
|
+
//#region src/menu/menu-checkbox-item.tsx
|
|
11
|
+
/**
|
|
12
|
+
* A checkable menu item: `<button type="button" role="menuitemcheckbox" aria-checked>`. Click,
|
|
13
|
+
* Enter or Space toggles it through its own transition with optimistic `aria-checked` /
|
|
14
|
+
* `data-checked` and `data-pending`; the menu stays open unless `closeOnActivate`.
|
|
15
|
+
*
|
|
16
|
+
* Owned keys (conventions 1.2): those of `Menu.Item`, plus `aria-checked` and `data-checked`.
|
|
17
|
+
*/
|
|
18
|
+
function MenuCheckboxItem({ checked, defaultChecked, checkedChangeAction, closeOnActivate = false, disabled = false, label, className, style, children, render, ...rest }) {
|
|
19
|
+
const menu = useMenuContext("Menu.CheckboxItem");
|
|
20
|
+
const after = useRef(/* @__PURE__ */ new WeakMap());
|
|
21
|
+
const { optimistic, pending, dispatch } = useControllableAction({
|
|
22
|
+
controlled: checked,
|
|
23
|
+
defaultValue: defaultChecked,
|
|
24
|
+
initialValue: false,
|
|
25
|
+
action: async (next, details) => {
|
|
26
|
+
await checkedChangeAction?.(next, details);
|
|
27
|
+
after.current.get(details)?.();
|
|
28
|
+
},
|
|
29
|
+
transitionTypes: (next) => transitionTypes("menu", next ? "check" : "uncheck"),
|
|
30
|
+
warn: {
|
|
31
|
+
component: "Menu.CheckboxItem",
|
|
32
|
+
prop: "checked"
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const { libraryProps } = useMenuItem({
|
|
36
|
+
part: "Menu.CheckboxItem",
|
|
37
|
+
role: "menuitemcheckbox",
|
|
38
|
+
disabled,
|
|
39
|
+
label,
|
|
40
|
+
activate: (event) => {
|
|
41
|
+
const details = createActionDetails(event, {});
|
|
42
|
+
if (closeOnActivate) after.current.set(details, closeStackLater(menu, event));
|
|
43
|
+
dispatch(!optimistic, details);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return useRender({
|
|
47
|
+
defaultElement: "button",
|
|
48
|
+
render,
|
|
49
|
+
state: {
|
|
50
|
+
checked: optimistic,
|
|
51
|
+
pending,
|
|
52
|
+
disabled
|
|
53
|
+
},
|
|
54
|
+
className,
|
|
55
|
+
style,
|
|
56
|
+
children,
|
|
57
|
+
props: rest,
|
|
58
|
+
libraryProps: {
|
|
59
|
+
...rendersButton(render) && { type: "button" },
|
|
60
|
+
...libraryProps,
|
|
61
|
+
"aria-checked": optimistic,
|
|
62
|
+
"data-checked": dataAttr(optimistic),
|
|
63
|
+
"data-pending": dataAttr(pending)
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
MenuCheckboxItem.displayName = "MenuCheckboxItem";
|
|
68
|
+
//#endregion
|
|
69
|
+
export { MenuCheckboxItem };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=menu-checkbox-item.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-checkbox-item.mjs","names":[],"sources":["../../src/menu/menu-checkbox-item.tsx"],"sourcesContent":["\"use client\";\nimport { useRef } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { transitionTypes } from \"../internal/transition-types.js\";\nimport type { ActionDetails, ChangeAction, RenderProp, StateValue } from \"../internal/types.js\";\nimport { useControllableAction } from \"../internal/use-controllable-action.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\nimport { closeStackLater, useMenuContext } from \"./menu-context.js\";\nimport type { MenuItemProps } from \"./menu-item.js\";\nimport { useMenuItem } from \"./use-menu-item.js\";\n\nexport interface MenuCheckboxItemState {\n /** Optimistic checked state (`aria-checked`). */\n readonly checked: boolean;\n /** `checkedChangeAction` is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface MenuCheckboxItemProps extends Omit<\n MenuItemProps,\n \"action\" | \"closeOnActivate\" | \"className\" | \"style\" | \"children\" | \"render\"\n> {\n /** Controlled checked state. `undefined` means uncontrolled. */\n checked?: boolean;\n /** Initial checked state when uncontrolled. Default `false`. */\n defaultChecked?: boolean;\n /**\n * Awaited inside the item's transition; `aria-checked` flips at once and rolls back on\n * rejection (which reaches the nearest Error Boundary above the item). May be a Server\n * Function.\n */\n checkedChangeAction?: ChangeAction<boolean>;\n /** Close the whole menu once the change settles. Default `false`: the menu stays open. */\n closeOnActivate?: boolean;\n className?: StateValue<string | undefined, MenuCheckboxItemState>;\n style?: StateValue<CSSProperties | undefined, MenuCheckboxItemState>;\n children?: StateValue<ReactNode, MenuCheckboxItemState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, MenuCheckboxItemState>;\n}\n\n/**\n * A checkable menu item: `<button type=\"button\" role=\"menuitemcheckbox\" aria-checked>`. Click,\n * Enter or Space toggles it through its own transition with optimistic `aria-checked` /\n * `data-checked` and `data-pending`; the menu stays open unless `closeOnActivate`.\n *\n * Owned keys (conventions 1.2): those of `Menu.Item`, plus `aria-checked` and `data-checked`.\n */\nexport function MenuCheckboxItem({\n checked,\n defaultChecked,\n checkedChangeAction,\n closeOnActivate = false,\n disabled = false,\n label,\n className,\n style,\n children,\n render,\n ...rest\n}: MenuCheckboxItemProps) {\n const menu = useMenuContext(\"Menu.CheckboxItem\");\n // The close to run after a dispatch's action resolved, keyed by the dispatch's details.\n const after = useRef(new WeakMap<object, () => void>());\n const { optimistic, pending, dispatch } = useControllableAction<boolean>({\n controlled: checked,\n defaultValue: defaultChecked,\n initialValue: false,\n action: async (next, details) => {\n await checkedChangeAction?.(next, details);\n after.current.get(details)?.();\n },\n transitionTypes: (next) => transitionTypes(\"menu\", next ? \"check\" : \"uncheck\"),\n warn: { component: \"Menu.CheckboxItem\", prop: \"checked\" },\n });\n\n const { libraryProps } = useMenuItem({\n part: \"Menu.CheckboxItem\",\n role: \"menuitemcheckbox\",\n disabled,\n label,\n activate: (event) => {\n const details = createActionDetails(event, {}) as ActionDetails;\n if (closeOnActivate) after.current.set(details, closeStackLater(menu, event));\n dispatch(!optimistic, details);\n },\n });\n\n const state: MenuCheckboxItemState = { checked: optimistic, pending, disabled };\n\n return useRender<ComponentPropsWithRef<\"button\">, MenuCheckboxItemState>({\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 \"aria-checked\": optimistic,\n \"data-checked\": dataAttr(optimistic),\n \"data-pending\": dataAttr(pending),\n },\n });\n}\n\nMenuCheckboxItem.displayName = \"MenuCheckboxItem\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAkDA,SAAgB,iBAAiB,EAC/B,SACA,gBACA,qBACA,kBAAkB,OAClB,WAAW,OACX,OACA,WACA,OACA,UACA,QACA,GAAG,QACqB;CACxB,MAAM,OAAO,eAAe,mBAAmB;CAE/C,MAAM,QAAQ,uBAAO,IAAI,QAA4B,CAAC;CACtD,MAAM,EAAE,YAAY,SAAS,aAAa,sBAA+B;EACvE,YAAY;EACZ,cAAc;EACd,cAAc;EACd,QAAQ,OAAO,MAAM,YAAY;GAC/B,MAAM,sBAAsB,MAAM,OAAO;GACzC,MAAM,QAAQ,IAAI,OAAO,CAAC,GAAG;EAC/B;EACA,kBAAkB,SAAS,gBAAgB,QAAQ,OAAO,UAAU,SAAS;EAC7E,MAAM;GAAE,WAAW;GAAqB,MAAM;EAAU;CAC1D,CAAC;CAED,MAAM,EAAE,iBAAiB,YAAY;EACnC,MAAM;EACN,MAAM;EACN;EACA;EACA,WAAW,UAAU;GACnB,MAAM,UAAU,oBAAoB,OAAO,CAAC,CAAC;GAC7C,IAAI,iBAAiB,MAAM,QAAQ,IAAI,SAAS,gBAAgB,MAAM,KAAK,CAAC;GAC5E,SAAS,CAAC,YAAY,OAAO;EAC/B;CACF,CAAC;CAID,OAAO,UAAkE;EACvE,gBAAgB;EAChB;EACA,OAAA;GALqC,SAAS;GAAY;GAAS;EAK/D;EACJ;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;GACvD,GAAG;GACH,gBAAgB;GAChB,gBAAgB,SAAS,UAAU;GACnC,gBAAgB,SAAS,OAAO;EAClC;CACF,CAAC;AACH;AAEA,iBAAiB,cAAc"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "../internal/use-popover-root.mjs";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, use } from "react";
|
|
3
|
+
//#region src/menu/menu-context.ts
|
|
4
|
+
const MenuContext = createContext(null);
|
|
5
|
+
/**
|
|
6
|
+
* The close an activated item runs once its action settled: the whole stack, through the
|
|
7
|
+
* top-level Root (design 2.2). Recorded at activation: if the menu was dismissed and opened again
|
|
8
|
+
* meanwhile, the close belongs to the previous opening and does nothing (as `useActionTrigger`).
|
|
9
|
+
*/
|
|
10
|
+
function closeStackLater(menu, event) {
|
|
11
|
+
const { popover } = menu.root;
|
|
12
|
+
const generation = popover.openGeneration();
|
|
13
|
+
return () => {
|
|
14
|
+
if (popover.openGeneration() === generation) popover.requestClose(event, "activate");
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function useMenuContext(part) {
|
|
18
|
+
const context = use(MenuContext);
|
|
19
|
+
if (context === null) throw new Error(`${part} must be rendered inside a Menu.Root.`);
|
|
20
|
+
return context;
|
|
21
|
+
}
|
|
22
|
+
const MenuListContext = createContext(null);
|
|
23
|
+
const MenuGroupContext = createContext(null);
|
|
24
|
+
const MenuRadioGroupContext = createContext(null);
|
|
25
|
+
//#endregion
|
|
26
|
+
export { MenuContext, MenuGroupContext, MenuListContext, MenuRadioGroupContext, closeStackLater, useMenuContext };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=menu-context.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-context.mjs","names":[],"sources":["../../src/menu/menu-context.ts"],"sourcesContent":["\"use client\";\nimport { createContext, use } from \"react\";\nimport type { DelayedPair } from \"../internal/use-delayed-pair.js\";\nimport type { PopoverRootValue } from \"../internal/use-popover-root.js\";\n\n/**\n * Where focus goes when the menu opens (design/components/menu.md 6.1): the first enabled item,\n * the last one (ArrowUp on the Trigger), or nowhere (a hover-opened submenu keeps focus on its\n * SubmenuTrigger).\n */\nexport type MenuOpenCause = \"first\" | \"last\" | \"none\";\n\n/** How the open being shown should place focus (design 6.1; review H1). */\nexport interface MenuOpenFocus {\n readonly cause: MenuOpenCause;\n /**\n * The open came from a pointer click (`detail > 0`): the item is focused without the focus\n * ring where the engine supports `focus({ focusVisible: false })`.\n */\n readonly pointer: boolean;\n}\n\n/** One menu level: what its Root provides to its Trigger, Popup and items. */\nexport interface MenuContextValue {\n /** The shared popover machinery (`usePopoverRoot`). */\n readonly popover: PopoverRootValue;\n /** This Root is nested in a `Menu.Popup`. */\n readonly submenu: boolean;\n /** The top-level menu: item activation closes the whole stack through it (design 2.2). */\n readonly root: MenuContextValue;\n /** The enclosing menu of a submenu; `null` at the top level. */\n readonly parent: MenuContextValue | null;\n readonly loop: boolean;\n readonly delay: number;\n readonly closeDelay: number;\n /** The Trigger's id (`aria-labelledby` of the Popup). */\n readonly triggerId: string;\n /** Record the focus target of the next open (and whether a pointer asked for it). */\n readonly setOpenCause: (cause: MenuOpenCause, pointer?: boolean) => void;\n /** Read and clear the focus target of the open being shown (default `\"first\"`, keyboard). */\n readonly takeOpenCause: () => MenuOpenFocus;\n /** Submenu hover-intent timers, shared by the SubmenuTrigger and the Popup. */\n readonly timers: DelayedPair;\n}\n\nexport const MenuContext = createContext<MenuContextValue | null>(null);\n\n/**\n * The close an activated item runs once its action settled: the whole stack, through the\n * top-level Root (design 2.2). Recorded at activation: if the menu was dismissed and opened again\n * meanwhile, the close belongs to the previous opening and does nothing (as `useActionTrigger`).\n */\nexport function closeStackLater(menu: MenuContextValue, event: Event): () => void {\n const { popover } = menu.root;\n const generation = popover.openGeneration();\n return () => {\n if (popover.openGeneration() === generation) popover.requestClose(event, \"activate\");\n };\n}\n\nexport function useMenuContext(part: string): MenuContextValue {\n const context = use(MenuContext);\n if (context === null) {\n throw new Error(`${part} must be rendered inside a Menu.Root.`);\n }\n return context;\n}\n\n/** What a `Menu.Popup` tells the items rendered in it. */\nexport interface MenuListContextValue {\n /** The menu this popup belongs to. */\n readonly menu: MenuContextValue;\n /**\n * `experimental_focusgroup` is on and the engine implements it: items render no `tabindex`\n * (Chromium leaves `tabindex=\"-1\"` elements out of a focusgroup) and the JS arrow keys step\n * aside.\n */\n readonly nativeFocusgroup: boolean;\n /** A typeahead buffer is active: Space is a character, not an activation (design 5.1). */\n readonly typingAhead: () => boolean;\n}\n\nexport const MenuListContext = createContext<MenuListContextValue | null>(null);\n\n/** `Menu.Group` / `Menu.RadioGroup`: the id their `Menu.GroupLabel` renders. */\nexport interface MenuGroupContextValue {\n readonly labelId: string;\n}\n\nexport const MenuGroupContext = createContext<MenuGroupContextValue | null>(null);\n\n/** `Menu.RadioGroup`'s state for its `Menu.RadioItem`s. */\nexport interface MenuRadioGroupContextValue {\n /** Optimistic value. */\n readonly value: string | null;\n readonly pending: boolean;\n /**\n * Dispatch `value` through the group's transition; `then` runs once the change action\n * resolved (an item's `closeOnActivate`).\n */\n readonly select: (value: string, event: Event, then: (() => void) | undefined) => void;\n}\n\nexport const MenuRadioGroupContext = createContext<MenuRadioGroupContextValue | null>(null);\n"],"mappings":";;;AA6CA,MAAa,cAAc,cAAuC,IAAI;;;;;;AAOtE,SAAgB,gBAAgB,MAAwB,OAA0B;CAChF,MAAM,EAAE,YAAY,KAAK;CACzB,MAAM,aAAa,QAAQ,eAAe;CAC1C,aAAa;EACX,IAAI,QAAQ,eAAe,MAAM,YAAY,QAAQ,aAAa,OAAO,UAAU;CACrF;AACF;AAEA,SAAgB,eAAe,MAAgC;CAC7D,MAAM,UAAU,IAAI,WAAW;CAC/B,IAAI,YAAY,MACd,MAAM,IAAI,MAAM,GAAG,KAAK,sCAAsC;CAEhE,OAAO;AACT;AAgBA,MAAa,kBAAkB,cAA2C,IAAI;AAO9E,MAAa,mBAAmB,cAA4C,IAAI;AAchF,MAAa,wBAAwB,cAAiD,IAAI"}
|
|
@@ -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"}
|