@fusion.dev/helix-headless 0.143.0 → 0.145.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 (314) hide show
  1. package/custom-elements.json +16990 -1383
  2. package/dist/accordion/item/accordion-item.d.ts +48 -12
  3. package/dist/accordion/item/accordion-item.js +26 -5
  4. package/dist/accordion/root/accordion-root.d.ts +6 -0
  5. package/dist/accordion/root/accordion-root.js +7 -1
  6. package/dist/announcement/root/announcement-root.d.ts +21 -1
  7. package/dist/announcement/root/announcement-root.js +36 -1
  8. package/dist/app-header/root/app-header-root.d.ts +7 -0
  9. package/dist/app-header/root/app-header-root.js +5 -1
  10. package/dist/app-header/sectioning.d.ts +7 -0
  11. package/dist/app-header/sectioning.js +26 -0
  12. package/dist/area-chart/root/area-chart-root.d.ts +12 -4
  13. package/dist/area-chart/root/area-chart-root.js +24 -7
  14. package/dist/avatar/root/avatar-root.d.ts +14 -0
  15. package/dist/avatar/root/avatar-root.js +23 -9
  16. package/dist/badge/root/badge-root.d.ts +11 -7
  17. package/dist/badge/root/badge-root.js +1 -1
  18. package/dist/bar-chart/root/bar-chart-root.d.ts +12 -3
  19. package/dist/bar-chart/root/bar-chart-root.js +17 -10
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +32 -1
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +25 -2
  22. package/dist/button/root/button-root.d.ts +47 -1
  23. package/dist/button/root/button-root.js +38 -4
  24. package/dist/callout/root/callout-root.d.ts +18 -1
  25. package/dist/callout/root/callout-root.js +15 -4
  26. package/dist/canvas/root/canvas-root.d.ts +38 -3
  27. package/dist/canvas/root/canvas-root.js +121 -40
  28. package/dist/card/root/card-root.d.ts +30 -1
  29. package/dist/card/root/card-root.js +38 -5
  30. package/dist/carousel/root/carousel-root.d.ts +90 -0
  31. package/dist/carousel/root/carousel-root.js +134 -12
  32. package/dist/chat/composer/chat-composer.d.ts +38 -6
  33. package/dist/chat/composer/chat-composer.js +42 -9
  34. package/dist/chat/message/chat-message.d.ts +32 -1
  35. package/dist/chat/message/chat-message.js +25 -9
  36. package/dist/chat/naming.d.ts +22 -0
  37. package/dist/chat/naming.js +12 -0
  38. package/dist/chat/relative-clock.d.ts +39 -0
  39. package/dist/chat/relative-clock.js +31 -0
  40. package/dist/chat/root/chat-root.d.ts +36 -0
  41. package/dist/chat/root/chat-root.js +24 -1
  42. package/dist/checkbox/root/checkbox-root.js +2 -0
  43. package/dist/checkbox-group/root/checkbox-group-root.d.ts +53 -3
  44. package/dist/checkbox-group/root/checkbox-group-root.js +71 -22
  45. package/dist/code-block/lines.d.ts +13 -0
  46. package/dist/code-block/lines.js +2 -0
  47. package/dist/code-block/root/code-block-root.d.ts +38 -1
  48. package/dist/code-block/root/code-block-root.js +28 -7
  49. package/dist/color-input/root/color-input-root.d.ts +34 -1
  50. package/dist/color-input/root/color-input-root.js +23 -4
  51. package/dist/color-picker/channels.d.ts +26 -0
  52. package/dist/color-picker/channels.js +84 -0
  53. package/dist/color-picker/root/color-picker-root.d.ts +81 -5
  54. package/dist/color-picker/root/color-picker-root.js +103 -10
  55. package/dist/combobox/root/combobox-root.d.ts +38 -3
  56. package/dist/combobox/root/combobox-root.js +64 -2
  57. package/dist/command/root/command-root.d.ts +30 -4
  58. package/dist/command/root/command-root.js +33 -3
  59. package/dist/copy-button/from.d.ts +22 -0
  60. package/dist/copy-button/from.js +28 -0
  61. package/dist/copy-button/root/copy-button-root.d.ts +47 -1
  62. package/dist/copy-button/root/copy-button-root.js +44 -8
  63. package/dist/data-table/root/data-table-root.d.ts +150 -2
  64. package/dist/data-table/root/data-table-root.js +303 -29
  65. package/dist/data-table/tree.d.ts +40 -0
  66. package/dist/data-table/tree.js +55 -0
  67. package/dist/date-input/root/date-input-root.d.ts +73 -4
  68. package/dist/date-input/root/date-input-root.js +93 -43
  69. package/dist/date-picker/root/date-picker-root.d.ts +145 -1
  70. package/dist/date-picker/root/date-picker-root.js +239 -48
  71. package/dist/description-list/item/description-list-item.d.ts +17 -2
  72. package/dist/description-list/item/description-list-item.js +15 -2
  73. package/dist/description-list/root/description-list-root.d.ts +27 -1
  74. package/dist/description-list/root/description-list-root.js +26 -1
  75. package/dist/dialog/description.d.ts +9 -0
  76. package/dist/dialog/description.js +14 -0
  77. package/dist/dialog/root/dialog-root.d.ts +64 -1
  78. package/dist/dialog/root/dialog-root.js +49 -23
  79. package/dist/donut-chart/root/donut-chart-root.d.ts +25 -4
  80. package/dist/donut-chart/root/donut-chart-root.js +21 -6
  81. package/dist/drawer/root/drawer-root.d.ts +63 -2
  82. package/dist/drawer/root/drawer-root.js +95 -19
  83. package/dist/drawer/swipe-start.d.ts +9 -0
  84. package/dist/drawer/swipe-start.js +12 -0
  85. package/dist/drilldown/item/drilldown-item.d.ts +11 -6
  86. package/dist/drilldown/item/drilldown-item.js +8 -5
  87. package/dist/drilldown/root/drilldown-root.d.ts +11 -0
  88. package/dist/drilldown/root/drilldown-root.js +8 -3
  89. package/dist/element-events.generated.js +61 -13
  90. package/dist/empty/root/empty-root.d.ts +1 -1
  91. package/dist/file-input/root/file-input-root.d.ts +47 -0
  92. package/dist/file-input/root/file-input-root.js +60 -22
  93. package/dist/file-input/screen.d.ts +35 -0
  94. package/dist/file-input/screen.js +49 -0
  95. package/dist/filter-input/menu/filter-menu.d.ts +3 -0
  96. package/dist/filter-input/menu/filter-menu.js +8 -4
  97. package/dist/filter-input/root/filter-input-root.d.ts +38 -4
  98. package/dist/filter-input/root/filter-input-root.js +67 -3
  99. package/dist/filter-input/suggest.d.ts +17 -0
  100. package/dist/filter-input/suggest.js +23 -0
  101. package/dist/format-date/options.d.ts +33 -0
  102. package/dist/format-date/options.js +42 -0
  103. package/dist/format-date/root/format-date-root.d.ts +51 -0
  104. package/dist/format-date/root/format-date-root.js +58 -38
  105. package/dist/heatmap/root/heatmap-root.d.ts +23 -6
  106. package/dist/heatmap/root/heatmap-root.js +24 -13
  107. package/dist/icon/index.d.ts +2 -0
  108. package/dist/icon/index.js +2 -0
  109. package/dist/icon/registry.d.ts +37 -0
  110. package/dist/icon/registry.js +83 -0
  111. package/dist/icon/root/icon-root.d.ts +30 -4
  112. package/dist/icon/root/icon-root.js +41 -6
  113. package/dist/icon/root/index.d.ts +2 -0
  114. package/dist/icon/root/index.js +2 -0
  115. package/dist/icon/set.d.ts +1 -0
  116. package/dist/icon/set.js +3 -0
  117. package/dist/image/root/image-root.d.ts +14 -0
  118. package/dist/image/root/image-root.js +23 -2
  119. package/dist/input/root/input-root.d.ts +11 -0
  120. package/dist/input/root/input-root.js +16 -1
  121. package/dist/kbd/root/kbd-root.d.ts +20 -13
  122. package/dist/kbd/root/kbd-root.js +35 -37
  123. package/dist/kbd/shortcut.d.ts +18 -0
  124. package/dist/kbd/shortcut.js +35 -0
  125. package/dist/legend/hidden-series.d.ts +8 -0
  126. package/dist/legend/hidden-series.js +13 -0
  127. package/dist/legend/root/legend-root.d.ts +56 -0
  128. package/dist/legend/root/legend-root.js +54 -5
  129. package/dist/lightbox/item/lightbox-item.d.ts +15 -0
  130. package/dist/lightbox/item/lightbox-item.js +10 -1
  131. package/dist/lightbox/root/lightbox-root.d.ts +61 -1
  132. package/dist/lightbox/root/lightbox-root.js +94 -4
  133. package/dist/line-chart/root/line-chart-root.d.ts +11 -3
  134. package/dist/line-chart/root/line-chart-root.js +19 -5
  135. package/dist/list/item/list-item.d.ts +68 -1
  136. package/dist/list/item/list-item.js +66 -4
  137. package/dist/list/numbering.d.ts +14 -0
  138. package/dist/list/numbering.js +8 -0
  139. package/dist/list/root/list-root.d.ts +106 -9
  140. package/dist/list/root/list-root.js +189 -4
  141. package/dist/list/selection.d.ts +19 -0
  142. package/dist/list/selection.js +21 -0
  143. package/dist/listbox/root/listbox-root.d.ts +74 -3
  144. package/dist/listbox/root/listbox-root.js +86 -9
  145. package/dist/loader/busy.d.ts +17 -0
  146. package/dist/loader/busy.js +24 -0
  147. package/dist/loader/root/loader-root.d.ts +37 -0
  148. package/dist/loader/root/loader-root.js +52 -3
  149. package/dist/map/root/map-root.d.ts +6 -2
  150. package/dist/map/root/map-root.js +5 -2
  151. package/dist/megamenu/root/megamenu-root.d.ts +21 -0
  152. package/dist/megamenu/root/megamenu-root.js +16 -2
  153. package/dist/menu/index.d.ts +1 -0
  154. package/dist/menu/index.js +1 -0
  155. package/dist/menu/item/menu-item.d.ts +38 -11
  156. package/dist/menu/item/menu-item.js +33 -1
  157. package/dist/menu/root/menu-root.d.ts +25 -1
  158. package/dist/menu/root/menu-root.js +63 -6
  159. package/dist/menu/section/menu-section.d.ts +55 -0
  160. package/dist/menu/section/menu-section.js +60 -0
  161. package/dist/meter/root/meter-root.d.ts +7 -0
  162. package/dist/meter/root/meter-root.js +6 -3
  163. package/dist/navigation/root/navigation-root.d.ts +16 -1
  164. package/dist/navigation/root/navigation-root.js +27 -1
  165. package/dist/notifications/feed-keys.d.ts +10 -0
  166. package/dist/notifications/feed-keys.js +14 -0
  167. package/dist/notifications/notification/notification.d.ts +17 -0
  168. package/dist/notifications/notification/notification.js +21 -2
  169. package/dist/notifications/root/notifications-root.d.ts +60 -0
  170. package/dist/notifications/root/notifications-root.js +76 -1
  171. package/dist/number-input/root/number-input-root.d.ts +47 -0
  172. package/dist/number-input/root/number-input-root.js +58 -22
  173. package/dist/object-inspector/root/object-inspector-root.d.ts +98 -16
  174. package/dist/object-inspector/root/object-inspector-root.js +180 -80
  175. package/dist/object-inspector/rows.d.ts +57 -0
  176. package/dist/object-inspector/rows.js +86 -0
  177. package/dist/option/root/option-root.d.ts +54 -5
  178. package/dist/option/root/option-root.js +34 -5
  179. package/dist/outline/levels.d.ts +31 -0
  180. package/dist/outline/levels.js +23 -0
  181. package/dist/outline/root/outline-root.d.ts +68 -3
  182. package/dist/outline/root/outline-root.js +73 -8
  183. package/dist/pagination/size/pagination-size.d.ts +11 -2
  184. package/dist/pagination/size/pagination-size.js +19 -4
  185. package/dist/pin-input/format.d.ts +7 -0
  186. package/dist/pin-input/format.js +16 -0
  187. package/dist/pin-input/root/pin-input-root.d.ts +21 -0
  188. package/dist/pin-input/root/pin-input-root.js +29 -2
  189. package/dist/popover/root/popover-root.d.ts +62 -4
  190. package/dist/popover/root/popover-root.js +53 -5
  191. package/dist/progress/root/progress-root.d.ts +7 -0
  192. package/dist/progress/root/progress-root.js +9 -3
  193. package/dist/qr-code/export.d.ts +31 -0
  194. package/dist/qr-code/export.js +10 -0
  195. package/dist/qr-code/root/qr-code-root.d.ts +26 -0
  196. package/dist/qr-code/root/qr-code-root.js +49 -2
  197. package/dist/radio/group/radio-group.d.ts +17 -0
  198. package/dist/radio/group/radio-group.js +14 -1
  199. package/dist/rating/root/rating-root.d.ts +25 -0
  200. package/dist/rating/root/rating-root.js +46 -4
  201. package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
  202. package/dist/scatter-chart/root/scatter-chart-root.js +25 -7
  203. package/dist/scroll-area/root/scroll-area-root.d.ts +113 -0
  204. package/dist/scroll-area/root/scroll-area-root.js +218 -3
  205. package/dist/scroll-area/scrollbar.d.ts +65 -0
  206. package/dist/scroll-area/scrollbar.js +24 -0
  207. package/dist/select/root/select-root.d.ts +24 -0
  208. package/dist/select/root/select-root.js +21 -7
  209. package/dist/skeleton/root/skeleton-root.d.ts +25 -0
  210. package/dist/skeleton/root/skeleton-root.js +17 -2
  211. package/dist/slider/root/slider-root.d.ts +39 -0
  212. package/dist/slider/root/slider-root.js +45 -9
  213. package/dist/splitter/root/splitter-root.d.ts +13 -0
  214. package/dist/splitter/root/splitter-root.js +20 -2
  215. package/dist/splitter/snap.d.ts +9 -0
  216. package/dist/splitter/snap.js +12 -0
  217. package/dist/stat/root/stat-root.d.ts +2 -0
  218. package/dist/stat/root/stat-root.js +1 -1
  219. package/dist/steps/reach.d.ts +15 -0
  220. package/dist/steps/reach.js +12 -0
  221. package/dist/steps/root/steps-root.d.ts +30 -0
  222. package/dist/steps/root/steps-root.js +53 -0
  223. package/dist/steps/step/step.d.ts +39 -2
  224. package/dist/steps/step/step.js +29 -7
  225. package/dist/switch/root/switch-root.d.ts +12 -2
  226. package/dist/switch/root/switch-root.js +6 -1
  227. package/dist/table/column/table-column.d.ts +11 -0
  228. package/dist/table/column/table-column.js +6 -1
  229. package/dist/table/root/table-root.d.ts +101 -0
  230. package/dist/table/root/table-root.js +166 -5
  231. package/dist/table/row/table-row.d.ts +47 -1
  232. package/dist/table/row/table-row.js +39 -2
  233. package/dist/tabs/panel/tab-panel.d.ts +9 -5
  234. package/dist/tabs/panel/tab-panel.js +3 -4
  235. package/dist/tabs/root/tabs-root.d.ts +25 -1
  236. package/dist/tabs/root/tabs-root.js +21 -3
  237. package/dist/tabs/tab/tab.d.ts +36 -4
  238. package/dist/tabs/tab/tab.js +46 -3
  239. package/dist/tag/group/tag-group.d.ts +107 -0
  240. package/dist/tag/group/tag-group.js +157 -0
  241. package/dist/tag/index.d.ts +1 -0
  242. package/dist/tag/index.js +1 -0
  243. package/dist/tag/root/tag-root.d.ts +60 -0
  244. package/dist/tag/root/tag-root.js +74 -18
  245. package/dist/tag/selection.d.ts +10 -0
  246. package/dist/tag/selection.js +12 -0
  247. package/dist/textarea/grow.d.ts +22 -0
  248. package/dist/textarea/grow.js +20 -0
  249. package/dist/textarea/root/textarea-root.d.ts +9 -0
  250. package/dist/textarea/root/textarea-root.js +9 -1
  251. package/dist/time-input/root/time-input-root.d.ts +44 -5
  252. package/dist/time-input/root/time-input-root.js +69 -39
  253. package/dist/time-picker/root/time-picker-root.d.ts +54 -8
  254. package/dist/time-picker/root/time-picker-root.js +121 -91
  255. package/dist/timeline/group/timeline-group.d.ts +46 -0
  256. package/dist/timeline/group/timeline-group.js +37 -0
  257. package/dist/timeline/index.d.ts +1 -0
  258. package/dist/timeline/index.js +1 -0
  259. package/dist/timeline/root/timeline-root.d.ts +10 -3
  260. package/dist/toast/hotkey.d.ts +22 -0
  261. package/dist/toast/hotkey.js +23 -0
  262. package/dist/toast/item/toast-item.d.ts +26 -18
  263. package/dist/toast/item/toast-item.js +55 -48
  264. package/dist/toast/root/toast-root.d.ts +42 -8
  265. package/dist/toast/root/toast-root.js +64 -4
  266. package/dist/toggle/group/toggle-group.d.ts +57 -15
  267. package/dist/toggle/group/toggle-group.js +69 -9
  268. package/dist/toggle/root/toggle-root.d.ts +12 -1
  269. package/dist/toggle/root/toggle-root.js +11 -3
  270. package/dist/toolbar/root/toolbar-root.d.ts +13 -0
  271. package/dist/toolbar/root/toolbar-root.js +15 -5
  272. package/dist/tooltip/focus-target.d.ts +6 -0
  273. package/dist/tooltip/focus-target.js +9 -0
  274. package/dist/tooltip/root/tooltip-root.d.ts +42 -17
  275. package/dist/tooltip/root/tooltip-root.js +50 -9
  276. package/dist/tour/naming.d.ts +30 -0
  277. package/dist/tour/naming.js +10 -0
  278. package/dist/tour/root/tour-root.d.ts +35 -0
  279. package/dist/tour/root/tour-root.js +56 -5
  280. package/dist/tour/stop/tour-stop.d.ts +53 -8
  281. package/dist/tour/stop/tour-stop.js +45 -15
  282. package/dist/tour/wait.d.ts +11 -0
  283. package/dist/tour/wait.js +19 -0
  284. package/dist/tree/item/tree-item.d.ts +54 -1
  285. package/dist/tree/item/tree-item.js +89 -20
  286. package/dist/tree/root/tree-root.d.ts +37 -0
  287. package/dist/tree/root/tree-root.js +51 -6
  288. package/dist/tree/selection.d.ts +24 -0
  289. package/dist/tree/selection.js +26 -0
  290. package/dist/trend/root/trend-root.d.ts +13 -0
  291. package/dist/trend/root/trend-root.js +14 -9
  292. package/dist/video/media-events.d.ts +49 -0
  293. package/dist/video/media-events.js +39 -0
  294. package/dist/video/root/video-root.d.ts +99 -13
  295. package/dist/video/root/video-root.js +84 -47
  296. package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -0
  297. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  298. package/dist/video-playlist/order.d.ts +41 -0
  299. package/dist/video-playlist/order.js +34 -0
  300. package/dist/video-playlist/root/video-playlist-root.d.ts +70 -2
  301. package/dist/video-playlist/root/video-playlist-root.js +60 -16
  302. package/dist/virtual-list/controls.d.ts +4 -0
  303. package/dist/virtual-list/controls.js +30 -0
  304. package/dist/virtual-list/fill.d.ts +30 -0
  305. package/dist/virtual-list/fill.js +39 -0
  306. package/dist/virtual-list/keys.d.ts +15 -0
  307. package/dist/virtual-list/keys.js +24 -0
  308. package/dist/virtual-list/root/virtual-list-root.d.ts +155 -23
  309. package/dist/virtual-list/root/virtual-list-root.js +175 -34
  310. package/lint.json +1 -1
  311. package/package.json +5 -5
  312. package/types/jsx.d.ts +8871 -1286
  313. package/types/svelte.d.ts +3 -0
  314. package/types/vue.d.ts +1918 -269
