@navikt/ds-react 7.2.0 → 7.3.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 (294) hide show
  1. package/cjs/accordion/AccordionHeader.js +1 -1
  2. package/cjs/accordion/AccordionHeader.js.map +1 -1
  3. package/cjs/alert/Alert.d.ts +0 -3
  4. package/cjs/alert/Alert.js +11 -17
  5. package/cjs/alert/Alert.js.map +1 -1
  6. package/cjs/chips/Removable.d.ts +5 -5
  7. package/cjs/chips/Removable.js +4 -2
  8. package/cjs/chips/Removable.js.map +1 -1
  9. package/cjs/collapsible/Collapsible.context.d.ts +1 -1
  10. package/cjs/date/datepicker/DatePicker.d.ts +2 -2
  11. package/cjs/date/monthpicker/MonthPicker.d.ts +2 -2
  12. package/cjs/dropdown/Menu/index.js +1 -1
  13. package/cjs/dropdown/Menu/index.js.map +1 -1
  14. package/cjs/form/checkbox/useCheckbox.js +3 -2
  15. package/cjs/form/checkbox/useCheckbox.js.map +1 -1
  16. package/cjs/form/combobox/ComboboxProvider.js +4 -1
  17. package/cjs/form/combobox/ComboboxProvider.js.map +1 -1
  18. package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js +1 -1
  19. package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
  20. package/cjs/form/combobox/FilteredOptions/useVirtualFocus.d.ts +3 -0
  21. package/cjs/form/combobox/FilteredOptions/useVirtualFocus.js +33 -10
  22. package/cjs/form/combobox/FilteredOptions/useVirtualFocus.js.map +1 -1
  23. package/cjs/form/combobox/Input/Input.js +23 -10
  24. package/cjs/form/combobox/Input/Input.js.map +1 -1
  25. package/cjs/form/file-upload/FileUpload.context.d.ts +1 -1
  26. package/cjs/form/radio/useRadio.js +3 -2
  27. package/cjs/form/radio/useRadio.js.map +1 -1
  28. package/cjs/form/search/Search.js +1 -1
  29. package/cjs/form/search/Search.js.map +1 -1
  30. package/cjs/form/switch/Switch.js +2 -1
  31. package/cjs/form/switch/Switch.js.map +1 -1
  32. package/cjs/index.d.ts +1 -0
  33. package/cjs/index.js +4 -2
  34. package/cjs/index.js.map +1 -1
  35. package/cjs/layout/base/BasePrimitive.d.ts +3 -0
  36. package/cjs/layout/base/BasePrimitive.js.map +1 -1
  37. package/cjs/layout/box/Box.d.ts +2 -2
  38. package/cjs/layout/box/Box.js.map +1 -1
  39. package/cjs/layout/grid/HGrid.d.ts +2 -2
  40. package/cjs/layout/grid/HGrid.js.map +1 -1
  41. package/cjs/layout/stack/Stack.d.ts +2 -2
  42. package/cjs/layout/stack/Stack.js.map +1 -1
  43. package/cjs/modal/ModalHeader.js +6 -1
  44. package/cjs/modal/ModalHeader.js.map +1 -1
  45. package/cjs/modal/dialog-polyfill.js +2 -2
  46. package/cjs/modal/dialog-polyfill.js.map +1 -1
  47. package/cjs/overlays/action-menu/ActionMenu.d.ts +310 -0
  48. package/cjs/overlays/action-menu/ActionMenu.js +227 -0
  49. package/cjs/overlays/action-menu/ActionMenu.js.map +1 -0
  50. package/cjs/overlays/action-menu/index.d.ts +1 -0
  51. package/cjs/overlays/action-menu/index.js +19 -0
  52. package/cjs/overlays/action-menu/index.js.map +1 -0
  53. package/cjs/overlays/floating/Floating.js +9 -10
  54. package/cjs/overlays/floating/Floating.js.map +1 -1
  55. package/cjs/overlays/floating/Floating.utils.d.ts +3 -5
  56. package/cjs/overlays/floating/Floating.utils.js +0 -2
  57. package/cjs/overlays/floating/Floating.utils.js.map +1 -1
  58. package/cjs/overlays/floating-menu/Menu.d.ts +15 -21
  59. package/cjs/overlays/floating-menu/Menu.js +119 -230
  60. package/cjs/overlays/floating-menu/Menu.js.map +1 -1
  61. package/cjs/overlays/floating-menu/parts/RovingFocus.d.ts +1 -1
  62. package/cjs/overlays/floating-menu/parts/RovingFocus.js +4 -4
  63. package/cjs/overlays/floating-menu/parts/RovingFocus.js.map +1 -1
  64. package/cjs/pagination/Pagination.d.ts +1 -6
  65. package/cjs/pagination/Pagination.js.map +1 -1
  66. package/cjs/provider/i18n/LanguageProvider.d.ts +3 -3
  67. package/cjs/stepper/context.d.ts +1 -1
  68. package/cjs/table/Body.d.ts +2 -4
  69. package/cjs/table/Body.js.map +1 -1
  70. package/cjs/table/ColumnHeader.d.ts +1 -2
  71. package/cjs/table/ColumnHeader.js.map +1 -1
  72. package/cjs/table/ExpandableRow.d.ts +1 -2
  73. package/cjs/table/ExpandableRow.js.map +1 -1
  74. package/cjs/table/Header.d.ts +2 -4
  75. package/cjs/table/Header.js.map +1 -1
  76. package/cjs/table/HeaderCell.d.ts +1 -2
  77. package/cjs/table/HeaderCell.js.map +1 -1
  78. package/cjs/table/Row.d.ts +1 -2
  79. package/cjs/table/Row.js.map +1 -1
  80. package/cjs/tabs/Tabs.context.d.ts +1 -1
  81. package/cjs/tabs/parts/tablist/useScrollButtons.js +1 -1
  82. package/cjs/tabs/parts/tablist/useScrollButtons.js.map +1 -1
  83. package/cjs/tabs/parts/tablist/useTabList.js +4 -4
  84. package/cjs/tabs/parts/tablist/useTabList.js.map +1 -1
  85. package/cjs/timeline/TimelineRow.js +9 -10
  86. package/cjs/timeline/TimelineRow.js.map +1 -1
  87. package/cjs/toggle-group/ToggleGroup.context.d.ts +1 -1
  88. package/cjs/toggle-group/parts/useToggleItem.js +4 -4
  89. package/cjs/toggle-group/parts/useToggleItem.js.map +1 -1
  90. package/cjs/util/TextareaAutoSize.js +2 -2
  91. package/cjs/util/TextareaAutoSize.js.map +1 -1
  92. package/cjs/util/hooks/descendants/descendant.js +1 -1
  93. package/cjs/util/hooks/descendants/descendant.js.map +1 -1
  94. package/cjs/util/hooks/descendants/useDescendant.js +1 -1
  95. package/cjs/util/hooks/descendants/useDescendant.js.map +1 -1
  96. package/cjs/util/i18n/get.d.ts +2 -2
  97. package/cjs/util/i18n/get.js.map +1 -1
  98. package/cjs/util/i18n/i18n.context.d.ts +2 -3
  99. package/cjs/util/i18n/i18n.context.js.map +1 -1
  100. package/cjs/util/i18n/i18n.types.d.ts +5 -9
  101. package/cjs/util/i18n/locales/en.d.ts +39 -0
  102. package/cjs/util/i18n/locales/en.js +41 -0
  103. package/cjs/util/i18n/locales/en.js.map +1 -0
  104. package/cjs/util/i18n/locales/nb.d.ts +14 -0
  105. package/cjs/util/i18n/locales/nb.js +14 -0
  106. package/cjs/util/i18n/locales/nb.js.map +1 -1
  107. package/cjs/util/i18n/locales/nn.d.ts +39 -0
  108. package/cjs/util/i18n/locales/nn.js +41 -0
  109. package/cjs/util/i18n/locales/nn.js.map +1 -0
  110. package/cjs/util/requireReactElement.d.ts +2 -0
  111. package/cjs/util/requireReactElement.js +22 -0
  112. package/cjs/util/requireReactElement.js.map +1 -0
  113. package/cjs/util/virtualfocus/Context.d.ts +1 -1
  114. package/cjs/util/virtualfocus/parts/VirtualFocusContent.d.ts +1 -2
  115. package/cjs/util/virtualfocus/parts/VirtualFocusContent.js.map +1 -1
  116. package/esm/accordion/AccordionHeader.js +1 -1
  117. package/esm/accordion/AccordionHeader.js.map +1 -1
  118. package/esm/alert/Alert.d.ts +0 -3
  119. package/esm/alert/Alert.js +11 -17
  120. package/esm/alert/Alert.js.map +1 -1
  121. package/esm/chips/Removable.d.ts +5 -5
  122. package/esm/chips/Removable.js +4 -2
  123. package/esm/chips/Removable.js.map +1 -1
  124. package/esm/collapsible/Collapsible.context.d.ts +1 -1
  125. package/esm/date/datepicker/DatePicker.d.ts +2 -2
  126. package/esm/date/monthpicker/MonthPicker.d.ts +2 -2
  127. package/esm/dropdown/Menu/index.js +1 -1
  128. package/esm/dropdown/Menu/index.js.map +1 -1
  129. package/esm/form/checkbox/useCheckbox.js +3 -2
  130. package/esm/form/checkbox/useCheckbox.js.map +1 -1
  131. package/esm/form/combobox/ComboboxProvider.js +4 -1
  132. package/esm/form/combobox/ComboboxProvider.js.map +1 -1
  133. package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js +1 -1
  134. package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
  135. package/esm/form/combobox/FilteredOptions/useVirtualFocus.d.ts +3 -0
  136. package/esm/form/combobox/FilteredOptions/useVirtualFocus.js +34 -11
  137. package/esm/form/combobox/FilteredOptions/useVirtualFocus.js.map +1 -1
  138. package/esm/form/combobox/Input/Input.js +23 -10
  139. package/esm/form/combobox/Input/Input.js.map +1 -1
  140. package/esm/form/file-upload/FileUpload.context.d.ts +1 -1
  141. package/esm/form/radio/useRadio.js +3 -2
  142. package/esm/form/radio/useRadio.js.map +1 -1
  143. package/esm/form/search/Search.js +1 -1
  144. package/esm/form/search/Search.js.map +1 -1
  145. package/esm/form/switch/Switch.js +2 -1
  146. package/esm/form/switch/Switch.js.map +1 -1
  147. package/esm/index.d.ts +1 -0
  148. package/esm/index.js +1 -0
  149. package/esm/index.js.map +1 -1
  150. package/esm/layout/base/BasePrimitive.d.ts +3 -0
  151. package/esm/layout/base/BasePrimitive.js.map +1 -1
  152. package/esm/layout/box/Box.d.ts +2 -2
  153. package/esm/layout/box/Box.js.map +1 -1
  154. package/esm/layout/grid/HGrid.d.ts +2 -2
  155. package/esm/layout/grid/HGrid.js.map +1 -1
  156. package/esm/layout/stack/Stack.d.ts +2 -2
  157. package/esm/layout/stack/Stack.js.map +1 -1
  158. package/esm/modal/ModalHeader.js +6 -1
  159. package/esm/modal/ModalHeader.js.map +1 -1
  160. package/esm/modal/dialog-polyfill.js +2 -2
  161. package/esm/modal/dialog-polyfill.js.map +1 -1
  162. package/esm/overlays/action-menu/ActionMenu.d.ts +310 -0
  163. package/esm/overlays/action-menu/ActionMenu.js +197 -0
  164. package/esm/overlays/action-menu/ActionMenu.js.map +1 -0
  165. package/esm/overlays/action-menu/index.d.ts +1 -0
  166. package/esm/overlays/action-menu/index.js +3 -0
  167. package/esm/overlays/action-menu/index.js.map +1 -0
  168. package/esm/overlays/floating/Floating.js +9 -10
  169. package/esm/overlays/floating/Floating.js.map +1 -1
  170. package/esm/overlays/floating/Floating.utils.d.ts +3 -5
  171. package/esm/overlays/floating/Floating.utils.js +0 -2
  172. package/esm/overlays/floating/Floating.utils.js.map +1 -1
  173. package/esm/overlays/floating-menu/Menu.d.ts +15 -21
  174. package/esm/overlays/floating-menu/Menu.js +119 -230
  175. package/esm/overlays/floating-menu/Menu.js.map +1 -1
  176. package/esm/overlays/floating-menu/parts/RovingFocus.d.ts +1 -1
  177. package/esm/overlays/floating-menu/parts/RovingFocus.js +4 -4
  178. package/esm/overlays/floating-menu/parts/RovingFocus.js.map +1 -1
  179. package/esm/pagination/Pagination.d.ts +1 -6
  180. package/esm/pagination/Pagination.js.map +1 -1
  181. package/esm/provider/i18n/LanguageProvider.d.ts +3 -3
  182. package/esm/stepper/context.d.ts +1 -1
  183. package/esm/table/Body.d.ts +2 -4
  184. package/esm/table/Body.js.map +1 -1
  185. package/esm/table/ColumnHeader.d.ts +1 -2
  186. package/esm/table/ColumnHeader.js.map +1 -1
  187. package/esm/table/ExpandableRow.d.ts +1 -2
  188. package/esm/table/ExpandableRow.js.map +1 -1
  189. package/esm/table/Header.d.ts +2 -4
  190. package/esm/table/Header.js.map +1 -1
  191. package/esm/table/HeaderCell.d.ts +1 -2
  192. package/esm/table/HeaderCell.js.map +1 -1
  193. package/esm/table/Row.d.ts +1 -2
  194. package/esm/table/Row.js.map +1 -1
  195. package/esm/tabs/Tabs.context.d.ts +1 -1
  196. package/esm/tabs/parts/tablist/useScrollButtons.js +1 -1
  197. package/esm/tabs/parts/tablist/useScrollButtons.js.map +1 -1
  198. package/esm/tabs/parts/tablist/useTabList.js +4 -4
  199. package/esm/tabs/parts/tablist/useTabList.js.map +1 -1
  200. package/esm/timeline/TimelineRow.js +9 -10
  201. package/esm/timeline/TimelineRow.js.map +1 -1
  202. package/esm/toggle-group/ToggleGroup.context.d.ts +1 -1
  203. package/esm/toggle-group/parts/useToggleItem.js +4 -4
  204. package/esm/toggle-group/parts/useToggleItem.js.map +1 -1
  205. package/esm/util/TextareaAutoSize.js +2 -2
  206. package/esm/util/TextareaAutoSize.js.map +1 -1
  207. package/esm/util/hooks/descendants/descendant.js +1 -1
  208. package/esm/util/hooks/descendants/descendant.js.map +1 -1
  209. package/esm/util/hooks/descendants/useDescendant.js +1 -1
  210. package/esm/util/hooks/descendants/useDescendant.js.map +1 -1
  211. package/esm/util/i18n/get.d.ts +2 -2
  212. package/esm/util/i18n/get.js.map +1 -1
  213. package/esm/util/i18n/i18n.context.d.ts +2 -3
  214. package/esm/util/i18n/i18n.context.js.map +1 -1
  215. package/esm/util/i18n/i18n.types.d.ts +5 -9
  216. package/esm/util/i18n/locales/en.d.ts +39 -0
  217. package/esm/util/i18n/locales/en.js +39 -0
  218. package/esm/util/i18n/locales/en.js.map +1 -0
  219. package/esm/util/i18n/locales/nb.d.ts +14 -0
  220. package/esm/util/i18n/locales/nb.js +14 -0
  221. package/esm/util/i18n/locales/nb.js.map +1 -1
  222. package/esm/util/i18n/locales/nn.d.ts +39 -0
  223. package/esm/util/i18n/locales/nn.js +39 -0
  224. package/esm/util/i18n/locales/nn.js.map +1 -0
  225. package/esm/util/requireReactElement.d.ts +2 -0
  226. package/esm/util/requireReactElement.js +15 -0
  227. package/esm/util/requireReactElement.js.map +1 -0
  228. package/esm/util/virtualfocus/Context.d.ts +1 -1
  229. package/esm/util/virtualfocus/parts/VirtualFocusContent.d.ts +1 -2
  230. package/esm/util/virtualfocus/parts/VirtualFocusContent.js.map +1 -1
  231. package/package.json +15 -7
  232. package/src/accordion/AccordionHeader.tsx +0 -1
  233. package/src/alert/Alert.tsx +11 -20
  234. package/src/chips/Removable.tsx +13 -9
  235. package/src/date/datepicker/DatePicker.tsx +2 -2
  236. package/src/date/monthpicker/MonthPicker.tsx +2 -2
  237. package/src/dropdown/Menu/index.tsx +1 -1
  238. package/src/form/checkbox/Checkbox.test.tsx +2 -3
  239. package/src/form/checkbox/useCheckbox.ts +2 -2
  240. package/src/form/combobox/ComboboxProvider.tsx +9 -1
  241. package/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx +1 -1
  242. package/src/form/combobox/FilteredOptions/useVirtualFocus.ts +42 -11
  243. package/src/form/combobox/Input/Input.tsx +19 -10
  244. package/src/form/combobox/__tests__/combobox.test.tsx +36 -0
  245. package/src/form/confirmation-panel/ConfirmationPanel.test.tsx +1 -2
  246. package/src/form/radio/Radio.test.tsx +4 -5
  247. package/src/form/radio/useRadio.ts +2 -2
  248. package/src/form/search/Search.tsx +1 -1
  249. package/src/form/switch/Switch.tsx +1 -1
  250. package/src/index.ts +1 -0
  251. package/src/layout/base/BasePrimitive.tsx +3 -0
  252. package/src/layout/box/Box.tsx +35 -36
  253. package/src/layout/grid/HGrid.tsx +26 -27
  254. package/src/layout/stack/Stack.tsx +53 -54
  255. package/src/modal/ModalHeader.tsx +6 -0
  256. package/src/modal/dialog-polyfill.ts +2 -2
  257. package/src/overlays/action-menu/ActionMenu.tsx +971 -0
  258. package/src/overlays/action-menu/index.ts +29 -0
  259. package/src/overlays/floating/Floating.tsx +6 -12
  260. package/src/overlays/floating/Floating.utils.ts +2 -5
  261. package/src/overlays/floating-menu/Menu.tsx +183 -332
  262. package/src/overlays/floating-menu/parts/RovingFocus.tsx +7 -7
  263. package/src/pagination/Pagination.tsx +4 -1
  264. package/src/pagination/steps.test.ts +15 -16
  265. package/src/provider/i18n/LanguageProvider.tsx +3 -3
  266. package/src/table/Body.tsx +4 -6
  267. package/src/table/ColumnHeader.tsx +3 -4
  268. package/src/table/ExpandableRow.tsx +3 -4
  269. package/src/table/Header.tsx +4 -6
  270. package/src/table/HeaderCell.tsx +3 -4
  271. package/src/table/Row.tsx +3 -4
  272. package/src/tabs/parts/tablist/useScrollButtons.ts +1 -1
  273. package/src/tabs/parts/tablist/useTabList.ts +4 -4
  274. package/src/timeline/TimelineRow.tsx +20 -21
  275. package/src/toggle-group/parts/useToggleItem.ts +4 -4
  276. package/src/util/TextareaAutoSize.tsx +2 -2
  277. package/src/util/hooks/descendants/descendant.ts +1 -1
  278. package/src/util/hooks/descendants/useDescendant.tsx +1 -1
  279. package/src/util/i18n/get.ts +3 -3
  280. package/src/util/i18n/i18n.context.ts +2 -3
  281. package/src/util/i18n/i18n.types.ts +7 -11
  282. package/src/util/i18n/locales/en.ts +40 -0
  283. package/src/util/i18n/locales/nb.ts +23 -1
  284. package/src/util/i18n/locales/nn.ts +40 -0
  285. package/src/util/i18n/locales.test.tsx +23 -0
  286. package/src/util/requireReactElement.ts +25 -0
  287. package/src/util/virtualfocus/parts/VirtualFocusContent.tsx +4 -2
  288. package/cjs/util/i18n/merge.d.ts +0 -2
  289. package/cjs/util/i18n/merge.js +0 -28
  290. package/cjs/util/i18n/merge.js.map +0 -1
  291. package/esm/util/i18n/merge.d.ts +0 -2
  292. package/esm/util/i18n/merge.js +0 -25
  293. package/esm/util/i18n/merge.js.map +0 -1
  294. package/src/util/i18n/merge.ts +0 -35
