@fusion.dev/helix-headless 0.147.0 → 0.149.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 (296) hide show
  1. package/custom-elements.json +11222 -2332
  2. package/dist/accordion/item/accordion-item.d.ts +5 -2
  3. package/dist/accordion/item/accordion-item.js +11 -7
  4. package/dist/accordion/root/accordion-root.js +4 -6
  5. package/dist/announcement/root/announcement-root.d.ts +61 -5
  6. package/dist/announcement/root/announcement-root.js +126 -20
  7. package/dist/area-chart/root/area-chart-root.d.ts +11 -0
  8. package/dist/area-chart/root/area-chart-root.js +21 -9
  9. package/dist/avatar/group/avatar-group.d.ts +10 -2
  10. package/dist/avatar/group/avatar-group.js +10 -5
  11. package/dist/badge/count.d.ts +19 -0
  12. package/dist/badge/count.js +20 -0
  13. package/dist/badge/root/badge-root.d.ts +6 -2
  14. package/dist/badge/root/badge-root.js +8 -22
  15. package/dist/bar-chart/root/bar-chart-root.d.ts +13 -1
  16. package/dist/bar-chart/root/bar-chart-root.js +17 -4
  17. package/dist/blockquote/root/blockquote-root.d.ts +66 -8
  18. package/dist/blockquote/root/blockquote-root.js +85 -8
  19. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +2 -2
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +8 -1
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +5 -2
  22. package/dist/button/root/button-root.d.ts +10 -1
  23. package/dist/button/root/button-root.js +3 -2
  24. package/dist/button-group/arrows.d.ts +14 -0
  25. package/dist/button-group/arrows.js +10 -0
  26. package/dist/button-group/root/button-group-root.d.ts +144 -3
  27. package/dist/button-group/root/button-group-root.js +142 -2
  28. package/dist/callout/root/callout-root.d.ts +16 -2
  29. package/dist/callout/root/callout-root.js +16 -4
  30. package/dist/canvas/describe.d.ts +10 -0
  31. package/dist/canvas/describe.js +9 -0
  32. package/dist/canvas/edge-pan.d.ts +61 -0
  33. package/dist/canvas/edge-pan.js +63 -0
  34. package/dist/canvas/neighbor.d.ts +17 -0
  35. package/dist/canvas/neighbor.js +1 -0
  36. package/dist/canvas/node/canvas-node.d.ts +12 -2
  37. package/dist/canvas/node/canvas-node.js +8 -3
  38. package/dist/canvas/root/canvas-document.d.ts +10 -0
  39. package/dist/canvas/root/canvas-document.js +1 -0
  40. package/dist/canvas/root/canvas-root.d.ts +168 -9
  41. package/dist/canvas/root/canvas-root.js +239 -69
  42. package/dist/canvas/snap.d.ts +42 -0
  43. package/dist/canvas/snap.js +32 -0
  44. package/dist/carousel/root/carousel-root.d.ts +9 -3
  45. package/dist/carousel/root/carousel-root.js +17 -10
  46. package/dist/chat/composer/chat-composer.d.ts +5 -0
  47. package/dist/chat/composer/chat-composer.js +4 -1
  48. package/dist/chat/message/chat-message.d.ts +9 -5
  49. package/dist/chat/message/chat-message.js +8 -4
  50. package/dist/chat/naming.js +2 -2
  51. package/dist/chat/root/chat-root.d.ts +40 -4
  52. package/dist/chat/root/chat-root.js +38 -14
  53. package/dist/checkbox/root/checkbox-root.d.ts +11 -0
  54. package/dist/checkbox/root/checkbox-root.js +8 -2
  55. package/dist/checkbox-group/root/checkbox-group-root.d.ts +24 -0
  56. package/dist/checkbox-group/root/checkbox-group-root.js +28 -12
  57. package/dist/checkbox-group/select-all.d.ts +11 -0
  58. package/dist/checkbox-group/select-all.js +5 -0
  59. package/dist/code-block/root/code-block-root.d.ts +20 -8
  60. package/dist/code-block/root/code-block-root.js +16 -5
  61. package/dist/color-input/root/color-input-root.d.ts +13 -0
  62. package/dist/color-input/root/color-input-root.js +18 -5
  63. package/dist/color-picker/root/color-picker-root.d.ts +19 -3
  64. package/dist/color-picker/root/color-picker-root.js +52 -24
  65. package/dist/color-picker/wheel.d.ts +11 -0
  66. package/dist/color-picker/wheel.js +9 -0
  67. package/dist/combobox/root/combobox-root.d.ts +106 -18
  68. package/dist/combobox/root/combobox-root.js +101 -26
  69. package/dist/command/root/command-root.d.ts +19 -9
  70. package/dist/command/root/command-root.js +9 -4
  71. package/dist/copy-button/root/copy-button-root.d.ts +5 -0
  72. package/dist/copy-button/root/copy-button-root.js +7 -2
  73. package/dist/data-table/root/data-table-root.d.ts +171 -29
  74. package/dist/data-table/root/data-table-root.js +284 -161
  75. package/dist/date-input/root/date-input-root.d.ts +32 -9
  76. package/dist/date-input/root/date-input-root.js +66 -44
  77. package/dist/date-picker/root/date-picker-root.d.ts +38 -15
  78. package/dist/date-picker/root/date-picker-root.js +52 -33
  79. package/dist/description-list/item/description-list-item.d.ts +4 -0
  80. package/dist/description-list/item/description-list-item.js +7 -1
  81. package/dist/dialog/root/dialog-root.d.ts +12 -1
  82. package/dist/dialog/root/dialog-root.js +11 -4
  83. package/dist/divider/root/divider-root.d.ts +45 -16
  84. package/dist/divider/root/divider-root.js +28 -4
  85. package/dist/donut-chart/root/donut-chart-root.d.ts +5 -0
  86. package/dist/donut-chart/root/donut-chart-root.js +8 -5
  87. package/dist/drawer/root/drawer-root.d.ts +12 -1
  88. package/dist/drawer/root/drawer-root.js +9 -2
  89. package/dist/drilldown/item/drilldown-item.js +3 -4
  90. package/dist/drilldown/root/drilldown-root.d.ts +5 -0
  91. package/dist/drilldown/root/drilldown-root.js +4 -1
  92. package/dist/element-events.generated.js +26 -3
  93. package/dist/empty/root/empty-root.d.ts +17 -3
  94. package/dist/empty/root/empty-root.js +29 -5
  95. package/dist/file-input/root/file-input-root.d.ts +18 -3
  96. package/dist/file-input/root/file-input-root.js +34 -18
  97. package/dist/file-input/status.js +2 -1
  98. package/dist/filter-input/chip/filter-chip.d.ts +13 -0
  99. package/dist/filter-input/chip/filter-chip.js +7 -3
  100. package/dist/filter-input/menu/filter-menu.d.ts +5 -0
  101. package/dist/filter-input/menu/filter-menu.js +3 -1
  102. package/dist/filter-input/root/filter-input-root.d.ts +20 -3
  103. package/dist/filter-input/root/filter-input-root.js +8 -4
  104. package/dist/format-date/fields.d.ts +20 -0
  105. package/dist/format-date/fields.js +14 -0
  106. package/dist/format-date/options.d.ts +2 -1
  107. package/dist/format-date/options.js +9 -38
  108. package/dist/format-date/root/format-date-root.d.ts +55 -5
  109. package/dist/format-date/root/format-date-root.js +29 -34
  110. package/dist/graph/root/graph-model.d.ts +11 -0
  111. package/dist/graph/root/graph-model.js +4 -0
  112. package/dist/graph/root/graph-root.d.ts +101 -14
  113. package/dist/graph/root/graph-root.js +140 -46
  114. package/dist/heatmap/root/heatmap-root.d.ts +37 -4
  115. package/dist/heatmap/root/heatmap-root.js +51 -16
  116. package/dist/icon-button/root/icon-button-root.d.ts +5 -0
  117. package/dist/icon-button/root/icon-button-root.js +1 -0
  118. package/dist/image/root/image-root.d.ts +11 -0
  119. package/dist/input/root/input-root.d.ts +5 -0
  120. package/dist/input/root/input-root.js +4 -1
  121. package/dist/kbd/root/kbd-root.js +1 -2
  122. package/dist/legend/root/legend-root.js +4 -17
  123. package/dist/lightbox/root/lightbox-root.d.ts +14 -4
  124. package/dist/lightbox/root/lightbox-root.js +25 -7
  125. package/dist/line-chart/root/line-chart-root.d.ts +5 -0
  126. package/dist/line-chart/root/line-chart-root.js +4 -2
  127. package/dist/list/item/list-item.d.ts +32 -0
  128. package/dist/list/item/list-item.js +34 -7
  129. package/dist/list/root/list-root.d.ts +87 -0
  130. package/dist/list/root/list-root.js +119 -23
  131. package/dist/listbox/reorder/index.d.ts +1 -0
  132. package/dist/listbox/reorder/index.js +4 -0
  133. package/dist/listbox/reorder/listbox-reorder.d.ts +83 -0
  134. package/dist/listbox/reorder/listbox-reorder.js +151 -0
  135. package/dist/listbox/root/listbox-root.d.ts +185 -33
  136. package/dist/listbox/root/listbox-root.js +166 -136
  137. package/dist/loader/root/loader-root.d.ts +5 -0
  138. package/dist/loader/root/loader-root.js +3 -1
  139. package/dist/map/area/map-area.d.ts +5 -0
  140. package/dist/map/area/map-area.js +3 -1
  141. package/dist/map/buildings/map-buildings.d.ts +5 -0
  142. package/dist/map/buildings/map-buildings.js +3 -1
  143. package/dist/map/contours/map-contours.d.ts +5 -0
  144. package/dist/map/contours/map-contours.js +3 -1
  145. package/dist/map/landcover/map-landcover.d.ts +5 -0
  146. package/dist/map/landcover/map-landcover.js +3 -1
  147. package/dist/map/marker/map-marker.d.ts +18 -2
  148. package/dist/map/marker/map-marker.js +13 -5
  149. package/dist/map/points/map-points.d.ts +5 -0
  150. package/dist/map/points/map-points.js +6 -4
  151. package/dist/map/position/map-position.d.ts +5 -0
  152. package/dist/map/position/map-position.js +3 -1
  153. package/dist/map/raster/map-raster.d.ts +5 -0
  154. package/dist/map/raster/map-raster.js +3 -1
  155. package/dist/map/root/map-root.d.ts +73 -16
  156. package/dist/map/root/map-root.js +122 -44
  157. package/dist/map/route/map-route.d.ts +5 -0
  158. package/dist/map/route/map-route.js +3 -1
  159. package/dist/map/terrain/map-terrain.d.ts +5 -0
  160. package/dist/map/terrain/map-terrain.js +3 -1
  161. package/dist/megamenu/root/megamenu-root.js +4 -11
  162. package/dist/menu/root/menu-root.d.ts +7 -2
  163. package/dist/menu/root/menu-root.js +3 -1
  164. package/dist/meter/root/meter-root.d.ts +16 -3
  165. package/dist/meter/root/meter-root.js +12 -5
  166. package/dist/navigation/root/navigation-root.d.ts +5 -0
  167. package/dist/navigation/root/navigation-root.js +7 -8
  168. package/dist/notifications/group/notification-group.d.ts +8 -1
  169. package/dist/notifications/group/notification-group.js +12 -6
  170. package/dist/notifications/notification/notification.d.ts +17 -1
  171. package/dist/notifications/notification/notification.js +6 -2
  172. package/dist/notifications/root/notifications-root.d.ts +49 -5
  173. package/dist/notifications/root/notifications-root.js +36 -8
  174. package/dist/number-input/root/number-input-root.d.ts +21 -8
  175. package/dist/number-input/root/number-input-root.js +26 -11
  176. package/dist/object-inspector/root/object-inspector-root.d.ts +56 -13
  177. package/dist/object-inspector/root/object-inspector-root.js +41 -30
  178. package/dist/outline/root/outline-root.d.ts +5 -0
  179. package/dist/outline/root/outline-root.js +3 -1
  180. package/dist/pagination/root/pagination-root.d.ts +7 -1
  181. package/dist/pagination/root/pagination-root.js +16 -7
  182. package/dist/pagination/size/pagination-size.d.ts +8 -3
  183. package/dist/pagination/size/pagination-size.js +5 -3
  184. package/dist/pagination/summary/pagination-summary.d.ts +8 -6
  185. package/dist/pagination/summary/pagination-summary.js +4 -9
  186. package/dist/pin-input/code.d.ts +23 -0
  187. package/dist/pin-input/code.js +21 -0
  188. package/dist/pin-input/root/pin-input-root.d.ts +9 -3
  189. package/dist/pin-input/root/pin-input-root.js +13 -23
  190. package/dist/popover/root/popover-root.d.ts +12 -2
  191. package/dist/popover/root/popover-root.js +10 -4
  192. package/dist/progress/root/progress-root.d.ts +7 -2
  193. package/dist/progress/root/progress-root.js +5 -2
  194. package/dist/qr-code/name.d.ts +3 -2
  195. package/dist/qr-code/name.js +2 -2
  196. package/dist/qr-code/root/qr-code-root.d.ts +26 -5
  197. package/dist/qr-code/root/qr-code-root.js +27 -6
  198. package/dist/radio/group/radio-group.d.ts +11 -0
  199. package/dist/radio/group/radio-group.js +8 -2
  200. package/dist/rating/root/rating-root.d.ts +12 -1
  201. package/dist/rating/root/rating-root.js +10 -3
  202. package/dist/scatter-chart/measures.d.ts +22 -0
  203. package/dist/scatter-chart/measures.js +11 -0
  204. package/dist/scatter-chart/root/scatter-chart-root.d.ts +22 -8
  205. package/dist/scatter-chart/root/scatter-chart-root.js +25 -23
  206. package/dist/select/root/select-root.d.ts +67 -8
  207. package/dist/select/root/select-root.js +45 -11
  208. package/dist/skip-link/root/skip-link-root.d.ts +5 -0
  209. package/dist/skip-link/root/skip-link-root.js +3 -1
  210. package/dist/slider/root/slider-root.d.ts +7 -2
  211. package/dist/slider/root/slider-root.js +11 -5
  212. package/dist/sparkline/readings.d.ts +15 -5
  213. package/dist/sparkline/readings.js +3 -3
  214. package/dist/sparkline/root/sparkline-root.d.ts +9 -4
  215. package/dist/sparkline/root/sparkline-root.js +9 -3
  216. package/dist/speed-dial/group/speed-dial-group.d.ts +48 -0
  217. package/dist/speed-dial/group/speed-dial-group.js +43 -0
  218. package/dist/speed-dial/index.d.ts +1 -0
  219. package/dist/speed-dial/index.js +1 -0
  220. package/dist/speed-dial/item/speed-dial-item.d.ts +26 -1
  221. package/dist/speed-dial/item/speed-dial-item.js +41 -3
  222. package/dist/speed-dial/long-press.d.ts +50 -0
  223. package/dist/speed-dial/long-press.js +48 -0
  224. package/dist/speed-dial/root/speed-dial-root.d.ts +85 -1
  225. package/dist/speed-dial/root/speed-dial-root.js +117 -5
  226. package/dist/splitter/root/splitter-root.d.ts +12 -3
  227. package/dist/splitter/root/splitter-root.js +22 -4
  228. package/dist/steps/root/steps-root.d.ts +9 -2
  229. package/dist/steps/root/steps-root.js +6 -3
  230. package/dist/steps/step/step.d.ts +12 -0
  231. package/dist/steps/step/step.js +11 -2
  232. package/dist/switch/root/switch-root.d.ts +11 -0
  233. package/dist/switch/root/switch-root.js +8 -2
  234. package/dist/table/cell/table-cell.d.ts +8 -1
  235. package/dist/table/cell/table-cell.js +6 -2
  236. package/dist/table/column/table-column.d.ts +11 -4
  237. package/dist/table/column/table-column.js +15 -10
  238. package/dist/table/grid-controller.d.ts +122 -0
  239. package/dist/table/grid-controller.js +299 -0
  240. package/dist/table/root/table-root.d.ts +154 -8
  241. package/dist/table/root/table-root.js +197 -38
  242. package/dist/table/row/table-row.d.ts +55 -1
  243. package/dist/table/row/table-row.js +60 -7
  244. package/dist/tabs/root/tabs-root.d.ts +5 -0
  245. package/dist/tabs/root/tabs-root.js +3 -1
  246. package/dist/tag/group/tag-group.d.ts +5 -0
  247. package/dist/tag/group/tag-group.js +8 -15
  248. package/dist/tag/root/tag-root.d.ts +14 -1
  249. package/dist/tag/root/tag-root.js +11 -8
  250. package/dist/textarea/root/textarea-root.d.ts +5 -0
  251. package/dist/textarea/root/textarea-root.js +3 -1
  252. package/dist/time-input/root/time-input-root.d.ts +14 -7
  253. package/dist/time-input/root/time-input-root.js +43 -32
  254. package/dist/time-picker/root/time-picker-root.d.ts +16 -7
  255. package/dist/time-picker/root/time-picker-root.js +28 -12
  256. package/dist/toast/item/toast-item.d.ts +13 -3
  257. package/dist/toast/item/toast-item.js +2 -0
  258. package/dist/toast/root/toast-root.d.ts +22 -2
  259. package/dist/toast/root/toast-root.js +24 -17
  260. package/dist/toggle/group/toggle-group.js +3 -5
  261. package/dist/toolbar/root/toolbar-root.d.ts +36 -0
  262. package/dist/toolbar/root/toolbar-root.js +43 -9
  263. package/dist/tooltip/root/tooltip-root.d.ts +27 -17
  264. package/dist/tooltip/root/tooltip-root.js +1 -2
  265. package/dist/tour/root/tour-root.d.ts +8 -2
  266. package/dist/tour/root/tour-root.js +4 -2
  267. package/dist/tour/stop/tour-stop.d.ts +5 -0
  268. package/dist/tour/stop/tour-stop.js +6 -1
  269. package/dist/tree/grip.d.ts +5 -0
  270. package/dist/tree/grip.js +1 -0
  271. package/dist/tree/item/tree-item.d.ts +24 -0
  272. package/dist/tree/item/tree-item.js +27 -5
  273. package/dist/tree/root/tree-root.d.ts +66 -1
  274. package/dist/tree/root/tree-root.js +77 -5
  275. package/dist/trend/root/trend-root.d.ts +20 -4
  276. package/dist/trend/root/trend-root.js +20 -5
  277. package/dist/video/root/video-root.d.ts +23 -3
  278. package/dist/video/root/video-root.js +56 -15
  279. package/dist/video-playlist/menu/video-playlist-menu.d.ts +5 -0
  280. package/dist/video-playlist/menu/video-playlist-menu.js +3 -1
  281. package/dist/video-playlist/root/video-playlist-root.d.ts +5 -0
  282. package/dist/video-playlist/root/video-playlist-root.js +3 -1
  283. package/dist/virtual-list/root/virtual-list-root.d.ts +19 -3
  284. package/dist/virtual-list/root/virtual-list-root.js +10 -19
  285. package/lint.json +1 -1
  286. package/package.json +7 -6
  287. package/types/jsx.d.ts +6005 -2114
  288. package/types/svelte.d.ts +1 -0
  289. package/types/vue.d.ts +1594 -676
  290. package/types/words.d.ts +3280 -0
  291. package/dist/kbd/keys.d.ts +0 -29
  292. package/dist/kbd/keys.js +0 -52
  293. package/dist/toast/hotkey.d.ts +0 -22
  294. package/dist/toast/hotkey.js +0 -23
  295. package/dist/tooltip/focus-target.d.ts +0 -6
  296. package/dist/tooltip/focus-target.js +0 -9
