@proyecto-viviana/solidaria-components 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (398) hide show
  1. package/dist/ActionBar.js +184 -0
  2. package/dist/ActionBar.js.map +1 -0
  3. package/dist/ActionBar.jsx +128 -0
  4. package/dist/ActionBar.jsx.map +1 -0
  5. package/dist/ActionGroup.js +179 -0
  6. package/dist/ActionGroup.js.map +1 -0
  7. package/dist/ActionGroup.jsx +129 -0
  8. package/dist/ActionGroup.jsx.map +1 -0
  9. package/dist/Alert.js +111 -0
  10. package/dist/Alert.js.map +1 -0
  11. package/dist/Alert.jsx +82 -0
  12. package/dist/Alert.jsx.map +1 -0
  13. package/dist/Autocomplete.js +130 -0
  14. package/dist/Autocomplete.js.map +1 -0
  15. package/dist/Autocomplete.jsx +116 -0
  16. package/dist/Autocomplete.jsx.map +1 -0
  17. package/dist/Breadcrumbs.d.ts.map +1 -1
  18. package/dist/Breadcrumbs.js +334 -0
  19. package/dist/Breadcrumbs.js.map +1 -0
  20. package/dist/Breadcrumbs.jsx +266 -0
  21. package/dist/Breadcrumbs.jsx.map +1 -0
  22. package/dist/Button.d.ts.map +1 -1
  23. package/dist/Button.js +348 -0
  24. package/dist/Button.js.map +1 -0
  25. package/dist/Button.jsx +314 -0
  26. package/dist/Button.jsx.map +1 -0
  27. package/dist/Calendar.d.ts +4 -4
  28. package/dist/Calendar.d.ts.map +1 -1
  29. package/dist/Calendar.js +538 -0
  30. package/dist/Calendar.js.map +1 -0
  31. package/dist/Calendar.jsx +333 -0
  32. package/dist/Calendar.jsx.map +1 -0
  33. package/dist/CalendarPicker.d.ts +33 -0
  34. package/dist/CalendarPicker.d.ts.map +1 -0
  35. package/dist/CalendarPicker.js +44 -0
  36. package/dist/CalendarPicker.js.map +1 -0
  37. package/dist/CalendarPicker.jsx +44 -0
  38. package/dist/CalendarPicker.jsx.map +1 -0
  39. package/dist/Checkbox.d.ts +83 -4
  40. package/dist/Checkbox.d.ts.map +1 -1
  41. package/dist/Checkbox.js +923 -0
  42. package/dist/Checkbox.js.map +1 -0
  43. package/dist/Checkbox.jsx +707 -0
  44. package/dist/Checkbox.jsx.map +1 -0
  45. package/dist/Collection.d.ts.map +1 -1
  46. package/dist/Collection.js +202 -0
  47. package/dist/Collection.js.map +1 -0
  48. package/dist/Collection.jsx +152 -0
  49. package/dist/Collection.jsx.map +1 -0
  50. package/dist/Color.d.ts.map +1 -1
  51. package/dist/Color.js +1720 -0
  52. package/dist/Color.js.map +1 -0
  53. package/dist/Color.jsx +1344 -0
  54. package/dist/Color.jsx.map +1 -0
  55. package/dist/ColorEditor.js +208 -0
  56. package/dist/ColorEditor.js.map +1 -0
  57. package/dist/ColorEditor.jsx +120 -0
  58. package/dist/ColorEditor.jsx.map +1 -0
  59. package/dist/ComboBox.d.ts +8 -5
  60. package/dist/ComboBox.d.ts.map +1 -1
  61. package/dist/ComboBox.js +992 -0
  62. package/dist/ComboBox.js.map +1 -0
  63. package/dist/ComboBox.jsx +714 -0
  64. package/dist/ComboBox.jsx.map +1 -0
  65. package/dist/ContextualHelpTrigger.js +146 -0
  66. package/dist/ContextualHelpTrigger.js.map +1 -0
  67. package/dist/ContextualHelpTrigger.jsx +109 -0
  68. package/dist/ContextualHelpTrigger.jsx.map +1 -0
  69. package/dist/DateField.d.ts +3 -3
  70. package/dist/DateField.d.ts.map +1 -1
  71. package/dist/DateField.js +4 -0
  72. package/dist/DateField.jsx +4 -0
  73. package/dist/DateField2.js +499 -0
  74. package/dist/DateField2.js.map +1 -0
  75. package/dist/DateField2.jsx +317 -0
  76. package/dist/DateField2.jsx.map +1 -0
  77. package/dist/DatePicker.d.ts +4 -4
  78. package/dist/DatePicker.d.ts.map +1 -1
  79. package/dist/DatePicker.js +991 -0
  80. package/dist/DatePicker.js.map +1 -0
  81. package/dist/DatePicker.jsx +682 -0
  82. package/dist/DatePicker.jsx.map +1 -0
  83. package/dist/Dialog.d.ts.map +1 -1
  84. package/dist/Dialog.js +337 -0
  85. package/dist/Dialog.js.map +1 -0
  86. package/dist/Dialog.jsx +192 -0
  87. package/dist/Dialog.jsx.map +1 -0
  88. package/dist/Disclosure.d.ts.map +1 -1
  89. package/dist/Disclosure.js +328 -0
  90. package/dist/Disclosure.js.map +1 -0
  91. package/dist/Disclosure.jsx +230 -0
  92. package/dist/Disclosure.jsx.map +1 -0
  93. package/dist/DragAndDrop.d.ts.map +1 -1
  94. package/dist/DragAndDrop.js +176 -0
  95. package/dist/DragAndDrop.js.map +1 -0
  96. package/dist/DragAndDrop.jsx +160 -0
  97. package/dist/DragAndDrop.jsx.map +1 -0
  98. package/dist/DropZone.d.ts.map +1 -1
  99. package/dist/DropZone.js +189 -0
  100. package/dist/DropZone.js.map +1 -0
  101. package/dist/DropZone.jsx +144 -0
  102. package/dist/DropZone.jsx.map +1 -0
  103. package/dist/FieldError.js +66 -0
  104. package/dist/FieldError.js.map +1 -0
  105. package/dist/FieldError.jsx +47 -0
  106. package/dist/FieldError.jsx.map +1 -0
  107. package/dist/FileTrigger.js +79 -0
  108. package/dist/FileTrigger.js.map +1 -0
  109. package/dist/FileTrigger.jsx +49 -0
  110. package/dist/FileTrigger.jsx.map +1 -0
  111. package/dist/Focusable.js +70 -0
  112. package/dist/Focusable.js.map +1 -0
  113. package/dist/Focusable.jsx +69 -0
  114. package/dist/Focusable.jsx.map +1 -0
  115. package/dist/Form.js +99 -0
  116. package/dist/Form.js.map +1 -0
  117. package/dist/Form.jsx +73 -0
  118. package/dist/Form.jsx.map +1 -0
  119. package/dist/GridList.d.ts +34 -2
  120. package/dist/GridList.d.ts.map +1 -1
  121. package/dist/GridList.js +723 -0
  122. package/dist/GridList.js.map +1 -0
  123. package/dist/GridList.jsx +552 -0
  124. package/dist/GridList.jsx.map +1 -0
  125. package/dist/HiddenTimeInput.js +104 -0
  126. package/dist/HiddenTimeInput.js.map +1 -0
  127. package/dist/HiddenTimeInput.jsx +69 -0
  128. package/dist/HiddenTimeInput.jsx.map +1 -0
  129. package/dist/Icon.d.ts.map +1 -1
  130. package/dist/Icon.js +127 -0
  131. package/dist/Icon.js.map +1 -0
  132. package/dist/Icon.jsx +72 -0
  133. package/dist/Icon.jsx.map +1 -0
  134. package/dist/Keyboard.js +30 -0
  135. package/dist/Keyboard.js.map +1 -0
  136. package/dist/Keyboard.jsx +24 -0
  137. package/dist/Keyboard.jsx.map +1 -0
  138. package/dist/Landmark.js +134 -0
  139. package/dist/Landmark.js.map +1 -0
  140. package/dist/Landmark.jsx +118 -0
  141. package/dist/Landmark.jsx.map +1 -0
  142. package/dist/Link.d.ts.map +1 -1
  143. package/dist/Link.js +219 -0
  144. package/dist/Link.js.map +1 -0
  145. package/dist/Link.jsx +189 -0
  146. package/dist/Link.jsx.map +1 -0
  147. package/dist/ListBox.d.ts.map +1 -1
  148. package/dist/ListBox.js +670 -0
  149. package/dist/ListBox.js.map +1 -0
  150. package/dist/ListBox.jsx +508 -0
  151. package/dist/ListBox.jsx.map +1 -0
  152. package/dist/Menu.d.ts +27 -9
  153. package/dist/Menu.d.ts.map +1 -1
  154. package/dist/Menu.js +1254 -0
  155. package/dist/Menu.js.map +1 -0
  156. package/dist/Menu.jsx +1042 -0
  157. package/dist/Menu.jsx.map +1 -0
  158. package/dist/Meter.d.ts.map +1 -1
  159. package/dist/Meter.js +123 -0
  160. package/dist/Meter.js.map +1 -0
  161. package/dist/Meter.jsx +107 -0
  162. package/dist/Meter.jsx.map +1 -0
  163. package/dist/Modal.d.ts.map +1 -1
  164. package/dist/Modal.js +380 -0
  165. package/dist/Modal.js.map +1 -0
  166. package/dist/Modal.jsx +314 -0
  167. package/dist/Modal.jsx.map +1 -0
  168. package/dist/NumberField.d.ts +3 -3
  169. package/dist/NumberField.d.ts.map +1 -1
  170. package/dist/NumberField.js +573 -0
  171. package/dist/NumberField.js.map +1 -0
  172. package/dist/NumberField.jsx +443 -0
  173. package/dist/NumberField.jsx.map +1 -0
  174. package/dist/Popover.d.ts +10 -2
  175. package/dist/Popover.d.ts.map +1 -1
  176. package/dist/Popover.js +464 -0
  177. package/dist/Popover.js.map +1 -0
  178. package/dist/Popover.jsx +333 -0
  179. package/dist/Popover.jsx.map +1 -0
  180. package/dist/Pressable.js +69 -0
  181. package/dist/Pressable.js.map +1 -0
  182. package/dist/Pressable.jsx +68 -0
  183. package/dist/Pressable.jsx.map +1 -0
  184. package/dist/ProgressBar.d.ts.map +1 -1
  185. package/dist/ProgressBar.js +130 -0
  186. package/dist/ProgressBar.js.map +1 -0
  187. package/dist/ProgressBar.jsx +114 -0
  188. package/dist/ProgressBar.jsx.map +1 -0
  189. package/dist/RadioGroup.d.ts +82 -4
  190. package/dist/RadioGroup.d.ts.map +1 -1
  191. package/dist/RadioGroup.js +931 -0
  192. package/dist/RadioGroup.js.map +1 -0
  193. package/dist/RadioGroup.jsx +681 -0
  194. package/dist/RadioGroup.jsx.map +1 -0
  195. package/dist/RangeCalendar.d.ts.map +1 -1
  196. package/dist/RangeCalendar.js +410 -0
  197. package/dist/RangeCalendar.js.map +1 -0
  198. package/dist/RangeCalendar.jsx +253 -0
  199. package/dist/RangeCalendar.jsx.map +1 -0
  200. package/dist/RouterProvider.js +118 -0
  201. package/dist/RouterProvider.js.map +1 -0
  202. package/dist/RouterProvider.jsx +112 -0
  203. package/dist/RouterProvider.jsx.map +1 -0
  204. package/dist/SearchField.d.ts +2 -2
  205. package/dist/SearchField.d.ts.map +1 -1
  206. package/dist/SearchField.js +643 -0
  207. package/dist/SearchField.js.map +1 -0
  208. package/dist/SearchField.jsx +526 -0
  209. package/dist/SearchField.jsx.map +1 -0
  210. package/dist/Select.d.ts.map +1 -1
  211. package/dist/Select.js +1031 -0
  212. package/dist/Select.js.map +1 -0
  213. package/dist/Select.jsx +744 -0
  214. package/dist/Select.jsx.map +1 -0
  215. package/dist/SelectionIndicator.d.ts.map +1 -1
  216. package/dist/SelectionIndicator.js +86 -0
  217. package/dist/SelectionIndicator.js.map +1 -0
  218. package/dist/SelectionIndicator.jsx +50 -0
  219. package/dist/SelectionIndicator.jsx.map +1 -0
  220. package/dist/Separator.js +97 -0
  221. package/dist/Separator.js.map +1 -0
  222. package/dist/Separator.jsx +85 -0
  223. package/dist/Separator.jsx.map +1 -0
  224. package/dist/SharedElementTransition.d.ts.map +1 -1
  225. package/dist/SharedElementTransition.js +165 -0
  226. package/dist/SharedElementTransition.js.map +1 -0
  227. package/dist/SharedElementTransition.jsx +138 -0
  228. package/dist/SharedElementTransition.jsx.map +1 -0
  229. package/dist/Slider.d.ts +33 -1
  230. package/dist/Slider.d.ts.map +1 -1
  231. package/dist/Slider.js +515 -0
  232. package/dist/Slider.js.map +1 -0
  233. package/dist/Slider.jsx +403 -0
  234. package/dist/Slider.jsx.map +1 -0
  235. package/dist/StepList.js +198 -0
  236. package/dist/StepList.js.map +1 -0
  237. package/dist/StepList.jsx +141 -0
  238. package/dist/StepList.jsx.map +1 -0
  239. package/dist/Switch.d.ts +65 -2
  240. package/dist/Switch.d.ts.map +1 -1
  241. package/dist/Switch.js +534 -0
  242. package/dist/Switch.js.map +1 -0
  243. package/dist/Switch.jsx +393 -0
  244. package/dist/Switch.jsx.map +1 -0
  245. package/dist/Table.d.ts +48 -0
  246. package/dist/Table.d.ts.map +1 -1
  247. package/dist/Table.js +1759 -0
  248. package/dist/Table.js.map +1 -0
  249. package/dist/Table.jsx +1348 -0
  250. package/dist/Table.jsx.map +1 -0
  251. package/dist/Tabs.d.ts +0 -4
  252. package/dist/Tabs.d.ts.map +1 -1
  253. package/dist/Tabs.js +595 -0
  254. package/dist/Tabs.js.map +1 -0
  255. package/dist/Tabs.jsx +367 -0
  256. package/dist/Tabs.jsx.map +1 -0
  257. package/dist/TagGroup.d.ts.map +1 -1
  258. package/dist/TagGroup.js +378 -0
  259. package/dist/TagGroup.js.map +1 -0
  260. package/dist/TagGroup.jsx +270 -0
  261. package/dist/TagGroup.jsx.map +1 -0
  262. package/dist/Text.d.ts +18 -6
  263. package/dist/Text.d.ts.map +1 -1
  264. package/dist/Text.js +42 -0
  265. package/dist/Text.js.map +1 -0
  266. package/dist/Text.jsx +35 -0
  267. package/dist/Text.jsx.map +1 -0
  268. package/dist/TextField.d.ts +7 -6
  269. package/dist/TextField.d.ts.map +1 -1
  270. package/dist/TextField.js +436 -0
  271. package/dist/TextField.js.map +1 -0
  272. package/dist/TextField.jsx +403 -0
  273. package/dist/TextField.jsx.map +1 -0
  274. package/dist/TimeField.d.ts +3 -3
  275. package/dist/TimeField.d.ts.map +1 -1
  276. package/dist/TimeField.js +372 -0
  277. package/dist/TimeField.js.map +1 -0
  278. package/dist/TimeField.jsx +224 -0
  279. package/dist/TimeField.jsx.map +1 -0
  280. package/dist/Toast.d.ts +14 -0
  281. package/dist/Toast.d.ts.map +1 -1
  282. package/dist/Toast.js +551 -0
  283. package/dist/Toast.js.map +1 -0
  284. package/dist/Toast.jsx +361 -0
  285. package/dist/Toast.jsx.map +1 -0
  286. package/dist/ToggleButton.d.ts.map +1 -1
  287. package/dist/ToggleButton.js +123 -0
  288. package/dist/ToggleButton.js.map +1 -0
  289. package/dist/ToggleButton.jsx +88 -0
  290. package/dist/ToggleButton.jsx.map +1 -0
  291. package/dist/ToggleButtonGroup.d.ts.map +1 -1
  292. package/dist/ToggleButtonGroup.js +114 -0
  293. package/dist/ToggleButtonGroup.js.map +1 -0
  294. package/dist/ToggleButtonGroup.jsx +85 -0
  295. package/dist/ToggleButtonGroup.jsx.map +1 -0
  296. package/dist/Toolbar.js +112 -0
  297. package/dist/Toolbar.js.map +1 -0
  298. package/dist/Toolbar.jsx +92 -0
  299. package/dist/Toolbar.jsx.map +1 -0
  300. package/dist/Tooltip.d.ts.map +1 -1
  301. package/dist/Tooltip.js +539 -0
  302. package/dist/Tooltip.js.map +1 -0
  303. package/dist/Tooltip.jsx +465 -0
  304. package/dist/Tooltip.jsx.map +1 -0
  305. package/dist/Tree.d.ts +14 -2
  306. package/dist/Tree.d.ts.map +1 -1
  307. package/dist/Tree.js +1424 -0
  308. package/dist/Tree.js.map +1 -0
  309. package/dist/Tree.jsx +1172 -0
  310. package/dist/Tree.jsx.map +1 -0
  311. package/dist/Virtualizer.d.ts +16 -2
  312. package/dist/Virtualizer.d.ts.map +1 -1
  313. package/dist/Virtualizer.js +2 -0
  314. package/dist/Virtualizer.jsx +2 -0
  315. package/dist/Virtualizer2.js +751 -0
  316. package/dist/Virtualizer2.js.map +1 -0
  317. package/dist/Virtualizer2.jsx +730 -0
  318. package/dist/Virtualizer2.jsx.map +1 -0
  319. package/dist/VirtualizerLayouts.d.ts +18 -0
  320. package/dist/VirtualizerLayouts.d.ts.map +1 -1
  321. package/dist/VisuallyHidden.js +42 -0
  322. package/dist/VisuallyHidden.js.map +1 -0
  323. package/dist/VisuallyHidden.jsx +35 -0
  324. package/dist/VisuallyHidden.jsx.map +1 -0
  325. package/dist/contexts.js +33 -0
  326. package/dist/contexts.js.map +1 -0
  327. package/dist/contexts.jsx +33 -0
  328. package/dist/contexts.jsx.map +1 -0
  329. package/dist/index.d.ts +7 -6
  330. package/dist/index.d.ts.map +1 -1
  331. package/dist/index.js +70 -23711
  332. package/dist/index.jsx +70 -18116
  333. package/dist/useDragAndDrop.js +2 -0
  334. package/dist/useDragAndDrop.jsx +2 -0
  335. package/dist/useDragAndDrop2.js +395 -0
  336. package/dist/useDragAndDrop2.js.map +1 -0
  337. package/dist/useDragAndDrop2.jsx +395 -0
  338. package/dist/useDragAndDrop2.jsx.map +1 -0
  339. package/dist/utils.d.ts +83 -11
  340. package/dist/utils.d.ts.map +1 -1
  341. package/dist/utils.js +285 -0
  342. package/dist/utils.js.map +1 -0
  343. package/dist/utils.jsx +277 -0
  344. package/dist/utils.jsx.map +1 -0
  345. package/dist/virtualizer/Layout.js +144 -0
  346. package/dist/virtualizer/Layout.js.map +1 -0
  347. package/dist/virtualizer/Layout.jsx +144 -0
  348. package/dist/virtualizer/Layout.jsx.map +1 -0
  349. package/package.json +401 -4
  350. package/src/Breadcrumbs.tsx +3 -1
  351. package/src/Button.tsx +90 -23
  352. package/src/Calendar.tsx +27 -10
  353. package/src/CalendarPicker.tsx +73 -0
  354. package/src/Checkbox.tsx +539 -3
  355. package/src/Collection.tsx +9 -3
  356. package/src/Color.tsx +42 -14
  357. package/src/ComboBox.tsx +126 -14
  358. package/src/DateField.tsx +20 -2
  359. package/src/DatePicker.tsx +117 -10
  360. package/src/Dialog.tsx +13 -5
  361. package/src/Disclosure.tsx +3 -1
  362. package/src/DragAndDrop.tsx +3 -1
  363. package/src/DropZone.tsx +3 -1
  364. package/src/GridList.tsx +125 -24
  365. package/src/Icon.tsx +3 -1
  366. package/src/Link.tsx +3 -1
  367. package/src/ListBox.tsx +31 -4
  368. package/src/Menu.tsx +298 -131
  369. package/src/Meter.tsx +3 -1
  370. package/src/Modal.tsx +100 -52
  371. package/src/NumberField.tsx +73 -10
  372. package/src/Popover.tsx +49 -11
  373. package/src/ProgressBar.tsx +3 -1
  374. package/src/RadioGroup.tsx +468 -4
  375. package/src/RangeCalendar.tsx +3 -1
  376. package/src/SearchField.tsx +27 -3
  377. package/src/Select.tsx +22 -7
  378. package/src/SelectionIndicator.tsx +3 -1
  379. package/src/SharedElementTransition.tsx +3 -1
  380. package/src/Slider.tsx +159 -13
  381. package/src/Switch.tsx +395 -2
  382. package/src/Table.tsx +409 -95
  383. package/src/Tabs.tsx +16 -45
  384. package/src/TagGroup.tsx +4 -1
  385. package/src/Text.tsx +31 -10
  386. package/src/TextField.tsx +73 -10
  387. package/src/TimeField.tsx +20 -2
  388. package/src/Toast.tsx +79 -14
  389. package/src/ToggleButton.tsx +3 -1
  390. package/src/ToggleButtonGroup.tsx +3 -1
  391. package/src/Tooltip.tsx +55 -8
  392. package/src/Tree.tsx +77 -9
  393. package/src/Virtualizer.tsx +180 -31
  394. package/src/VirtualizerLayouts.ts +45 -10
  395. package/src/index.ts +50 -0
  396. package/src/utils.tsx +243 -27
  397. package/dist/index.js.map +0 -1
  398. package/dist/index.jsx.map +0 -1