@@ -1,10 +1,4 @@
1
- import React, {
2
- forwardRef,
3
- useCallback,
4
- useEffect,
5
- useRef,
6
- useState,
7
- } from "react";
1
+ import React, { forwardRef, useEffect, useRef, useState } from "react";
8
2
  import ReactDOM from "react-dom";
9
3
  import { Portal } from "../../portal";
10
4
  import { composeEventHandlers } from "../../util/composeEventHandlers";
@@ -24,17 +18,10 @@ import {
24
18
  /* -------------------------------------------------------------------------- */
25
19
  /* Constants */
26
20
  /* -------------------------------------------------------------------------- */
27
- const SELECTION_KEYS = ["Enter", " "];
28
- const SUB_OPEN_KEYS = [...SELECTION_KEYS, "ArrowRight"];
29
- const SUB_CLOSE_KEYS = ["ArrowLeft"];
30
21
  const FIRST_KEYS = ["ArrowDown", "PageUp", "Home"];
31
22
  const LAST_KEYS = ["ArrowUp", "PageDown", "End"];
32
23
  const FIRST_LAST_KEYS = [...FIRST_KEYS, ...LAST_KEYS];
33
24
 
34
- type Point = { x: number; y: number };
35
- type Polygon = Point[];
36
- type SubMenuSide = "left" | "right";
37
- type GraceIntent = { area: Polygon; side: SubMenuSide };
38
25
  type CheckedState = boolean | "indeterminate";
39
26
 
40
27
  /* -------------------------------------------------------------------------- */
@@ -56,7 +43,7 @@ interface MenuComponent extends React.FC<MenuProps> {
56
43
  CheckboxItem: typeof MenuCheckboxItem;
57
44
  RadioGroup: typeof MenuRadioGroup;
58
45
  RadioItem: typeof MenuRadioItem;
59
- Separator: typeof MenuSeparator;
46
+ Divider: typeof MenuDivider;
60
47
  Sub: typeof MenuSub;
61
48
  SubTrigger: typeof MenuSubTrigger;
62
49
  SubContent: typeof MenuSubContent;
@@ -68,7 +55,12 @@ const [
68
55
  useMenuDescendantsContext,
69
56
  useMenuDescendants,
70
57
  useMenuDescendant,
71
- ] = createDescendantContext<SlottedDivElementRef>();
58
+ ] = createDescendantContext<
59
+ SlottedDivElementRef,
60
+ {
61
+ closeMenu: () => void;
62
+ }
63
+ >();
72
64
 
73
65
  type MenuContentElementRef = React.ElementRef<typeof Floating.Content>;
74
66
 
@@ -180,22 +172,8 @@ const MenuAnchor = forwardRef<MenuAnchorElement, MenuAnchorProps>(
180
172
  /* -------------------------------------------------------------------------- */
181
173
  /* Menu Content */
182
174
  /* -------------------------------------------------------------------------- */
183
- type MenuContentContextValue = {
184
- onItemEnter: (event: React.PointerEvent) => void;
185
- onItemLeave: (event: React.PointerEvent) => void;
186
- onPointerLeaveTrigger: (event: React.PointerEvent) => void;
187
- pointerGraceTimerRef: React.MutableRefObject<number>;
188
- onPointerGraceIntentChange: (intent: GraceIntent | null) => void;
189
- };
190
-
191
- const [MenuContentProvider, useMenuContentContext] =
192
- createContext<MenuContentContextValue>({
193
- providerName: "MenuContentProvider",
194
- hookName: "useMenuContentContext",
195
- });
196
-
197
175
  type MenuContentElement = MenuContentInternalElement;
198
- interface MenuContentProps extends MenuContentInternalTypeProps {}
176
+ type MenuContentProps = MenuContentInternalTypeProps;
199
177
 
200
178
  const MenuContent = React.forwardRef<
201
179
  MenuContentInternalElement,
@@ -286,6 +264,7 @@ interface MenuContentInternalProps
286
264
  onPointerDownOutside?: DismissableLayerProps["onPointerDownOutside"];
287
265
  onFocusOutside?: DismissableLayerProps["onFocusOutside"];
288
266
  onInteractOutside?: DismissableLayerProps["onInteractOutside"];
267
+ safeZone?: DismissableLayerProps["safeZone"];
289
268
  }
290
269
 
291
270
  const MenuContentInternal = forwardRef<
@@ -303,6 +282,7 @@ const MenuContentInternal = forwardRef<
303
282
  onFocusOutside,
304
283
  onInteractOutside,
305
284
  onDismiss,
285
+ safeZone,
306
286
  ...rest
307
287
  }: MenuContentInternalProps,