@@ -9,19 +9,62 @@ import { type PropertyValues, type TemplateResult } from 'lit';
9
9
  * controls, and a development build warns once with the kind `missing-name`.
10
10
  *
11
11
  * The group does not manage focus, so each button keeps its own tab stop. For
12
- * one tab stop with arrow keys between the buttons, the APG toolbar pattern,
13
- * use Toolbar.
12
+ * one tab stop with arrow keys between a mix of controls, the APG toolbar
13
+ * pattern, use Toolbar.
14
14
  *
15
15
  * Buttons are slotted, so they stay your own elements and keep their own
16
16
  * appearance and size.
17
17
  *
18
+ * **Buttons that stay pressed.** With `selection-mode`, each button with a
19
+ * `value` is a toggle button: a press presses or releases it, and it says so
20
+ * with `aria-pressed`. `single` keeps one pressed at a time, and a press on the
21
+ * pressed one releases it. `multiple` lets any number be pressed. `value` is
22
+ * the pressed values, comma separated. A press sends a cancelable
23
+ * `helix-before-change` with the value it would leave, then `change`.
24
+ *
25
+ * The pressed state is written on the control inside each button's shadow
26
+ * root, so the markup you rendered is never changed and a framework hydrating
27
+ * it sees what its server sent. That is also why only Helix buttons are
28
+ * toggles: a native `<button>` or a button without a `value` stays a plain
29
+ * action button in the group, and a native one without its own
30
+ * `aria-pressed` warns once, with the runtime kind `untold-toggle`. The inner
31
+ * control also carries
32
+ * `--button-group-pressed`, `1` or `0`, for a `::part(button)` rule of yours.
33
+ *
34
+ * Use Toggle Group instead for a choice that belongs in a form, or one where
35
+ * the arrow keys choose as they move: it is a `radiogroup` of options with a
36
+ * form value, `required` and validation. A Button Group in a selection mode is
37
+ * a set of actions that stay on, such as bold and italic, or a view that a
38
+ * press switches, and it submits nothing.
39
+ *
40
+ * **Keyboard.** In a selection mode the group is one tab stop: the button
41
+ * focus was last on, then the first pressed one, then the first one.
42
+ *
43
+ * | Key | What it does |
44
+ * |---|---|
45
+ * | Right, Down | Moves to the next button, from the last back to the first. |
46
+ * | Left, Up | Moves to the button before, from the first round to the last. |
47
+ * | Home, End | Moves to the first or the last button. |
48
+ * | Space, Enter | Presses or releases the focused button. |
49
+ *
50
+ * Left and Right swap on a right-to-left page, and a disabled button is
51
+ * passed over. In a selection mode the group is a `toolbar`, with
52
+ * `aria-orientation` under `orientation="vertical"`. Inside a toolbar it stays
53
+ * a named `group`, and the toolbar's own arrow keys walk its buttons, so a
54
+ * reader meets one toolbar.
55
+ *
18
56
  * @since 0.1.0
