@fusion.dev/helix-headless 0.144.0 → 0.146.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 (319) hide show
  1. package/custom-elements.json +15195 -4240
  2. package/dist/accordion/item/accordion-item.d.ts +62 -12
  3. package/dist/accordion/item/accordion-item.js +41 -10
  4. package/dist/accordion/root/accordion-root.d.ts +14 -5
  5. package/dist/accordion/root/accordion-root.js +7 -1
  6. package/dist/announcement/root/announcement-root.d.ts +24 -3
  7. package/dist/announcement/root/announcement-root.js +39 -2
  8. package/dist/app-header/root/app-header-root.d.ts +12 -0
  9. package/dist/app-header/root/app-header-root.js +13 -1
  10. package/dist/app-header/sectioning.d.ts +9 -0
  11. package/dist/app-header/sectioning.js +27 -0
  12. package/dist/avatar/group/avatar-group.d.ts +20 -14
  13. package/dist/avatar/group/avatar-group.js +17 -19
  14. package/dist/badge/root/badge-root.d.ts +3 -1
  15. package/dist/badge/root/badge-root.js +2 -2
  16. package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
  17. package/dist/bar-chart/root/bar-chart-root.js +52 -15
  18. package/dist/bar-chart/stack.d.ts +15 -0
  19. package/dist/bar-chart/stack.js +19 -0
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +57 -1
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +73 -3
  22. package/dist/button/root/button-root.d.ts +26 -0
  23. package/dist/button/root/button-root.js +38 -18
  24. package/dist/button-group/root/button-group-root.d.ts +2 -1
  25. package/dist/button-group/root/button-group-root.js +5 -1
  26. package/dist/canvas/node/canvas-node.d.ts +4 -3
  27. package/dist/canvas/node/canvas-node.js +3 -2
  28. package/dist/canvas/root/canvas-root.d.ts +38 -3
  29. package/dist/canvas/root/canvas-root.js +123 -42
  30. package/dist/card/root/card-root.d.ts +24 -3
  31. package/dist/card/root/card-root.js +20 -8
  32. package/dist/carousel/root/carousel-root.d.ts +133 -4
  33. package/dist/carousel/root/carousel-root.js +174 -24
  34. package/dist/carousel/stops.d.ts +20 -0
  35. package/dist/carousel/stops.js +30 -0
  36. package/dist/chat/composer/chat-composer.d.ts +38 -6
  37. package/dist/chat/composer/chat-composer.js +42 -9
  38. package/dist/chat/message/chat-message.d.ts +32 -1
  39. package/dist/chat/message/chat-message.js +27 -10
  40. package/dist/chat/naming.d.ts +26 -0
  41. package/dist/chat/naming.js +14 -0
  42. package/dist/chat/root/chat-root.d.ts +58 -0
  43. package/dist/chat/root/chat-root.js +43 -3
  44. package/dist/checkbox/root/checkbox-root.d.ts +20 -0
  45. package/dist/checkbox/root/checkbox-root.js +14 -1
  46. package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
  47. package/dist/checkbox-group/root/checkbox-group-root.js +12 -10
  48. package/dist/checkbox-group/select-all.d.ts +27 -0
  49. package/dist/checkbox-group/select-all.js +12 -0
  50. package/dist/code-block/highlight.d.ts +15 -0
  51. package/dist/code-block/highlight.js +4 -0
  52. package/dist/code-block/root/code-block-root.d.ts +57 -2
  53. package/dist/code-block/root/code-block-root.js +63 -8
  54. package/dist/color-input/root/color-input-root.d.ts +3 -0
  55. package/dist/color-input/root/color-input-root.js +6 -3
  56. package/dist/color-picker/root/color-picker-root.d.ts +33 -4
  57. package/dist/color-picker/root/color-picker-root.js +80 -21
  58. package/dist/color-picker/swatches.d.ts +12 -0
  59. package/dist/color-picker/swatches.js +26 -0
  60. package/dist/combobox/root/combobox-root.d.ts +43 -0
  61. package/dist/combobox/root/combobox-root.js +79 -15
  62. package/dist/command/root/command-root.d.ts +16 -1
  63. package/dist/command/root/command-root.js +22 -4
  64. package/dist/copy-button/root/copy-button-root.d.ts +26 -1
  65. package/dist/copy-button/root/copy-button-root.js +12 -5
  66. package/dist/data-table/index.d.ts +1 -1
  67. package/dist/data-table/index.js +1 -1
  68. package/dist/data-table/keys.d.ts +24 -0
  69. package/dist/data-table/keys.js +24 -0
  70. package/dist/data-table/root/data-table-root.d.ts +170 -13
  71. package/dist/data-table/root/data-table-root.js +294 -52
  72. package/dist/data-table/tree.d.ts +55 -0
  73. package/dist/data-table/tree.js +54 -0
  74. package/dist/date-input/root/date-input-root.d.ts +3 -1
  75. package/dist/date-input/root/date-input-root.js +18 -7
  76. package/dist/date-picker/root/date-picker-root.js +12 -6
  77. package/dist/description-list/item/description-list-item.d.ts +17 -2
  78. package/dist/description-list/item/description-list-item.js +15 -2
  79. package/dist/description-list/root/description-list-root.d.ts +27 -1
  80. package/dist/description-list/root/description-list-root.js +26 -1
  81. package/dist/dialog/description.js +15 -1
  82. package/dist/dialog/root/dialog-root.d.ts +8 -2
  83. package/dist/dialog/root/dialog-root.js +16 -1
  84. package/dist/divider/root/divider-root.d.ts +3 -0
  85. package/dist/divider/root/divider-root.js +2 -1
  86. package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
  87. package/dist/donut-chart/root/donut-chart-root.js +48 -1
  88. package/dist/drawer/reveal.d.ts +8 -0
  89. package/dist/drawer/reveal.js +1 -0
  90. package/dist/drawer/root/drawer-root.d.ts +59 -0
  91. package/dist/drawer/root/drawer-root.js +63 -4
  92. package/dist/drilldown/item/drilldown-item.d.ts +11 -6
  93. package/dist/drilldown/item/drilldown-item.js +8 -5
  94. package/dist/drilldown/root/drilldown-root.d.ts +29 -0
  95. package/dist/drilldown/root/drilldown-root.js +28 -5
  96. package/dist/element-events.generated.js +83 -12
  97. package/dist/empty/root/empty-root.d.ts +3 -1
  98. package/dist/empty/root/empty-root.js +3 -2
  99. package/dist/file-input/root/file-input-root.d.ts +69 -6
  100. package/dist/file-input/root/file-input-root.js +82 -19
  101. package/dist/file-input/screen.d.ts +2 -1
  102. package/dist/filter-input/menu/filter-menu.d.ts +5 -0
  103. package/dist/filter-input/menu/filter-menu.js +16 -4
  104. package/dist/filter-input/root/filter-input-root.d.ts +30 -6
  105. package/dist/filter-input/root/filter-input-root.js +1 -1
  106. package/dist/filter-input/suggest.d.ts +28 -3
  107. package/dist/filter-input/suggest.js +18 -8
  108. package/dist/format-date/root/format-date-root.d.ts +9 -16
  109. package/dist/format-date/root/format-date-root.js +10 -27
  110. package/dist/heatmap/root/heatmap-root.d.ts +24 -13
  111. package/dist/heatmap/root/heatmap-root.js +34 -12
  112. package/dist/icon/root/icon-root.d.ts +12 -1
  113. package/dist/icon/root/icon-root.js +21 -10
  114. package/dist/icon/settle.d.ts +7 -0
  115. package/dist/icon/settle.js +10 -0
  116. package/dist/icon-button/root/icon-button-root.d.ts +2 -0
  117. package/dist/image/root/image-root.d.ts +5 -1
  118. package/dist/image/root/image-root.js +10 -2
  119. package/dist/input/root/input-root.d.ts +13 -3
  120. package/dist/input/root/input-root.js +12 -3
  121. package/dist/kbd/root/kbd-root.d.ts +35 -1
  122. package/dist/kbd/root/kbd-root.js +23 -4
  123. package/dist/legend/root/legend-root.d.ts +55 -8
  124. package/dist/legend/root/legend-root.js +55 -13
  125. package/dist/lightbox/root/lightbox-root.d.ts +49 -6
  126. package/dist/lightbox/root/lightbox-root.js +86 -19
  127. package/dist/lightbox/zoom-controller.d.ts +59 -0
  128. package/dist/lightbox/zoom-controller.js +128 -0
  129. package/dist/lightbox/zoom.d.ts +22 -0
  130. package/dist/lightbox/zoom.js +16 -0
  131. package/dist/line-chart/root/line-chart-root.d.ts +22 -1
  132. package/dist/line-chart/root/line-chart-root.js +21 -1
  133. package/dist/list/item/list-item.d.ts +70 -1
  134. package/dist/list/item/list-item.js +77 -6
  135. package/dist/list/numbering.d.ts +14 -0
  136. package/dist/list/numbering.js +8 -0
  137. package/dist/list/root/list-root.d.ts +155 -9
  138. package/dist/list/root/list-root.js +246 -4
  139. package/dist/list/selection.d.ts +49 -0
  140. package/dist/list/selection.js +30 -0
  141. package/dist/listbox/root/listbox-root.d.ts +37 -1
  142. package/dist/listbox/root/listbox-root.js +49 -4
  143. package/dist/loader/busy.d.ts +10 -0
  144. package/dist/loader/busy.js +11 -0
  145. package/dist/loader/root/loader-root.d.ts +9 -2
  146. package/dist/loader/root/loader-root.js +23 -3
  147. package/dist/map/root/map-root.d.ts +6 -2
  148. package/dist/map/root/map-root.js +5 -2
  149. package/dist/megamenu/root/megamenu-root.d.ts +62 -0
  150. package/dist/megamenu/root/megamenu-root.js +90 -5
  151. package/dist/menu/index.d.ts +1 -0
  152. package/dist/menu/index.js +1 -0
  153. package/dist/menu/item/menu-item.d.ts +87 -11
  154. package/dist/menu/item/menu-item.js +78 -22
  155. package/dist/menu/root/menu-root.d.ts +39 -1
  156. package/dist/menu/root/menu-root.js +65 -8
  157. package/dist/menu/section/menu-section.d.ts +55 -0
  158. package/dist/menu/section/menu-section.js +60 -0
  159. package/dist/meter/root/meter-root.d.ts +20 -1
  160. package/dist/meter/root/meter-root.js +11 -15
  161. package/dist/navigation/root/navigation-root.d.ts +18 -2
  162. package/dist/navigation/root/navigation-root.js +29 -3
  163. package/dist/notifications/root/notifications-root.d.ts +25 -0
  164. package/dist/notifications/root/notifications-root.js +33 -5
  165. package/dist/number-input/root/number-input-root.d.ts +17 -8
  166. package/dist/number-input/root/number-input-root.js +2 -7
  167. package/dist/object-inspector/root/object-inspector-root.d.ts +161 -32
  168. package/dist/object-inspector/root/object-inspector-root.js +264 -123
  169. package/dist/object-inspector/rows.d.ts +75 -0
  170. package/dist/object-inspector/rows.js +119 -0
  171. package/dist/option/root/option-root.d.ts +5 -3
  172. package/dist/outline/levels.d.ts +31 -0
  173. package/dist/outline/levels.js +23 -0
  174. package/dist/outline/root/outline-root.d.ts +86 -3
  175. package/dist/outline/root/outline-root.js +95 -12
  176. package/dist/pagination/size/pagination-size.d.ts +12 -2
  177. package/dist/pagination/size/pagination-size.js +20 -5
  178. package/dist/pin-input/code.d.ts +10 -0
  179. package/dist/pin-input/code.js +13 -0
  180. package/dist/pin-input/root/pin-input-root.d.ts +25 -1
  181. package/dist/pin-input/root/pin-input-root.js +10 -11
  182. package/dist/popover/hide-outside.d.ts +14 -0
  183. package/dist/popover/hide-outside.js +41 -0
  184. package/dist/popover/root/popover-root.d.ts +57 -7
  185. package/dist/popover/root/popover-root.js +58 -3
  186. package/dist/progress/root/progress-root.d.ts +20 -0
  187. package/dist/progress/root/progress-root.js +15 -15
  188. package/dist/qr-code/export.d.ts +26 -1
  189. package/dist/qr-code/export.js +14 -0
  190. package/dist/qr-code/root/qr-code-root.d.ts +19 -5
  191. package/dist/qr-code/root/qr-code-root.js +58 -9
  192. package/dist/quote/marks.d.ts +8 -0
  193. package/dist/quote/marks.js +5 -0
  194. package/dist/quote/root/quote-root.d.ts +14 -2
  195. package/dist/quote/root/quote-root.js +23 -1
  196. package/dist/radio/group/radio-group.d.ts +4 -10
  197. package/dist/radio/group/radio-group.js +5 -10
  198. package/dist/radio/root/radio-root.d.ts +6 -2
  199. package/dist/radio/root/radio-root.js +6 -1
  200. package/dist/rating/root/rating-root.d.ts +10 -5
  201. package/dist/rating/root/rating-root.js +32 -8
  202. package/dist/rating/steps.d.ts +7 -0
  203. package/dist/rating/steps.js +9 -0
  204. package/dist/scatter-chart/fit.d.ts +15 -0
  205. package/dist/scatter-chart/fit.js +35 -0
  206. package/dist/scatter-chart/root/scatter-chart-root.d.ts +21 -1
  207. package/dist/scatter-chart/root/scatter-chart-root.js +30 -17
  208. package/dist/scroll-area/root/scroll-area-root.d.ts +84 -0
  209. package/dist/scroll-area/root/scroll-area-root.js +106 -7
  210. package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
  211. package/dist/scroll-area/scrollbar-controller.js +172 -0
  212. package/dist/scroll-area/scrollbar.d.ts +92 -0
  213. package/dist/scroll-area/scrollbar.js +33 -0
  214. package/dist/select/root/select-root.d.ts +15 -0
  215. package/dist/select/root/select-root.js +18 -6
  216. package/dist/skeleton/root/skeleton-root.d.ts +3 -0
  217. package/dist/skeleton/root/skeleton-root.js +4 -2
  218. package/dist/skip-link/root/skip-link-root.js +2 -2
  219. package/dist/slider/root/slider-root.d.ts +41 -4
  220. package/dist/slider/root/slider-root.js +23 -3
  221. package/dist/sparkline/readings.d.ts +18 -0
  222. package/dist/sparkline/readings.js +19 -0
  223. package/dist/sparkline/root/sparkline-root.d.ts +78 -7
  224. package/dist/sparkline/root/sparkline-root.js +118 -23
  225. package/dist/speed-dial/root/speed-dial-root.js +2 -2
  226. package/dist/splitter/root/splitter-root.d.ts +20 -12
  227. package/dist/splitter/root/splitter-root.js +226 -203
  228. package/dist/splitter/snap.d.ts +9 -0
  229. package/dist/splitter/snap.js +12 -0
  230. package/dist/stat/root/stat-root.d.ts +14 -5
  231. package/dist/stat/root/stat-root.js +21 -5
  232. package/dist/steps/reach.d.ts +20 -0
  233. package/dist/steps/reach.js +15 -0
  234. package/dist/steps/root/steps-root.d.ts +76 -0
  235. package/dist/steps/root/steps-root.js +93 -1
  236. package/dist/steps/step/step.d.ts +45 -2
  237. package/dist/steps/step/step.js +33 -6
  238. package/dist/switch/root/switch-root.d.ts +10 -0
  239. package/dist/table/column/table-column.d.ts +11 -10
  240. package/dist/table/column/table-column.js +8 -19
  241. package/dist/table/root/table-root.d.ts +126 -4
  242. package/dist/table/root/table-root.js +156 -6
  243. package/dist/table/row/table-row.d.ts +47 -1
  244. package/dist/table/row/table-row.js +39 -2
  245. package/dist/tabs/panel/tab-panel.d.ts +22 -5
  246. package/dist/tabs/panel/tab-panel.js +34 -6
  247. package/dist/tabs/root/tabs-root.d.ts +37 -1
  248. package/dist/tabs/root/tabs-root.js +22 -4
  249. package/dist/tabs/tab/tab.d.ts +41 -4
  250. package/dist/tabs/tab/tab.js +49 -3
  251. package/dist/tag/group/tag-group.d.ts +31 -4
  252. package/dist/tag/group/tag-group.js +46 -7
  253. package/dist/tag/root/tag-root.js +9 -2
  254. package/dist/textarea/root/textarea-root.d.ts +28 -1
  255. package/dist/textarea/root/textarea-root.js +40 -1
  256. package/dist/time-input/root/time-input-root.d.ts +5 -1
  257. package/dist/time-input/root/time-input-root.js +16 -8
  258. package/dist/time-picker/root/time-picker-root.d.ts +18 -4
  259. package/dist/time-picker/root/time-picker-root.js +7 -10
  260. package/dist/time-picker/wheel-name.d.ts +9 -0
  261. package/dist/time-picker/wheel-name.js +15 -0
  262. package/dist/timeline/group/timeline-group.d.ts +46 -0
  263. package/dist/timeline/group/timeline-group.js +37 -0
  264. package/dist/timeline/index.d.ts +1 -0
  265. package/dist/timeline/index.js +1 -0
  266. package/dist/timeline/root/timeline-root.d.ts +10 -3
  267. package/dist/toast/item/toast-item.d.ts +24 -18
  268. package/dist/toast/item/toast-item.js +36 -40
  269. package/dist/toast/root/toast-root.d.ts +6 -0
  270. package/dist/toast/root/toast-root.js +16 -1
  271. package/dist/toggle/group/toggle-group.d.ts +24 -3
  272. package/dist/toggle/group/toggle-group.js +34 -5
  273. package/dist/toolbar/root/toolbar-root.d.ts +13 -0
  274. package/dist/toolbar/root/toolbar-root.js +15 -5
  275. package/dist/tooltip/root/tooltip-root.d.ts +13 -2
  276. package/dist/tooltip/root/tooltip-root.js +9 -1
  277. package/dist/tour/back.d.ts +10 -0
  278. package/dist/tour/back.js +8 -0
  279. package/dist/tour/root/tour-root.d.ts +30 -2
  280. package/dist/tour/root/tour-root.js +34 -8
  281. package/dist/tree/item/tree-item.d.ts +61 -4
  282. package/dist/tree/item/tree-item.js +92 -20
  283. package/dist/tree/root/tree-root.d.ts +58 -0
  284. package/dist/tree/root/tree-root.js +80 -16
  285. package/dist/tree/selection.d.ts +24 -0
  286. package/dist/tree/selection.js +13 -0
  287. package/dist/trend/root/trend-root.d.ts +43 -18
  288. package/dist/trend/root/trend-root.js +31 -6
  289. package/dist/video/media-events.d.ts +44 -0
  290. package/dist/video/media-events.js +35 -0
  291. package/dist/video/root/video-root.d.ts +117 -18
  292. package/dist/video/root/video-root.js +93 -47
  293. package/dist/video-playlist/menu/video-playlist-menu.d.ts +5 -0
  294. package/dist/video-playlist/menu/video-playlist-menu.js +3 -1
  295. package/dist/video-playlist/order.d.ts +41 -0
  296. package/dist/video-playlist/order.js +34 -0
  297. package/dist/video-playlist/root/video-playlist-root.d.ts +77 -3
  298. package/dist/video-playlist/root/video-playlist-root.js +60 -16
  299. package/dist/virtual-list/controls.d.ts +4 -0
  300. package/dist/virtual-list/controls.js +30 -0
  301. package/dist/virtual-list/fill.d.ts +22 -0
  302. package/dist/virtual-list/fill.js +23 -0
  303. package/dist/virtual-list/keys.d.ts +15 -0
  304. package/dist/virtual-list/keys.js +24 -0
  305. package/dist/virtual-list/plan.d.ts +49 -0
  306. package/dist/virtual-list/plan.js +16 -0
  307. package/dist/virtual-list/root/virtual-list-root.d.ts +259 -26
  308. package/dist/virtual-list/root/virtual-list-root.js +333 -49
  309. package/lint.json +1 -1
  310. package/package.json +8 -8
  311. package/types/jsx.d.ts +8113 -1394
  312. package/types/svelte.d.ts +2 -0
  313. package/types/vue.d.ts +1678 -353
  314. package/dist/data-table/root/order.d.ts +0 -141
  315. package/dist/data-table/root/order.js +0 -76
  316. package/dist/listbox/range.d.ts +0 -17
  317. package/dist/listbox/range.js +0 -5
  318. package/dist/tag/selection.d.ts +0 -10
  319. package/dist/tag/selection.js +0 -12