@@ -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,30 @@ 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
+ *
26
32
  * @since 0.1.0
27
33
  * @status stable
28
34
  * @aliases Menu option, Menu row
29
35
  * @documentation https://fusion.dev/helix/components/menu
30
36
  *
31
37
  * @slot - The label.
38
+ * @slot end - Words after the label, such as a count or a detail, beside any `shortcut`.
32
39
  * @slot start - Content before the label, such as an icon.
33
40
  * @slot submenu - The rows of a submenu that this row opens.
34
41
  *
35
42
  * @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
43
  * @event {HelixSelectEvent} helix-select - The row was chosen. Cancelable: cancel it to keep the menu open.
37
44
  *
45
+ * @csspart end - The trailing column that holds the shortcut and the `end` slot. It is always rendered.
38
46
  * @csspart item - The row, which is an `<a>` where `href` is set.
39
47
  * @csspart mark - The trailing mark: a check mark when the row is checked, or a caret
40
48
  * when it opens a submenu. A row never shows both, so they share one column.
49
+ * @csspart shortcut - The key drawn for `shortcut`.
50
+ * @csspart shortcut-kbd - The key inside it.
41
51
  * @csspart start - The leading column that holds the `start` slot. It is always rendered.
42
52
  *
43
53
  * @cssstate checked - The row is checked.