package/src/Menu.tsx CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  createHover,
27
27
  createButton,
28
28
  createInteractOutside,
29
+ createScrollIntoViewOnFocus,
29
30
  mergeProps,
30
31
  FocusScope,
31
32
  type AriaMenuProps,
@@ -86,7 +87,7 @@ export interface MenuRenderProps {
86
87
 
87
88
  export interface MenuProps<T>
88
89
  extends
89
- Omit<AriaMenuProps, "children">,
90
+ Omit<AriaMenuProps<T>, "children">,
90
91
  SlotProps,
91
92
  Pick<
92
93
  MenuStateProps<T>,
@@ -108,8 +109,10 @@ export interface MenuProps<T>
108
109
  getDisabled?: (item: T) => boolean;
109
110
  /** Keys of disabled items. */
110
111
  disabledKeys?: Iterable<Key>;
111
- /** Handler called when an item is activated. */
112
- onAction?: (key: Key) => void;
112
+ /** Whether disabled items can still receive focus. */
113
+ disabledBehavior?: "selection" | "all";
114
+ /** Handler called when an item is activated, with the activated item's value. */
115
+ onAction?: (key: Key, value: T) => void;
113
116
  /** Handler called when the menu should close. */
114
117
  onClose?: () => void;
115
118
  /** The children of the component. A function may be provided to render each item. */
@@ -125,12 +128,9 @@ export interface MenuProps<T>
125
128
  /** Whether the menu should close when an item is selected. */
126
129
  shouldCloseOnSelect?: boolean;
127
130
  /** Ref for the menu element. */
128
- ref?: RefLike<HTMLUListElement>;
131
+ ref?: RefLike<HTMLDivElement>;
129
132
  /** Custom renderer for the menu element. */
130
- render?: (
131
- props: JSX.HTMLAttributes<HTMLUListElement>,
132
- renderProps: MenuRenderProps,
133
- ) => JSX.Element;
133
+ render?: (props: JSX.HTMLAttributes<HTMLDivElement>, renderProps: MenuRenderProps) => JSX.Element;
134
134
  /** Drag and drop hooks from `useDragAndDrop`. */
135
135
  dragAndDropHooks?: DragAndDropHooks<T>;
136
136
  }
