@funstack/ui 0.1.0-alpha.0 → 0.1.0-alpha.2

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