@@ -100,19 +110,16 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
100
110
  * roves, which is the same maneuver `Tree.Item` makes and the reason `href`
101
111
  * changes the markup rather than only adding an attribute.
102
112
  *
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.
113
+ * **`Option` takes an `href` too, and keeps its role a different way.** A
114
+ * listbox keeps focus somewhere else, in the text field for a combobox, and
115
+ * names the current option with `aria-activedescendant`, which can only point
116
+ * at the option's host. So an option stays `role="option"` on its host, draws
117
+ * no anchor, and follows its link through an anchor made for the moment. A
118
+ * menu has no such constraint, because focus really moves to the row.
112
119
  */
113
120
  href: string;
114
121
  /**
115
- * Whether the row is on the menu page that is showing.
122
+ * @internal Whether the row is on the menu page that is showing.
116
123
  *
117
124
  * Set by the menu, and a property rather than an attribute for that reason.
118
125
  * Public because the menu has to reach it, and not something a consumer
@@ -124,7 +131,14 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
124
131
  */
125
132
  rel: string;
126
133
  /**
127
- * Whether the page showing is this row's submenu, or one below it.
134
+ * The row's keyboard shortcut as a chord, such as `mod+s`, shown after the
135
+ * label in the platform's glyphs and given to the row as
136
+ * `aria-keyshortcuts`. `mod` is Command on an Apple keyboard and Control
137
+ * elsewhere. Showing a shortcut does not bind it.
138
+ */
139
+ shortcut: string;
140
+ /**
141
+ * @internal Whether the page showing is this row's submenu, or one below it.
128
142
  *
129
143
  * Separate from `onPage`, and it has to be. A row that opened a page is not
130
144
  * itself on that page, so both are false for it, and rendering nothing on
@@ -185,6 +199,8 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
185
199
  * exactly the thing that is.
186
200
  */