308
288
  forwardedRef,
@@ -318,149 +298,80 @@ const MenuContentInternal = forwardRef<
318
298
  contentRef,
319
299
  context.onContentChange,
320
300
  );
321
- const pointerGraceTimerRef = React.useRef(0);
322
- const pointerGraceIntentRef = React.useRef<GraceIntent | null>(null);
323
- const pointerDirRef = React.useRef<SubMenuSide>("right");
324
- const lastPointerXRef = React.useRef(0);
325
-
326
- const isPointerMovingToSubmenu = React.useCallback(
327
- (event: React.PointerEvent) => {
328
- const isMovingTowards =
329
- pointerDirRef.current === pointerGraceIntentRef.current?.side;
330
- return (
331
- isMovingTowards &&
332
- isPointerInGraceArea(event, pointerGraceIntentRef.current?.area)
333
- );
334
- },
335
- [],
336
- );
337
301
 
338
302
  return (
339
- <MenuContentProvider
340
- onItemEnter={React.useCallback(
341
- (event) => {
342
- if (isPointerMovingToSubmenu(event)) event.preventDefault();
343
- },
344
- [isPointerMovingToSubmenu],
345
- )}
346
- onItemLeave={React.useCallback(
347
- (event) => {
348
- if (isPointerMovingToSubmenu(event)) return;
349
-
350
- /**
351
- * Resets focus from current active item to content area
352
- * This is to prevent focus from being stuck on an item when we move pointer outside the menu or onto a disabled item
353
- */
354
- contentRef.current?.focus();
355
- },
356
- [isPointerMovingToSubmenu],
357
- )}
358
- onPointerLeaveTrigger={React.useCallback(
359
- (event) => {
360
- if (isPointerMovingToSubmenu(event)) event.preventDefault();
361
- },
362
- [isPointerMovingToSubmenu],
363
- )}
364
- pointerGraceTimerRef={pointerGraceTimerRef}
365
- onPointerGraceIntentChange={React.useCallback((intent) => {
366
- pointerGraceIntentRef.current = intent;
367
- }, [])}
303
+ <FocusScope
304
+ onMountHandler={composeEventHandlers(onOpenAutoFocus, (event) => {
305
+ // when opening, explicitly focus the content area only and leave
306
+ // `onEntryFocus` in control of focusing first item
307
+ event.preventDefault();
308
+ contentRef.current?.focus({ preventScroll: true });
309
+ })}
310
+ onUnmountHandler={onCloseAutoFocus}
368
311
  >
369
- <FocusScope
370
- onMountHandler={composeEventHandlers(onOpenAutoFocus, (event) => {
371
- // when opening, explicitly focus the content area only and leave
372
- // `onEntryFocus` in control of focusing first item
373
- event.preventDefault();
374
- contentRef.current?.focus({ preventScroll: true });
375
- })}
376
- onUnmountHandler={onCloseAutoFocus}
312
+ <DismissableLayer
313
+ asChild
314
+ disableOutsidePointerEvents={disableOutsidePointerEvents}
315
+ onEscapeKeyDown={onEscapeKeyDown}
316
+ onPointerDownOutside={onPointerDownOutside}
317
+ onFocusOutside={onFocusOutside}
318
+ onInteractOutside={onInteractOutside}
319
+ onDismiss={onDismiss}
320
+ safeZone={safeZone}
377
321
  >
378
- <DismissableLayer
322
+ <RovingFocus
379
323
  asChild
380
- disableOutsidePointerEvents={disableOutsidePointerEvents}
381
- onEscapeKeyDown={onEscapeKeyDown}
382
- onPointerDownOutside={onPointerDownOutside}
383
- onFocusOutside={onFocusOutside}
384
- onInteractOutside={onInteractOutside}
385
- onDismiss={onDismiss}
324
+ descendants={descendants}
325
+ onEntryFocus={composeEventHandlers(onEntryFocus, (event) => {
326
+ // only focus first item when using keyboard
327
+ if (!rootContext.isUsingKeyboardRef.current)
328
+ event.preventDefault();
329
+ })}
386
330
  >
387
- <RovingFocus
388
- asChild
389
- descendants={descendants}
390
- onEntryFocus={composeEventHandlers(onEntryFocus, (event) => {
391
- // only focus first item when using keyboard
392
- if (!rootContext.isUsingKeyboardRef.current)
393
- event.preventDefault();
331
+ <Floating.Content
332
+ role="menu"
333
+ aria-orientation="vertical"
334
+ data-state={getOpenState(context.open)}
335
+ data-aksel-menu-content=""
336
+ dir="ltr"
337
+ {...rest}
338
+ ref={composedRefs}
339
+ style={{ outline: "none", ...rest.style }}
340
+ onKeyDown={composeEventHandlers(rest.onKeyDown, (event) => {
341
+ // submenu key events bubble through portals. We only care about keys in this menu.
342
+ const target = event.target as HTMLElement;
343
+ const isKeyDownInside =
344
+ target.closest("[data-aksel-menu-content]") ===
345
+ event.currentTarget;
346
+ if (isKeyDownInside) {
347
+ // menus should not be navigated using tab key so we prevent it
348
+ if (event.key === "Tab") event.preventDefault();
349
+ }
350
+
351
+ // focus first/last item based on key pressed
352
+ const content = contentRef.current;
353
+ if (event.target !== content) return;
354
+ if (!FIRST_LAST_KEYS.includes(event.key)) return;
355
+ event.preventDefault();
356
+
357
+ if (LAST_KEYS.includes(event.key)) {
358
+ descendants.lastEnabled()?.node?.focus();
359
+ return;
360
+ }
361
+ descendants.firstEnabled()?.node?.focus();
394
362
  })}
395
- >
396
- <Floating.Content
397
- role="menu"
398
- aria-orientation="vertical"
399
- data-state={getOpenState(context.open)}
400
- data-aksel-menu-content=""
401
- dir="ltr"
402
- {...rest}
403
- ref={composedRefs}
404
- style={{ outline: "none", ...rest.style }}
405
- onKeyDown={composeEventHandlers(rest.onKeyDown, (event) => {
406
- // submenu key events bubble through portals. We only care about keys in this menu.
407
- const target = event.target as HTMLElement;
408
- const isKeyDownInside =
409
- target.closest("[data-aksel-menu-content]") ===
410
- event.currentTarget;
411
- if (isKeyDownInside) {
412
- // menus should not be navigated using tab key so we prevent it
413
- if (event.key === "Tab") event.preventDefault();
414
- }
415
-
416
- // focus first/last item based on key pressed
417
- const content = contentRef.current;
418
- if (event.target !== content) return;
419
- if (!FIRST_LAST_KEYS.includes(event.key)) return;
420
- event.preventDefault();
421
-
422
- if (LAST_KEYS.includes(event.key)) {
423
- descendants.lastEnabled()?.node?.focus();
424
- return;
425
- }
426
- descendants.firstEnabled()?.node?.focus();
427
- })}
428
- onPointerMove={composeEventHandlers(
429
- rest.onPointerMove,
430
- whenMouse((event) => {
431
- const target = event.target as HTMLElement;
432
- const pointerXHasChanged =
433
- lastPointerXRef.current !== event.clientX;
434
-
435
- // We don't use `event.movementX` for this check because Safari will
436
- // always return `0` on a pointer event.
437
- if (
438
- event.currentTarget.contains(target) &&
439
- pointerXHasChanged
440
- ) {
441
- const newDir =
442
- event.clientX > lastPointerXRef.current
443
- ? "right"
444
- : "left";
445
- pointerDirRef.current = newDir;
446
- lastPointerXRef.current = event.clientX;
447
- }
448
- }),
449
- )}
450
- />
451
- </RovingFocus>
452
- </DismissableLayer>
453
- </FocusScope>
454
- </MenuContentProvider>
363
+ />
364
+ </RovingFocus>
365
+ </DismissableLayer>
366
+ </FocusScope>
455
367
  );
456
368
  },
457
369
  );
458
370
 
459
- interface MenuContentInternalTypeProps
460
- extends Omit<
461
- MenuContentInternalProps,
462
- keyof MenuContentInternalPrivateProps
463
- > {}
371
+ type MenuContentInternalTypeProps = Omit<
372
+ MenuContentInternalProps,
373
+ keyof MenuContentInternalPrivateProps
374
+ >;
464
375
 
465
376
  /* -------------------------------------------------------------------------- */
466
377
  /* Menu item */
@@ -482,6 +393,7 @@ const MenuItem = forwardRef<MenuItemElement, MenuItemProps>(
482
393
  onPointerUp,
483
394
  onPointerDown,
484
395
  onKeyDown,
396
+ onKeyUp,
485
397
  ...rest
486
398
  }: MenuItemProps,
487
399
  forwardedRef,
@@ -510,6 +422,28 @@ const MenuItem = forwardRef<MenuItemElement, MenuItemProps>(
510
422
  } else {
511
423
  rootContext.onClose();
512
424
  }
425
+ } else if (!disabled && menuItem) {
426
+ rootContext.onClose();
427
+ }
428
+ };
429
+
430
+ const handleKey = (
431
+ event: React.KeyboardEvent<HTMLDivElement>,
432
+ key: "Enter" | " ",
433
+ ) => {
434
+ if (disabled || event.repeat) {
435
+ return;
436
+ }
437
+
438
+ if (key === event.key) {
439
+ event.currentTarget.click();
440
+ /**
441
+ * We prevent default browser behaviour for selection keys as they should only trigger
442
+ * selection.
443
+ * - Prevents space from scrolling the page.
444
+ * - If keydown causes focus to move, prevents keydown from firing on the new target.
445
+ */
446
+ event.preventDefault();
513
447
  }
514
448
  };