@@ -5,10 +5,17 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  var HelixHeadlessMegamenu_1;
8
- import { anchorElement, customElement, deepContains, HelixElement, MixinControllerOpen, PopoverController, tellDisclosure, } from '@fusion.dev/helix-core';
8
+ import { anchorElement, customElement, deepContains, focusableElements, HelixElement, MixinControllerOpen, PopoverController, tellDisclosure, } from '@fusion.dev/helix-core';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  let showing;
12
+ const connected = new Set();
13
+ let pressed;
14
+ const release = () => {
15
+ setTimeout(() => {
16
+ pressed = undefined;
17
+ });
18
+ };
12
19
  let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerOpen(HelixElement) {
13
20
  constructor() {
14
21
  super(...arguments);
@@ -21,13 +28,27 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
21
28
  };
22
29
  this.panel = new PopoverController(this, {
23
30
  anchor: () => this.trigger,
24
- boundary: () => this,
31
+ boundary: () => [...connected],
25
32
  onDismiss: () => this.close(),
26
33
  open: () => this.open,
27
34
  place: () => !this.banded,
28
35
  placement: () => 'bottom-start',
29
36
  surface: () => this.partNamed('surface'),
30
37
  });
38
+ this.handleKeyDown = (event) => {
39
+ if (event.key !== 'ArrowDown' || !this.open || event.defaultPrevented) {
40
+ return;
41
+ }
42
+ const trigger = this.trigger;
43
+ if (!event.composedPath().includes(trigger)) {
44
+ return;
45
+ }
46
+ const first = focusableElements(this).find((element) => element !== trigger && !element.closest('[slot="trigger"]'));
47
+ if (first) {
48
+ event.preventDefault();
49
+ first.focus();
50
+ }
51
+ };
31
52
  this.handleScroll = () => {
32
53
  if (!this.open || !this.banded) {
33
54
  return;
@@ -40,6 +61,9 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
40
61
  };
41
62
  this.handlePointerDown = (event) => {
42
63
  this.pointer = event.pointerType;
64
+ pressed = this;
65
+ window.addEventListener('pointerup', release, { capture: true, once: true });
66
+ window.addEventListener('pointercancel', release, { capture: true, once: true });
43
67
  };
44
68
  this.handlePointerEnter = (event) => {
45
69
  if (this.openOn !== 'hover' || event.pointerType !== 'mouse') {
@@ -62,6 +86,9 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
62
86
  if (!this.open || next === null || deepContains(this, next)) {
63
87
  return;
64
88
  }
89
+ if (pressed !== undefined && pressed !== this && deepContains(pressed, next)) {
90
+ return;
91
+ }
65
92
  this.requestOpen(false);
66
93
  };
67
94
  this.handleTriggerClick = (event) => {
@@ -72,6 +99,10 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
72
99
  }
73
100
  if (event.detail === 0 || (this.pointer !== undefined && this.pointer !== 'mouse')) {
74
101
  this.requestOpen(!this.open);
102
+ return;
103
+ }
104
+ if (!this.open) {
105
+ this.requestOpen(true);
75
106
  }
76
107
  };
77
108
  this.handleContentClick = (event) => {
@@ -88,6 +119,7 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
88
119
  }
89
120
  this.requestOpen(false);
90
121
  };
122
+ this.handedOver = false;
91
123
  }