187
201
  private start;
202
+ /** Whether anything is slotted after the label, read the way `start` is. */
203
+ private end;
188
204
  /**
189
205
  * Both answers, from the children, before each render.
190
206
  *
@@ -205,6 +221,17 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
205
221
  * is never rebuilt and never fires on its way out.
206
222
  */
207
223
  private readonly readStart;
224
+ /** Re-reads whether anything is after the label, as `readStart` does before it. */
225
+ private readonly readEnd;
226
+ /**
227
+ * The trailing column: the shortcut's key, then whatever is slotted in `end`.
228
+ * Drawn by `Kbd`, interpolated so each tier draws its own.
229
+ *
230
+ * The key is hidden from the accessibility tree, since the row's
231
+ * `aria-keyshortcuts` says it in words a screen reader reads as keys: inside
232
+ * the row its glyphs became part of the row's name.
233
+ */
234
+ protected renderEnd(): TemplateResult;
208
235
  /**
209
236
  * Re-reads whether anything is in the submenu, from whichever slot is current.
210
237
  *
@@ -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,22 @@ 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;
29
33
  this.readStart = () => {
30
34
  const slot = this.renderRoot.querySelector('slot[name="start"]');
31
35
  this.start = (slot?.assignedNodes().length ?? 0) > 0;
32
36
  };
37
+ this.readEnd = () => {
38
+ const slot = this.renderRoot.querySelector('slot[name="end"]');
39
+ this.end = (slot?.assignedNodes().length ?? 0) > 0;
40
+ };
33
41
  this.readSubmenu = () => {
34
42
  const slot = this.renderRoot.querySelector('slot[name="submenu"]');
35
43
  this.submenu = (slot?.assignedElements().length ?? 0) > 0;
@@ -65,11 +73,25 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
65
73
  const named = (name) => this.knownChildren.some((child) => child.slot === name);
66
74
  this.submenu = named('submenu');
67
75
  this.start = named('start');
76
+ this.end = named('end');
68
77
  }
69
78
  firstUpdated(changed) {
70
79
  super.firstUpdated(changed);
71
80
  this.finishHydration();
72
81
  }
82
+ renderEnd() {
83
+ const kbd = this.tag('kbd');
84
+ return html `<span part="end" ?data-empty=${!this.end && this.shortcut === ''}
85
+ >${this.shortcut === ''
86
+ ? nothing
87
+ : staticHtml `<${kbd}
88
+ part="shortcut"
89
+ exportparts="kbd: shortcut-kbd"
90
+ aria-hidden="true"
91
+ keys=${this.shortcut}
92
+ ></${kbd}>`}<slot name="end" @slotchange=${this.readEnd}></slot
93
+ ></span>`;
94
+ }
73
95
  focus() {
74
96
  this.partNamed('item')?.focus();
75
97
  }
@@ -142,10 +164,12 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
142
164
  const ticks = this.checkable || this.checked;
143
165
  const role = ticks ? (this.exclusive ? 'menuitemradio' : 'menuitemcheckbox') : 'menuitem';
144
166
  const checkedState = ticks ? (this.checked ? 'true' : 'false') : nothing;
167
+ const keys = this.shortcut === '' ? nothing : ariaChord(this.shortcut, !this.hydrating && onApple());
145
168
  const inside = html `<span part="start" ?data-empty=${!this.start}
146
169
  ><slot name="start" @slotchange=${this.readStart}></slot
147
170
  ></span>
148
171
  <slot></slot>
172
+ ${this.renderEnd()}
149
173
  ${submenu
150
174
  ? html `<svg
151
175
  part="mark"
@@ -177,6 +201,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
177
201
  role=${role}
178
202
  aria-checked=${checkedState}
179
203
  aria-haspopup=${submenu ? 'menu' : nothing}
204
+ aria-keyshortcuts=${keys}
180
205
  tabindex="-1"
181
206
  @click=${this.handleClick}
182
207
  @keydown=${this.handleKeydown}
@@ -187,6 +212,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
187
212
  aria-checked=${checkedState}
188
213
  aria-disabled=${this.disabled ? 'true' : nothing}
189
214
  aria-haspopup=${submenu ? 'menu' : nothing}
215
+ aria-keyshortcuts=${keys}
190
216
  tabindex=${this.disabled ? nothing : '-1'}
191
217
  @click=${this.handleClick}
192
218
  @keydown=${this.handleKeydown}
@@ -218,6 +244,9 @@ __decorate([
218
244
  __decorate([
219
245
  property()
220
246
  ], HelixHeadlessMenuItem.prototype, "rel", void 0);
247
+ __decorate([
248
+ property()
249
+ ], HelixHeadlessMenuItem.prototype, "shortcut", void 0);
221
250
  __decorate([
222
251
  property({ attribute: false })
223
252
  ], HelixHeadlessMenuItem.prototype, "showingSubmenu", void 0);
@@ -233,6 +262,9 @@ __decorate([
233
262
  __decorate([
234
263
  state()
235
264
  ], HelixHeadlessMenuItem.prototype, "start", void 0);
265
+ __decorate([
266
+ state()
267
+ ], HelixHeadlessMenuItem.prototype, "end", void 0);
236
268
  HelixHeadlessMenuItem = __decorate([
237
269
  customElement('helix-headless-menu-item')
238
270
  ], HelixHeadlessMenuItem);
@@ -24,6 +24,16 @@ 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
+ * With `context`, the trigger is an area and the menu is its context menu: a
32
+ * right-click, a long press, Shift+F10 or the menu key over the area opens it
33
+ * at the pointer, or at the area's corner from the keyboard, as Radix's context
34
+ * menu does. A press on the area does not open it, and closing it returns focus
35
+ * to where it was.
36
+ *
27
37
  * @since 0.1.0
28
38
  * @status stable
29
39
  * @category overlays
@@ -31,7 +41,7 @@ declare const HelixHeadlessMenu_base: (new (...args: any[]) => import("@fusion.d
31
41
  * @documentation https://fusion.dev/helix/components/menu
32
42
  *
33
43
  * @slot - The items.
34
- * @slot trigger - The control that opens the menu.
44
+ * @slot trigger - The control that opens the menu, or with `context` the area a context menu opens over.
35
45
  *
36
46
  * @event {HelixBeforeCloseEvent} helix-before-close - The menu is about to close. Cancelable: cancel it to keep it open.
37
47
  * @event {HelixBeforeOpenEvent} helix-before-open - The menu is about to open. Cancelable: cancel it to keep it closed.
@@ -57,6 +67,11 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
57
67
  * The text of the row that returns from a submenu.
58
68
  */