@@ -154,9 +154,22 @@ export interface MenuItemRenderProps {
154
154
  hasSubmenu: boolean;
155
155
  /** Whether the submenu is currently open. */
156
156
  isOpen: boolean;
157
+ /**
158
+ * Internal: props (carrying the slot `id` the item's `aria-describedby`
159
+ * references) for the item's description text element. Threaded to the styled
160
+ * `Text slot="description"` so it receives the id — mirrors upstream's
161
+ * `TextContext` description-slot delegation.
162
+ */
163
+ descriptionProps?: JSX.HTMLAttributes<HTMLElement>;
164
+ /**
165
+ * Internal: props (id + `aria-hidden`) for the item's keyboard-shortcut
166
+ * element, threaded to the styled `Keyboard`.
167
+ */
168
+ keyboardShortcutProps?: JSX.HTMLAttributes<HTMLElement>;
157
169
  }
158
170
 
159
- export interface MenuItemProps<T> extends Omit<AriaMenuItemProps, "children" | "key">, SlotProps {
171
+ export interface MenuItemProps<T>
172
+ extends Omit<AriaMenuItemProps, "children" | "key" | "id">, SlotProps {
160
173
  /** The unique key for the item. */
161
174
  id: Key;
162
175
  /** The item value. */
@@ -185,11 +198,11 @@ export interface MenuItemProps<T> extends Omit<AriaMenuItemProps, "children" | "
185
198
  onHoverEnd?: () => void;
186
199
  /** Handler called when hover state changes. */
187
200
  onHoverChange?: (isHovered: boolean) => void;
188
- /** Ref for the menu item element. */
189
- ref?: RefLike<HTMLLIElement>;
201
+ /** Ref for the menu item element (a `<div>`, or an `<a>` for link items). */
202
+ ref?: RefLike<HTMLElement>;
190
203
  /** Custom renderer for the menu item element. */
191
204
  render?: (
192
- props: JSX.HTMLAttributes<HTMLLIElement>,
205
+ props: JSX.HTMLAttributes<HTMLElement>,
193
206
  renderProps: MenuItemRenderProps,
194
207
  ) => JSX.Element;
195
208
  }
@@ -249,13 +262,15 @@ interface MenuTriggerContextValue {
249
262
  state: OverlayTriggerState;
250
263
  triggerProps: JSX.HTMLAttributes<HTMLElement>;
251
264
  menuProps: JSX.HTMLAttributes<HTMLElement>;
265
+ isPressed?: () => boolean;
266
+ onPressStart?: (e: { pointerType?: string }) => void;
252
267
  }
253
268
 
254
269
  interface MenuItemContextValue {
255
270
  props?: () => JSX.HTMLAttributes<HTMLElement>;
256
271
  closeOnSelect?: boolean;
257
272
  onAction?: () => void;
258
- setItemRef?: (el: HTMLLIElement | null) => void;
273
+ setItemRef?: (el: HTMLElement | null) => void;
259
274
  }
260
275
 
261
276
  interface StaticMenuCollectionItem {
@@ -283,6 +298,12 @@ interface MenuSectionSelectionRegistryContextValue {
283
298
  selectItem(key: Key, event?: MenuSelectionEvent): boolean;
284
299
  }
285
300
 
301
+ interface MenuItemCloseRegistryContextValue {
302
+ registerItem(key: Key, shouldCloseOnSelect: () => boolean | undefined): void;
303
+ unregisterItem(key: Key, shouldCloseOnSelect: () => boolean | undefined): void;
304
+ shouldCloseOnSelect(key: Key | null): boolean | undefined;
305
+ }
306
+
286
307
  export const MenuContext = createContext<MenuContextValue<unknown> | null>(null);
287
308
  export const MenuStateContext = createContext<MenuState<unknown> | null>(null);
288
309
  export const MenuTriggerContext = createContext<MenuTriggerContextValue | null>(null);
@@ -292,6 +313,7 @@ const StaticMenuCollectionContext = createContext<StaticMenuCollectionContextVal
292
313
  const MenuSectionSelectionContext = createContext<MenuSectionSelectionContextValue | null>(null);
293
314
  const MenuSectionSelectionRegistryContext =
294
315
  createContext<MenuSectionSelectionRegistryContextValue | null>(null);
316
+ const MenuItemCloseRegistryContext = createContext<MenuItemCloseRegistryContextValue | null>(null);
295
317
 
296
318
  type RefLike<T> = ((el: T) => void) | { current?: T | null } | undefined;
297
319
 
@@ -323,6 +345,22 @@ export function MenuTrigger(props: MenuTriggerProps): JSX.Element {
323
345
  },
324
346
  });