515
449
 
@@ -519,7 +453,13 @@ const MenuItem = forwardRef<MenuItemElement, MenuItemProps>(
519
453
  tabIndex={disabled ? -1 : 0}
520
454
  ref={composedRefs}
521
455
  disabled={disabled}
522
- onClick={composeEventHandlers(onClick, handleSelect)}
456
+ onClick={composeEventHandlers(onClick, handleSelect, {
457
+ /**
458
+ * Nextjs prevents default on click when using Link component, so we have to force click-event
459
+ * https://github.com/vercel/next.js/blob/77dcd4c66a35d0e8ef639bda4d05873bd3c0f52d/packages/next/src/client/link.tsx#L211
460
+ */
461
+ checkForDefaultPrevented: false,
462
+ })}
523
463
  onPointerDown={composeEventHandlers(
524
464
  onPointerDown,
525
465
  () => {
@@ -533,21 +473,12 @@ const MenuItem = forwardRef<MenuItemElement, MenuItemProps>(
533
473
  // prevent Firefox from getting stuck in text selection mode when the menu closes.
534
474
  if (!isPointerDownRef.current) event.currentTarget?.click();
535
475
  })}
536
- onKeyDown={composeEventHandlers(onKeyDown, (event) => {
537
- if (disabled) {
538
- return;
539
- }
540
- if (SELECTION_KEYS.includes(event.key)) {
541
- event.currentTarget.click();
542
- /**
543
- * We prevent default browser behaviour for selection keys as they should only trigger
544
- * selection.
545
- * - Prevents space from scrolling the page.
546
- * - If keydown causes focus to move, prevents keydown from firing on the new target.
547
- */
548
- event.preventDefault();
549
- }
550
- })}
476
+ onKeyDown={composeEventHandlers(onKeyDown, (event) =>
477
+ handleKey(event, "Enter"),
478
+ )}
479
+ onKeyUp={composeEventHandlers(onKeyUp, (event) =>
480
+ handleKey(event, " "),
481
+ )}
551
482
  />
552
483
  );
553
484
  },