59
69
  backLabel: string;
70
+ /**
71
+ * Whether the menu is the trigger's context menu: it opens where the reader
72
+ * asks for one, with a right-click, a long press, Shift+F10 or the menu key.
73
+ */
74
+ context: boolean;
60
75
  /**
61
76
  * Accessible name for the surface.
62
77
  *
@@ -104,6 +119,10 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
104
119
  protected readonly keys: KeyboardNavigationController;
105
120
  /** Carries a host `aria-label` and `aria-labelledby` to the surface. */
106
121
  protected readonly aria: AriaForwardController;
122
+ /** Where a context menu was asked for, in the viewport, while it is open. */
123
+ private point;
124
+ /** Where focus was when a context menu opened, so closing it returns there. */
125
+ private returnTo;
107
126
  private readonly panel;
108
127
  /**
109
128
  * Tells a slotted trigger the disclosure state, by property.
@@ -124,6 +143,11 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
124
143
  private openAt;
125
144
  /** Closes the menu and returns it to its first page. */
126
145
  hide(): void;
146
+ /**
147
+ * Opens a context menu where the reader asked for it: at the pointer, or at
148
+ * the area's corner where the keyboard asked and the event has no position.
149
+ */
150
+ private readonly handleContextMenu;
127
151
  /** Shows an item's submenu in place of the current page. */