325
347
 
348
+ const [isMousePressed, setMousePressed] = createSignal(false);
349
+ const onPressStart = (e: { pointerType?: string }) => {
350
+ if (e.pointerType !== "mouse" || typeof document === "undefined") {
351
+ return;
352
+ }
353
+
354
+ setMousePressed(true);
355
+ document.addEventListener(
356
+ "pointerup",
357
+ () => {
358
+ setMousePressed(false);
359
+ },
360
+ { once: true, capture: true },
361
+ );
362
+ };
363
+
326
364
  const menuTrigger = createMenuTrigger(
327
365
  {
328
366
  get isDisabled() {
@@ -343,6 +381,8 @@ export function MenuTrigger(props: MenuTriggerProps): JSX.Element {
343
381
  get menuProps() {
344
382
  return menuTrigger.menuProps;
345
383
  },
384
+ isPressed: isMousePressed,
385
+ onPressStart,
346
386
  }}
347
387
  >
348
388
  {props.children}
@@ -369,10 +409,11 @@ export function SubmenuTrigger(props: SubmenuTriggerProps): JSX.Element {
369
409
  },
370
410
  });
371
411
 
372
- let triggerRef: HTMLLIElement | null = null;
412
+ let triggerRef: HTMLElement | null = null;
373
413
  const triggerId = createUniqueId();
