@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.
Files changed (154) hide show
  1. package/README.md +1 -1
  2. package/dist/checkbox-group/checkbox-group-context.mjs +24 -0
  3. package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -0
  4. package/dist/checkbox-group/checkbox-group-item.d.mts +55 -0
  5. package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -0
  6. package/dist/checkbox-group/checkbox-group-item.mjs +90 -0
  7. package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -0
  8. package/dist/checkbox-group/checkbox-group-label.d.mts +21 -0
  9. package/dist/checkbox-group/checkbox-group-label.d.mts.map +1 -0
  10. package/dist/checkbox-group/checkbox-group-label.mjs +30 -0
  11. package/dist/checkbox-group/checkbox-group-label.mjs.map +1 -0
  12. package/dist/checkbox-group/checkbox-group-parent.d.mts +68 -0
  13. package/dist/checkbox-group/checkbox-group-parent.d.mts.map +1 -0
  14. package/dist/checkbox-group/checkbox-group-parent.mjs +145 -0
  15. package/dist/checkbox-group/checkbox-group-parent.mjs.map +1 -0
  16. package/dist/checkbox-group/checkbox-group-root.d.mts +100 -0
  17. package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -0
  18. package/dist/checkbox-group/checkbox-group-root.mjs +291 -0
  19. package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -0
  20. package/dist/checkbox-group/index.d.mts +23 -0
  21. package/dist/checkbox-group/index.d.mts.map +1 -0
  22. package/dist/checkbox-group/index.mjs +23 -0
  23. package/dist/checkbox-group/index.mjs.map +1 -0
  24. package/dist/dialog/dialog-close.d.mts.map +1 -1
  25. package/dist/dialog/dialog-close.mjs +4 -4
  26. package/dist/dialog/dialog-close.mjs.map +1 -1
  27. package/dist/dialog/dialog-context.mjs.map +1 -1
  28. package/dist/dialog/dialog-popup.mjs +1 -0
  29. package/dist/dialog/dialog-popup.mjs.map +1 -1
  30. package/dist/dialog/dialog-root.mjs +9 -1
  31. package/dist/dialog/dialog-root.mjs.map +1 -1
  32. package/dist/index.d.mts +23 -1
  33. package/dist/index.mjs +4 -1
  34. package/dist/internal/anchor-position.d.mts +7 -0
  35. package/dist/internal/anchor-position.d.mts.map +1 -0
  36. package/dist/internal/anchor-position.mjs +16 -0
  37. package/dist/internal/anchor-position.mjs.map +1 -0
  38. package/dist/internal/platform-attributes.d.mts +6 -0
  39. package/dist/internal/platform-attributes.d.mts.map +1 -1
  40. package/dist/internal/sync-presence.mjs +19 -0
  41. package/dist/internal/sync-presence.mjs.map +1 -0
  42. package/dist/internal/use-action-trigger.mjs +58 -0
  43. package/dist/internal/use-action-trigger.mjs.map +1 -0
  44. package/dist/internal/use-delayed-pair.mjs +40 -0
  45. package/dist/internal/use-delayed-pair.mjs.map +1 -0
  46. package/dist/internal/use-exit-animation.mjs +55 -0
  47. package/dist/internal/use-exit-animation.mjs.map +1 -0
  48. package/dist/internal/use-native-group-reconciliation.mjs +2 -2
  49. package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
  50. package/dist/internal/use-popover-popup.mjs +274 -0
  51. package/dist/internal/use-popover-popup.mjs.map +1 -0
  52. package/dist/internal/use-popover-root.d.mts +2 -0
  53. package/dist/internal/use-popover-root.mjs +112 -0
  54. package/dist/internal/use-popover-root.mjs.map +1 -0
  55. package/dist/internal/use-popover-trigger.mjs +86 -0
  56. package/dist/internal/use-popover-trigger.mjs.map +1 -0
  57. package/dist/menu/index.d.mts +34 -0
  58. package/dist/menu/index.d.mts.map +1 -0
  59. package/dist/menu/index.mjs +34 -0
  60. package/dist/menu/index.mjs.map +1 -0
  61. package/dist/menu/menu-checkbox-item.d.mts +42 -0
  62. package/dist/menu/menu-checkbox-item.d.mts.map +1 -0
  63. package/dist/menu/menu-checkbox-item.mjs +71 -0
  64. package/dist/menu/menu-checkbox-item.mjs.map +1 -0
  65. package/dist/menu/menu-context.d.mts +1 -0
  66. package/dist/menu/menu-context.mjs +28 -0
  67. package/dist/menu/menu-context.mjs.map +1 -0
  68. package/dist/menu/menu-dom.mjs +37 -0
  69. package/dist/menu/menu-dom.mjs.map +1 -0
  70. package/dist/menu/menu-group.d.mts +48 -0
  71. package/dist/menu/menu-group.d.mts.map +1 -0
  72. package/dist/menu/menu-group.mjs +69 -0
  73. package/dist/menu/menu-group.mjs.map +1 -0
  74. package/dist/menu/menu-hover.mjs +29 -0
  75. package/dist/menu/menu-hover.mjs.map +1 -0
  76. package/dist/menu/menu-item.d.mts +45 -0
  77. package/dist/menu/menu-item.d.mts.map +1 -0
  78. package/dist/menu/menu-item.mjs +54 -0
  79. package/dist/menu/menu-item.mjs.map +1 -0
  80. package/dist/menu/menu-popup.d.mts +69 -0
  81. package/dist/menu/menu-popup.d.mts.map +1 -0
  82. package/dist/menu/menu-popup.mjs +207 -0
  83. package/dist/menu/menu-popup.mjs.map +1 -0
  84. package/dist/menu/menu-radio-group.d.mts +37 -0
  85. package/dist/menu/menu-radio-group.d.mts.map +1 -0
  86. package/dist/menu/menu-radio-group.mjs +72 -0
  87. package/dist/menu/menu-radio-group.mjs.map +1 -0
  88. package/dist/menu/menu-radio-item.d.mts +33 -0
  89. package/dist/menu/menu-radio-item.d.mts.map +1 -0
  90. package/dist/menu/menu-radio-item.mjs +62 -0
  91. package/dist/menu/menu-radio-item.mjs.map +1 -0
  92. package/dist/menu/menu-root.d.mts +54 -0
  93. package/dist/menu/menu-root.d.mts.map +1 -0
  94. package/dist/menu/menu-root.mjs +102 -0
  95. package/dist/menu/menu-root.mjs.map +1 -0
  96. package/dist/menu/menu-submenu-trigger.d.mts +31 -0
  97. package/dist/menu/menu-submenu-trigger.d.mts.map +1 -0
  98. package/dist/menu/menu-submenu-trigger.mjs +92 -0
  99. package/dist/menu/menu-submenu-trigger.mjs.map +1 -0
  100. package/dist/menu/menu-trigger.d.mts +36 -0
  101. package/dist/menu/menu-trigger.d.mts.map +1 -0
  102. package/dist/menu/menu-trigger.mjs +81 -0
  103. package/dist/menu/menu-trigger.mjs.map +1 -0
  104. package/dist/menu/use-menu-item.mjs +54 -0
  105. package/dist/menu/use-menu-item.mjs.map +1 -0
  106. package/dist/popover/index.d.mts +28 -0
  107. package/dist/popover/index.d.mts.map +1 -0
  108. package/dist/popover/index.mjs +28 -0
  109. package/dist/popover/index.mjs.map +1 -0
  110. package/dist/popover/popover-close.d.mts +41 -0
  111. package/dist/popover/popover-close.d.mts.map +1 -0
  112. package/dist/popover/popover-close.mjs +90 -0
  113. package/dist/popover/popover-close.mjs.map +1 -0
  114. package/dist/popover/popover-context.mjs +26 -0
  115. package/dist/popover/popover-context.mjs.map +1 -0
  116. package/dist/popover/popover-description.d.mts +22 -0
  117. package/dist/popover/popover-description.d.mts.map +1 -0
  118. package/dist/popover/popover-description.mjs +28 -0
  119. package/dist/popover/popover-description.mjs.map +1 -0
  120. package/dist/popover/popover-popup.d.mts +82 -0
  121. package/dist/popover/popover-popup.d.mts.map +1 -0
  122. package/dist/popover/popover-popup.mjs +93 -0
  123. package/dist/popover/popover-popup.mjs.map +1 -0
  124. package/dist/popover/popover-root.d.mts +64 -0
  125. package/dist/popover/popover-root.d.mts.map +1 -0
  126. package/dist/popover/popover-root.mjs +39 -0
  127. package/dist/popover/popover-root.mjs.map +1 -0
  128. package/dist/popover/popover-title.d.mts +23 -0
  129. package/dist/popover/popover-title.d.mts.map +1 -0
  130. package/dist/popover/popover-title.mjs +28 -0
  131. package/dist/popover/popover-title.mjs.map +1 -0
  132. package/dist/popover/popover-trigger.d.mts +37 -0
  133. package/dist/popover/popover-trigger.d.mts.map +1 -0
  134. package/dist/popover/popover-trigger.mjs +40 -0
  135. package/dist/popover/popover-trigger.mjs.map +1 -0
  136. package/dist/radio-group/radio-group-item.d.mts.map +1 -1
  137. package/dist/radio-group/radio-group-item.mjs +1 -6
  138. package/dist/radio-group/radio-group-item.mjs.map +1 -1
  139. package/dist/tabs/tabs-context.mjs +2 -1
  140. package/dist/tabs/tabs-context.mjs.map +1 -1
  141. package/dist/tabs/tabs-list.d.mts +1 -1
  142. package/dist/tabs/tabs-list.d.mts.map +1 -1
  143. package/dist/tabs/tabs-list.mjs +17 -9
  144. package/dist/tabs/tabs-list.mjs.map +1 -1
  145. package/dist/tabs/tabs-tab.d.mts +1 -1
  146. package/dist/tabs/tabs-tab.d.mts.map +1 -1
  147. package/dist/tabs/tabs-tab.mjs +6 -3
  148. package/dist/tabs/tabs-tab.mjs.map +1 -1
  149. package/dist/tooltip/tooltip-popup.d.mts.map +1 -1
  150. package/dist/tooltip/tooltip-popup.mjs +2 -7
  151. package/dist/tooltip/tooltip-popup.mjs.map +1 -1
  152. package/dist/tooltip/use-tooltip-interaction.mjs +5 -17
  153. package/dist/tooltip/use-tooltip-interaction.mjs.map +1 -1
  154. package/package.json +4 -1