@@ -573,9 +504,16 @@ const MenuItemInternal = forwardRef<
573
504
  }: MenuItemInternalProps,
574
505
  forwardedRef,
575
506
  ) => {
576
- const { register } = useMenuDescendant({ disabled });
507
+ const context = useMenuContext();
508
+ const { register } = useMenuDescendant({
509
+ disabled,
510
+ closeMenu: () => {
511
+ rest["data-submenu-trigger"] &&
512
+ context.open &&
513
+ context.onOpenChange(false);
514
+ },
515
+ });
577
516
 
578
- const contentContext = useMenuContentContext();
579
517
  const ref = useRef<HTMLDivElement>(null);
580
518
  const composedRefs = useMergeRefs(forwardedRef, ref, register);
581
519
 
@@ -601,18 +539,15 @@ const MenuItemInternal = forwardRef<
601
539
  * In the edgecase the focus is still stuck on a previous item, we make sure to reset it
602
540
  * even when the disabled item can't be focused itself to reset it.
603
541
  */
604
- contentContext.onItemLeave(event);
542
+ context.content?.focus();
605
543
  } else {
606
- contentContext.onItemEnter(event);
607
- if (!event.defaultPrevented) {
608
- event.currentTarget.focus();
609
- }
544
+ event.currentTarget.focus();
610
545
  }