374
414
  const menuId = createUniqueId();
375
415
  let hoverTimeout: number | undefined;
416
+ let hasPointerHover = false;
376
417
  const delay = () => props.delay ?? 200;
377
418
 
378
419
  const clearHoverTimeout = () => {
@@ -387,6 +428,16 @@ export function SubmenuTrigger(props: SubmenuTriggerProps): JSX.Element {
387
428
  state.open();
388
429
  };
389
430
 
431
+ const queueOpenSubmenu = () => {
432
+ clearHoverTimeout();
433
+ const open = () => state.open();
434
+ if (typeof queueMicrotask === "function") {
435
+ queueMicrotask(open);
436
+ } else {
437
+ Promise.resolve().then(open);
438
+ }
439
+ };
440
+
390
441
  const scheduleOpen = () => {
391
442
  clearHoverTimeout();
392
443
  hoverTimeout = window.setTimeout(() => {
@@ -395,6 +446,28 @@ export function SubmenuTrigger(props: SubmenuTriggerProps): JSX.Element {
395
446
  }, delay());
396
447
  };
397
448
 
449
+ const schedulePointerOpen = (event: PointerEvent) => {
450
+ hasPointerHover = true;
451
+ if (event.isTrusted === false) {
452
+ queueOpenSubmenu();
453
+ return;
454
+ }
455
+
456
+ scheduleOpen();
457
+ };
458
+
459
+ const openFromMouseHover = () => {
460
+ if (state.isOpen()) {
461
+ return;
462
+ }
463
+
464
+ if (hasPointerHover) {
465
+ scheduleOpen();
466
+ } else {
467
+ queueOpenSubmenu();
468
+ }
469
+ };
470
+
398
471
  onCleanup(clearHoverTimeout);
399
472
 
400
473
  const menuTriggerContext = createMemo<MenuTriggerContextValue>(() => ({
@@ -415,7 +488,7 @@ export function SubmenuTrigger(props: SubmenuTriggerProps): JSX.Element {
415
488
  },
416
489
  triggerRef: () => triggerRef,
417
490
  setTriggerRef: (el: HTMLElement | null) => {
418
- triggerRef = el as HTMLLIElement | null;
491
+ triggerRef = el;
419
492
  },
420
493
  triggerId,
421
494
  trigger: "SubmenuTrigger",
@@ -430,17 +503,22 @@ export function SubmenuTrigger(props: SubmenuTriggerProps): JSX.Element {
430
503
  props: () => ({
431
504
  id: triggerId,
432
505
  "aria-haspopup": "menu",
433
- "aria-expanded": state.isOpen() || undefined,
434
- "aria-controls": state.isOpen() ? menuId : undefined,
506
+ get "aria-expanded"() {
507
+ return state.isOpen() || undefined;
508
+ },
509
+ get "aria-controls"() {
510
+ return state.isOpen() ? menuId : undefined;
511
+ },
435
512
  onPointerEnter: (event: PointerEvent) => {
436
513
  if (event.pointerType === "touch") return;
437
- scheduleOpen();
514
+ schedulePointerOpen(event);
438
515
  },
439
516
  onPointerOver: (event: PointerEvent) => {
440
517
  if (event.pointerType === "touch") return;
441
- scheduleOpen();
518
+ schedulePointerOpen(event);
442
519
  },
443
- onMouseEnter: () => scheduleOpen(),
520
+ onMouseEnter: openFromMouseHover,
521
+ onMouseOver: openFromMouseHover,
444
522
  onKeyDown: (event: KeyboardEvent) => {
445
523
  if (event.key === "ArrowRight" || event.key === "Enter" || event.key === " ") {
446
524
  event.preventDefault();
@@ -512,6 +590,9 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
512
590
  get isDisabled() {
513
591
  return local.isDisabled;
514
592
  },
593
+ onPressStart(e) {
594
+ context.onPressStart?.(e);
595
+ },
515
596
  onPress() {
516
597
  state.toggle();
517
598
  },
@@ -529,14 +610,16 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
529
610
  isOpen: state.isOpen(),
530
611
  isFocused: isFocused(),
531
612
  isFocusVisible: isFocusVisible(),
532
- isPressed: buttonAria.isPressed(),
613
+ isPressed: context.isPressed?.() || buttonAria.isPressed(),
533
614
  isHovered: isHovered(),
534
615
  isDisabled: !!local.isDisabled,
535
616
  }));
536
617
 
537
618
  const renderProps = useRenderProps(
538
619
  {
539
- children: props.children,
620
+ get children() {
621
+ return props.children;
622
+ },
540
623
  class: local.class,
541
624
  style: local.style,
542
625
  defaultClassName: "solidaria-MenuButton",
@@ -586,7 +669,7 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
586
669
  data-open={state.isOpen() || undefined}
587
670
  data-focused={isFocused() || undefined}
588
671
  data-focus-visible={isFocusVisible() || undefined}
589
- data-pressed={buttonAria.isPressed() || undefined}
672
+ data-pressed={context.isPressed?.() || buttonAria.isPressed() || undefined}
590
673
  data-hovered={isHovered() || undefined}
591
674
  data-disabled={local.isDisabled || undefined}
592
675
  >
@@ -601,23 +684,14 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
601
684
  export function Menu<T>(props: MenuProps<T>): JSX.Element {
602
685
  const [local, stateProps, ariaProps] = splitProps(
603
686
  props,
604
- [
605
- "children",
606
- "class",
607
- "style",
608
- "render",
609
- "slot",
610
- "renderEmptyState",
611
- "shouldCloseOnSelect",
612
- "ref",
613
- "staticChildren",
614
- ],
687
+ ["children", "class", "style", "render", "slot", "renderEmptyState", "ref", "staticChildren"],
615
688
  [
616
689
  "items",
617
690
  "getKey",
618
691
  "getTextValue",
619
692
  "getDisabled",
620
693
  "disabledKeys",
694
+ "disabledBehavior",
621
695
  "selectionMode",
622
696
  "selectionBehavior",
623
697
  "disallowEmptySelection",
@@ -627,16 +701,18 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
627
701
  "allowDuplicateSelectionEvents",
628
702
  "onAction",
629
703
  "onClose",
704
+ "shouldCloseOnSelect",
630
705
  "dragAndDropHooks",
631
706
  ],
632
707
  );
633
708
 
634
709
  const triggerContext = useContext(MenuTriggerContext);
635
710
 
636
- const [menuRef, setMenuRef] = createSignal<HTMLUListElement | null>(null);
711
+ const [menuRef, setMenuRef] = createSignal<HTMLDivElement | null>(null);
637
712
  const [staticItems, setStaticItems] = createSignal<StaticMenuCollectionItem[]>([]);
638
713
  const staticItemMap = new Map<Key, StaticMenuCollectionItem>();
639
714
  const sectionSelectionMap = new Map<Key, MenuSectionSelectionContextValue>();
715
+ const itemCloseMap = new Map<Key, () => boolean | undefined>();
640
716
  const usesStaticChildren = () => local.staticChildren != null || stateProps.items == null;
641
717
 
642
718
  const syncStaticItems = () => {
@@ -682,9 +758,27 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
682
758
  return true;
683
759
  },
684
760
  };
685
- const handleAction = (key: Key) => {
761
+ const itemCloseRegistry: MenuItemCloseRegistryContextValue = {
762
+ registerItem(key, shouldCloseOnSelect) {
763
+ itemCloseMap.set(key, shouldCloseOnSelect);
764
+ },
765
+ unregisterItem(key, shouldCloseOnSelect) {
766
+ if (itemCloseMap.get(key) === shouldCloseOnSelect) {
767
+ itemCloseMap.delete(key);
768
+ }
769
+ },
770
+ shouldCloseOnSelect(key) {
771
+ return key == null ? undefined : itemCloseMap.get(key)?.();
772
+ },
773
+ };
774
+ const handleAction = (key: Key, value?: T) => {
686
775
  sectionSelectionRegistry.selectItem(key);
687
- stateProps.onAction?.(key);
776
+ // Forward the activated item's value, which `createMenuState` sets from the
777
+ // collection node. For dynamic collections this is the user's data item.
778
+ // Static children are modeled with an internal item descriptor that upstream
779
+ // RAC does not expose as `value` (MenuItem only carries `value` for dynamic
780
+ // collections), so it is suppressed there to match.
781
+ stateProps.onAction?.(key, (usesStaticChildren() ? undefined : value) as T);
688
782
  };
689
783
 
690
784
  const flatItems = createMemo<T[]>(() => {
@@ -719,6 +813,9 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
719
813
  get disabledKeys() {
720
814
  return stateProps.disabledKeys;
721
815
  },
816
+ get disabledBehavior() {
817
+ return stateProps.disabledBehavior;
818
+ },
722
819
  get selectionMode() {
723
820
  return stateProps.selectionMode;
724
821
  },
@@ -770,6 +867,12 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
770
867
  get onClose() {
771
868
  return stateProps.onClose ?? (() => triggerContext?.state.close());
772
869
  },
870
+ get shouldCloseOnSelect() {
871
+ return (
872
+ itemCloseRegistry.shouldCloseOnSelect(state.focusedKey()) ??
873
+ stateProps.shouldCloseOnSelect
874
+ );
875
+ },
773
876
  get "aria-label"() {
774
877
  return ariaProps["aria-label"];
775
878
  },
@@ -783,6 +886,15 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
783
886
  state,
784
887
  );
785
888
 
889
+ // Reveal the activedescendant-focused item on keyboard navigation. The menu
890
+ // keeps real DOM focus on itself, so the browser won't natively scroll an
891
+ // off-screen focused item into view the way roving tabindex would.
892
+ createScrollIntoViewOnFocus({
893
+ focusedKey: () => state.focusedKey(),
894
+ isActive: () => state.isFocused(),
895
+ ref: () => menuRef(),
896
+ });
897
+
786
898
  const { isFocused, focusProps } = createFocusRing();
787
899
 
788
900
  createInteractOutside({
@@ -832,7 +944,7 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
832
944
  const { ref: _ref4, ...rest } = labelProps as Record<string, unknown>;
833
945
  return rest;
834
946
  };
835
- const setResolvedMenuRef = (el: HTMLUListElement): void => {
947
+ const setResolvedMenuRef = (el: HTMLDivElement): void => {
836
948
  setMenuRef(el);
837
949
  assignRef(local.ref, el);
838
950
  };
@@ -1028,27 +1140,32 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
1028
1140
  dndDropIndicator(index, position) ??
1029
1141
  parentCollectionRenderer?.renderDropIndicator?.(index, position),
1030
1142
  }));
1143
+ const menuItemContextValue = createMemo<MenuItemContextValue>(() =>
1144
+ stateProps.shouldCloseOnSelect !== undefined
1145
+ ? { closeOnSelect: stateProps.shouldCloseOnSelect }
1146
+ : {},
1147
+ );
1031
1148
  const menuListChildren = () => (
1032
1149
  <SharedElementTransition>
1033
1150
  {state.collection().size === 0 && !usesStaticChildren() && local.renderEmptyState ? (
1034
- <li role="presentation" data-empty-state>
1151
+ <div role="presentation" data-empty-state>
1035
1152
  <div role="menuitem" style={{ display: "contents" }}>
1036
1153
  {local.renderEmptyState()}
1037
1154
  </div>
1038
- </li>
1155
+ </div>
1039
1156
  ) : usesStaticChildren() ? (
1040
1157
  renderStaticChildren()
1041
1158
  ) : hasSections() ? (
1042
1159
  <For each={sectionedRenderEntries()}>
1043
1160
  {(entry) =>
1044
1161
  entry.type === "section" ? (
1045
- <li role="presentation" data-section-wrapper>
1162
+ <div role="presentation" data-section-wrapper>
1046
1163
  <Section class="solidaria-Menu-section">
1047
1164
  {entry.section.title != null && (
1048
1165
  <Header class="solidaria-Menu-sectionHeader">{entry.section.title}</Header>
1049
1166
  )}
1050
1167
  <Group class="solidaria-Menu-sectionGroup">
1051
- <ul role="group" aria-label={entry.section["aria-label"]}>
1168
+ <div role="group" aria-label={entry.section["aria-label"]}>
1052
1169
  <For each={entry.items}>
1053
1170
  {(indexedItem) => (
1054
1171
  <>
@@ -1062,10 +1179,10 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
1062
1179
  </>
1063
1180
  )}
1064
1181
  </For>
1065
- </ul>
1182
+ </div>
1066
1183
  </Group>
1067
1184
  </Section>
1068
- </li>
1185
+ </div>
1069
1186
  ) : (
1070
1187
  <>
1071
1188
  {collectionRenderer().renderDropIndicator?.(entry.item.index, "before")}
@@ -1079,7 +1196,7 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
1079
1196
  ) : (
1080
1197
  <>
1081
1198
  {virtualRange()?.offsetTop ? (
1082
- <li
1199
+ <div
1083
1200
  role="presentation"
1084
1201
  aria-hidden="true"
1085
1202
  style={{ height: `${virtualRange()!.offsetTop}px` }}
@@ -1106,7 +1223,7 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
1106
1223
  }}
1107
1224
  </For>
1108
1225
  {virtualRange()?.offsetBottom ? (
1109
- <li
1226
+ <div
1110
1227
  role="presentation"
1111
1228
  aria-hidden="true"
1112
1229
  style={{ height: `${virtualRange()!.offsetBottom}px` }}
@@ -1135,7 +1252,7 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
1135
1252
  "data-empty": state.collection().size === 0 || undefined,
1136
1253
  "data-drop-target": isRootDropTarget() || undefined,
1137
1254
  children: menuListChildren(),
1138
- }) as JSX.HTMLAttributes<HTMLUListElement>;
1255
+ }) as JSX.HTMLAttributes<HTMLDivElement>;
1139
1256
 
1140
1257
  // Only use FocusScope when inside a MenuTrigger (for popover behavior)
1141
1258
  // Standalone menus don't need focus restoration
@@ -1153,44 +1270,46 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
1153
1270
  >
1154
1271
  <MenuStateContext.Provider value={state}>
1155
1272
  <MenuSectionSelectionRegistryContext.Provider value={sectionSelectionRegistry}>
1156
- <StaticMenuCollectionContext.Provider
1157
- value={usesStaticChildren() ? staticCollectionContext : null}
1158
- >
1159
- <MenuItemContext.Provider value={{ closeOnSelect: local.shouldCloseOnSelect }}>
1160
- <CollectionRendererContext.Provider value={collectionRenderer()}>
1161
- <>
1162
- <Show when={ariaProps.label}>
1163
- <span {...cleanLabelProps()}>{ariaProps.label as JSX.Element}</span>
1164
- </Show>
1165
- {local.render ? (
1166
- local.render(menuListProps(), renderValues())
1167
- ) : (
1168
- <ul
1169
- ref={setResolvedMenuRef}
1170
- {...mergeProps(
1171
- domProps(),
1172
- cleanMenuProps(),
1173
- cleanTriggerMenuProps(),
1174
- cleanFocusProps(),
1175
- (droppableCollection()?.collectionProps as
1176
- | Record<string, unknown>
1177
- | undefined) ?? {},
1178
- )}
1179
- class={renderProps.class()}
1180
- style={renderProps.style()}
1181
- slot={local.slot}
1182
- data-focused={state.isFocused() || undefined}
1183
- data-disabled={resolveDisabled() || undefined}
1184
- data-empty={state.collection().size === 0 || undefined}
1185
- data-drop-target={isRootDropTarget() || undefined}
1186
- >
1187
- {menuListChildren()}
1188
- </ul>
1189
- )}
1190
- </>
1191
- </CollectionRendererContext.Provider>
1192
- </MenuItemContext.Provider>
1193
- </StaticMenuCollectionContext.Provider>
1273
+ <MenuItemCloseRegistryContext.Provider value={itemCloseRegistry}>
1274
+ <StaticMenuCollectionContext.Provider
1275
+ value={usesStaticChildren() ? staticCollectionContext : null}
1276
+ >
1277
+ <MenuItemContext.Provider value={menuItemContextValue()}>
1278
+ <CollectionRendererContext.Provider value={collectionRenderer()}>
1279
+ <>
1280
+ <Show when={ariaProps.label}>
1281
+ <span {...cleanLabelProps()}>{ariaProps.label as JSX.Element}</span>
1282
+ </Show>
1283
+ {local.render ? (
1284
+ local.render(menuListProps(), renderValues())
1285
+ ) : (
1286
+ <div
1287
+ ref={setResolvedMenuRef}
1288
+ {...mergeProps(
1289
+ domProps(),
1290
+ cleanMenuProps(),
1291
+ cleanTriggerMenuProps(),
1292
+ cleanFocusProps(),
1293
+ (droppableCollection()?.collectionProps as
1294
+ | Record<string, unknown>
1295
+ | undefined) ?? {},
1296
+ )}
1297
+ class={renderProps.class()}
1298
+ style={renderProps.style()}
1299
+ slot={local.slot}
1300
+ data-focused={state.isFocused() || undefined}
1301
+ data-disabled={resolveDisabled() || undefined}
1302
+ data-empty={state.collection().size === 0 || undefined}
1303
+ data-drop-target={isRootDropTarget() || undefined}
1304
+ >
1305
+ {menuListChildren()}
1306
+ </div>
1307
+ )}
1308
+ </>
1309
+ </CollectionRendererContext.Provider>
1310
+ </MenuItemContext.Provider>
1311
+ </StaticMenuCollectionContext.Provider>
1312
+ </MenuItemCloseRegistryContext.Provider>
1194
1313
  </MenuSectionSelectionRegistryContext.Provider>
1195
1314
  </MenuStateContext.Provider>
1196
1315
  </MenuContext.Provider>
@@ -1240,17 +1359,26 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1240
1359
  const staticCollection = useContext(StaticMenuCollectionContext);
1241
1360
  const sectionSelection = useContext(MenuSectionSelectionContext);
1242
1361
  const sectionSelectionRegistry = useContext(MenuSectionSelectionRegistryContext);
1243
- const [ref, setRef] = createSignal<HTMLLIElement | null>(null);
1362
+ const itemCloseRegistry = useContext(MenuItemCloseRegistryContext);
1363
+ // Tracks the focusable menuitem element (the <div>, or the <a> for link
1364
+ // items) so roving focus can be moved onto it imperatively.
1365
+ const [ref, setRef] = createSignal<HTMLElement | null>(null);
1244
1366
  const contextProps = () => itemContext?.props?.() ?? {};
1245
1367
  const combinedOnAction = () => {
1246
1368
  local.onAction?.();
1247
1369
  itemContext?.onAction?.();
1248
1370
  };
1371
+ const itemCloseOnSelect = () =>
1372
+ ariaProps.closeOnSelect ??
1373
+ sectionSelection?.shouldCloseOnSelect() ??
1374
+ itemContext?.closeOnSelect;
1249
1375
  const activeSectionSelection = () =>
1250
1376
  sectionSelection && sectionSelection.selectionMode() !== "none" ? sectionSelection : null;
1251
1377
  let registeredStaticKey: Key | null = null;
1252
1378
  let registeredSectionSelectionKey: Key | null = null;
1253
1379
  let registeredSectionSelection: MenuSectionSelectionContextValue | null = null;
1380
+ let registeredCloseKey: Key | null = null;
1381
+ let registeredCloseAccessor: (() => boolean | undefined) | null = null;
1254
1382
 
1255
1383
  const unregisterSectionSelection = () => {
1256
1384
  if (registeredSectionSelectionKey != null && registeredSectionSelection) {
@@ -1304,9 +1432,38 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1304
1432
 
1305
1433
  onCleanup(unregisterSectionSelection);
1306
1434
 
1435
+ const unregisterItemClose = () => {
1436
+ if (registeredCloseKey != null && registeredCloseAccessor) {
1437
+ itemCloseRegistry?.unregisterItem(registeredCloseKey, registeredCloseAccessor);
1438
+ registeredCloseKey = null;
1439
+ registeredCloseAccessor = null;
1440
+ }
1441
+ };
1442
+
1443
+ createEffect(() => {
1444
+ if (!itemCloseRegistry) {
1445
+ unregisterItemClose();
1446
+ return;
1447
+ }
1448
+
1449
+ if (registeredCloseKey === local.id && registeredCloseAccessor === itemCloseOnSelect) {
1450
+ return;
1451
+ }
1452
+
1453
+ unregisterItemClose();
1454
+ registeredCloseKey = local.id;
1455
+ registeredCloseAccessor = itemCloseOnSelect;
1456
+ itemCloseRegistry.registerItem(local.id, itemCloseOnSelect);
1457
+ });
1458
+
1459
+ onCleanup(unregisterItemClose);
1460
+
1307
1461
  const itemAria = createMenuItem<T>(
1308
1462
  {
1309
1463
  key: local.id,
1464
+ get id() {
1465
+ return contextProps().id as string | undefined;
1466
+ },
1310
1467
  get isDisabled() {
1311
1468
  return Boolean(
1312
1469
  ariaProps.isDisabled ||
@@ -1317,15 +1474,20 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1317
1474
  get "aria-label"() {
1318
1475
  return ariaProps["aria-label"] ?? local.textValue;
1319
1476
  },
1477
+ get "aria-controls"() {
1478
+ return contextProps()["aria-controls"] as string | undefined;
1479
+ },
1480
+ get "aria-haspopup"() {
1481
+ return contextProps()["aria-haspopup"] as string | boolean | undefined;
1482
+ },
1483
+ get "aria-expanded"() {
1484
+ return contextProps()["aria-expanded"] as boolean | "true" | "false" | undefined;
1485
+ },
1320
1486
  get onAction() {
1321
1487
  return combinedOnAction;
1322
1488
  },
1323
1489
  get closeOnSelect() {
1324
- return (
1325
- ariaProps.closeOnSelect ??
1326
- sectionSelection?.shouldCloseOnSelect() ??
1327
- itemContext?.closeOnSelect
1328
- );
1490
+ return itemCloseOnSelect();
1329
1491
  },
1330
1492
  get href() {
1331
1493
  return local.href;
@@ -1341,6 +1503,7 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1341
1503
  },
1342
1504
  },
1343
1505
  state,
1506
+ () => ref(),
1344
1507
  );
1345
1508
 
1346
1509
  const { isHovered, hoverProps } = createHover({
@@ -1364,12 +1527,16 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1364
1527
  isDisabled: itemAria.isDisabled(),
1365
1528
  hasSubmenu: Boolean(contextProps()["aria-haspopup"]),
1366
1529
  isOpen: contextProps()["aria-expanded"] === true,
1530
+ descriptionProps: itemAria.descriptionProps,
1531
+ keyboardShortcutProps: itemAria.keyboardShortcutProps,
1367
1532
  };
1368
1533
  });
1369
1534
 
1370
1535
  const renderProps = useRenderProps(
1371
1536
  {
1372
- children: props.children,
1537
+ get children() {
1538
+ return props.children;
1539
+ },
1373
1540
  class: local.class,
1374
1541
  style: local.style,
1375
1542
  defaultClassName: "solidaria-Menu-item",
@@ -1381,11 +1548,13 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1381
1548
  };
1382
1549
 
1383
1550
  const cleanItemProps = () => {
1384
- const {
1385
- ref: _ref1,
1386
- "aria-describedby": _ariaDescribedby,
1387
- ...rest
1388
- } = itemAria.menuItemProps as Record<string, unknown>;
1551
+ // Keep `aria-describedby`: the headless emits it (via `createSlotId`) only
1552
+ // when a description/keyboard element is actually rendered, so it points at
1553
+ // the styled `Text slot="description"` + `Keyboard` ids threaded through
1554
+ // `renderValues` below — restoring upstream's two-context description
1555
+ // delegation. `createSlotId` clears it to `undefined` on description-less
1556
+ // items, so nothing dangles.
1557
+ const { ref: _ref1, ...rest } = itemAria.menuItemProps as Record<string, unknown>;
1389
1558
  if (!hasPrimitiveLabel() && rest["aria-label"] == null) {
1390
1559
  delete rest["aria-labelledby"];
1391
1560
  }
@@ -1476,7 +1645,7 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1476
1645
  ) : (
1477
1646
  renderProps.renderChildren()
1478
1647
  );
1479
- const setResolvedItemRef = (el: HTMLLIElement | null) => {
1648
+ const setResolvedItemRef = (el: HTMLElement | null) => {
1480
1649
  setRef(el);
1481
1650
  itemContext?.setItemRef?.(el);
1482
1651
  if (el) assignRef(local.ref, el);
@@ -1496,7 +1665,7 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1496
1665
  style: renderProps.style(),
1497
1666
  ...dataAttrs(),
1498
1667
  children: childContent(),
1499
- }) as JSX.HTMLAttributes<HTMLLIElement>;
1668
+ }) as JSX.HTMLAttributes<HTMLElement>;
1500
1669
  const linkMenuItemProps = () =>
1501
1670
  ({
1502
1671
  ...mergeProps(
@@ -1512,7 +1681,7 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1512
1681
  style: renderProps.style(),
1513
1682
  ...dataAttrs(),
1514
1683
  children: childContent(),
1515
- }) as JSX.HTMLAttributes<HTMLLIElement>;
1684
+ }) as JSX.HTMLAttributes<HTMLElement>;
1516
1685
 
1517
1686
  if (local.render && !isLink()) {
1518
1687
  return local.render(menuItemProps(), renderValues());
@@ -1522,7 +1691,7 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1522
1691
  <Show
1523
1692
  when={isLink()}
1524
1693
  fallback={
1525
- <li
1694
+ <div
1526
1695
  ref={(el) => {
1527
1696
  setRef(el);
1528
1697
  itemContext?.setItemRef?.(el);
@@ -1541,38 +1710,36 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
1541
1710
  {...dataAttrs()}
1542
1711
  >
1543
1712
  {childContent()}
1544
- </li>
1713
+ </div>
1545
1714
  }
1546
1715
  >
1547
- <li
1548
- ref={(el) => {
1549
- setRef(el);
1550
- itemContext?.setItemRef?.(el);
1551
- assignRef(local.ref, el);
1552
- }}
1553
- role="presentation"
1554
- >
1555
- {local.render ? (
1556
- local.render(linkMenuItemProps(), renderValues())
1557
- ) : (
1558
- <a
1559
- {...mergeProps(
1560
- cleanItemPropsForLink(),
1561
- contextProps() as Record<string, unknown>,
1562
- domProps(),
1563
- cleanHoverProps(),
1564
- linkDomProps(),
1565
- (draggableItem()?.dragProps as Record<string, unknown> | undefined) ?? {},
1566
- (droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {},
1567
- )}
1568
- class={renderProps.class()}
1569
- style={renderProps.style()}
1570
- {...dataAttrs()}
1571
- >
1572
- {childContent()}
1573
- </a>
1574
- )}
1575
- </li>
1716
+ {local.render ? (
1717
+ local.render(linkMenuItemProps(), renderValues())
1718
+ ) : (
1719
+ // Upstream renders the link menuitem as a bare <a role="menuitem"> (no
1720
+ // presentation wrapper); the <div> root makes the wrapper unnecessary.
1721
+ <a
1722
+ ref={(el) => {
1723
+ setRef(el);
1724
+ itemContext?.setItemRef?.(el);
1725
+ assignRef(local.ref, el);
1726
+ }}
1727
+ {...mergeProps(
1728
+ cleanItemPropsForLink(),
1729
+ contextProps() as Record<string, unknown>,
1730
+ domProps(),
1731
+ cleanHoverProps(),
1732
+ linkDomProps(),
1733
+ (draggableItem()?.dragProps as Record<string, unknown> | undefined) ?? {},
1734
+ (droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {},
1735
+ )}
1736
+ class={renderProps.class()}
1737
+ style={renderProps.style()}
1738
+ {...dataAttrs()}
1739
+ >
1740
+ {childContent()}
1741
+ </a>
1742
+ )}
1576
1743
  </Show>
1577
1744
  );
1578
1745
  }