@@ -0,0 +1,102 @@
1
+ "use client";
2
+ import { resolveStateValue } from "../internal/use-render.mjs";
3
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
4
+ import { useDelayedPair } from "../internal/use-delayed-pair.mjs";
5
+ import { usePopoverRoot } from "../internal/use-popover-root.mjs";
6
+ import { MenuContext, MenuListContext } from "./menu-context.mjs";
7
+ import { use, useCallback, useMemo, useRef } from "react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ //#region src/menu/menu-root.tsx
10
+ /**
11
+ * Owns the ids, the open state and the context of one menu level. Renders no DOM. A `Menu.Root`
12
+ * rendered inside a `Menu.Popup` is a **submenu**: its Trigger is a `Menu.SubmenuTrigger` (an
13
+ * item of the parent menu) and its Popup opens beside that item.
14
+ *
15
+ * A menu is always in library mode (design/components/menu.md 2.1): opening and the closes a
16
+ * part asks for are transitions (pending Trigger, items render from committed state,
17
+ * `showPopover()` / `hidePopover()` once the change has committed); dismissals are the
18
+ * browser's and `openChangeAction(false)` is a notification.
19
+ */
20
+ const DEFAULT_FOCUS = {
21
+ cause: "first",
22
+ pointer: false
23
+ };
24
+ function MenuRoot({ open, defaultOpen, openChangeAction, loop = true, delay, closeDelay, children }) {
25
+ const parent = use(MenuContext);
26
+ const list = use(MenuListContext);
27
+ const parentMenu = list !== null ? list.menu : null;
28
+ const submenu = parentMenu !== null;
29
+ useDevWarning(!submenu && (delay !== void 0 || closeDelay !== void 0), "Menu.Root: `delay` and `closeDelay` only apply to a submenu (a Menu.Root inside a Menu.Popup); a top-level menu does not open on hover.");
30
+ useDevWarning(parent !== null && !submenu, "Menu.Root: rendered inside another Menu.Root but outside its Menu.Popup. A submenu's Root goes inside the parent's Menu.Popup.");
31
+ const timers = useDelayedPair();
32
+ const nextCause = useRef(null);
33
+ const openCause = useRef(null);
34
+ const popover = usePopoverRoot({
35
+ component: "menu",
36
+ part: "Menu.Root",
37
+ mode: "library",
38
+ open,
39
+ defaultOpen,
40
+ openChangeAction,
41
+ onDispatch: (next) => {
42
+ timers.cancel();
43
+ if (next) openCause.current = nextCause.current ?? DEFAULT_FOCUS;
44
+ nextCause.current = null;
45
+ }
46
+ });
47
+ const setOpenCause = useCallback((cause, pointer = false) => {
48
+ nextCause.current = {
49
+ cause,
50
+ pointer
51
+ };
52
+ }, []);
53
+ const takeOpenCause = useCallback(() => {
54
+ const cause = openCause.current ?? DEFAULT_FOCUS;
55
+ openCause.current = null;
56
+ return cause;
57
+ }, []);
58
+ const resolvedDelay = delay ?? 100;
59
+ const resolvedCloseDelay = closeDelay ?? 150;
60
+ const value = useMemo(() => {
61
+ const self = {
62
+ popover,
63
+ submenu,
64
+ get root() {
65
+ return parentMenu?.root ?? self;
66
+ },
67
+ parent: parentMenu,
68
+ loop,
69
+ delay: resolvedDelay,
70
+ closeDelay: resolvedCloseDelay,
71
+ triggerId: `${popover.ids.popup}-trigger`,
72
+ setOpenCause,
73
+ takeOpenCause,
74
+ timers
75
+ };
76
+ return self;
77
+ }, [
78
+ popover,
79
+ submenu,
80
+ parentMenu,
81
+ loop,
82
+ resolvedDelay,
83
+ resolvedCloseDelay,
84
+ setOpenCause,
85
+ takeOpenCause,
86
+ timers
87
+ ]);
88
+ const state = {
89
+ open: popover.optimistic,
90
+ pending: popover.pending,
91
+ submenu
92
+ };
93
+ return /* @__PURE__ */ jsx(MenuContext, {
94
+ value,
95
+ children: resolveStateValue(children, state)
96
+ });
97
+ }
98
+ MenuRoot.displayName = "MenuRoot";
99
+ //#endregion
100
+ export { MenuRoot };
101
+
102
+ //# sourceMappingURL=menu-root.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-root.mjs","names":[],"sources":["../../src/menu/menu-root.tsx"],"sourcesContent":["\"use client\";\nimport { use, useCallback, useMemo, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { ActionDetails, ChangeAction, ChangeReason, StateValue } from \"../internal/types.js\";\nimport { useDelayedPair } from \"../internal/use-delayed-pair.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { usePopoverRoot } from \"../internal/use-popover-root.js\";\nimport { resolveStateValue } from \"../internal/use-render.js\";\nimport { MenuContext, MenuListContext } from \"./menu-context.js\";\nimport type { MenuContextValue, MenuOpenFocus } from \"./menu-context.js\";\n\n/** Details passed as the second argument of `Menu.Root`'s `openChangeAction`. */\nexport interface MenuOpenChangeDetails extends ActionDetails {\n /**\n * `\"activate\"`: the user acted on a part: the Trigger or SubmenuTrigger (click, Enter / Space,\n * arrow keys), an item's activation (the close after its action), ArrowLeft in a submenu, or a\n * foreign invoker.\n * `\"dismiss\"`: Escape, a click outside, Tab or focus leaving the menu, the pointer leaving a\n * hover-opened submenu, the parent menu closing (a submenu when the whole stack closes), script.\n * `\"platform\"`: the menu was found open on mount (opened before hydration); `event` is\n * `undefined`.\n */\n readonly reason: ChangeReason;\n}\n\nexport interface MenuRootState {\n /** Optimistic open state. Content rendered from it must not suspend. */\n readonly open: boolean;\n /** An open or close of this Root is in flight (its `openChangeAction`). */\n readonly pending: boolean;\n /** This Root is a submenu (rendered inside a `Menu.Popup`). */\n readonly submenu: boolean;\n}\n\nexport interface MenuRootProps {\n /** Controlled open state. `undefined` means uncontrolled. */\n open?: boolean;\n /** Initial open state when uncontrolled. Opens after hydration. */\n defaultOpen?: boolean;\n /**\n * Awaited for `true` (the menu opens when it resolves and the items have rendered) and for the\n * closes the user asks a part for (Trigger toggle, an item's activation, ArrowLeft in a\n * submenu); a notification for dismissals (Escape, a click outside, Tab, the parent menu\n * closing). May be a Server Function. A `setState` placed after your own `await` needs\n * `startTransition` (React limitation).\n */\n openChangeAction?: ChangeAction<boolean, MenuOpenChangeDetails>;\n /** Arrow keys wrap from the last item to the first and back. Default `true`. */\n loop?: boolean;\n /** Submenu only: milliseconds from hovering the SubmenuTrigger to the open. Default 100. */\n delay?: number;\n /**\n * Submenu only: milliseconds from the pointer leaving the SubmenuTrigger and the submenu to the\n * close. Default 150.\n */\n closeDelay?: number;\n children?: StateValue<ReactNode, MenuRootState>;\n}\n\n/**\n * Owns the ids, the open state and the context of one menu level. Renders no DOM. A `Menu.Root`\n * rendered inside a `Menu.Popup` is a **submenu**: its Trigger is a `Menu.SubmenuTrigger` (an\n * item of the parent menu) and its Popup opens beside that item.\n *\n * A menu is always in library mode (design/components/menu.md 2.1): opening and the closes a\n * part asks for are transitions (pending Trigger, items render from committed state,\n * `showPopover()` / `hidePopover()` once the change has committed); dismissals are the\n * browser's and `openChangeAction(false)` is a notification.\n */\nconst DEFAULT_FOCUS: MenuOpenFocus = { cause: \"first\", pointer: false };\n\nexport function MenuRoot({\n open,\n defaultOpen,\n openChangeAction,\n loop = true,\n delay,\n closeDelay,\n children,\n}: MenuRootProps) {\n const parent = use(MenuContext);\n const list = use(MenuListContext);\n // A Root inside a Popup's content is a submenu (design 1); the enclosing menu is the Popup's.\n const parentMenu = list !== null ? list.menu : null;\n const submenu = parentMenu !== null;\n\n useDevWarning(\n !submenu && (delay !== undefined || closeDelay !== undefined),\n \"Menu.Root: `delay` and `closeDelay` only apply to a submenu (a Menu.Root inside a \" +\n \"Menu.Popup); a top-level menu does not open on hover.\",\n );\n useDevWarning(\n parent !== null && !submenu,\n \"Menu.Root: rendered inside another Menu.Root but outside its Menu.Popup. A submenu's Root \" +\n \"goes inside the parent's Menu.Popup.\",\n );\n\n const timers = useDelayedPair();\n // The cause recorded by the part about to dispatch an open, and the cause of the open being\n // shown (design 14, \"Open cause\").\n const nextCause = useRef<MenuOpenFocus | null>(null);\n const openCause = useRef<MenuOpenFocus | null>(null);\n\n const popover = usePopoverRoot<MenuOpenChangeDetails>({\n component: \"menu\",\n part: \"Menu.Root\",\n mode: \"library\",\n open,\n defaultOpen,\n openChangeAction,\n onDispatch: (next) => {\n // Any dispatch cancels hover intent (walkthrough 9).\n timers.cancel();\n if (next) openCause.current = nextCause.current ?? DEFAULT_FOCUS;\n nextCause.current = null;\n },\n });\n\n const setOpenCause = useCallback((cause: MenuOpenFocus[\"cause\"], pointer = false) => {\n nextCause.current = { cause, pointer };\n }, []);\n const takeOpenCause = useCallback(() => {\n const cause = openCause.current ?? DEFAULT_FOCUS;\n openCause.current = null;\n return cause;\n }, []);\n\n const resolvedDelay = delay ?? 100;\n const resolvedCloseDelay = closeDelay ?? 150;\n // Note: `popover` is a new object on every render (the shared `usePopoverRoot` is not memoised:\n // its `dispatch` closes over the render's state), so today this recomputes on every render of\n // the Root and the parts re-render with it, as Popover's do. The other members are stable; the\n // memo starts to hit without further change once that hook returns a stable value.\n const value = useMemo<MenuContextValue>(() => {\n const self: MenuContextValue = {\n popover,\n submenu,\n // The top-level menu: this one, or the parent's root (design 2.2).\n get root() {\n return parentMenu?.root ?? self;\n },\n parent: parentMenu,\n loop,\n delay: resolvedDelay,\n closeDelay: resolvedCloseDelay,\n triggerId: `${popover.ids.popup}-trigger`,\n setOpenCause,\n takeOpenCause,\n timers,\n };\n return self;\n }, [\n popover,\n submenu,\n parentMenu,\n loop,\n resolvedDelay,\n resolvedCloseDelay,\n setOpenCause,\n takeOpenCause,\n timers,\n ]);\n\n const state: MenuRootState = {\n open: popover.optimistic,\n pending: popover.pending,\n submenu,\n };\n\n return <MenuContext value={value}>{resolveStateValue(children, state)}</MenuContext>;\n}\n\nMenuRoot.displayName = \"MenuRoot\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqEA,MAAM,gBAA+B;CAAE,OAAO;CAAS,SAAS;AAAM;AAEtE,SAAgB,SAAS,EACvB,MACA,aACA,kBACA,OAAO,MACP,OACA,YACA,YACgB;CAChB,MAAM,SAAS,IAAI,WAAW;CAC9B,MAAM,OAAO,IAAI,eAAe;CAEhC,MAAM,aAAa,SAAS,OAAO,KAAK,OAAO;CAC/C,MAAM,UAAU,eAAe;CAE/B,cACE,CAAC,YAAY,UAAU,KAAA,KAAa,eAAe,KAAA,IACnD,yIAEF;CACA,cACE,WAAW,QAAQ,CAAC,SACpB,gIAEF;CAEA,MAAM,SAAS,eAAe;CAG9B,MAAM,YAAY,OAA6B,IAAI;CACnD,MAAM,YAAY,OAA6B,IAAI;CAEnD,MAAM,UAAU,eAAsC;EACpD,WAAW;EACX,MAAM;EACN,MAAM;EACN;EACA;EACA;EACA,aAAa,SAAS;GAEpB,OAAO,OAAO;GACd,IAAI,MAAM,UAAU,UAAU,UAAU,WAAW;GACnD,UAAU,UAAU;EACtB;CACF,CAAC;CAED,MAAM,eAAe,aAAa,OAA+B,UAAU,UAAU;EACnF,UAAU,UAAU;GAAE;GAAO;EAAQ;CACvC,GAAG,CAAC,CAAC;CACL,MAAM,gBAAgB,kBAAkB;EACtC,MAAM,QAAQ,UAAU,WAAW;EACnC,UAAU,UAAU;EACpB,OAAO;CACT,GAAG,CAAC,CAAC;CAEL,MAAM,gBAAgB,SAAS;CAC/B,MAAM,qBAAqB,cAAc;CAKzC,MAAM,QAAQ,cAAgC;EAC5C,MAAM,OAAyB;GAC7B;GACA;GAEA,IAAI,OAAO;IACT,OAAO,YAAY,QAAQ;GAC7B;GACA,QAAQ;GACR;GACA,OAAO;GACP,YAAY;GACZ,WAAW,GAAG,QAAQ,IAAI,MAAM;GAChC;GACA;GACA;EACF;EACA,OAAO;CACT,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,MAAM,QAAuB;EAC3B,MAAM,QAAQ;EACd,SAAS,QAAQ;EACjB;CACF;CAEA,OAAO,oBAAC,aAAD;EAAoB;EAAQ,UAAA,kBAAkB,UAAU,KAAK;CAAe,CAAA;AACrF;AAEA,SAAS,cAAc"}
@@ -0,0 +1,31 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { MenuItemProps } from "./menu-item.mjs";
3
+ import { MenuTriggerState } from "./menu-trigger.mjs";
4
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
5
+ //#region src/menu/menu-submenu-trigger.d.ts
6
+ export type MenuSubmenuTriggerState = MenuTriggerState;
7
+ export interface MenuSubmenuTriggerProps extends Omit<MenuItemProps, "action" | "closeOnActivate" | "aria-expanded" | "aria-haspopup" | "onPointerEnter" | "onPointerLeave" | "className" | "style" | "children" | "render"> {
8
+ className?: StateValue<string | undefined, MenuSubmenuTriggerState>;
9
+ style?: StateValue<CSSProperties | undefined, MenuSubmenuTriggerState>;
10
+ /** Rendered from optimistic state; must not suspend. */
11
+ children?: StateValue<ReactNode, MenuSubmenuTriggerState>;
12
+ render?: RenderProp<ComponentPropsWithRef<"button">, MenuSubmenuTriggerState>;
13
+ }
14
+ /**
15
+ * The item that opens a submenu: an item of the parent menu (`role="menuitem"`, roving
16
+ * `tabindex`, typeahead) and the Trigger of the submenu's Root (`commandfor`,
17
+ * `command="toggle-popover"`, `aria-haspopup="menu"`, `aria-expanded`). ArrowRight (ArrowLeft in
18
+ * RTL), Enter, Space or a click open the submenu with its first item focused; hovering it opens
19
+ * the submenu after the Root's `delay` and leaves focus where it is.
20
+ *
21
+ * Owned keys (conventions 1.2): the Trigger's (`type`, `id`, `commandfor`, `command`,
22
+ * `aria-haspopup`, `aria-expanded`, `data-open`, `data-pending`) and the item's (`role`,
23
+ * `tabIndex`, `disabled` (absent), `aria-disabled`, `data-disabled`, `data-typeahead`,
24
+ * `onClick`, `onKeyDown`, `onPointerMove`, `onPointerEnter`, `onPointerLeave`).
25
+ */
26
+ export declare function MenuSubmenuTrigger({ disabled, label, className, style, children, render, ...rest }: MenuSubmenuTriggerProps): ReactNode;
27
+ export declare namespace MenuSubmenuTrigger {
28
+ var displayName: string;
29
+ }
30
+ //#endregion
31
+ //# sourceMappingURL=menu-submenu-trigger.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-submenu-trigger.d.mts","names":[],"sources":["../../src/menu/menu-submenu-trigger.tsx"],"mappings":";;;;;YAoBY,0BAA0B;iBAErB,gCAAgC,KAC/C;EAYA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;;;wBAevC,qBACd,UACA,OACA,WACA,OACA,UACA,WACG,QACF,0BAAuB;yBARV"}
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
4
+ import { usePopoverTrigger } from "../internal/use-popover-trigger.mjs";
5
+ import { useMenuContext } from "./menu-context.mjs";
6
+ import { hasModifier, isRtl } from "./menu-dom.mjs";
7
+ import { useMenuItem } from "./use-menu-item.mjs";
8
+ import { scheduleHoverClose } from "./menu-hover.mjs";
9
+ import { openMenuWithFocus } from "./menu-trigger.mjs";
10
+ //#region src/menu/menu-submenu-trigger.tsx
11
+ /**
12
+ * The item that opens a submenu: an item of the parent menu (`role="menuitem"`, roving
13
+ * `tabindex`, typeahead) and the Trigger of the submenu's Root (`commandfor`,
14
+ * `command="toggle-popover"`, `aria-haspopup="menu"`, `aria-expanded`). ArrowRight (ArrowLeft in
15
+ * RTL), Enter, Space or a click open the submenu with its first item focused; hovering it opens
16
+ * the submenu after the Root's `delay` and leaves focus where it is.
17
+ *
18
+ * Owned keys (conventions 1.2): the Trigger's (`type`, `id`, `commandfor`, `command`,
19
+ * `aria-haspopup`, `aria-expanded`, `data-open`, `data-pending`) and the item's (`role`,
20
+ * `tabIndex`, `disabled` (absent), `aria-disabled`, `data-disabled`, `data-typeahead`,
21
+ * `onClick`, `onKeyDown`, `onPointerMove`, `onPointerEnter`, `onPointerLeave`).
22
+ */
23
+ function MenuSubmenuTrigger({ disabled = false, label, className, style, children, render, ...rest }) {
24
+ const menu = useMenuContext("Menu.SubmenuTrigger");
25
+ useDevWarning(!menu.submenu, "Menu.SubmenuTrigger: rendered in a top-level Menu.Root. Use Menu.Trigger, or put this Root inside the parent's Menu.Popup.");
26
+ const open = (event) => openMenuWithFocus(menu, event, "first");
27
+ const item = useMenuItem({
28
+ part: "Menu.SubmenuTrigger",
29
+ role: "menuitem",
30
+ disabled,
31
+ label,
32
+ activate: open
33
+ });
34
+ const onKeyDown = (event) => {
35
+ if (event.defaultPrevented || hasModifier(event) || disabled) return;
36
+ const parentPopup = event.currentTarget.closest("[role=\"menu\"]");
37
+ const forward = parentPopup !== null && isRtl(parentPopup) ? "ArrowLeft" : "ArrowRight";
38
+ if (event.key !== forward) return;
39
+ event.preventDefault();
40
+ open(event.nativeEvent);
41
+ };
42
+ const { state, libraryProps, ref } = usePopoverTrigger(menu.popover, {
43
+ part: "Menu.SubmenuTrigger",
44
+ disabled,
45
+ render,
46
+ disabledAs: "aria",
47
+ handlers: {
48
+ onKeyDown: (event) => {
49
+ item.libraryProps.onKeyDown(event);
50
+ onKeyDown(event);
51
+ },
52
+ onPointerMove: item.libraryProps.onPointerMove,
53
+ onPointerEnter: (event) => {
54
+ if (disabled || event.pointerType === "touch") return;
55
+ const native = event.nativeEvent;
56
+ menu.timers.open(menu.delay, () => {
57
+ menu.setOpenCause("none");
58
+ menu.popover.requestOpen(native, "activate");
59
+ });
60
+ },
61
+ onPointerLeave: (event) => {
62
+ if (event.pointerType === "touch") return;
63
+ if (menu.popover.intended()) scheduleHoverClose(menu, event.nativeEvent);
64
+ else menu.timers.cancel();
65
+ }
66
+ },
67
+ activate: (event) => open(event.nativeEvent)
68
+ });
69
+ return useRender({
70
+ defaultElement: "button",
71
+ render,
72
+ state,
73
+ className,
74
+ style,
75
+ children,
76
+ props: rest,
77
+ libraryProps: {
78
+ ...libraryProps,
79
+ role: item.libraryProps.role,
80
+ tabIndex: item.libraryProps.tabIndex,
81
+ "data-typeahead": item.libraryProps["data-typeahead"],
82
+ id: menu.triggerId,
83
+ "aria-haspopup": "menu"
84
+ },
85
+ ref
86
+ });
87
+ }
88
+ MenuSubmenuTrigger.displayName = "MenuSubmenuTrigger";
89
+ //#endregion
90
+ export { MenuSubmenuTrigger };
91
+
92
+ //# sourceMappingURL=menu-submenu-trigger.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-submenu-trigger.mjs","names":[],"sources":["../../src/menu/menu-submenu-trigger.tsx"],"sourcesContent":["\"use client\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n KeyboardEvent,\n PointerEvent,\n ReactNode,\n} from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { usePopoverTrigger } from \"../internal/use-popover-trigger.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useMenuContext } from \"./menu-context.js\";\nimport { hasModifier, isRtl } from \"./menu-dom.js\";\nimport { scheduleHoverClose } from \"./menu-hover.js\";\nimport type { MenuItemProps } from \"./menu-item.js\";\nimport { openMenuWithFocus } from \"./menu-trigger.js\";\nimport type { MenuTriggerState } from \"./menu-trigger.js\";\nimport { useMenuItem } from \"./use-menu-item.js\";\n\nexport type MenuSubmenuTriggerState = MenuTriggerState;\n\nexport interface MenuSubmenuTriggerProps extends Omit<\n MenuItemProps,\n | \"action\"\n | \"closeOnActivate\"\n | \"aria-expanded\"\n | \"aria-haspopup\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"className\"\n | \"style\"\n | \"children\"\n | \"render\"\n> {\n className?: StateValue<string | undefined, MenuSubmenuTriggerState>;\n style?: StateValue<CSSProperties | undefined, MenuSubmenuTriggerState>;\n /** Rendered from optimistic state; must not suspend. */\n children?: StateValue<ReactNode, MenuSubmenuTriggerState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, MenuSubmenuTriggerState>;\n}\n\n/**\n * The item that opens a submenu: an item of the parent menu (`role=\"menuitem\"`, roving\n * `tabindex`, typeahead) and the Trigger of the submenu's Root (`commandfor`,\n * `command=\"toggle-popover\"`, `aria-haspopup=\"menu\"`, `aria-expanded`). ArrowRight (ArrowLeft in\n * RTL), Enter, Space or a click open the submenu with its first item focused; hovering it opens\n * the submenu after the Root's `delay` and leaves focus where it is.\n *\n * Owned keys (conventions 1.2): the Trigger's (`type`, `id`, `commandfor`, `command`,\n * `aria-haspopup`, `aria-expanded`, `data-open`, `data-pending`) and the item's (`role`,\n * `tabIndex`, `disabled` (absent), `aria-disabled`, `data-disabled`, `data-typeahead`,\n * `onClick`, `onKeyDown`, `onPointerMove`, `onPointerEnter`, `onPointerLeave`).\n */\nexport function MenuSubmenuTrigger({\n disabled = false,\n label,\n className,\n style,\n children,\n render,\n ...rest\n}: MenuSubmenuTriggerProps) {\n const menu = useMenuContext(\"Menu.SubmenuTrigger\");\n useDevWarning(\n !menu.submenu,\n \"Menu.SubmenuTrigger: rendered in a top-level Menu.Root. Use Menu.Trigger, or put this Root \" +\n \"inside the parent's Menu.Popup.\",\n );\n\n const open = (event: Event) => openMenuWithFocus(menu, event, \"first\");\n\n // The item half: role, roving tabindex, aria-disabled, Enter / Space, pointer focus. Its\n // activation is the click, which the trigger half handles (`activate` below).\n const item = useMenuItem({\n part: \"Menu.SubmenuTrigger\",\n role: \"menuitem\",\n disabled,\n label,\n activate: open,\n });\n\n const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (event.defaultPrevented || hasModifier(event) || disabled) return;\n const parentPopup = event.currentTarget.closest('[role=\"menu\"]');\n const forward = parentPopup !== null && isRtl(parentPopup) ? \"ArrowLeft\" : \"ArrowRight\";\n if (event.key !== forward) return;\n event.preventDefault();\n open(event.nativeEvent);\n };\n\n const { state, libraryProps, ref } = usePopoverTrigger(menu.popover, {\n part: \"Menu.SubmenuTrigger\",\n disabled,\n render,\n disabledAs: \"aria\",\n // Item first (Enter / Space activation, pointer focus), then the submenu keys.\n handlers: {\n onKeyDown: (event) => {\n item.libraryProps.onKeyDown(event);\n onKeyDown(event);\n },\n onPointerMove: item.libraryProps.onPointerMove,\n onPointerEnter: (event: PointerEvent<HTMLButtonElement>) => {\n if (disabled || event.pointerType === \"touch\") return;\n const native = event.nativeEvent;\n menu.timers.open(menu.delay, () => {\n menu.setOpenCause(\"none\");\n menu.popover.requestOpen(native, \"activate\");\n });\n },\n onPointerLeave: (event: PointerEvent<HTMLButtonElement>) => {\n if (event.pointerType === \"touch\") return;\n // Leaving before the open delay cancels it; leaving an open submenu's trigger starts the\n // close delay (cancelled by entering the submenu).\n if (menu.popover.intended()) scheduleHoverClose(menu, event.nativeEvent);\n else menu.timers.cancel();\n },\n },\n // A click opens (or moves focus into the open submenu); it never closes.\n activate: (event) => open(event.nativeEvent),\n });\n\n return useRender<ComponentPropsWithRef<\"button\">, MenuSubmenuTriggerState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n ...libraryProps,\n role: item.libraryProps.role,\n tabIndex: item.libraryProps.tabIndex,\n \"data-typeahead\": item.libraryProps[\"data-typeahead\"],\n id: menu.triggerId,\n \"aria-haspopup\": \"menu\",\n },\n ref,\n });\n}\n\nMenuSubmenuTrigger.displayName = \"MenuSubmenuTrigger\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsDA,SAAgB,mBAAmB,EACjC,WAAW,OACX,OACA,WACA,OACA,UACA,QACA,GAAG,QACuB;CAC1B,MAAM,OAAO,eAAe,qBAAqB;CACjD,cACE,CAAC,KAAK,SACN,4HAEF;CAEA,MAAM,QAAQ,UAAiB,kBAAkB,MAAM,OAAO,OAAO;CAIrE,MAAM,OAAO,YAAY;EACvB,MAAM;EACN,MAAM;EACN;EACA;EACA,UAAU;CACZ,CAAC;CAED,MAAM,aAAa,UAA4C;EAC7D,IAAI,MAAM,oBAAoB,YAAY,KAAK,KAAK,UAAU;EAC9D,MAAM,cAAc,MAAM,cAAc,QAAQ,iBAAe;EAC/D,MAAM,UAAU,gBAAgB,QAAQ,MAAM,WAAW,IAAI,cAAc;EAC3E,IAAI,MAAM,QAAQ,SAAS;EAC3B,MAAM,eAAe;EACrB,KAAK,MAAM,WAAW;CACxB;CAEA,MAAM,EAAE,OAAO,cAAc,QAAQ,kBAAkB,KAAK,SAAS;EACnE,MAAM;EACN;EACA;EACA,YAAY;EAEZ,UAAU;GACR,YAAY,UAAU;IACpB,KAAK,aAAa,UAAU,KAAK;IACjC,UAAU,KAAK;GACjB;GACA,eAAe,KAAK,aAAa;GACjC,iBAAiB,UAA2C;IAC1D,IAAI,YAAY,MAAM,gBAAgB,SAAS;IAC/C,MAAM,SAAS,MAAM;IACrB,KAAK,OAAO,KAAK,KAAK,aAAa;KACjC,KAAK,aAAa,MAAM;KACxB,KAAK,QAAQ,YAAY,QAAQ,UAAU;IAC7C,CAAC;GACH;GACA,iBAAiB,UAA2C;IAC1D,IAAI,MAAM,gBAAgB,SAAS;IAGnC,IAAI,KAAK,QAAQ,SAAS,GAAG,mBAAmB,MAAM,MAAM,WAAW;SAClE,KAAK,OAAO,OAAO;GAC1B;EACF;EAEA,WAAW,UAAU,KAAK,MAAM,WAAW;CAC7C,CAAC;CAED,OAAO,UAAoE;EACzE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,GAAG;GACH,MAAM,KAAK,aAAa;GACxB,UAAU,KAAK,aAAa;GAC5B,kBAAkB,KAAK,aAAa;GACpC,IAAI,KAAK;GACT,iBAAiB;EACnB;EACA;CACF,CAAC;AACH;AAEA,mBAAmB,cAAc"}
@@ -0,0 +1,36 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import "./menu-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/menu/menu-trigger.d.ts
5
+ export interface MenuTriggerState {
6
+ /** Optimistic open state. */
7
+ readonly open: boolean;
8
+ /** The Root's transition is pending (an open, a part-initiated close or a dismissal notification). */
9
+ readonly pending: boolean;
10
+ readonly disabled: boolean;
11
+ }
12
+ export interface MenuTriggerProps extends Omit<ComponentPropsWithRef<"button">, "onClick" | "onKeyDown" | "aria-expanded" | "aria-haspopup" | "className" | "style" | "children"> {
13
+ /** Native `disabled`: the menu cannot be opened from this button. */
14
+ disabled?: boolean;
15
+ className?: StateValue<string | undefined, MenuTriggerState>;
16
+ style?: StateValue<CSSProperties | undefined, MenuTriggerState>;
17
+ /** Rendered from optimistic state; must not suspend. */
18
+ children?: StateValue<ReactNode, MenuTriggerState>;
19
+ /** Should render a `<button>` (the command path before hydration). */
20
+ render?: RenderProp<ComponentPropsWithRef<"button">, MenuTriggerState>;
21
+ }
22
+ /**
23
+ * The menu button: `<button type="button" commandfor command="toggle-popover"
24
+ * aria-haspopup="menu" aria-expanded>`. A click (Enter, Space) toggles the menu through the
25
+ * Root's transition; ArrowDown / ArrowUp open it with the first / last item focused.
26
+ *
27
+ * Owned keys (conventions 1.2): `type` (on a `<button>` only), `id`, `disabled`, `commandfor`,
28
+ * `command`, `aria-haspopup`, `aria-expanded`, `onClick`, `onKeyDown`, `data-open`,
29
+ * `data-pending`, `data-disabled`.
30
+ */
31
+ export declare function MenuTrigger({ disabled, className, style, children, render, ...rest }: MenuTriggerProps): ReactNode;
32
+ export declare namespace MenuTrigger {
33
+ var displayName: string;
34
+ }
35
+ //#endregion
36
+ //# sourceMappingURL=menu-trigger.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-trigger.d.mts","names":[],"sources":["../../src/menu/menu-trigger.tsx"],"mappings":";;;;iBAUiB;;WAEN;;WAEA;WACA;;iBAGM,yBAAyB,KACxC;;EAIA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;wBAuCvC,cACd,UACA,WACA,OACA,UACA,WACG,QACF,mBAAgB;yBAPH"}
@@ -0,0 +1,81 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
4
+ import { usePopoverTrigger } from "../internal/use-popover-trigger.mjs";
5
+ import { useMenuContext } from "./menu-context.mjs";
6
+ import { edgeItem, focusItem, hasModifier } from "./menu-dom.mjs";
7
+ //#region src/menu/menu-trigger.tsx
8
+ /**
9
+ * Opens with the first (`"first"`) or last (`"last"`) item focused, or focuses that item when the
10
+ * menu is already open (a pre-hydration open, walkthrough 10). Shared with SubmenuTrigger.
11
+ */
12
+ function openMenuWithFocus(menu, event, which) {
13
+ const { popover } = menu;
14
+ if (popover.intended()) {
15
+ const popup = popover.popupRef.current;
16
+ if (popup !== null && popup.matches(":popover-open")) {
17
+ const item = edgeItem(popup, which);
18
+ if (item !== null) focusItem(item);
19
+ }
20
+ return;
21
+ }
22
+ menu.setOpenCause(which, isPointerClick(event));
23
+ popover.requestOpen(event, "activate");
24
+ }
25
+ /** A click from a pointing device (`detail` counts clicks; keyboard and `click()` give 0). */
26
+ function isPointerClick(event) {
27
+ return event.type === "click" && event.detail > 0;
28
+ }
29
+ /**
30
+ * The menu button: `<button type="button" commandfor command="toggle-popover"
31
+ * aria-haspopup="menu" aria-expanded>`. A click (Enter, Space) toggles the menu through the
32
+ * Root's transition; ArrowDown / ArrowUp open it with the first / last item focused.
33
+ *
34
+ * Owned keys (conventions 1.2): `type` (on a `<button>` only), `id`, `disabled`, `commandfor`,
35
+ * `command`, `aria-haspopup`, `aria-expanded`, `onClick`, `onKeyDown`, `data-open`,
36
+ * `data-pending`, `data-disabled`.
37
+ */
38
+ function MenuTrigger({ disabled = false, className, style, children, render, ...rest }) {
39
+ const menu = useMenuContext("Menu.Trigger");
40
+ useDevWarning(menu.submenu, "Menu.Trigger: rendered in a submenu. Use Menu.SubmenuTrigger, which is also an item of the parent menu.");
41
+ const onKeyDown = (event) => {
42
+ if (disabled || hasModifier(event)) return;
43
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
44
+ event.preventDefault();
45
+ openMenuWithFocus(menu, event.nativeEvent, event.key === "ArrowDown" ? "first" : "last");
46
+ };
47
+ const { state, libraryProps, ref } = usePopoverTrigger(menu.popover, {
48
+ part: "Menu.Trigger",
49
+ disabled,
50
+ render,
51
+ handlers: { onKeyDown },
52
+ activate: (event) => {
53
+ const native = event.nativeEvent;
54
+ if (menu.popover.intended()) menu.popover.requestClose(native, "activate");
55
+ else {
56
+ menu.setOpenCause("first", isPointerClick(native));
57
+ menu.popover.requestOpen(native, "activate");
58
+ }
59
+ }
60
+ });
61
+ return useRender({
62
+ defaultElement: "button",
63
+ render,
64
+ state,
65
+ className,
66
+ style,
67
+ children,
68
+ props: rest,
69
+ libraryProps: {
70
+ ...libraryProps,
71
+ id: menu.triggerId,
72
+ "aria-haspopup": "menu"
73
+ },
74
+ ref
75
+ });
76
+ }
77
+ MenuTrigger.displayName = "MenuTrigger";
78
+ //#endregion
79
+ export { MenuTrigger, isPointerClick, openMenuWithFocus };
80
+
81
+ //# sourceMappingURL=menu-trigger.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-trigger.mjs","names":[],"sources":["../../src/menu/menu-trigger.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, KeyboardEvent, ReactNode } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { usePopoverTrigger } from \"../internal/use-popover-trigger.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useMenuContext } from \"./menu-context.js\";\nimport type { MenuContextValue } from \"./menu-context.js\";\nimport { edgeItem, focusItem, hasModifier } from \"./menu-dom.js\";\n\nexport interface MenuTriggerState {\n /** Optimistic open state. */\n readonly open: boolean;\n /** The Root's transition is pending (an open, a part-initiated close or a dismissal notification). */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface MenuTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"onKeyDown\" | \"aria-expanded\" | \"aria-haspopup\" | \"className\" | \"style\" | \"children\"\n> {\n /** Native `disabled`: the menu cannot be opened from this button. */\n disabled?: boolean;\n className?: StateValue<string | undefined, MenuTriggerState>;\n style?: StateValue<CSSProperties | undefined, MenuTriggerState>;\n /** Rendered from optimistic state; must not suspend. */\n children?: StateValue<ReactNode, MenuTriggerState>;\n /** Should render a `<button>` (the command path before hydration). */\n render?: RenderProp<ComponentPropsWithRef<\"button\">, MenuTriggerState>;\n}\n\n/**\n * Opens with the first (`\"first\"`) or last (`\"last\"`) item focused, or focuses that item when the\n * menu is already open (a pre-hydration open, walkthrough 10). Shared with SubmenuTrigger.\n */\nexport function openMenuWithFocus(\n menu: MenuContextValue,\n event: Event,\n which: \"first\" | \"last\",\n): void {\n const { popover } = menu;\n if (popover.intended()) {\n const popup = popover.popupRef.current;\n if (popup !== null && popup.matches(\":popover-open\")) {\n const item = edgeItem(popup, which);\n if (item !== null) focusItem(item);\n }\n return;\n }\n menu.setOpenCause(which, isPointerClick(event));\n popover.requestOpen(event, \"activate\");\n}\n\n/** A click from a pointing device (`detail` counts clicks; keyboard and `click()` give 0). */\nexport function isPointerClick(event: Event): boolean {\n return event.type === \"click\" && (event as MouseEvent).detail > 0;\n}\n\n/**\n * The menu button: `<button type=\"button\" commandfor command=\"toggle-popover\"\n * aria-haspopup=\"menu\" aria-expanded>`. A click (Enter, Space) toggles the menu through the\n * Root's transition; ArrowDown / ArrowUp open it with the first / last item focused.\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `id`, `disabled`, `commandfor`,\n * `command`, `aria-haspopup`, `aria-expanded`, `onClick`, `onKeyDown`, `data-open`,\n * `data-pending`, `data-disabled`.\n */\nexport function MenuTrigger({\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: MenuTriggerProps) {\n const menu = useMenuContext(\"Menu.Trigger\");\n useDevWarning(\n menu.submenu,\n \"Menu.Trigger: rendered in a submenu. Use Menu.SubmenuTrigger, which is also an item of the \" +\n \"parent menu.\",\n );\n\n const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (disabled || hasModifier(event)) return;\n if (event.key !== \"ArrowDown\" && event.key !== \"ArrowUp\") return;\n // No page scroll; Enter and Space stay the native click.\n event.preventDefault();\n openMenuWithFocus(menu, event.nativeEvent, event.key === \"ArrowDown\" ? \"first\" : \"last\");\n };\n\n const { state, libraryProps, ref } = usePopoverTrigger(menu.popover, {\n part: \"Menu.Trigger\",\n disabled,\n render,\n handlers: { onKeyDown },\n activate: (event) => {\n const native = event.nativeEvent;\n if (menu.popover.intended()) {\n menu.popover.requestClose(native, \"activate\");\n } else {\n menu.setOpenCause(\"first\", isPointerClick(native));\n menu.popover.requestOpen(native, \"activate\");\n }\n },\n });\n\n return useRender<ComponentPropsWithRef<\"button\">, MenuTriggerState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n ...libraryProps,\n id: menu.triggerId,\n \"aria-haspopup\": \"menu\",\n },\n ref,\n });\n}\n\nMenuTrigger.displayName = \"MenuTrigger\";\n"],"mappings":";;;;;;;;;;;AAoCA,SAAgB,kBACd,MACA,OACA,OACM;CACN,MAAM,EAAE,YAAY;CACpB,IAAI,QAAQ,SAAS,GAAG;EACtB,MAAM,QAAQ,QAAQ,SAAS;EAC/B,IAAI,UAAU,QAAQ,MAAM,QAAQ,eAAe,GAAG;GACpD,MAAM,OAAO,SAAS,OAAO,KAAK;GAClC,IAAI,SAAS,MAAM,UAAU,IAAI;EACnC;EACA;CACF;CACA,KAAK,aAAa,OAAO,eAAe,KAAK,CAAC;CAC9C,QAAQ,YAAY,OAAO,UAAU;AACvC;;AAGA,SAAgB,eAAe,OAAuB;CACpD,OAAO,MAAM,SAAS,WAAY,MAAqB,SAAS;AAClE;;;;;;;;;;AAWA,SAAgB,YAAY,EAC1B,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACgB;CACnB,MAAM,OAAO,eAAe,cAAc;CAC1C,cACE,KAAK,SACL,yGAEF;CAEA,MAAM,aAAa,UAA4C;EAC7D,IAAI,YAAY,YAAY,KAAK,GAAG;EACpC,IAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,WAAW;EAE1D,MAAM,eAAe;EACrB,kBAAkB,MAAM,MAAM,aAAa,MAAM,QAAQ,cAAc,UAAU,MAAM;CACzF;CAEA,MAAM,EAAE,OAAO,cAAc,QAAQ,kBAAkB,KAAK,SAAS;EACnE,MAAM;EACN;EACA;EACA,UAAU,EAAE,UAAU;EACtB,WAAW,UAAU;GACnB,MAAM,SAAS,MAAM;GACrB,IAAI,KAAK,QAAQ,SAAS,GACxB,KAAK,QAAQ,aAAa,QAAQ,UAAU;QACvC;IACL,KAAK,aAAa,SAAS,eAAe,MAAM,CAAC;IACjD,KAAK,QAAQ,YAAY,QAAQ,UAAU;GAC7C;EACF;CACF,CAAC;CAED,OAAO,UAA6D;EAClE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,GAAG;GACH,IAAI,KAAK;GACT,iBAAiB;EACnB;EACA;CACF,CAAC;AACH;AAEA,YAAY,cAAc"}
@@ -0,0 +1,54 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
4
+ import { MenuListContext } from "./menu-context.mjs";
5
+ import { hasModifier } from "./menu-dom.mjs";
6
+ import { use } from "react";
7
+ //#region src/menu/use-menu-item.ts
8
+ /**
9
+ * The parts every menu item shares: role, roving `tabindex` (none on the native `focusgroup`
10
+ * path), `aria-disabled`, activation by click / Enter / Space (Space is a typeahead character
11
+ * while a buffer is active), and the current item following the pointer (design 6.2).
12
+ */
13
+ function useMenuItem(options) {
14
+ const { part, role, disabled, label, activate } = options;
15
+ const list = use(MenuListContext);
16
+ useDevWarning(list === null, `${part}: rendered outside a Menu.Popup.`);
17
+ const onClick = (event) => {
18
+ if (disabled) {
19
+ event.preventDefault();
20
+ return;
21
+ }
22
+ activate(event.nativeEvent);
23
+ };
24
+ const onKeyDown = (event) => {
25
+ if (event.defaultPrevented || hasModifier(event)) return;
26
+ if (event.key !== "Enter" && event.key !== " ") return;
27
+ if (event.key === " " && list?.typingAhead()) return;
28
+ event.preventDefault();
29
+ if (disabled) return;
30
+ event.currentTarget.click();
31
+ };
32
+ const onPointerMove = (event) => {
33
+ const element = event.currentTarget;
34
+ if (element.ownerDocument.activeElement !== element) element.focus();
35
+ };
36
+ return {
37
+ list,
38
+ libraryProps: {
39
+ role,
40
+ tabIndex: list?.nativeFocusgroup === true ? void 0 : -1,
41
+ "aria-disabled": disabled ? true : void 0,
42
+ disabled: void 0,
43
+ "data-disabled": dataAttr(disabled),
44
+ "data-typeahead": label,
45
+ onClick,
46
+ onKeyDown,
47
+ onPointerMove
48
+ }
49
+ };
50
+ }
51
+ //#endregion
52
+ export { useMenuItem };
53
+
54
+ //# sourceMappingURL=use-menu-item.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-menu-item.mjs","names":[],"sources":["../../src/menu/use-menu-item.ts"],"sourcesContent":["\"use client\";\nimport { use } from \"react\";\nimport type { KeyboardEvent, MouseEvent, PointerEvent } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { MenuListContext } from \"./menu-context.js\";\nimport type { MenuListContextValue } from \"./menu-context.js\";\nimport { hasModifier } from \"./menu-dom.js\";\n\nexport interface MenuItemBaseOptions {\n /** Public name of the part for development warnings. */\n readonly part: string;\n readonly role: \"menuitem\" | \"menuitemcheckbox\" | \"menuitemradio\";\n readonly disabled: boolean;\n readonly label: string | undefined;\n /** Runs for a click, Enter or Space on an enabled item (never for a disabled one). */\n readonly activate: (event: Event) => void;\n}\n\nexport interface MenuItemBase {\n readonly list: MenuListContextValue | null;\n /** Owned props shared by every item kind (design 3.4). */\n readonly libraryProps: {\n role: string;\n tabIndex: number | undefined;\n \"aria-disabled\": true | undefined;\n disabled: undefined;\n onClick: (event: MouseEvent<HTMLElement>) => void;\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;\n onPointerMove: (event: PointerEvent<HTMLElement>) => void;\n } & DataAttributes;\n}\n\n/**\n * The parts every menu item shares: role, roving `tabindex` (none on the native `focusgroup`\n * path), `aria-disabled`, activation by click / Enter / Space (Space is a typeahead character\n * while a buffer is active), and the current item following the pointer (design 6.2).\n */\nexport function useMenuItem(options: MenuItemBaseOptions): MenuItemBase {\n const { part, role, disabled, label, activate } = options;\n const list = use(MenuListContext);\n useDevWarning(list === null, `${part}: rendered outside a Menu.Popup.`);\n\n const onClick = (event: MouseEvent<HTMLElement>) => {\n if (disabled) {\n // A disabled link must not navigate either.\n event.preventDefault();\n return;\n }\n activate(event.nativeEvent);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (event.defaultPrevented || hasModifier(event)) return;\n if (event.key !== \"Enter\" && event.key !== \" \") return;\n if (event.key === \" \" && list?.typingAhead()) return;\n // No page scroll for Space, no second native click.\n event.preventDefault();\n if (disabled) return;\n // Activation is always the element's click: it runs `onClick` (and so `activate`) and the\n // element's own activation behaviour, which a link (navigation) and an item composed with\n // another trigger (`Dialog.Trigger render={<Menu.Item />}`: its command) depend on.\n event.currentTarget.click();\n };\n\n const onPointerMove = (event: PointerEvent<HTMLElement>) => {\n const element = event.currentTarget;\n if (element.ownerDocument.activeElement !== element) element.focus();\n };\n\n return {\n list,\n libraryProps: {\n role,\n tabIndex: list?.nativeFocusgroup === true ? undefined : -1,\n \"aria-disabled\": disabled ? true : undefined,\n // Never the native attribute: disabled items stay focusable (APG), and Chromium's\n // focusgroup skips natively disabled buttons.\n disabled: undefined,\n \"data-disabled\": dataAttr(disabled),\n \"data-typeahead\": label,\n onClick,\n onKeyDown,\n onPointerMove,\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AAuCA,SAAgB,YAAY,SAA4C;CACtE,MAAM,EAAE,MAAM,MAAM,UAAU,OAAO,aAAa;CAClD,MAAM,OAAO,IAAI,eAAe;CAChC,cAAc,SAAS,MAAM,GAAG,KAAK,iCAAiC;CAEtE,MAAM,WAAW,UAAmC;EAClD,IAAI,UAAU;GAEZ,MAAM,eAAe;GACrB;EACF;EACA,SAAS,MAAM,WAAW;CAC5B;CAEA,MAAM,aAAa,UAAsC;EACvD,IAAI,MAAM,oBAAoB,YAAY,KAAK,GAAG;EAClD,IAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;EAChD,IAAI,MAAM,QAAQ,OAAO,MAAM,YAAY,GAAG;EAE9C,MAAM,eAAe;EACrB,IAAI,UAAU;EAId,MAAM,cAAc,MAAM;CAC5B;CAEA,MAAM,iBAAiB,UAAqC;EAC1D,MAAM,UAAU,MAAM;EACtB,IAAI,QAAQ,cAAc,kBAAkB,SAAS,QAAQ,MAAM;CACrE;CAEA,OAAO;EACL;EACA,cAAc;GACZ;GACA,UAAU,MAAM,qBAAqB,OAAO,KAAA,IAAY;GACxD,iBAAiB,WAAW,OAAO,KAAA;GAGnC,UAAU,KAAA;GACV,iBAAiB,SAAS,QAAQ;GAClC,kBAAkB;GAClB;GACA;GACA;EACF;CACF;AACF"}
@@ -0,0 +1,28 @@
1
+ import "../internal/platform-attributes.mjs";
2
+ import { PopoverClose, PopoverCloseProps, PopoverCloseState } from "./popover-close.mjs";
3
+ import { PopoverAlign, PopoverPopup, PopoverPopupProps, PopoverPopupState, PopoverSide } from "./popover-popup.mjs";
4
+ import { PopoverDescription, PopoverDescriptionProps, PopoverDescriptionState } from "./popover-description.mjs";
5
+ import { PopoverOpenChangeDetails, PopoverRoot, PopoverRootProps, PopoverRootState } from "./popover-root.mjs";
6
+ import { PopoverTitle, PopoverTitleProps, PopoverTitleState } from "./popover-title.mjs";
7
+ import { PopoverTrigger, PopoverTriggerProps, PopoverTriggerState } from "./popover-trigger.mjs";
8
+ //#region src/popover/index.d.ts
9
+ /**
10
+ * Popover built on `popover="auto"` and Invoker Commands: `Root` (no DOM) > `Trigger`
11
+ * (`button[command=toggle-popover]`) + `Popup` (`dialog[popover=auto][aria-modal=false]`) >
12
+ * `Title`, `Description`, `Close`. The platform gives the top layer, light dismiss, Escape,
13
+ * nesting, the implicit anchor and focus return. JS-free when the Root has none of `open` /
14
+ * `defaultOpen` / `openChangeAction`; otherwise opening and the closes a part asks for are
15
+ * transitions (pending Trigger, content from committed state), and dismissals are notified
16
+ * through `openChangeAction(false)`.
17
+ */
18
+ export declare const Popover: {
19
+ readonly Root: typeof PopoverRoot;
20
+ readonly Trigger: typeof PopoverTrigger;
21
+ readonly Popup: typeof PopoverPopup;
22
+ readonly Close: typeof PopoverClose;
23
+ readonly Title: typeof PopoverTitle;
24
+ readonly Description: typeof PopoverDescription;
25
+ };
26
+ //#endregion
27
+ export type { PopoverAlign, PopoverCloseProps, PopoverCloseState, PopoverDescriptionProps, PopoverDescriptionState, PopoverOpenChangeDetails, PopoverPopupProps, PopoverPopupState, PopoverRootProps, PopoverRootState, PopoverSide, PopoverTitleProps, PopoverTitleState, PopoverTriggerProps, PopoverTriggerState };
28
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/popover/index.ts"],"mappings":";;;;;;;;;;;;;;;;;qBAmBa;WACX,aAAI;WACJ,gBAAO;WACP,cAAK;WACL,cAAK;WACL,cAAK;WACL,oBAAW"}
@@ -0,0 +1,28 @@
1
+ import { PopoverClose } from "./popover-close.mjs";
2
+ import { PopoverDescription } from "./popover-description.mjs";
3
+ import { PopoverPopup } from "./popover-popup.mjs";
4
+ import { PopoverRoot } from "./popover-root.mjs";
5
+ import { PopoverTitle } from "./popover-title.mjs";
6
+ import { PopoverTrigger } from "./popover-trigger.mjs";
7
+ //#region src/popover/index.ts
8
+ /**
9
+ * Popover built on `popover="auto"` and Invoker Commands: `Root` (no DOM) > `Trigger`
10
+ * (`button[command=toggle-popover]`) + `Popup` (`dialog[popover=auto][aria-modal=false]`) >
11
+ * `Title`, `Description`, `Close`. The platform gives the top layer, light dismiss, Escape,
12
+ * nesting, the implicit anchor and focus return. JS-free when the Root has none of `open` /
13
+ * `defaultOpen` / `openChangeAction`; otherwise opening and the closes a part asks for are
14
+ * transitions (pending Trigger, content from committed state), and dismissals are notified
15
+ * through `openChangeAction(false)`.
16
+ */
17
+ const Popover = {
18
+ Root: PopoverRoot,
19
+ Trigger: PopoverTrigger,
20
+ Popup: PopoverPopup,
21
+ Close: PopoverClose,
22
+ Title: PopoverTitle,
23
+ Description: PopoverDescription
24
+ };
25
+ //#endregion
26
+ export { Popover };
27
+
28
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/popover/index.ts"],"sourcesContent":["// Ships the platform attribute typings (`command`, `commandfor`) with this subpath\n// (conventions 7.4). Type-only: the built JS has no import of it.\nimport \"../internal/platform-attributes.js\";\nimport { PopoverClose } from \"./popover-close.js\";\nimport { PopoverDescription } from \"./popover-description.js\";\nimport { PopoverPopup } from \"./popover-popup.js\";\nimport { PopoverRoot } from \"./popover-root.js\";\nimport { PopoverTitle } from \"./popover-title.js\";\nimport { PopoverTrigger } from \"./popover-trigger.js\";\n\n/**\n * Popover built on `popover=\"auto\"` and Invoker Commands: `Root` (no DOM) > `Trigger`\n * (`button[command=toggle-popover]`) + `Popup` (`dialog[popover=auto][aria-modal=false]`) >\n * `Title`, `Description`, `Close`. The platform gives the top layer, light dismiss, Escape,\n * nesting, the implicit anchor and focus return. JS-free when the Root has none of `open` /\n * `defaultOpen` / `openChangeAction`; otherwise opening and the closes a part asks for are\n * transitions (pending Trigger, content from committed state), and dismissals are notified\n * through `openChangeAction(false)`.\n */\nexport const Popover = {\n Root: PopoverRoot,\n Trigger: PopoverTrigger,\n Popup: PopoverPopup,\n Close: PopoverClose,\n Title: PopoverTitle,\n Description: PopoverDescription,\n} as const;\n\nexport type {\n PopoverRootProps,\n PopoverRootState,\n PopoverOpenChangeDetails,\n} from \"./popover-root.js\";\nexport type { PopoverTriggerProps, PopoverTriggerState } from \"./popover-trigger.js\";\nexport type {\n PopoverPopupProps,\n PopoverPopupState,\n PopoverSide,\n PopoverAlign,\n} from \"./popover-popup.js\";\nexport type { PopoverCloseProps, PopoverCloseState } from \"./popover-close.js\";\nexport type { PopoverTitleProps, PopoverTitleState } from \"./popover-title.js\";\nexport type { PopoverDescriptionProps, PopoverDescriptionState } from \"./popover-description.js\";\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,MAAa,UAAU;CACrB,MAAM;CACN,SAAS;CACT,OAAO;CACP,OAAO;CACP,OAAO;CACP,aAAa;AACf"}
@@ -0,0 +1,41 @@
1
+ import { Action, ActionDetails, RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
3
+ //#region src/popover/popover-close.d.ts
4
+ export interface PopoverCloseState {
5
+ /** The `action` is in flight. */
6
+ readonly pending: boolean;
7
+ readonly disabled: boolean;
8
+ }
9
+ export interface PopoverCloseProps extends Omit<ComponentPropsWithRef<"button">, "onClick" | "className" | "style" | "children"> {
10
+ /**
11
+ * Runs inside this part's own transition (`data-pending` on the button), then closes the
12
+ * popover: in library mode through the Root (`openChangeAction(false)` awaited, view
13
+ * transition if set), in JS-free mode with `hidePopover()`. A rejection reaches the nearest
14
+ * Error Boundary above the Close part and leaves the popover open. If the popover was
15
+ * dismissed and opened again while the action ran, the close is dropped. Without `action` in JS-free
16
+ * mode the button is a native `command="hide-popover"` invoker.
17
+ */
18
+ action?: Action<[details: ActionDetails]>;
19
+ /** Native `disabled`; owned, so a `render` element cannot re-enable the button. */
20
+ disabled?: boolean;
21
+ className?: StateValue<string | undefined, PopoverCloseState>;
22
+ style?: StateValue<CSSProperties | undefined, PopoverCloseState>;
23
+ children?: StateValue<ReactNode, PopoverCloseState>;
24
+ render?: RenderProp<ComponentPropsWithRef<"button">, PopoverCloseState>;
25
+ }
26
+ /**
27
+ * A close button: `<button type="button" commandfor command="hide-popover">`. In JS-free mode
28
+ * without `action` the browser hides the popover. In library mode the click is always cancelled
29
+ * and the close goes through the Root (`openChangeAction(false)` awaited, `hidePopover()` in the
30
+ * commit). With `action` the action runs first in this part's own transition (`data-pending` on
31
+ * the button); a rejection leaves the popover open.
32
+ *
33
+ * Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `commandfor`,
34
+ * `command`, `onClick` (library mode, or with `action`), `data-pending`, `data-disabled`.
35
+ */
36
+ export declare function PopoverClose({ action, disabled, className, style, children, render, ...rest }: PopoverCloseProps): ReactNode;
37
+ export declare namespace PopoverClose {
38
+ var displayName: string;
39
+ }
40
+ //#endregion
41
+ //# sourceMappingURL=popover-close.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popover-close.d.mts","names":[],"sources":["../../src/popover/popover-close.tsx"],"mappings":";;;iBAWiB;;WAEN;WACA;;iBAGM,0BAA0B,KACzC;;;;;;;;;EAWA,SAAS,QAAQ,SAAS;;EAE1B;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;wBAuBvC,eACd,QACA,UACA,WACA,OACA,UACA,WACG,QACF,oBAAiB;yBARJ"}