19
57
  * @status stable
20
58
  * @category actions
21
- * @aliases Segmented buttons, Split button
59
+ * @aliases Segmented buttons, Split button, Toggle buttons
22
60
  * @documentation https://fusion.dev/helix/components/button-group
23
61
  *
24
62
  * @slot - The buttons in the group.
63
+ *
64
+ * @event {Event} change - Fired after a press changes `value`, in a selection mode.
65
+ * @event {HelixBeforeChangeEvent} helix-before-change - Fired before a press changes `value`, with the value it would leave in `detail.value`. Cancelable: call `preventDefault()` to keep the buttons as they are.
66
+ *
67
+ * @cssproperty --button-group-pressed - Written on the control inside each toggle button of a group with `selection-mode`: `1` while it is pressed and `0` while it is not. Read it in a `::part(button)` rule to draw the pressed look.
25
68
  */
26
69
  export declare class HelixHeadlessButtonGroup extends HelixElement {
27
70
  /**
@@ -51,8 +94,106 @@ export declare class HelixHeadlessButtonGroup extends HelixElement {
51
94
  * column.
52
95
  */
53
96
  orientation?: 'horizontal' | 'vertical';
97
+ /**
98
+ * Whether the buttons stay pressed: `single` keeps one pressed at a time and
99
+ * `multiple` any number. Unset, they are action buttons, each its own tab
100
+ * stop, as before.
101
+ *
102
+ * Optional, so the attribute is absent until a consumer writes it.
103
+ */
104
+ selectionMode?: 'multiple' | 'single';
105
+ /**
106
+ * The values of the pressed buttons, comma separated, in a selection mode.
107
+ * A button's own `value` names it, so a value holds no comma.
108
+ *
109
+ * The attribute is the seed, and a press changes the property, which does
110
+ * not reflect, as a native control's `value` does not.
111
+ */
112
+ value: string;
113
+ /**
114
+ * The Helix buttons of a group in a selection mode, in the order they are
115
+ * written, and none otherwise. Empty on a server, which has no tree.
116
+ */
117
+ private get buttons();
118
+ /**
119
+ * Whether the group holds the tab stop and the arrow keys: in a selection
120
+ * mode, and not inside a toolbar, whose own keys walk the buttons.
121
+ *
122
+ * Truthiness, as `buttons` reads it: a removed attribute leaves the property
123
+ * null, which is no selection mode.
124
+ */
125
+ private get roving();
126
+ /** Whether a button is pressed: its value is among the group's. */
127
+ private pressed;
128
+ /** The button focus was last on, which holds the tab stop. */
129
+ private lastFocused;
130
+ /**
131
+ * The buttons this group has told something, each with whether it moved
132
+ * their tab stop, so a button that leaves the group, or a group that leaves
133
+ * its selection mode, is put back as it was.
134
+ */
135
+ private readonly told;
136
+ private watcher;
137
+ /**
138
+ * Tells every button whether it is pressed and whether it holds the stop,
139
+ * and puts back the ones that left.
140
+ */
141
+ private sync;
142
+ /** Whether this group has said a native button in it cannot be a toggle, so it says so once. */
143
+ private warned;
144
+ /**
145
+ * Says once that a native `<button>` in a selection mode stays an action
146
+ * button, since its pressed state would be an attribute on the page's markup.
147
+ * One with its own `aria-pressed` is the page's to say, and is not reported.
148
+ */
149
+ private warn;
150
+ /**
151
+ * Writes a button's pressed state onto its control, once the button has
152
+ * rendered it, and takes it off a button the group no longer holds.
153
+ *
154
+ * **On the control inside the button's shadow root, never on the button.**
155
+ * An attribute written on the consumer's element is a hydration mismatch a
156
+ * React consumer's hydration reports and does not patch up, which is why
157
+ * `tellDisclosure` hands a trigger `expanded` as a property. Inside the
158
+ * button's own shadow root no framework compares the attribute, and the
159
+ * reader is on that control. Read at the moment of the write, so the last
160
+ * press wins whatever order renders finish in.
161
+ *
162
+ * The control is the `<button>` a Helix button renders, which is where its
163
+ * `delegatesFocus` sends focus and what a reader is on.
164
+ *
165
+ * A button that carries `aria-pressed` itself is the consumer's to say, and
166
+ * its own forwarding carries that down instead.
167
+ */
168
+ private mark;
169
+ /**
170
+ * Says on a button's control whether it is pressed, or that it is no toggle
171
+ * at all where `pressed` is undefined. A styled tier draws the look here.
172
+ *
173
+ * @param control The focusable control inside the button's shadow root.
174
+ * @param pressed Whether the button is pressed, or undefined where it is not a toggle.
175
+ */
176
+ protected markPressed(control: HTMLElement, pressed: boolean | undefined): void;
177
+ /**
178
+ * Presses or releases the button a press acted on. `helix-press` rather than
179
+ * `click`, since a button sends it only for a press that acted: never while
180
+ * it is disabled or pending.
181
+ */
182
+ private readonly handlePress;
183
+ /**
184
+ * Moves focus between the buttons without pressing them, a toolbar's
185
+ * reading: a press is its own act, on Space or Enter, which the button
186
+ * answers natively. Both axes, as every set here answers both, and the
187
+ * horizontal pair turns on a right-to-left page.
188
+ */
189
+ private readonly handleKeydown;
190
+ /** Moves the tab stop to the button focus arrived on. */
191
+ private readonly noteFocus;
192
+ connectedCallback(): void;
193
+ disconnectedCallback(): void;
54
194
  protected willUpdate(changed: PropertyValues): void;
55
195
  protected firstUpdated(changed: PropertyValues): void;
196
+ protected updated(changed: PropertyValues): void;
56
197
  protected render(): TemplateResult;
57
198
  }