92
124
  static { HelixHeadlessMegamenu_1 = this; }
93
125
  get banded() {
@@ -117,6 +149,9 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
117
149
  get trigger() {
118
150
  return anchorElement(this.renderRoot?.querySelector('slot[name="trigger"]'), this.partNamed('trigger') ?? this);
119
151
  }
152
+ get navigationControl() {
153
+ return this.trigger;
154
+ }
120
155
  close() {
121
156
  if (!this.requestOpen(false)) {
122
157
  return;
@@ -156,12 +191,14 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
156
191
  connectedCallback() {
157
192
  super.connectedCallback();
158
193
  this.watchWidth();
194
+ connected.add(this);
159
195
  window.addEventListener('scroll', this.handleScroll, { capture: true, passive: true });
160
196
  this.addEventListener('pointerdown', this.handlePointerDown);
161
197
  this.addEventListener('click', this.handleContentClick);
162
198
  this.addEventListener('pointerenter', this.handlePointerEnter);
163
199
  this.addEventListener('pointerleave', this.handlePointerLeave);
164
200
  this.addEventListener('focusout', this.handleFocusOut);
201
+ this.addEventListener('keydown', this.handleKeyDown);
165
202
  }
166
203
  disconnectedCallback() {
167
204
  super.disconnectedCallback();
@@ -173,6 +210,8 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
173
210
  this.removeEventListener('pointerenter', this.handlePointerEnter);
174
211
  this.removeEventListener('pointerleave', this.handlePointerLeave);
175
212
  this.removeEventListener('focusout', this.handleFocusOut);
213
+ this.removeEventListener('keydown', this.handleKeyDown);
214
+ connected.delete(this);
176
215
  this.stop();
177
216
  if (showing === this) {
178
217
  showing = undefined;
@@ -184,14 +223,59 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
184
223
  return;
185
224
  }
186
225
  if (this.open) {
187
- if (showing && showing !== this) {
188
- showing.requestOpen(false);
226
+ const previous = showing && showing !== this ? showing : undefined;
227
+ const forward = previous !== undefined &&
228
+ (previous.compareDocumentPosition(this) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0;
229
+ this.motion(previous ? (forward ? 'from-end' : 'from-start') : undefined);
230
+ this.from = previous?.size;
231
+ HelixHeadlessMegamenu_1.settle(this, previous);
232
+ if (previous) {
233
+ previous.motion(forward ? 'to-start' : 'to-end');
234
+ previous.handedOver = true;
235
+ if (!previous.requestOpen(false)) {
236
+ previous.handedOver = false;
237
+ previous.motion(undefined);
238
+ }
189
239
  }
190
240
  showing = this;
241
+ return;
191
242
  }
192
- else if (showing === this) {
243
+ if (showing === this) {
193
244
  showing = undefined;
194
245
  }
246
+ if (!this.handedOver) {
247
+ HelixHeadlessMegamenu_1.settle();
248
+ }
249
+ this.handedOver = false;
250
+ }
251
+ static settle(...moving) {
252
+ for (const megamenu of connected) {
253
+ if (!moving.includes(megamenu)) {
254
+ megamenu.motion(undefined);
255
+ }
256
+ }
257
+ }
258
+ measure() {
259
+ const surface = this.partNamed('surface');
260
+ if (!surface) {
261
+ return;
262
+ }
263
+ surface.style.removeProperty('--megamenu-from-inline-size');
264
+ surface.style.removeProperty('--megamenu-from-block-size');
265
+ const box = surface.getBoundingClientRect();
266
+ this.size = { block: Math.round(box.height), inline: Math.round(box.width) };
267
+ surface.style.setProperty('--megamenu-surface-inline-size', `${this.size.inline}px`);
268
+ surface.style.setProperty('--megamenu-surface-block-size', `${this.size.block}px`);
269
+ if (this.from) {
270
+ surface.style.setProperty('--megamenu-from-inline-size', `${this.from.inline}px`);
271
+ surface.style.setProperty('--megamenu-from-block-size', `${this.from.block}px`);
272
+ }
273
+ }
274
+ motion(to) {
275
+ this.toggleState('from-end', to === 'from-end');
276
+ this.toggleState('from-start', to === 'from-start');
277
+ this.toggleState('to-end', to === 'to-end');
278
+ this.toggleState('to-start', to === 'to-start');
195
279
  }
196
280
  updated(changed) {
197
281
  super.updated?.(changed);
@@ -202,6 +286,7 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
202
286
  this.tellTrigger();
203
287
  if (this.open) {
204
288
  this.band();
289
+ this.measure();
205
290
  }
206
291
  }
207
292
  renderTrigger() {
@@ -1,2 +1,3 @@
1
1
  export * from './item/menu-item.js';
2
2
  export * from './root/menu-root.js';
3
+ export * from './section/menu-section.js';
@@ -1,2 +1,3 @@
1
1
  export * from './item/menu-item.js';
2
2
  export * from './root/menu-root.js';
3
+ export * from './section/menu-section.js';
@@ -1,5 +1,6 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
2
  import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
+ import '../../kbd/index.js';
3
4
  /**
4
5
  * @summary One row in a menu: a command, a choice, or a way into a submenu.
5
6
  *
@@ -23,21 +24,44 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
23
24
  * attribute. An attribute written onto your element after it upgrades causes a
24
25
  * hydration mismatch.
25
26
  *
27
+ * `shortcut` shows a row's keyboard shortcut after its label, drawn by `Kbd`
28
+ * in the platform's glyphs, and gives the row `aria-keyshortcuts`. It binds
29
+ * nothing: the shortcut is the page's to bind. Other trailing words, such as a
30
+ * count, go in the `end` slot.
31
+ *
32
+ * The row is named by its label alone. Words in `description`, a line under the
33
+ * label, and in `end` describe it instead, through `aria-describedby`, as
34
+ * React Aria's menu item does with its description: a reader hears "Copy",
35
+ * then "Copies the selection".
36
+ *
37
+ * The check mark and the caret are the fallbacks of the `check` and `chevron`
38
+ * slots, so a mark of your own replaces either by being slotted. Neither is
39
+ * read as part of the row's name.
40
+ *
26
41
  * @since 0.1.0
27
42
  * @status stable
28
43
  * @aliases Menu option, Menu row
29
44
  * @documentation https://fusion.dev/helix/components/menu
30
45
  *
31
46
  * @slot - The label.
47
+ * @slot check - Replaces the check mark of a checked row, such as with your own icon.
48
+ * @slot chevron - Replaces the caret of a row that opens a submenu.
49
+ * @slot description - A line under the label that describes the row. It describes the row to a screen reader and is not part of its name.
50
+ * @slot end - Words after the label, such as a count or a detail, beside any `shortcut`. They describe the row and are not part of its name.
32
51
  * @slot start - Content before the label, such as an icon.
33
52
  * @slot submenu - The rows of a submenu that this row opens.
34
53
  *
35
54
  * @event {HelixBeforeToggleEvent} helix-before-toggle - A choice row is about to be checked or unchecked, carrying the state asked for. Cancelable: cancel it to keep the row as it is.
36
55
  * @event {HelixSelectEvent} helix-select - The row was chosen. Cancelable: cancel it to keep the menu open.
37
56
  *
57
+ * @csspart end - The trailing column that holds the shortcut and the `end` slot. It is always rendered.
38
58
  * @csspart item - The row, which is an `<a>` where `href` is set.
39
59
  * @csspart mark - The trailing mark: a check mark when the row is checked, or a caret
40
60
  * when it opens a submenu. A row never shows both, so they share one column.
61
+ * @csspart shortcut - The key drawn for `shortcut`.
62
+ * @csspart shortcut-kbd - The key inside it.
63
+ * @csspart shortcut-key - One key of the shortcut's chord, such as `Ctrl`.
64
+ * @csspart shortcut-separator - The `+` between two keys of the shortcut, where the platform writes one.
41
65
  * @csspart start - The leading column that holds the `start` slot. It is always rendered.
42
66
  *
43
67
  * @cssstate checked - The row is checked.
@@ -100,19 +124,16 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
100
124
  * roves, which is the same maneuver `Tree.Item` makes and the reason `href`
101
125
  * changes the markup rather than only adding an attribute.
102
126
  *
103
- * **`Option` cannot have this and the reason is worth knowing**, since a
104
- * command palette is the obvious place to want it. A listbox has to keep
105
- * focus somewhere else, in the text field for a combobox, so it says which
106
- * option is current with `aria-activedescendant`. That reference can only
107
- * point at the option's host, measured: assigning
108
- * `ariaActiveDescendantElement` an element inside a sibling shadow root reads
109
- * back `null`. So `role="option"` is pinned to the host, and an anchor under
110
- * it hits the naming failure above. A menu has no such constraint because
111
- * focus really moves to the row.
127
+ * **`Option` takes an `href` too, and keeps its role a different way.** A
128
+ * listbox keeps focus somewhere else, in the text field for a combobox, and
129
+ * names the current option with `aria-activedescendant`, which can only point
130
+ * at the option's host. So an option stays `role="option"` on its host, draws
131
+ * no anchor, and follows its link through an anchor made for the moment. A
132
+ * menu has no such constraint, because focus really moves to the row.
112
133
  */
113
134
  href: string;
114
135
  /**
115
- * Whether the row is on the menu page that is showing.
136
+ * @internal Whether the row is on the menu page that is showing.
116
137
  *
117
138
  * Set by the menu, and a property rather than an attribute for that reason.
118
139
  * Public because the menu has to reach it, and not something a consumer
@@ -124,7 +145,14 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
124
145
  */
125
146
  rel: string;
126
147
  /**
127
- * Whether the page showing is this row's submenu, or one below it.
148
+ * The row's keyboard shortcut as a chord, such as `mod+s`, shown after the
149
+ * label in the platform's glyphs and given to the row as
150
+ * `aria-keyshortcuts`. `mod` is Command on an Apple keyboard and Control
151
+ * elsewhere. Showing a shortcut does not bind it.
152
+ */
153
+ shortcut: string;
154
+ /**
155
+ * @internal Whether the page showing is this row's submenu, or one below it.
128
156
  *
129
157
  * Separate from `onPage`, and it has to be. A row that opened a page is not
130
158
  * itself on that page, so both are false for it, and rendering nothing on
@@ -185,6 +213,10 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
185
213
  * exactly the thing that is.
186
214
  */
187
215
  private start;
216
+ /** Whether anything is slotted after the label, read the way `start` is. */
217
+ private end;
218
+ /** Whether anything is slotted under the label, read the way `start` is. */
219
+ private description;
188
220
  /**
189
221
  * Both answers, from the children, before each render.
190
222
  *
@@ -205,6 +237,29 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
205
237
  * is never rebuilt and never fires on its way out.
206
238
  */
207
239
  private readonly readStart;
240
+ /** Re-reads whether anything is after the label, as `readStart` does before it. */
241
+ private readonly readEnd;
242
+ /** Re-reads whether anything is under the label, as `readStart` does before it. */
243
+ private readonly readDescription;
244
+ /**
245
+ * The line under the label. The slot is hidden from the row's name and named
246
+ * by the row's `aria-describedby`, which reads it all the same: a description
247
+ * is read from what it points at, hidden or not. No box of its own, so a row
248
+ * with no description lays out as it did; `data-empty` says which it is.
249
+ */
250
+ protected renderDescription(): TemplateResult;
251
+ /** What describes the row: the line under its label and the words after it. */
252
+ private get describedBy();
253
+ /**
254
+ * The trailing column: the shortcut's key, then whatever is slotted in `end`.
255
+ * Drawn by `Kbd`, interpolated so each tier draws its own.
256
+ *
257
+ * The key is hidden from the accessibility tree, since the row's
258
+ * `aria-keyshortcuts` says it in words a screen reader reads as keys: inside
259
+ * the row its glyphs became part of the row's name. The `end` words are
260
+ * hidden from the name too, and describe the row instead.
261
+ */
262
+ protected renderEnd(): TemplateResult;
208
263
  /**
209
264
  * Re-reads whether anything is in the submenu, from whichever slot is current.
210
265
  *
@@ -284,6 +339,27 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
284
339
  * both announces and navigates.
285
340
  */
286
341
  private readonly handleKeydown;
342
+ /**
343
+ * The trailing mark: the caret of a row that opens a page, or the tick of a
344
+ * checked one, each the fallback of a slot a mark of your own replaces.
345
+ *
346
+ * One trailing column, and a row is never both: a way into a page has nothing
347
+ * to tick, and a choice opens nothing. The tick sits after the label rather
348
+ * than before it, so the labels start at the edge of the row and the marks
349
+ * line up down the side they are read from.
350
+ *
351
+ * `data-mirror` is what tells the two apart. They share a part name because
352
+ * they share a column, and only one of them is directional: a caret has to
353
+ * mirror in a right-to-left document and a tick must not, which Unicode's own
354
+ * mirroring list agrees on. The attribute is on an element in this shadow
355
+ * root rather than on the host, so it is the divider's `data-empty` and not a
356
+ * hydration hazard.
357
+ *
358
+ * Nothing to mark, so nothing is drawn. The column this would have sat in is
359
+ * reserved by the menu when some row on the page needs one, so the labels
360
+ * still line up without every row carrying an empty box to line them up with.
361
+ */
362
+ protected renderMark(submenu: boolean): TemplateResult | typeof nothing;
287
363
  protected render(): TemplateResult | typeof nothing;
288
364
  }
289
365
  declare global {
@@ -4,11 +4,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, deepActiveElement, HelixBeforeToggleEvent, HelixElement, HelixSelectEvent, requestChange, } from '@fusion.dev/helix-core';
7
+ import { ariaChord, customElement, deepActiveElement, HelixBeforeToggleEvent, HelixElement, HelixSelectEvent, onApple, requestChange, } from '@fusion.dev/helix-core';
8
8
  import { caretRight, check } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
+ import { html as staticHtml } from 'lit/static-html.js';
13
+ import '../../kbd/index.js';
12
14
  let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
13
15
  constructor() {
14
16
  super(...arguments);
@@ -20,16 +22,27 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
20
22
  this.href = '';
21
23
  this.onPage = true;
22
24
  this.rel = '';
25
+ this.shortcut = '';
23
26
  this.showingSubmenu = false;
24
27
  this.target = '';
25
28
  this.value = '';
26
29
  this.isMenuItem = true;
27
30
  this.submenu = false;
28
31
  this.start = false;
32
+ this.end = false;
33
+ this.description = false;
29
34
  this.readStart = () => {
30
35
  const slot = this.renderRoot.querySelector('slot[name="start"]');
31
36
  this.start = (slot?.assignedNodes().length ?? 0) > 0;
32
37
  };
38
+ this.readEnd = () => {
39
+ const slot = this.renderRoot.querySelector('slot[name="end"]');
40
+ this.end = (slot?.assignedNodes().length ?? 0) > 0;
41
+ };
42
+ this.readDescription = () => {
43
+ const slot = this.renderRoot.querySelector('slot[name="description"]');
44
+ this.description = (slot?.assignedNodes().length ?? 0) > 0;
45
+ };
33
46
  this.readSubmenu = () => {
34
47
  const slot = this.renderRoot.querySelector('slot[name="submenu"]');
35
48
  this.submenu = (slot?.assignedElements().length ?? 0) > 0;
@@ -65,11 +78,41 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
65
78
  const named = (name) => this.knownChildren.some((child) => child.slot === name);
66
79
  this.submenu = named('submenu');
67
80
  this.start = named('start');
81
+ this.end = named('end');
82
+ this.description = named('description');
68
83
  }
69
84
  firstUpdated(changed) {
70
85
  super.firstUpdated(changed);
71
86
  this.finishHydration();
72
87
  }
88
+ renderDescription() {
89
+ return html `<slot
90
+ id="description"
91
+ name="description"
92
+ aria-hidden="true"
93
+ ?data-empty=${!this.description}
94
+ @slotchange=${this.readDescription}
95
+ ></slot>`;
96
+ }
97
+ get describedBy() {
98
+ const ids = [this.description ? 'description' : '', this.end ? 'end-words' : '']
99
+ .filter((id) => id !== '')
100
+ .join(' ');
101
+ return ids === '' ? nothing : ids;
102
+ }
103
+ renderEnd() {
104
+ const kbd = this.tag('kbd');
105
+ return html `<span part="end" ?data-empty=${!this.end && this.shortcut === ''}
106
+ >${this.shortcut === ''
107
+ ? nothing
108
+ : staticHtml `<${kbd}
109
+ part="shortcut"
110
+ exportparts="kbd: shortcut-kbd, key: shortcut-key, separator: shortcut-separator"
111
+ aria-hidden="true"
112
+ keys=${this.shortcut}
113
+ ></${kbd}>`}<slot id="end-words" name="end" aria-hidden="true" @slotchange=${this.readEnd}></slot
114
+ ></span>`;
115
+ }
73
116
  focus() {
74
117
  this.partNamed('item')?.focus();
75
118
  }
@@ -130,6 +173,23 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
130
173
  this.tick();
131
174
  this.dispatchEvent(new HelixSelectEvent({ value: this.value }));
132
175
  }
176
+ renderMark(submenu) {
177
+ if (submenu) {
178
+ return html `<slot name="chevron" aria-hidden="true"
179
+ ><svg part="mark" data-mirror fill="currentColor" viewBox=${caretRight.viewBox} aria-hidden="true">
180
+ ${unsafeSVG(caretRight.content)}
181
+ </svg></slot
182
+ >`;
183
+ }
184
+ if (this.checked) {
185
+ return html `<slot name="check" aria-hidden="true"
186
+ ><svg part="mark" fill="currentColor" viewBox=${check.viewBox} aria-hidden="true">
187
+ ${unsafeSVG(check.content)}
188
+ </svg></slot
189
+ >`;
190
+ }
191
+ return nothing;
192
+ }
133
193
  render() {
134
194
  if (this.showingSubmenu) {
135
195
  return html `<slot name="submenu" @slotchange=${this.readSubmenu}></slot>`;
@@ -142,31 +202,14 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
142
202
  const ticks = this.checkable || this.checked;
143
203
  const role = ticks ? (this.exclusive ? 'menuitemradio' : 'menuitemcheckbox') : 'menuitem';
144
204
  const checkedState = ticks ? (this.checked ? 'true' : 'false') : nothing;
205
+ const keys = this.shortcut === '' ? nothing : ariaChord(this.shortcut, !this.hydrating && onApple());
145
206
  const inside = html `<span part="start" ?data-empty=${!this.start}
146
207
  ><slot name="start" @slotchange=${this.readStart}></slot
147
208
  ></span>
148
209
  <slot></slot>
149
- ${submenu
150
- ? html `<svg
151
- part="mark"
152
- data-mirror
153
- fill="currentColor"
154
- viewBox=${caretRight.viewBox}
155
- aria-hidden="true"
156
- >
157
- ${unsafeSVG(caretRight.content)}
158
- </svg>`
159
- : this.checked
160
- ? html `<svg
161
- part="mark"
162
- fill="currentColor"
163
- viewBox=${check.viewBox}
164
- aria-hidden="true"
165
- >
166
- ${unsafeSVG(check.content)}
167
- </svg>`
168
- :
169
- nothing}`;
210
+ ${this.renderDescription()}
211
+ ${this.renderEnd()}
212
+ ${this.renderMark(submenu)}`;
170
213
  return html `${linked
171
214
  ? html `<a
172
215
  part="item"
@@ -176,7 +219,9 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
176
219
  ?download=${this.download}
177
220
  role=${role}
178
221
  aria-checked=${checkedState}
222
+ aria-describedby=${this.describedBy}
179
223
  aria-haspopup=${submenu ? 'menu' : nothing}
224
+ aria-keyshortcuts=${keys}
180
225
  tabindex="-1"
181
226
  @click=${this.handleClick}
182
227
  @keydown=${this.handleKeydown}
@@ -185,8 +230,10 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
185
230
  part="item"
186
231
  role=${role}
187
232
  aria-checked=${checkedState}
233
+ aria-describedby=${this.describedBy}
188
234
  aria-disabled=${this.disabled ? 'true' : nothing}
189
235
  aria-haspopup=${submenu ? 'menu' : nothing}
236
+ aria-keyshortcuts=${keys}
190
237
  tabindex=${this.disabled ? nothing : '-1'}
191
238
  @click=${this.handleClick}
192
239
  @keydown=${this.handleKeydown}
@@ -218,6 +265,9 @@ __decorate([
218
265
  __decorate([
219
266
  property()
220
267
  ], HelixHeadlessMenuItem.prototype, "rel", void 0);
268
+ __decorate([
269
+ property()
270
+ ], HelixHeadlessMenuItem.prototype, "shortcut", void 0);
221
271
  __decorate([
222
272
  property({ attribute: false })
223
273
  ], HelixHeadlessMenuItem.prototype, "showingSubmenu", void 0);
@@ -233,6 +283,12 @@ __decorate([
233
283
  __decorate([
234
284
  state()
235
285
  ], HelixHeadlessMenuItem.prototype, "start", void 0);
286
+ __decorate([
287
+ state()
288
+ ], HelixHeadlessMenuItem.prototype, "end", void 0);
289
+ __decorate([
290
+ state()
291
+ ], HelixHeadlessMenuItem.prototype, "description", void 0);
236
292
  HelixHeadlessMenuItem = __decorate([
237
293
  customElement('helix-headless-menu-item')
238
294
  ], HelixHeadlessMenuItem);
@@ -24,6 +24,30 @@ declare const HelixHeadlessMenu_base: (new (...args: any[]) => import("@fusion.d
24
24
  * sets this on items as a property, never as an attribute. An attribute written
25
25
  * onto your element after it upgrades causes a hydration mismatch.
26
26
  *
27
+ * Group rows under a heading with `Menu.Section`, which is a `group` named by
28
+ * its heading. Between runs of rows, a `<helix-divider announced>` is a
29
+ * separator a screen reader announces.
30
+ *
31
+ * Keys: Enter, Space or Down Arrow on the trigger opens the menu on its first
32
+ * row, and Up Arrow on its last. In the menu, Up and Down Arrow move between
33
+ * rows, Home and End go to the first and the last, a typed letter moves to
34
+ * the next row whose name starts with it, Enter and Space choose, Right Arrow
35
+ * opens a row's submenu and Left Arrow goes back, the two swapping on a
36
+ * right-to-left page, and Escape closes the menu and returns focus to the
37
+ * trigger. Tab closes it and moves on.
38
+ *
39
+ * While it is open, the surface carries the trigger's width as
40
+ * `--floating-anchor-inline-size` and the room left below it as
41
+ * `--floating-available-block-size`, as Radix's dropdown menu publishes its
42
+ * trigger's width, for a menu as wide as its trigger:
43
+ * `helix-menu::part(surface) { min-inline-size: var(--floating-anchor-inline-size) }`.
44
+ *
45
+ * With `context`, the trigger is an area and the menu is its context menu: a
46
+ * right-click, a long press, Shift+F10 or the menu key over the area opens it
47
+ * at the pointer, or at the area's corner from the keyboard, as Radix's context
48
+ * menu does. A press on the area does not open it, and closing it returns focus
49
+ * to where it was.
50
+ *
27
51
  * @since 0.1.0
28
52
  * @status stable
29
53
  * @category overlays
@@ -31,7 +55,7 @@ declare const HelixHeadlessMenu_base: (new (...args: any[]) => import("@fusion.d
31
55
  * @documentation https://fusion.dev/helix/components/menu
32
56
  *
33
57
  * @slot - The items.
34
- * @slot trigger - The control that opens the menu.
58
+ * @slot trigger - The control that opens the menu, or with `context` the area a context menu opens over.
35
59
  *
36
60
  * @event {HelixBeforeCloseEvent} helix-before-close - The menu is about to close. Cancelable: cancel it to keep it open.
37
61
  * @event {HelixBeforeOpenEvent} helix-before-open - The menu is about to open. Cancelable: cancel it to keep it closed.
@@ -57,6 +81,11 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
57
81
  * The text of the row that returns from a submenu.
58
82
  */
59
83
  backLabel: string;
84
+ /**
85
+ * Whether the menu is the trigger's context menu: it opens where the reader
86
+ * asks for one, with a right-click, a long press, Shift+F10 or the menu key.
87
+ */
88
+ context: boolean;
60
89
  /**
61
90
  * Accessible name for the surface.
62
91
  *
@@ -104,6 +133,10 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
104
133
  protected readonly keys: KeyboardNavigationController;
105
134
  /** Carries a host `aria-label` and `aria-labelledby` to the surface. */
106
135
  protected readonly aria: AriaForwardController;
136
+ /** Where a context menu was asked for, in the viewport, while it is open. */
137
+ private point;
138
+ /** Where focus was when a context menu opened, so closing it returns there. */
139
+ private returnTo;
107
140
  private readonly panel;
108
141
  /**
109
142
  * Tells a slotted trigger the disclosure state, by property.
@@ -124,6 +157,11 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
124
157
  private openAt;
125
158
  /** Closes the menu and returns it to its first page. */
126
159
  hide(): void;
160
+ /**
161
+ * Opens a context menu where the reader asked for it: at the pointer, or at
162
+ * the area's corner where the keyboard asked and the event has no position.
163
+ */
164
+ private readonly handleContextMenu;
127
165
  /** Shows an item's submenu in place of the current page. */
128
166
  private enter;
129
167
  /** Returns to the page above, or closes when there is none. */