611
546
  }),
612
547
  )}
613
548
  onPointerLeave={composeEventHandlers(
614
549
  onPointerLeave,
615
- whenMouse(contentContext.onItemLeave),
550
+ whenMouse(() => context.content?.focus()),
616
551
  )}
617
552
  />
618
553
  );
@@ -622,7 +557,7 @@ const MenuItemInternal = forwardRef<
622
557
  /* -------------------------------------------------------------------------- */
623
558
  /* Menu Group */
624
559
  /* -------------------------------------------------------------------------- */
625
- interface MenuGroupProps extends SlottedDivProps {}
560
+ type MenuGroupProps = SlottedDivProps;
626
561
 
627
562
  const MenuGroup = forwardRef<SlottedDivElementRef, MenuGroupProps>(
628
563
  (props: MenuGroupProps, ref) => {
@@ -696,7 +631,7 @@ const [MenuItemIndicatorProvider, useMenuItemIndicatorContext] = createContext<{
696
631
  hookName: "useMenuItemIndicatorContext",
697
632
  });
698
633
 
699
- interface MenuItemIndicatorProps extends SlottedDivProps {}
634
+ type MenuItemIndicatorProps = SlottedDivProps;
700
635
 
701
636
  const MenuItemIndicator = forwardRef<
702
637
  SlottedDivElementRef,
@@ -786,12 +721,12 @@ const MenuCheckboxItem = forwardRef<MenuItemElement, MenuCheckboxItemProps>(
786
721
  );
787
722
 
788
723
  /* -------------------------------------------------------------------------- */
789
- /* Menu Separator */
724
+ /* Menu Divider */
790
725
  /* -------------------------------------------------------------------------- */
791
- interface MenuSeparatorProps extends SlottedDivProps {}
726
+ type MenuDividerProps = SlottedDivProps;
792
727
 
793
- const MenuSeparator = forwardRef<SlottedDivElementRef, MenuSeparatorProps>(
794
- (props: MenuSeparatorProps, ref) => {
728
+ const MenuDivider = forwardRef<SlottedDivElementRef, MenuDividerProps>(
729
+ (props: MenuDividerProps, ref) => {
795
730
  return (
796
731
  <SlottedDivElement
797
732
  role="separator"
@@ -833,6 +768,8 @@ const MenuSub: React.FC<MenuSubProps> = ({
833
768
  }: MenuSubProps) => {
834
769
  const parentMenuContext = useMenuContext();
835
770
 
771
+ const { values } = useMenuDescendantsContext();
772
+
836
773
  const [trigger, setTrigger] = useState<MenuItemElement | null>(null);
837
774
  const [content, setContent] = useState<MenuContentInternalElement | null>(
838
775
  null,
@@ -851,7 +788,17 @@ const MenuSub: React.FC<MenuSubProps> = ({
851
788
  <Floating>
852
789
  <MenuProvider
853
790
  open={open}
854
- onOpenChange={handleOpenChange}
791
+ onOpenChange={(_open) => {
792
+ handleOpenChange(_open);
793
+ if (_open) {
794
+ /* Makes sure to close all adjacent submenus if they are open */
795
+ values().forEach((descendant) => {
796
+ if (descendant.node !== trigger) {
797
+ descendant.closeMenu();
798
+ }
799
+ });
800
+ }
801
+ }}
855
802
  content={content}
856
803
  onContentChange={setContent}
857
804
  >
@@ -871,34 +818,31 @@ const MenuSub: React.FC<MenuSubProps> = ({
871
818
  /* -------------------------------------------------------------------------- */
872
819
  /* Menu SubMenu Trigger */
873
820
  /* -------------------------------------------------------------------------- */
874
- interface MenuSubTriggerProps extends MenuItemInternalProps {}
821
+ type MenuSubTriggerProps = MenuItemInternalProps;
875
822
 
876
823
  const MenuSubTrigger = forwardRef<MenuItemElement, MenuSubTriggerProps>(
877
824
  (props: MenuSubTriggerProps, forwardedRef) => {
878
825
  const context = useMenuContext();
879
826
  const subContext = useMenuSubContext();
880
- const contentContext = useMenuContentContext();
881
- const openTimerRef = useRef<number | null>(null);
882
- const { pointerGraceTimerRef, onPointerGraceIntentChange } = contentContext;
883
827
 
884
828
  const composedRefs = useMergeRefs(forwardedRef, subContext.onTriggerChange);
885
829
 
886
- const clearOpenTimer = useCallback(() => {
887
- if (openTimerRef.current) {
888
- window.clearTimeout(openTimerRef.current);
830
+ const handleKey = (
831
+ event: React.KeyboardEvent<HTMLDivElement>,
832
+ keys: string[],
833
+ ) => {
834
+ if (props.disabled) {
835
+ return;
889
836
  }
890
- openTimerRef.current = null;
891
- }, []);
892
-
893
- React.useEffect(() => clearOpenTimer, [clearOpenTimer]);
894
-
895
- React.useEffect(() => {
896
- const pointerGraceTimer = pointerGraceTimerRef.current;
897
- return () => {
898
- window.clearTimeout(pointerGraceTimer);
899
- onPointerGraceIntentChange(null);
900
- };
901
- }, [pointerGraceTimerRef, onPointerGraceIntentChange]);
837
+ if (keys.includes(event.key)) {
838
+ context.onOpenChange(true);
839
+ // The trigger may hold focus if opened via pointer interaction
840
+ // so we ensure content is given focus again when switching to keyboard.
841
+ context.content?.focus();
842
+ // prevent window from scrolling
843
+ event.preventDefault();
844
+ }
845
+ };
902
846
 
903
847
  return (
904
848
  <MenuAnchor asChild>
@@ -910,85 +854,25 @@ const MenuSubTrigger = forwardRef<MenuItemElement, MenuSubTriggerProps>(
910
854
  data-state={getOpenState(context.open)}
911
855
  {...props}
912
856
  ref={composedRefs}
913
- /**
914
- * onClick is added to solve edgecase where the user clicks the trigger,
915
- * but the focus is outside browser-window or viewport at first.
916
- */
857
+ data-submenu-trigger
917
858
  onClick={(event) => {
859
+ if (props.disabled || event.defaultPrevented) {
860
+ return;
861
+ }
918
862
  props.onClick?.(event);
919
- if (props.disabled || event.defaultPrevented) return;
920
-
863
+ /*
864
+ * Solves edgecase where the user clicks the trigger,
865
+ * but the focus is outside browser-window or viewport at first.
866
+ */
921
867
  event.currentTarget.focus();
922
- if (!context.open) context.onOpenChange(true);
868
+ context.onOpenChange(!context.open);
923
869
  }}
924
- onPointerMove={composeEventHandlers(
925
- props.onPointerMove,
926
- whenMouse((event) => {
927
- if (event.defaultPrevented) return;
928
- if (!props.disabled && !context.open && !openTimerRef.current) {
929
- contentContext.onPointerGraceIntentChange(null);
930
- openTimerRef.current = window.setTimeout(() => {
931
- context.onOpenChange(true);
932
- clearOpenTimer();
933
- }, 100);
934
- }
935
- }),
870
+ onKeyDown={composeEventHandlers(props.onKeyDown, (event) =>
871
+ handleKey(event, ["Enter", "ArrowRight"]),
936
872
  )}
937
- onPointerLeave={composeEventHandlers(
938
- props.onPointerLeave,
939
- whenMouse((event) => {
940
- clearOpenTimer();
941
-
942
- const contentRect = context.content?.getBoundingClientRect();
943
- if (contentRect) {
944
- const side = context.content?.dataset.side as SubMenuSide;
945
- const rightSide = side === "right";
946
- const bleed = rightSide ? -5 : +5;
947
- const contentNearEdge =
948
- contentRect[rightSide ? "left" : "right"];
949
- const contentFarEdge =
950
- contentRect[rightSide ? "right" : "left"];
951
-
952
- contentContext.onPointerGraceIntentChange({
953
- area: [
954
- // Apply a bleed on clientX to ensure that our exit point is
955
- // consistently within polygon bounds
956
- { x: event.clientX + bleed, y: event.clientY },
957
- { x: contentNearEdge, y: contentRect.top },
958
- { x: contentFarEdge, y: contentRect.top },
959
- { x: contentFarEdge, y: contentRect.bottom },
960
- { x: contentNearEdge, y: contentRect.bottom },
961
- ],
962
- side,
963
- });
964
-
965
- window.clearTimeout(pointerGraceTimerRef.current);
966
- pointerGraceTimerRef.current = window.setTimeout(
967
- () => contentContext.onPointerGraceIntentChange(null),
968
- 300,
969
- );
970
- } else {
971
- contentContext.onPointerLeaveTrigger(event);
972
- if (event.defaultPrevented) return;
973
-
974
- // There's 100ms where the user may leave an item before the submenu was opened.
975
- contentContext.onPointerGraceIntentChange(null);
976
- }
977
- }),
873
+ onKeyUp={composeEventHandlers(props.onKeyUp, (event) =>
874
+ handleKey(event, [" "]),
978
875
  )}
979
- onKeyDown={composeEventHandlers(props.onKeyDown, (event) => {
980
- if (props.disabled) {
981
- return;
982
- }
983
- if (SUB_OPEN_KEYS.includes(event.key)) {
984
- context.onOpenChange(true);
985
- // The trigger may hold focus if opened via pointer interaction
986
- // so we ensure content is given focus again when switching to keyboard.
987
- context.content?.focus();
988
- // prevent window from scrolling
989
- event.preventDefault();
990
- }
991
- })}
992
876
  />
993
877
  </MenuAnchor>
994
878
  );
@@ -998,15 +882,14 @@ const MenuSubTrigger = forwardRef<MenuItemElement, MenuSubTriggerProps>(
998
882
  /* -------------------------------------------------------------------------- */
999
883
  /* Menu SubMenu Content */
1000
884
  /* -------------------------------------------------------------------------- */
1001
- interface MenuSubContentProps
1002
- extends Omit<
1003
- MenuContentInternalProps,
1004
- | keyof MenuContentInternalPrivateProps
1005
- | "onCloseAutoFocus"
1006
- | "onEntryFocus"
1007
- | "side"
1008
- | "align"
1009
- > {}
885
+ type MenuSubContentProps = Omit<
886
+ MenuContentInternalProps,
887
+ | keyof MenuContentInternalPrivateProps
888
+ | "onCloseAutoFocus"
889
+ | "onEntryFocus"
890
+ | "side"
891
+ | "align"
892
+ >;
1010
893
 
1011
894
  const MenuSubContent = forwardRef<
1012
895
  MenuContentInternalElement,
@@ -1037,14 +920,8 @@ const MenuSubContent = forwardRef<
1037
920
  }
1038
921
  event.preventDefault();
1039
922
  }}
1040
- // The menu might close because of focusing another menu item in the parent menu. We
1041
- // don't want it to refocus the trigger in that case so we handle trigger focus ourselves.
923
+ /* Since we manually focus Subtrigger, we prevent use of auto-focus */
1042
924
  onCloseAutoFocus={(event) => event.preventDefault()}
1043
- onFocusOutside={composeEventHandlers(props.onFocusOutside, (event) => {
1044
- // We prevent closing when the trigger is focused to avoid triggering a re-open animation
1045
- // on pointer interaction.
1046
- if (event.target !== subContext.trigger) context.onOpenChange(false);
1047
- })}
1048
925
  onEscapeKeyDown={composeEventHandlers(
1049
926
  props.onEscapeKeyDown,
1050
927
  (event) => {
@@ -1058,7 +935,7 @@ const MenuSubContent = forwardRef<
1058
935
  const isKeyDownInside = event.currentTarget.contains(
1059
936
  event.target as HTMLElement,
1060
937
  );
1061
- let isCloseKey = SUB_CLOSE_KEYS.includes(event.key);
938
+ let isCloseKey = event.key === "ArrowLeft";
1062
939
 
1063
940
  /* When submenu opens to the left, we allow closing it with ArrowRight */
1064
941
  if (context.content?.dataset.side === "left") {
@@ -1097,32 +974,6 @@ function getCheckedState(checked: CheckedState) {
1097
974
  : "unchecked";
1098
975
  }
1099
976
 
1100
- /**
1101
- * Determine if a point is inside of a polygon.
1102
- */
1103
- function isPointInPolygon(point: Point, polygon: Polygon) {
1104
- const { x, y } = point;
1105
- let inside = false;
1106
- for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
1107
- const xi = polygon[i].x;
1108
- const yi = polygon[i].y;
1109
- const xj = polygon[j].x;
1110
- const yj = polygon[j].y;
1111
-
1112
- // prettier-ignore
1113
- const intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
1114
- if (intersect) inside = !inside;
1115
- }
1116
-
1117
- return inside;
1118
- }
1119
-
1120
- function isPointerInGraceArea(event: React.PointerEvent, area?: Polygon) {
1121
- if (!area) return false;
1122
- const cursorPos = { x: event.clientX, y: event.clientY };
1123
- return isPointInPolygon(cursorPos, area);
1124
- }
1125
-
1126
977
  function whenMouse<E>(
1127
978
  handler: React.PointerEventHandler<E>,
1128
979
  ): React.PointerEventHandler<E> {
@@ -1139,7 +990,7 @@ Menu.Item = MenuItem;
1139
990
  Menu.CheckboxItem = MenuCheckboxItem;
1140
991
  Menu.RadioGroup = MenuRadioGroup;
1141
992
  Menu.RadioItem = MenuRadioItem;
1142
- Menu.Separator = MenuSeparator;
993
+ Menu.Divider = MenuDivider;
1143
994
  Menu.Sub = MenuSub;
1144
995
  Menu.SubTrigger = MenuSubTrigger;
1145
996
  Menu.SubContent = MenuSubContent;
@@ -1150,19 +1001,20 @@ export {
1150
1001
  MenuAnchor,
1151
1002
  MenuCheckboxItem,
1152
1003
  MenuContent,
1004
+ MenuDivider,
1153
1005
  MenuGroup,
1154
1006
  MenuItem,
1155
1007
  MenuItemIndicator,
1156
1008
  MenuPortal,
1157
1009
  MenuRadioGroup,
1158
1010
  MenuRadioItem,
1159
- MenuSeparator,
1160
1011
  MenuSub,
1161
1012
  MenuSubContent,
1162
1013
  MenuSubTrigger,
1163
1014
  type MenuAnchorProps,
1164
1015
  type MenuCheckboxItemProps,
1165
1016
  type MenuContentProps,
1017
+ type MenuDividerProps,
1166
1018
  type MenuGroupProps,
1167
1019
  type MenuItemElement,
1168
1020
  type MenuItemIndicatorProps,
@@ -1170,7 +1022,6 @@ export {
1170
1022
  type MenuProps,
1171
1023
  type MenuRadioGroupProps,
1172
1024
  type MenuRadioItemProps,
1173
- type MenuSeparatorProps,
1174
1025
  type MenuSubContentProps,
1175
1026
  type MenuSubProps,
1176
1027
  type MenuSubTriggerProps,