128
152
  private enter;
129
153
  /** Returns to the page above, or closes when there is none. */
@@ -9,16 +9,20 @@ import { caretLeft } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
+ const isSection = (element) => 'isMenuSection' in element && element.isMenuSection === true;
12
13
  let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinControllerOpen(HelixElement)) {
13
14
  constructor() {
14
15
  super(...arguments);
15
16
  this.backLabel = 'Back';
17
+ this.context = false;
16
18
  this.label = '';
17
19
  this.offset = 4;
18
20
  this.placement = 'bottom-start';
19
21
  this.drilldown = new DrilldownController(this, {
20
22
  isRow: (element) => 'isMenuItem' in element && element.isMenuItem === true,
21
- root: () => [...this.children].filter((child) => child.slot !== 'trigger'),
23
+ root: () => [...this.children]
24
+ .filter((child) => child.slot !== 'trigger')
25
+ .flatMap((child) => (isSection(child) ? [...child.children] : [child])),
22
26
  slot: 'submenu',
23
27
  });
24
28
  this.keys = new KeyboardNavigationController(this, {
@@ -32,7 +36,9 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
32
36
  target: () => this.partNamed('surface'),
33
37
  });
34
38
  this.panel = new PopoverController(this, {
35
- anchor: () => this.trigger,
39
+ anchor: () => this.context && this.point
40
+ ? this.renderRoot?.querySelector('#point')
41
+ : this.trigger,
36
42
  boundary: () => this,
37
43
  offset: () => this.offset,
38
44
  onDismiss: () => this.hide(),
@@ -40,6 +46,28 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
40
46
  placement: () => this.placement,
41
47
  surface: () => this.partNamed('surface'),
42
48
  });
49
+ this.handleContextMenu = (event) => {
50
+ if (!this.context) {
51
+ return;
52
+ }
53
+ const slot = this.renderRoot?.querySelector('slot[name="trigger"]');
54
+ const areas = slot?.assignedElements({ flatten: true }) ?? [];
55
+ if (!event.composedPath().some((node) => areas.includes(node))) {
56
+ return;
57
+ }
58
+ event.preventDefault();
59
+ const box = this.trigger.getBoundingClientRect();
60
+ const keyed = event.clientX === 0 && event.clientY === 0;
61
+ this.point = keyed ? { x: box.left, y: box.top } : { x: event.clientX, y: event.clientY };
62
+ if (this.open) {
63
+ this.requestUpdate();
64
+ this.updateComplete.then(() => this.panel.reposition());
65
+ return;
66
+ }
67
+ const active = deepActiveElement();
68
+ this.returnTo = active instanceof HTMLElement ? active : undefined;
69
+ this.show();
70
+ };
43
71
  this.handleSelect = (event) => {
44
72
  const item = event.target;
45
73
  if (!this.items.includes(item)) {
@@ -93,6 +121,9 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
93
121
  event.preventDefault();
94
122
  };
95
123
  this.handleTriggerClick = () => {
124
+ if (this.context) {
125
+ return;
126
+ }
96
127
  if (this.open) {
97
128
  this.hide();
98
129
  }
@@ -130,8 +161,11 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
130
161
  this.panel.hide();
131
162
  this.drilldown.reset();
132
163
  if (deepContains(this, deepActiveElement())) {
133
- this.trigger.focus();
164
+ ;
165
+ (this.context ? (this.returnTo ?? this.trigger) : this.trigger).focus();
134
166
  }
167
+ this.point = undefined;
168
+ this.returnTo = undefined;
135
169
  }
136
170
  enter(item) {
137
171
  if (!this.drilldown.enter(item)) {
@@ -149,7 +183,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
149
183
  this.updateComplete.then(() => leaving?.focus());
150
184
  }
151
185
  handleClosedKeydown(event) {
152
- if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') {
186
+ if (this.context || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) {
153
187
  return;
154
188
  }
155
189
  const slot = this.renderRoot?.querySelector('slot[name="trigger"]');
@@ -165,17 +199,30 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
165
199
  this.addEventListener('helix-select', this.handleSelect, true);
166
200
  this.addEventListener('keydown', this.handleKeydown);
167
201
  this.addEventListener('focusin', this.handleFocusIn);
202
+ this.addEventListener('contextmenu', this.handleContextMenu);
168
203
  }
169
204
  disconnectedCallback() {
170
205
  super.disconnectedCallback();
171
206
  this.removeEventListener('helix-select', this.handleSelect, true);
172
207
  this.removeEventListener('keydown', this.handleKeydown);
173
208
  this.removeEventListener('focusin', this.handleFocusIn);
209
+ this.removeEventListener('contextmenu', this.handleContextMenu);
174
210
  }
175
211
  updated(changed) {
176
212
  super.updated(changed);
177
- this.tellTrigger();
213
+ if (!this.context) {
214
+ this.tellTrigger();
215
+ }
178
216
  for (const child of this.children) {
217
+ if (isSection(child)) {
218
+ child.onPage = this.drilldown.atRoot;
219
+ for (const row of child.children) {
220
+ if (row.isMenuItem === true) {
221
+ this.mark(row, this.drilldown.atRoot);
222
+ }
223
+ }
224
+ continue;
225
+ }
179
226
  const item = child;
180
227
  if (item.isMenuItem !== true) {
181
228
  continue;
@@ -217,7 +264,14 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
217
264
  }
218
265
  }
219
266
  render() {
220
- return html `<slot name="trigger" @click=${this.handleTriggerClick}></slot>
267
+ const point = this.context && this.point;
268
+ return html `<slot name="trigger" @click=${this.handleTriggerClick}></slot>${point
269
+ ? html `<span
270
+ id="point"
271
+ aria-hidden="true"
272
+ style="position: fixed; left: 0; top: 0; translate: ${point.x}px ${point.y}px"
273
+ ></span>`
274
+ : nothing}
221
275
  <div
222
276
  part="surface"
223
277
  popover="manual"
@@ -250,6 +304,9 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
250
304
  __decorate([
251
305
  property({ attribute: 'back-label' })
252
306
  ], HelixHeadlessMenu.prototype, "backLabel", void 0);
307
+ __decorate([
308
+ property({ reflect: true, type: Boolean })
309
+ ], HelixHeadlessMenu.prototype, "context", void 0);
253
310
  __decorate([
254
311
  property()
255
312
  ], HelixHeadlessMenu.prototype, "label", void 0);
@@ -0,0 +1,55 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
+ /**
4
+ * @summary A group of rows in a menu, under a heading.
5
+ *
6
+ * @description
7
+ * The rows inside it are the menu's rows: the arrow keys and typed letters move
8
+ * through them with the rest, and a set of `exclusive` rows is still the run
9
+ * of them next to each other. The section is a `group` named by its heading,
10
+ * as React Aria's menu section is, so a screen reader says which group a row
11
+ * is in as it arrives.
12
+ *
13
+ * A section goes on the menu's first page. A submenu's page takes rows and
14
+ * dividers.
15
+ *
16
+ * @since 0.1.0
17
+ * @status stable
18
+ * @aliases Menu group, Menu heading
19
+ * @documentation https://fusion.dev/helix/components/menu
20
+ *
21
+ * @slot - The rows.
22
+ * @slot heading - The heading, for content richer than `label`.
23
+ *
24
+ * @csspart heading - The words that name the section.
25
+ * @csspart section - The group that holds the heading and the rows.
26
+ */
27
+ export declare class HelixHeadlessMenuSection extends HelixElement {
28
+ /**
29
+ * The heading's words, when they are plain text. Content in the `heading`
30
+ * slot replaces it.
31
+ */
32
+ label: string;
33
+ /**
34
+ * @internal Set by the menu: whether the section is on the page showing. A
35
+ * property, because the menu writes it after the section upgrades.
36
+ */
37
+ onPage: boolean;
38
+ /** @internal What a menu reads to tell a section from a row. */
39
+ readonly isMenuSection = true;
40
+ /**
41
+ * Whether anything is slotted as the heading, read from the children before
42
+ * each render and from the slot after. A section with neither this nor a
43
+ * `label` draws no heading.
44
+ */
45
+ private heading;
46
+ protected willUpdate(changed: PropertyValues): void;
47
+ protected firstUpdated(changed: PropertyValues): void;
48
+ private readonly readHeading;
49
+ protected render(): TemplateResult | typeof nothing;
50
+ }
51
+ declare global {
52
+ interface HTMLElementTagNameMap {
53
+ 'helix-headless-menu-section': HelixHeadlessMenuSection;
54
+ }
55
+ }
@@ -0,0 +1,60 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { customElement, HelixElement } from '@fusion.dev/helix-core';
8
+ import { html, nothing } from 'lit';
9
+ import { property, state } from 'lit/decorators.js';
10
+ let HelixHeadlessMenuSection = class HelixHeadlessMenuSection extends HelixElement {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.label = '';
14
+ this.onPage = true;
15
+ this.isMenuSection = true;
16
+ this.heading = false;
17
+ this.readHeading = (event) => {
18
+ this.heading = event.target.assignedNodes().length > 0;
19
+ };
20
+ }
21
+ willUpdate(changed) {
22
+ super.willUpdate(changed);
23
+ if (this.childrenKnown) {
24
+ this.heading = this.knownChildren.some((child) => child.slot === 'heading');
25
+ }
26
+ }
27
+ firstUpdated(changed) {
28
+ super.firstUpdated(changed);
29
+ this.finishHydration();
30
+ }
31
+ render() {
32
+ if (!this.onPage) {
33
+ return nothing;
34
+ }
35
+ return html `<div part="section" role="group" aria-labelledby="heading">
36
+ <div
37
+ id="heading"
38
+ part="heading"
39
+ role="presentation"
40
+ ?hidden=${this.label === '' && !this.heading}
41
+ >
42
+ <slot name="heading" @slotchange=${this.readHeading}>${this.label}</slot>
43
+ </div>
44
+ <slot></slot>
45
+ </div>`;
46
+ }
47
+ };
48
+ __decorate([
49
+ property()
50
+ ], HelixHeadlessMenuSection.prototype, "label", void 0);
51
+ __decorate([
52
+ property({ attribute: false })
53
+ ], HelixHeadlessMenuSection.prototype, "onPage", void 0);
54
+ __decorate([
55
+ state()
56
+ ], HelixHeadlessMenuSection.prototype, "heading", void 0);
57
+ HelixHeadlessMenuSection = __decorate([
58
+ customElement('helix-headless-menu-section')
59
+ ], HelixHeadlessMenuSection);
60
+ export { HelixHeadlessMenuSection };
@@ -22,6 +22,8 @@ export type MeterBand = 'fair' | 'good' | 'poor';
22
22
  * @aliases Gauge, Level, Capacity
23
23
  * @documentation https://fusion.dev/helix/components/meter
24
24
  *
25
+ * @slot label - A visible label. Its words name the meter in place of `label`.
26
+ *
25
27
  * @csspart groove - The unfilled circle, in `ring` and `arc`.
26
28
  * @csspart indicator - The part of the track the value fills.
27
29
  * @csspart indicator-fair - The fill while the reading is in a band next to the good band. Set alongside `indicator`.
@@ -77,6 +79,9 @@ export declare class HelixHeadlessMeter extends HelixElement {
77
79
  high?: number;
78
80
  /**
79
81
  * Accessible name for the reading.
82
+ *
83
+ * A visible label in the `label` slot names it instead, with the words it
84
+ * shows, so the name and the label cannot disagree.
80
85
  */
81
86
  label: string;
82
87
  /**
@@ -218,6 +223,8 @@ export declare class HelixHeadlessMeter extends HelixElement {
218
223
  * where the arc starts.
219
224
  */
220
225
  private renderRound;
226
+ /** A label's words changed, so the name is written again. */
227
+ private readonly renamed;
221
228
  protected render(): TemplateResult;
222
229
  }
223
230
  declare global {
@@ -4,7 +4,7 @@ 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, HelixElement, hostAria } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, hostAria, slottedWords } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  const percents = new Map();
@@ -29,6 +29,9 @@ let HelixHeadlessMeter = class HelixHeadlessMeter extends HelixElement {
29
29
  this.showValue = false;
30
30
  this.value = 0;
31
31
  this.valueText = '';
32
+ this.renamed = () => {
33
+ this.requestUpdate();
34
+ };
32
35
  }
33
36
  get span() {
34
37
  const min = Number.isFinite(this.min) ? this.min : 0;
@@ -82,7 +85,7 @@ let HelixHeadlessMeter = class HelixHeadlessMeter extends HelixElement {
82
85
  willUpdate(changed) {
83
86
  super.willUpdate(changed);
84
87
  const { max, min } = this.span;
85
- hostAria(this.internals, { label: this.label, role: 'meter' });
88
+ hostAria(this.internals, { label: slottedWords(this, ['label']) || this.label, role: 'meter' });
86
89
  this.internals.ariaValueMin = String(min);
87
90
  this.internals.ariaValueMax = String(max);
88
91
  this.internals.ariaValueNow = String(this.reading);
@@ -121,7 +124,7 @@ let HelixHeadlessMeter = class HelixHeadlessMeter extends HelixElement {
121
124
  const beyond = this.beyond;
122
125
  const { max, min } = this.span;
123
126
  const at = (value) => `${((value - min) / (max - min)) * 100}%`;
124
- return html `${this.shape
127
+ return html `<slot name="label" @slotchange=${this.renamed}></slot>${this.shape
125
128
  ? this.renderRound()
126
129
  : html `<div part="track">
127
130
  ${bounds
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, HelixElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * @summary A set of destinations, with a marker on the current one.
@@ -22,6 +22,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
22
22
  * Set `orientation="vertical"` to stack the destinations in a column, for a
23
23
  * sidebar.
24
24
  *
25
+ * Set `arrows` and the arrow keys move focus between the destinations, as
26
+ * Radix's navigation menu does: left and right along a row, up and down a
27
+ * column, Home and End to the ends. A `Megamenu` among them is reached at its
28
+ * trigger. Tab still stops at every link, since a navigation is a list of
29
+ * links and not a single control, and the keys across the other axis are
30
+ * left to the page and to an open panel.
31
+ *
25
32
  * @since 0.1.0
26
33
  * @status stable
27
34
  * @category navigation
@@ -35,6 +42,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
35
42
  */
36
43
  export declare class HelixHeadlessNavigation extends HelixElement {
37
44
  private readonly indicator;
45
+ /**
46
+ * Whether the arrow keys move focus between the destinations.
47
+ *
48
+ * Off by default, so a page whose readers scroll with the arrow keys while
49
+ * a link has focus keeps doing so.
50
+ */
51
+ arrows: boolean;
38
52
  /**
39
53
  * The landmark's name, read by screen readers.
40
54
  *
@@ -43,6 +57,7 @@ export declare class HelixHeadlessNavigation extends HelixElement {
43
57
  * second "Main".
44
58
  */
45
59
  label: string;
60
+ protected readonly keys: KeyboardNavigationController;
46
61
  /** Carries a host `aria-label` and `aria-labelledby` to the `<nav>`. */
47
62
  protected readonly aria: AriaForwardController;
48
63
  /**