58
199
  declare global {
@@ -4,22 +4,156 @@ 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 { auditName, customElement, HelixElement, hostAria } from '@fusion.dev/helix-core';
7
+ import { auditName, commaList, customElement, HelixElement, hostAria, inlineStep, pressedValue, reportRuntime, requestValue, rovingStop, toolbarAround, whenHydrated, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ import { arrowTarget } from '../arrows.js';
11
+ const isGroupButton = (child) => child instanceof HTMLElement && 'focusable' in child && 'value' in child;
10
12
  let HelixHeadlessButtonGroup = class HelixHeadlessButtonGroup extends HelixElement {
11
13
  constructor() {
12
14
  super(...arguments);
13
15
  this.label = '';
16
+ this.value = '';
17
+ this.lastFocused = undefined;
18
+ this.told = new Map();
19
+ this.warned = false;
20
+ this.handlePress = (event) => {
21
+ const mode = this.selectionMode;
22
+ const button = this.buttons.find((one) => event.composedPath().includes(one));
23
+ if (mode && button && button.value !== '') {
24
+ requestValue(this, pressedValue(mode, this.value, button.value), (next) => {
25
+ this.value = next;
26
+ });
27
+ }
28
+ };
29
+ this.handleKeydown = (event) => {
30
+ const buttons = this.roving ? this.buttons.filter((one) => one.disabled !== true) : [];
31
+ const from = buttons.findIndex((one) => event.composedPath().includes(one));
32
+ const to = arrowTarget(event.key, inlineStep(this, event.key), from, buttons.length);
33
+ if (to >= 0 && !event.defaultPrevented) {
34
+ event.preventDefault();
35
+ buttons[to]?.focus();
36
+ }
37
+ };
38
+ this.noteFocus = (event) => {
39
+ const button = this.buttons.find((one) => event.composedPath().includes(one));
40
+ if (button && button !== this.lastFocused) {
41
+ this.lastFocused = button;
42
+ this.sync();
43
+ }
44
+ };
45
+ }
46
+ get buttons() {
47
+ return this.selectionMode && this.children ? [...this.children].filter(isGroupButton) : [];
48
+ }
49
+ get roving() {
50
+ return Boolean(this.selectionMode) && toolbarAround(this) === null;
51
+ }
52
+ pressed(button) {
53
+ return button.value !== '' && commaList(this.value).includes(button.value);
54
+ }
55
+ sync() {
56
+ const buttons = this.buttons;
57
+ const roving = this.roving;
58
+ const stop = roving
59
+ ? rovingStop(buttons, this.lastFocused, {
60
+ disabled: (button) => button.disabled === true,
61
+ selected: (button) => this.pressed(button),
62
+ })
63
+ : undefined;
64
+ for (const [button, roved] of this.told) {
65
+ if (!buttons.includes(button)) {
66
+ this.told.delete(button);
67
+ this.mark(button);
68
+ if (roved) {
69
+ whenHydrated(button, () => {
70
+ button.focusable = true;
71
+ });
72
+ }
73
+ }
74
+ }
75
+ for (const button of buttons) {
76
+ this.told.set(button, roving);
77
+ this.mark(button);
78
+ if (roving) {
79
+ whenHydrated(button, () => {
80
+ button.focusable = button === stop;
81
+ });
82
+ }
83
+ }
84
+ for (const child of this.selectionMode && this.children ? this.children : []) {
85
+ if (child.localName.includes('-') && !isGroupButton(child)) {
86
+ void customElements.whenDefined(child.localName).then(() => this.sync());
87
+ }
88
+ else if (child.localName === 'button' && !child.hasAttribute('aria-pressed')) {
89
+ this.warn(child);
90
+ }
91
+ }
92
+ }
93
+ warn(button) {
94
+ if (this.warned) {
95
+ return;
96
+ }
97
+ this.warned = true;
98
+ reportRuntime({
99
+ kind: 'untold-toggle',
100
+ message: `<${this.localName}> cannot press a native <button>: its state would be written onto your ` +
101
+ 'markup. Slot a Helix button with a `value`, or set `aria-pressed` on the button yourself.',
102
+ tag: button.localName,
103
+ });
104
+ }
105
+ mark(button) {
106
+ void button.updateComplete.then(() => {
107
+ const control = button.shadowRoot?.querySelector('button');
108
+ if (control && !button.hasAttribute('aria-pressed')) {
109
+ const toggle = this.told.has(button) && button.value !== '';
110
+ this.markPressed(control, toggle ? this.pressed(button) : undefined);
111
+ }
112
+ });
113
+ }
114
+ markPressed(control, pressed) {
115
+ if (pressed === undefined) {
116
+ control.removeAttribute('aria-pressed');
117
+ }
118
+ else {
119
+ control.setAttribute('aria-pressed', String(pressed));
120
+ }
121
+ control.style.setProperty('--button-group-pressed', pressed === undefined ? '' : pressed ? '1' : '0');
122
+ }
123
+ connectedCallback() {
124
+ super.connectedCallback();
125
+ this.addEventListener('focusin', this.noteFocus);
126
+ this.addEventListener('helix-press', this.handlePress);
127
+ this.addEventListener('keydown', this.handleKeydown);
128
+ this.watcher = new MutationObserver(() => this.sync());
129
+ this.watcher.observe(this, {
130
+ attributeFilter: ['disabled', 'value'],
131
+ childList: true,
132
+ subtree: true,
133
+ });
134
+ this.requestUpdate();
135
+ }
136
+ disconnectedCallback() {
137
+ super.disconnectedCallback();
138
+ this.removeEventListener('focusin', this.noteFocus);
139
+ this.removeEventListener('helix-press', this.handlePress);
140
+ this.removeEventListener('keydown', this.handleKeydown);
141
+ this.watcher?.disconnect();
14
142
  }
15
143
  willUpdate(changed) {
16
144
  super.willUpdate(changed);
17
- hostAria(this.internals, { label: this.label, role: 'group' });
145
+ const toolbar = this.roving;
146
+ hostAria(this.internals, { label: this.label, role: toolbar ? 'toolbar' : 'group' });
147
+ this.internals.ariaOrientation = toolbar ? (this.orientation ?? null) : null;
18
148
  }
19
149
  firstUpdated(changed) {
20
150
  super.firstUpdated(changed);
21
151
  auditName(this, () => null, 'Give it a `label`, or an `aria-label` that says what the buttons are for.', () => this.label !== '');
22
152
  }
153
+ updated(changed) {
154
+ super.updated(changed);
155
+ this.sync();
156
+ }
23
157
  render() {
24
158
  return html `<slot></slot>`;
25
159
  }
@@ -30,6 +164,12 @@ __decorate([
30
164
  __decorate([
31
165
  property({ reflect: true })
32
166
  ], HelixHeadlessButtonGroup.prototype, "orientation", void 0);
167
+ __decorate([
168
+ property({ attribute: 'selection-mode', reflect: true })
169
+ ], HelixHeadlessButtonGroup.prototype, "selectionMode", void 0);
170
+ __decorate([
171
+ property()
172
+ ], HelixHeadlessButtonGroup.prototype, "value", void 0);
33
173
  HelixHeadlessButtonGroup = __decorate([
34
174
  customElement('helix-headless-button-group')
35
175
  ], HelixHeadlessButtonGroup);
@@ -42,6 +42,11 @@ declare const HelixHeadlessCallout_base: (new (...args: any[]) => import("@fusio
42
42
  * @csspart icon-mark - The icon for the tone, shown when no icon is slotted.
43
43
  */
44
44
  export declare class HelixHeadlessCallout extends HelixHeadlessCallout_base {
45
+ /**
46
+ * The key this family's words are registered under, its styled tag:
47
+ * `registerWords('de', { 'helix-callout': { toneLabels: { danger: 'Fehler' } } })`.
48
+ */
49
+ static words: string;
45
50
  protected willUpdate(changed: PropertyValues): void;
46
51
  /**
47
52
  * What kind of attention the passage needs.
@@ -55,10 +60,19 @@ export declare class HelixHeadlessCallout extends HelixHeadlessCallout_base {
55
60
  /**
56
61
  * What a screen reader is told the tone is, such as "Fehler".
57
62
  *
58
- * Absent is the English for the tone: "Error", "Information", "Note",
59
- * "Success" or "Warning".
63
+ * Absent is the tone's words in `tone-labels`, "Error", "Information",
64
+ * "Note", "Success" or "Warning" in English.
60
65
  */
61
66
  toneLabel: string;
67
+ /**
68
+ * What a screen reader is told each tone is, by the tone's name, written as
69
+ * JSON in the attribute: `tone-labels='{"danger":"Fehler"}'`. A tone left
70
+ * out keeps its English, and `tone-label` wins over both.
71
+ *
72
+ * `danger` is said as "Error", the word a reader understands, and
73
+ * `neutral` as "Note", for what the passage is.
74
+ */
75
+ toneLabels: Partial<Record<'danger' | 'info' | 'neutral' | 'success' | 'warning', string>>;
62
76
  /**
63
77
  * The mark for the callout's tone, behind a seam.
64
78
  *
@@ -4,16 +4,23 @@ 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, drawDismiss, drawDismissGlyph, HelixElement, hostAria, MixinDismissible, TONE_MARKS, toneMark, } from '@fusion.dev/helix-core';
7
+ import { customElement, drawDismiss, drawDismissGlyph, HelixElement, hostAria, MixinDismissible, TONE_MARKS, toneMark, words, wordsConverter, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
11
- const NOTE = { icon: TONE_MARKS.info.icon, label: 'Note' };
12
11
  let HelixHeadlessCallout = class HelixHeadlessCallout extends MixinDismissible(HelixElement) {
13
12
  constructor() {
14
13
  super(...arguments);
15
14
  this.toneLabel = '';
15
+ this.toneLabels = {
16
+ danger: 'Error',
17
+ info: 'Information',
18
+ neutral: 'Note',
19
+ success: 'Success',
20
+ warning: 'Warning',
21
+ };
16
22
  }
23
+ static { this.words = 'helix-callout'; }
17
24
  willUpdate(changed) {
18
25
  super.willUpdate(changed);
19
26
  hostAria(this.internals, { role: 'note' });
@@ -31,8 +38,9 @@ let HelixHeadlessCallout = class HelixHeadlessCallout extends MixinDismissible(H
31
38
  </slot>`;
32
39
  }
33
40
  render() {
34
- const { icon, label } = this.tone === 'neutral' ? NOTE : (toneMark(this.tone) ?? TONE_MARKS.info);
35
- const named = this.toneLabel || label;
41
+ const tone = this.tone === 'neutral' ? 'neutral' : toneMark(this.tone) ? (this.tone ?? 'info') : 'info';
42
+ const { icon } = toneMark(tone) ?? TONE_MARKS.info;
43
+ const named = this.toneLabel || (this.toneLabels[tone] ?? '');
36
44
  return html `<span part="icon" role="img" aria-label=${named}>${this.renderIcon(icon, named)}</span>
37
45
  <div part="content">
38
46
  <slot name="heading"></slot>
@@ -52,6 +60,10 @@ __decorate([
52
60
  __decorate([
53
61
  property({ attribute: 'tone-label' })
54
62
  ], HelixHeadlessCallout.prototype, "toneLabel", void 0);
63
+ __decorate([
64
+ words,
65
+ property({ attribute: 'tone-labels', converter: wordsConverter })
66
+ ], HelixHeadlessCallout.prototype, "toneLabels", void 0);
55
67
  HelixHeadlessCallout = __decorate([
56
68
  customElement('helix-headless-callout')
57
69
  ], HelixHeadlessCallout);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The words that describe a card or a connection, such as whether it is
3
+ * selected, what its tone means and where its connections go, as one list in
4
+ * the way `locale` writes one: "selected, Blocked" in English, "selected und
5
+ * Blocked" in German. An empty word is left out, and nothing at all is empty.
6
+ *
7
+ * **A narrow conjunction**, which English writes with commas alone, so the
8
+ * English reads as it did when the words were joined with a comma.
9
+ */
10
+ export declare const descriptionOf: (locale: string, words: readonly (string | undefined)[]) => string;
@@ -0,0 +1,9 @@
1
+ const formatters = new Map();
2
+ export const descriptionOf = (locale, words) => {
3
+ let found = formatters.get(locale);
4
+ if (!found) {
5
+ found = new Intl.ListFormat(locale || undefined, { style: 'narrow', type: 'conjunction' });
6
+ formatters.set(locale, found);
7
+ }
8
+ return found.format(words.filter((one) => Boolean(one)));
9
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Moving the view while something is carried to its edge: a card, a corner, a
3
+ * connection being drawn, or the box that selects.
4
+ *
5
+ * **A board is larger than its box, and a hand stops at the edge of the box.**
6
+ * Without this, carrying a card to a place off screen took a drop at the edge,
7
+ * a pan, and a second pick-up, and a connection to a card off screen could not
8
+ * be drawn by pointer at all.
9
+ */
10
+ /** A point on screen, in pixels. */
11
+ export interface EdgePoint {
12
+ x: number;
13
+ y: number;
14
+ }
15
+ /** The box the view is drawn in, on screen. */
16
+ export interface EdgeRect {
17
+ bottom: number;
18
+ left: number;
19
+ right: number;
20
+ top: number;
21
+ }
22
+ /** How near an edge, in pixels, the view starts to move. */
23
+ export declare const EDGE_ZONE = 32;
24
+ /** The most the view moves in one frame, in pixels: at the edge and past it. */
25
+ export declare const EDGE_MOST = 12;
26
+ /**
27
+ * How far the view should move toward each side in one frame, in pixels, for
28
+ * a pointer at `point`: positive toward the right and the bottom, nothing
29
+ * outside the zone, and faster the nearer the edge, up to `most` at it and
30
+ * past it.
31
+ *
32
+ * A box narrower than two zones moves only once the pointer is past its edge,
33
+ * so a small board is not always moving.
34
+ */
35
+ export declare const edgeSpeed: (point: EdgePoint, rect: EdgeRect, zone?: number, most?: number) => EdgePoint;
36
+ /**
37
+ * The frames that move the view while a pointer holds something near an edge.
38
+ *
39
+ * `follow` with every pointer move keeps it going while the pointer is in the
40
+ * zone, and `stop` ends it, which the board calls when the gesture ends and
41
+ * when it leaves the page. Each frame hands `step` how far to move, and the
42
+ * board moves the view and carries what is in hand with it.
43
+ */
44
+ export declare class EdgePan {
45
+ private readonly rect;
46
+ private readonly step;
47
+ private readonly ended;
48
+ /** The frame asked for, while the view is moving. */
49
+ private frame;
50
+ /** Where the pointer last was. */
51
+ private point;
52
+ constructor(rect: () => EdgeRect | undefined, step: (by: EdgePoint) => void, ended?: () => void);
53
+ /** Whether the view is moving. */
54
+ get moving(): boolean;
55
+ /** Where the pointer is now, which starts the frames where it is in the zone. */
56
+ follow(point: EdgePoint): void;
57
+ /** Ends the frames. */
58
+ stop(): void;
59
+ private speed;
60
+ private readonly tick;
61
+ }
@@ -0,0 +1,63 @@
1
+ export const EDGE_ZONE = 32;
2
+ export const EDGE_MOST = 12;
3
+ export const edgeSpeed = (point, rect, zone = EDGE_ZONE, most = EDGE_MOST) => {
4
+ const along = (at, start, end) => {
5
+ if (at >= end) {
6
+ return 1;
7
+ }
8
+ if (at <= start) {
9
+ return -1;
10
+ }
11
+ const reach = end - start < zone * 2 ? 0 : zone;
12
+ if (reach > 0 && at > end - reach) {
13
+ return (at - (end - reach)) / reach;
14
+ }
15
+ if (reach > 0 && at < start + reach) {
16
+ return -(start + reach - at) / reach;
17
+ }
18
+ return 0;
19
+ };
20
+ return {
21
+ x: Math.round(along(point.x, rect.left, rect.right) * most),
22
+ y: Math.round(along(point.y, rect.top, rect.bottom) * most),
23
+ };
24
+ };
25
+ export class EdgePan {
26
+ constructor(rect, step, ended = () => undefined) {
27
+ this.rect = rect;
28
+ this.step = step;
29
+ this.ended = ended;
30
+ this.tick = () => {
31
+ const by = this.speed();
32
+ if (by.x === 0 && by.y === 0) {
33
+ this.frame = undefined;
34
+ this.ended();
35
+ return;
36
+ }
37
+ this.step(by);
38
+ this.frame = requestAnimationFrame(this.tick);
39
+ };
40
+ }
41
+ get moving() {
42
+ return this.frame !== undefined;
43
+ }
44
+ follow(point) {
45
+ this.point = point;
46
+ const by = this.speed();
47
+ if (this.frame === undefined && (by.x !== 0 || by.y !== 0)) {
48
+ this.frame = requestAnimationFrame(this.tick);
49
+ }
50
+ }
51
+ stop() {
52
+ this.point = undefined;
53
+ if (this.frame !== undefined) {
54
+ cancelAnimationFrame(this.frame);
55
+ this.frame = undefined;
56
+ this.ended();
57
+ }
58
+ }
59
+ speed() {
60
+ const rect = this.rect();
61
+ return this.point && rect ? edgeSpeed(this.point, rect) : { x: 0, y: 0 };
62
+ }
63
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Where focus goes when a stop on a board leaves: the arithmetic, kept apart
3
+ * from the board that moves focus there.
4
+ *
5
+ * Shared by the two ways a stop leaves, Delete and the page taking it away,
6
+ * so a reader lands in the same place either way.
7
+ */
8
+ /**
9
+ * The first stop after `at` in `walk` that `kept` keeps, or, with none after
10
+ * it, the nearest one before it. Undefined where none is kept.
11
+ *
12
+ * @param walk Every stop's key, in the order the arrow keys walk them.
13
+ * @param at Where the stop that left was, as an index into `walk`, or `-1`
14
+ * for nowhere, which takes the first stop kept.
15
+ * @param kept Whether a stop is still there.
16
+ */
17
+ export declare const neighborOf: (walk: readonly string[], at: number, kept: (key: string) => boolean) => string | undefined;
@@ -0,0 +1 @@
1
+ export const neighborOf = (walk, at, kept) => walk.slice(at + 1).find(kept) ?? walk.slice(0, Math.max(at, 0)).reverse().find(kept);
@@ -150,6 +150,16 @@ export declare class HelixHeadlessCanvasNode extends HelixElement {
150
150
  * point and one to a circle on its curve without the board knowing which.
151
151
  */
152
152
  shape?: 'circle' | 'diamond' | 'pill' | 'square';
153
+ /**
154
+ * @internal What the card's state adds to what it says, in the page's words,
155
+ * written by the board: its `selected-label` while it is selected. Read
156
+ * first, before what the tone means.
157
+ *
158
+ * **A selection a reader cannot see has to be said.** A card is an article,
159
+ * and an article has no selected state for `aria-selected` to carry, so a
160
+ * reader who selected three cards heard nothing on any of them.
161
+ */
162
+ status: string;
153
163
  /**
154
164
  * @internal Whether this is the card the reader selected, written by the
155
165
  * board. Kept apart from focus, so a reader who clicks a button beside the
@@ -211,8 +221,8 @@ export declare class HelixHeadlessCanvasNode extends HelixElement {
211
221
  */
212
222
  focus(options?: FocusOptions): void;
213
223
  /**
214
- * The card, and the sentence that says what its tone means and where it
215
- * leads.
224
+ * The card, and the sentence that says whether it is selected, what its
225
+ * tone means and where it leads.
216
226
  *
217
227
  * **`aria-describedby` at a hidden element, which is allowed and is the
218
228
  * point.** A description is computed from its target whether or not the
@@ -4,9 +4,10 @@ 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, PlacementController } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, PlacementController, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
+ import { descriptionOf } from '../describe.js';
10
11
  const DRAWN = [
11
12
  'group',
12
13
  'height',
@@ -33,6 +34,7 @@ let HelixHeadlessCanvasNode = class HelixHeadlessCanvasNode extends HelixElement
33
34
  this.links = '';
34
35
  this.meaning = '';
35
36
  this.readOnly = false;
37
+ this.status = '';
36
38
  this.selected = false;
37
39
  this.movable = false;
38
40
  this.resizable = false;
@@ -72,7 +74,7 @@ let HelixHeadlessCanvasNode = class HelixHeadlessCanvasNode extends HelixElement
72
74
  </svg>`
73
75
  : nothing}<div
74
76
  part="node"
75
- aria-describedby=${this.meaning || this.links ? 'about' : nothing}
77
+ aria-describedby=${this.status || this.meaning || this.links ? 'about' : nothing}
76
78
  aria-label=${this.label || nothing}
77
79
  aria-labelledby=${this.label || this.group ? nothing : 'content'}
78
80
  aria-keyshortcuts=${this.connectable ? 'Enter F2 Space Shift+ArrowRight Shift+ArrowDown Alt+ArrowRight Alt+ArrowDown C Delete' : nothing}
@@ -81,7 +83,7 @@ let HelixHeadlessCanvasNode = class HelixHeadlessCanvasNode extends HelixElement
81
83
  >
82
84
  <slot id="content"></slot>
83
85
  <slot name="description"></slot>
84
- <span id="about" hidden>${[this.meaning, this.links].filter(Boolean).join(', ')}</span>
86
+ <span id="about" hidden>${descriptionOf(wordsLocale(this), [this.status, this.meaning, this.links])}</span>
85
87
  </div>
86
88
  ${this.connectable
87
89
  ? html `<span part="handle handle-top" aria-hidden="true" data-side="top"></span>
@@ -131,6 +133,9 @@ __decorate([
131
133
  __decorate([
132
134
  property({ reflect: true })
133
135
  ], HelixHeadlessCanvasNode.prototype, "shape", void 0);
136
+ __decorate([
137
+ state()
138
+ ], HelixHeadlessCanvasNode.prototype, "status", void 0);
134
139
  __decorate([
135
140
  state()
136
141
  ], HelixHeadlessCanvasNode.prototype, "selected", void 0);