@fusion.dev/helix-headless 0.142.1 → 0.144.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 (285) hide show
  1. package/custom-elements.json +25381 -1202
  2. package/dist/accordion/item/accordion-item.d.ts +38 -2
  3. package/dist/accordion/item/accordion-item.js +38 -6
  4. package/dist/accordion/root/accordion-root.d.ts +23 -1
  5. package/dist/accordion/root/accordion-root.js +39 -3
  6. package/dist/announcement/root/announcement-root.d.ts +11 -7
  7. package/dist/announcement/root/announcement-root.js +1 -1
  8. package/dist/app-header/root/app-header-root.d.ts +21 -0
  9. package/dist/app-header/root/app-header-root.js +31 -5
  10. package/dist/area-chart/root/area-chart-root.d.ts +12 -4
  11. package/dist/area-chart/root/area-chart-root.js +39 -11
  12. package/dist/avatar/group/avatar-group.d.ts +24 -3
  13. package/dist/avatar/group/avatar-group.js +34 -2
  14. package/dist/avatar/root/avatar-root.d.ts +17 -1
  15. package/dist/avatar/root/avatar-root.js +31 -10
  16. package/dist/badge/root/badge-root.d.ts +12 -0
  17. package/dist/badge/root/badge-root.js +1 -1
  18. package/dist/bar-chart/root/bar-chart-root.d.ts +30 -5
  19. package/dist/bar-chart/root/bar-chart-root.js +58 -21
  20. package/dist/blockquote/root/blockquote-root.d.ts +12 -7
  21. package/dist/blockquote/root/blockquote-root.js +6 -4
  22. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
  23. package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
  24. package/dist/button/root/button-root.d.ts +84 -1
  25. package/dist/button/root/button-root.js +81 -3
  26. package/dist/callout/root/callout-root.d.ts +32 -6
  27. package/dist/callout/root/callout-root.js +26 -7
  28. package/dist/canvas/root/canvas-root.d.ts +1 -0
  29. package/dist/canvas/root/canvas-root.js +10 -1
  30. package/dist/card/root/card-root.d.ts +30 -1
  31. package/dist/card/root/card-root.js +38 -5
  32. package/dist/carousel/root/carousel-root.d.ts +47 -5
  33. package/dist/carousel/root/carousel-root.js +53 -21
  34. package/dist/chat/composer/chat-composer.d.ts +13 -11
  35. package/dist/chat/composer/chat-composer.js +6 -12
  36. package/dist/chat/message/chat-message.js +8 -1
  37. package/dist/chat/root/chat-root.d.ts +1 -0
  38. package/dist/chat/root/chat-root.js +2 -1
  39. package/dist/checkbox/root/checkbox-root.d.ts +7 -0
  40. package/dist/checkbox/root/checkbox-root.js +9 -3
  41. package/dist/checkbox-group/root/checkbox-group-root.d.ts +116 -33
  42. package/dist/checkbox-group/root/checkbox-group-root.js +141 -44
  43. package/dist/code/root/code-root.d.ts +1 -1
  44. package/dist/code-block/lines.d.ts +13 -0
  45. package/dist/code-block/lines.js +2 -0
  46. package/dist/code-block/root/code-block-root.d.ts +49 -0
  47. package/dist/code-block/root/code-block-root.js +44 -3
  48. package/dist/color-input/root/color-input-root.d.ts +60 -2
  49. package/dist/color-input/root/color-input-root.js +55 -7
  50. package/dist/color-picker/channels.d.ts +26 -0
  51. package/dist/color-picker/channels.js +84 -0
  52. package/dist/color-picker/root/color-picker-root.d.ts +118 -12
  53. package/dist/color-picker/root/color-picker-root.js +183 -13
  54. package/dist/combobox/root/combobox-root.d.ts +126 -3
  55. package/dist/combobox/root/combobox-root.js +224 -21
  56. package/dist/command/root/command-root.d.ts +47 -10
  57. package/dist/command/root/command-root.js +45 -4
  58. package/dist/copy-button/from.d.ts +22 -0
  59. package/dist/copy-button/from.js +28 -0
  60. package/dist/copy-button/root/copy-button-root.d.ts +48 -7
  61. package/dist/copy-button/root/copy-button-root.js +54 -8
  62. package/dist/data-table/root/data-table-root.d.ts +164 -10
  63. package/dist/data-table/root/data-table-root.js +488 -57
  64. package/dist/date-input/root/date-input-root.d.ts +123 -9
  65. package/dist/date-input/root/date-input-root.js +147 -45
  66. package/dist/date-picker/root/date-picker-root.d.ts +229 -18
  67. package/dist/date-picker/root/date-picker-root.js +342 -46
  68. package/dist/dialog/description.d.ts +9 -0
  69. package/dist/dialog/description.js +14 -0
  70. package/dist/dialog/root/dialog-root.d.ts +101 -10
  71. package/dist/dialog/root/dialog-root.js +83 -4
  72. package/dist/donut-chart/root/donut-chart-root.d.ts +61 -16
  73. package/dist/donut-chart/root/donut-chart-root.js +48 -13
  74. package/dist/drawer/root/drawer-root.d.ts +93 -7
  75. package/dist/drawer/root/drawer-root.js +126 -5
  76. package/dist/drawer/swipe-start.d.ts +9 -0
  77. package/dist/drawer/swipe-start.js +12 -0
  78. package/dist/drilldown/item/drilldown-item.d.ts +16 -13
  79. package/dist/drilldown/item/drilldown-item.js +6 -2
  80. package/dist/drilldown/root/drilldown-root.d.ts +21 -5
  81. package/dist/drilldown/root/drilldown-root.js +26 -10
  82. package/dist/element-events.generated.js +42 -13
  83. package/dist/empty/root/empty-root.d.ts +1 -1
  84. package/dist/file-input/root/file-input-root.d.ts +65 -1
  85. package/dist/file-input/root/file-input-root.js +76 -12
  86. package/dist/file-input/screen.d.ts +35 -0
  87. package/dist/file-input/screen.js +49 -0
  88. package/dist/filter-input/menu/filter-menu.d.ts +9 -0
  89. package/dist/filter-input/menu/filter-menu.js +8 -0
  90. package/dist/filter-input/root/filter-input-root.d.ts +43 -6
  91. package/dist/filter-input/root/filter-input-root.js +89 -10
  92. package/dist/filter-input/suggest.d.ts +17 -0
  93. package/dist/filter-input/suggest.js +23 -0
  94. package/dist/format-date/options.d.ts +33 -0
  95. package/dist/format-date/options.js +42 -0
  96. package/dist/format-date/root/format-date-root.d.ts +62 -3
  97. package/dist/format-date/root/format-date-root.js +78 -37
  98. package/dist/graph/root/graph-root.d.ts +24 -3
  99. package/dist/graph/root/graph-root.js +66 -12
  100. package/dist/heatmap/root/buckets.d.ts +2 -1
  101. package/dist/heatmap/root/buckets.js +5 -1
  102. package/dist/heatmap/root/heatmap-root.d.ts +47 -9
  103. package/dist/heatmap/root/heatmap-root.js +46 -18
  104. package/dist/icon/index.d.ts +2 -0
  105. package/dist/icon/index.js +2 -0
  106. package/dist/icon/registry.d.ts +37 -0
  107. package/dist/icon/registry.js +83 -0
  108. package/dist/icon/root/icon-root.d.ts +53 -6
  109. package/dist/icon/root/icon-root.js +72 -4
  110. package/dist/icon/root/index.d.ts +2 -0
  111. package/dist/icon/root/index.js +2 -0
  112. package/dist/icon/set.d.ts +1 -0
  113. package/dist/icon/set.js +3 -0
  114. package/dist/icon-button/root/icon-button-root.d.ts +1 -1
  115. package/dist/image/root/image-root.d.ts +23 -2
  116. package/dist/image/root/image-root.js +23 -4
  117. package/dist/input/root/input-root.d.ts +62 -9
  118. package/dist/input/root/input-root.js +61 -5
  119. package/dist/kbd/root/kbd-root.d.ts +26 -11
  120. package/dist/kbd/root/kbd-root.js +35 -38
  121. package/dist/kbd/shortcut.d.ts +18 -0
  122. package/dist/kbd/shortcut.js +35 -0
  123. package/dist/legend/hidden-series.d.ts +8 -0
  124. package/dist/legend/hidden-series.js +13 -0
  125. package/dist/legend/root/legend-root.d.ts +63 -0
  126. package/dist/legend/root/legend-root.js +61 -7
  127. package/dist/lightbox/item/lightbox-item.d.ts +17 -1
  128. package/dist/lightbox/item/lightbox-item.js +10 -1
  129. package/dist/lightbox/root/lightbox-root.d.ts +77 -6
  130. package/dist/lightbox/root/lightbox-root.js +116 -5
  131. package/dist/line-chart/root/line-chart-root.d.ts +29 -4
  132. package/dist/line-chart/root/line-chart-root.js +58 -17
  133. package/dist/list/item/list-item.d.ts +11 -5
  134. package/dist/list/item/list-item.js +5 -6
  135. package/dist/listbox/range.d.ts +17 -0
  136. package/dist/listbox/range.js +5 -0
  137. package/dist/listbox/root/listbox-root.d.ts +184 -4
  138. package/dist/listbox/root/listbox-root.js +229 -23
  139. package/dist/loader/busy.d.ts +17 -0
  140. package/dist/loader/busy.js +24 -0
  141. package/dist/loader/root/loader-root.d.ts +37 -0
  142. package/dist/loader/root/loader-root.js +52 -3
  143. package/dist/map/root/map-root.d.ts +19 -4
  144. package/dist/map/root/map-root.js +36 -7
  145. package/dist/megamenu/root/megamenu-root.d.ts +10 -1
  146. package/dist/megamenu/root/megamenu-root.js +10 -1
  147. package/dist/menu/item/menu-item.d.ts +41 -12
  148. package/dist/menu/item/menu-item.js +46 -3
  149. package/dist/menu/root/menu-root.d.ts +32 -2
  150. package/dist/menu/root/menu-root.js +48 -8
  151. package/dist/meter/root/meter-root.d.ts +13 -0
  152. package/dist/meter/root/meter-root.js +24 -5
  153. package/dist/navigation/root/navigation-root.d.ts +10 -2
  154. package/dist/navigation/root/navigation-root.js +13 -2
  155. package/dist/notifications/feed-keys.d.ts +10 -0
  156. package/dist/notifications/feed-keys.js +14 -0
  157. package/dist/notifications/group/notification-group.d.ts +1 -0
  158. package/dist/notifications/notification/notification.d.ts +29 -0
  159. package/dist/notifications/notification/notification.js +51 -3
  160. package/dist/notifications/root/notifications-root.d.ts +49 -0
  161. package/dist/notifications/root/notifications-root.js +76 -2
  162. package/dist/number-input/root/number-input-root.d.ts +109 -7
  163. package/dist/number-input/root/number-input-root.js +109 -21
  164. package/dist/object-inspector/root/object-inspector-root.d.ts +49 -5
  165. package/dist/object-inspector/root/object-inspector-root.js +92 -12
  166. package/dist/option/root/option-root.d.ts +58 -6
  167. package/dist/option/root/option-root.js +35 -5
  168. package/dist/outline/root/outline-root.d.ts +13 -2
  169. package/dist/outline/root/outline-root.js +4 -2
  170. package/dist/pagination/root/pagination-root.d.ts +19 -1
  171. package/dist/pagination/root/pagination-root.js +49 -9
  172. package/dist/pagination/size/pagination-size.d.ts +13 -3
  173. package/dist/pagination/size/pagination-size.js +7 -0
  174. package/dist/pin-input/format.d.ts +7 -0
  175. package/dist/pin-input/format.js +16 -0
  176. package/dist/pin-input/root/pin-input-root.d.ts +37 -0
  177. package/dist/pin-input/root/pin-input-root.js +48 -7
  178. package/dist/popover/root/popover-root.d.ts +78 -3
  179. package/dist/popover/root/popover-root.js +74 -5
  180. package/dist/progress/root/progress-root.d.ts +23 -1
  181. package/dist/progress/root/progress-root.js +40 -10
  182. package/dist/qr-code/export.d.ts +31 -0
  183. package/dist/qr-code/export.js +10 -0
  184. package/dist/qr-code/root/qr-code-root.d.ts +45 -0
  185. package/dist/qr-code/root/qr-code-root.js +78 -3
  186. package/dist/radio/group/radio-group.d.ts +64 -0
  187. package/dist/radio/group/radio-group.js +66 -12
  188. package/dist/radio/root/radio-root.d.ts +8 -5
  189. package/dist/radio/root/radio-root.js +1 -2
  190. package/dist/rating/root/rating-root.d.ts +49 -1
  191. package/dist/rating/root/rating-root.js +69 -10
  192. package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
  193. package/dist/scatter-chart/root/scatter-chart-root.js +40 -14
  194. package/dist/select/root/select-root.d.ts +93 -3
  195. package/dist/select/root/select-root.js +111 -13
  196. package/dist/skeleton/root/skeleton-root.d.ts +39 -3
  197. package/dist/skeleton/root/skeleton-root.js +33 -4
  198. package/dist/skip-link/root/skip-link-root.d.ts +10 -0
  199. package/dist/skip-link/root/skip-link-root.js +15 -1
  200. package/dist/slider/root/slider-root.d.ts +59 -2
  201. package/dist/slider/root/slider-root.js +82 -8
  202. package/dist/sparkline/root/sparkline-root.d.ts +9 -10
  203. package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
  204. package/dist/speed-dial/item/speed-dial-item.js +7 -2
  205. package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
  206. package/dist/speed-dial/root/speed-dial-root.js +19 -5
  207. package/dist/splitter/root/splitter-root.d.ts +28 -6
  208. package/dist/splitter/root/splitter-root.js +35 -1
  209. package/dist/stat/root/stat-root.d.ts +9 -1
  210. package/dist/stat/root/stat-root.js +15 -2
  211. package/dist/steps/root/steps-root.js +1 -1
  212. package/dist/steps/step/step.d.ts +1 -0
  213. package/dist/steps/step/step.js +1 -1
  214. package/dist/switch/root/switch-root.d.ts +14 -2
  215. package/dist/switch/root/switch-root.js +7 -1
  216. package/dist/table/cell/table-cell.d.ts +24 -4
  217. package/dist/table/cell/table-cell.js +21 -1
  218. package/dist/table/column/table-column.d.ts +32 -6
  219. package/dist/table/column/table-column.js +36 -6
  220. package/dist/table/root/table-root.d.ts +94 -3
  221. package/dist/table/root/table-root.js +128 -5
  222. package/dist/table/row/table-row.d.ts +7 -0
  223. package/dist/table/row/table-row.js +9 -1
  224. package/dist/tabs/root/tabs-root.d.ts +6 -1
  225. package/dist/tabs/root/tabs-root.js +6 -2
  226. package/dist/tag/group/tag-group.d.ts +107 -0
  227. package/dist/tag/group/tag-group.js +157 -0
  228. package/dist/tag/index.d.ts +1 -0
  229. package/dist/tag/index.js +1 -0
  230. package/dist/tag/root/tag-root.d.ts +83 -1
  231. package/dist/tag/root/tag-root.js +87 -17
  232. package/dist/tag/selection.d.ts +10 -0
  233. package/dist/tag/selection.js +12 -0
  234. package/dist/textarea/grow.d.ts +22 -0
  235. package/dist/textarea/grow.js +20 -0
  236. package/dist/textarea/root/textarea-root.d.ts +13 -0
  237. package/dist/textarea/root/textarea-root.js +10 -2
  238. package/dist/time-input/root/time-input-root.d.ts +71 -6
  239. package/dist/time-input/root/time-input-root.js +110 -44
  240. package/dist/time-picker/root/time-picker-root.d.ts +155 -26
  241. package/dist/time-picker/root/time-picker-root.js +192 -63
  242. package/dist/toast/hotkey.d.ts +22 -0
  243. package/dist/toast/hotkey.js +23 -0
  244. package/dist/toast/item/toast-item.d.ts +16 -1
  245. package/dist/toast/item/toast-item.js +41 -13
  246. package/dist/toast/root/toast-root.d.ts +105 -8
  247. package/dist/toast/root/toast-root.js +209 -4
  248. package/dist/toggle/group/toggle-group.d.ts +95 -16
  249. package/dist/toggle/group/toggle-group.js +99 -13
  250. package/dist/toggle/root/toggle-root.d.ts +26 -11
  251. package/dist/toggle/root/toggle-root.js +14 -6
  252. package/dist/toolbar/root/toolbar-root.d.ts +55 -15
  253. package/dist/toolbar/root/toolbar-root.js +99 -16
  254. package/dist/tooltip/focus-target.d.ts +6 -0
  255. package/dist/tooltip/focus-target.js +9 -0
  256. package/dist/tooltip/root/tooltip-root.d.ts +73 -15
  257. package/dist/tooltip/root/tooltip-root.js +96 -7
  258. package/dist/tour/naming.d.ts +30 -0
  259. package/dist/tour/naming.js +10 -0
  260. package/dist/tour/root/tour-root.d.ts +67 -3
  261. package/dist/tour/root/tour-root.js +105 -17
  262. package/dist/tour/stop/tour-stop.d.ts +65 -3
  263. package/dist/tour/stop/tour-stop.js +61 -11
  264. package/dist/tour/wait.d.ts +11 -0
  265. package/dist/tour/wait.js +19 -0
  266. package/dist/tree/item/tree-item.d.ts +21 -5
  267. package/dist/tree/item/tree-item.js +14 -4
  268. package/dist/tree/root/tree-root.d.ts +29 -5
  269. package/dist/tree/root/tree-root.js +64 -8
  270. package/dist/trend/root/trend-root.d.ts +36 -1
  271. package/dist/trend/root/trend-root.js +41 -15
  272. package/dist/video/root/keys.d.ts +5 -0
  273. package/dist/video/root/keys.js +13 -1
  274. package/dist/video/root/video-root.d.ts +19 -0
  275. package/dist/video/root/video-root.js +48 -4
  276. package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
  277. package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
  278. package/dist/video-playlist/root/video-playlist-root.js +6 -3
  279. package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
  280. package/dist/virtual-list/root/virtual-list-root.js +103 -21
  281. package/lint.json +1 -1
  282. package/package.json +5 -5
  283. package/types/jsx.d.ts +9140 -1149
  284. package/types/svelte.d.ts +1 -0
  285. package/types/vue.d.ts +2089 -267
@@ -9,9 +9,15 @@ import { type PropertyValues, type TemplateResult } from 'lit';
9
9
  * announce a region that arrives already holding its message. `Radio.Group` is
10
10
  * part of its family for the same reason.
11
11
  *
12
- * **The region is polite, never assertive.** `role="alert"` interrupts whatever
13
- * a screen reader is reading. For something that cannot wait, use a `Dialog`,
14
- * which stops the page for every user.
12
+ * **The region is a landmark named by `label`, and a polite live region.** A
13
+ * `danger` message is announced assertively, and every other one politely.
14
+ * Each message is an `alertdialog` named by its words, so a reader who moves
15
+ * into the stack hears which message they are on.
16
+ *
17
+ * **`hotkey` takes focus to the stack and back**, F8 unless you set it, and
18
+ * `label` says which key: `{hotkey}` in it is filled in, so the default name
19
+ * is "Notifications (F8)". Pressed while focus is in the stack, it gives focus
20
+ * back to where it was.
15
21
  *
16
22
  * **The region never creates or removes items.** You render items and remove
17
23
  * them. An item requests closing with a cancelable `helix-dismiss` event. A
@@ -19,9 +25,24 @@ import { type PropertyValues, type TemplateResult } from 'lit';
19
25
  * and the DOM stay in sync. It hides on its own unless you set `open`, which
20
26
  * makes the state yours.
21
27
  *
22
- * There is no `toast.show()`. For a `toast()` function, write a small one over
23
- * your app's state, which also lets you render the same messages on the server,
24
- * in a test, or more than once.
28
+ * **`add()` shows a message from script**, for a page with no framework state
29
+ * to render them from: it appends a message of the region's own tier and
30
+ * removes it once it has been dismissed and has finished leaving. Messages
31
+ * you render yourself stay yours, and the region never removes them. A touch
32
+ * or a pen swipes a message away toward the end of the line.
33
+ *
34
+ * **The stack is in the top layer.** The `region` part is a `popover="manual"`
35
+ * shown while a message is showing, and shown again above a modal dialog that
36
+ * opened after it. Position the `region` part, which is the box in the top
37
+ * layer. While a modal `Dialog`, `Drawer` or `Lightbox` is open, the browser
38
+ * makes everything outside it inert, the stack included, so a message stays
39
+ * visible and its actions cannot be pressed. Where the browser has
40
+ * `ariaNotify`, a message that arrives then is announced through it. For a
41
+ * message with an action, render a second region inside the dialog.
42
+ *
43
+ * When a message with focus inside it is dismissed, focus moves to the next
44
+ * message, then the one before it, then back to where it was before it entered
45
+ * the stack.
25
46
  *
26
47
  * @since 0.1.0
27
48
  * @status stable
@@ -33,11 +54,17 @@ import { type PropertyValues, type TemplateResult } from 'lit';
33
54
  *
34
55
  * @event {HelixDismissEvent} helix-dismiss - A message is being dismissed, by its control or by its timer. The message sends it, and you can listen for it on the region. Cancelable: canceling it keeps the message.
35
56
  *
36
- * @csspart region - The stack the messages are in.
57
+ * @csspart region - The stack the messages are in. It is in the top layer while a message shows, and takes focus from `hotkey` where no message holds a control.
37
58
  */
38
59
  export declare class HelixHeadlessToast extends HelixElement {
39
60
  /**
40
- * Accessible name for the region, such as "Notifications".
61
+ * The key that takes focus to the stack and back, such as `F8` or `Alt+T`:
62
+ * keys joined by `+`, the last one the key. Empty turns it off.
63
+ */
64
+ hotkey: string;
65
+ /**
66
+ * Accessible name for the region, with `{hotkey}` filled in. Translate it with
67
+ * the rest of your interface.
41
68
  */
42
69
  label: string;
43
70
  /**
@@ -68,6 +95,76 @@ export declare class HelixHeadlessToast extends HelixElement {
68
95
  */
69
96
  placement?: 'bottom-center' | 'bottom-end' | 'bottom-start' | 'top-center' | 'top-end' | 'top-start';
70
97
  protected willUpdate(changed: PropertyValues): void;
98
+ /** The element focus came from when it entered the stack. */
99
+ private focusOrigin;
100
+ private readonly scope;
101
+ /** Takes focus to the stack and back, on `hotkey`. */
102
+ private readonly handleHotkey;
103
+ /** Messages `add()` made, which are the region's to remove. */
104
+ private readonly made;
105
+ /**
106
+ * Shows a message from script, and returns the element.
107
+ *
108
+ * The message is the region's own tier of `Toast.Item`, appended to the
109
+ * stack, holding `message` as its text. The region removes it once it has
110
+ * been dismissed and has finished leaving, which is the one case where it
111
+ * removes anything: a message you render stays yours.
112
+ */
113
+ add(message: string, options?: {
114
+ dismissible?: boolean;
115
+ duration?: number;
116
+ tone?: 'danger' | 'info' | 'success' | 'warning';
117
+ }): HTMLElement;
118
+ /** Removes a message `add()` made, once it has hidden and finished leaving. */
119
+ private sweep;
120
+ /** Watches the messages arrive and hide, to raise or lower the stack. */
121
+ private watcher;
122
+ /** The messages that are showing, in order. */
123
+ private get showing();
124
+ private get region();
125
+ /**
126
+ * Puts the stack in the top layer while a message shows, and on top of it.
127
+ *
128
+ * Shown again when a message arrives under a modal opened after it, because
129
+ * the top layer is ordered by when each element entered it. Only then,
130
+ * because showing it again starts every message's entrance over.
131
+ */
132
+ private place;
133
+ /**
134
+ * Takes the stack out of the top layer once the last message has finished
135
+ * leaving. Hidden at once, the stack would cut the exit short.
136
+ */
137
+ private lowerWhenStill;
138
+ /**
139
+ * Whether a modal dialog this stack is not inside is open, which makes the
140
+ * stack inert.
141
+ *
142
+ * Two places to look: the modal `Dialog` and `Drawer` the scroll lock
143
+ * records, whose `<dialog>` is in a shadow root, and an open modal `<dialog>`
144
+ * in the document itself.
145
+ */
146
+ private blocked;
147
+ /**
148
+ * Announces messages that arrived while the live region is inert.
149
+ *
150
+ * `ariaNotify` speaks without a live region in the accessibility tree, which
151
+ * is the one channel left under a modal. A browser without it says nothing,
152
+ * as it did before.
153
+ */
154
+ private notify;
155
+ /** Remembers where focus came from when it enters the stack. */
156
+ private readonly handleFocusIn;
157
+ /**
158
+ * Keeps focus on the stack when the message holding it goes.
159
+ *
160
+ * Read after the item's update, because the dismissal is a request a
161
+ * listener after this one can still refuse. A refused one leaves the
162
+ * message, and focus with it.
163
+ */
164
+ private readonly handleDismiss;
165
+ connectedCallback(): void;
166
+ disconnectedCallback(): void;
167
+ protected firstUpdated(changed: PropertyValues): void;
71
168
  protected render(): TemplateResult;
72
169
  }
73
170
  declare global {
@@ -4,25 +4,219 @@ 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, deepActiveElement, deepContains, FocusScopeController, fillTemplate, focusableElements, HelixElement, hostAria, scrollHolders, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ import { matchesHotkey, parseHotkey } from '../hotkey.js';
10
11
  let HelixHeadlessToast = class HelixHeadlessToast extends HelixElement {
11
12
  constructor() {
12
13
  super(...arguments);
13
- this.label = 'Notifications';
14
+ this.hotkey = 'F8';
15
+ this.label = 'Notifications ({hotkey})';
16
+ this.scope = new FocusScopeController(this, {
17
+ contents: () => this.showing.flatMap((one) => {
18
+ const target = focusTarget(one);
19
+ return target ? [target] : [];
20
+ }),
21
+ surface: () => this.region,
22
+ });
23
+ this.handleHotkey = (event) => {
24
+ if (event.defaultPrevented || !matchesHotkey(event, parseHotkey(this.hotkey))) {
25
+ return;
26
+ }
27
+ if (this.scope.holdsFocus) {
28
+ event.preventDefault();
29
+ this.scope.leave();
30
+ return;
31
+ }
32
+ if (this.showing.length > 0) {
33
+ event.preventDefault();
34
+ this.scope.enter();
35
+ }
36
+ };
37
+ this.made = new WeakSet();
38
+ this.handleFocusIn = (event) => {
39
+ const from = event.relatedTarget;
40
+ if (from instanceof HTMLElement && !deepContains(this, from)) {
41
+ this.focusOrigin = from;
42
+ }
43
+ };
44
+ this.handleDismiss = (event) => {
45
+ const item = event.target;
46
+ if (!(item instanceof HTMLElement) || item.parentElement !== this) {
47
+ return;
48
+ }
49
+ if (!deepContains(item, deepActiveElement())) {
50
+ return;
51
+ }
52
+ const before = this.showing;
53
+ const at = before.indexOf(item);
54
+ void Promise.resolve()
55
+ .then(() => item.updateComplete)
56
+ .then(() => {
57
+ if (!item.hidden) {
58
+ return;
59
+ }
60
+ const active = deepActiveElement();
61
+ if (active && active !== document.body && !deepContains(item, active)) {
62
+ return;
63
+ }
64
+ const after = before.slice(at + 1).filter((one) => !one.hidden);
65
+ const earlier = before
66
+ .slice(0, at)
67
+ .filter((one) => !one.hidden)
68
+ .reverse();
69
+ for (const next of [...after, ...earlier]) {
70
+ const target = focusTarget(next);
71
+ if (target) {
72
+ target.focus();
73
+ return;
74
+ }
75
+ }
76
+ if (this.focusOrigin?.isConnected) {
77
+ this.focusOrigin.focus();
78
+ }
79
+ });
80
+ };
14
81
  }
15
82
  willUpdate(changed) {
16
83
  super.willUpdate(changed);
17
- hostAria(this.internals, { label: this.label, role: 'status' });
84
+ hostAria(this.internals, {
85
+ label: fillTemplate(this.label, { hotkey: this.hotkey }),
86
+ role: 'region',
87
+ });
18
88
  this.internals.ariaLive = 'polite';
19
89
  this.internals.ariaRelevant = 'additions';
20
90
  this.internals.ariaAtomic = 'false';
21
91
  }
92
+ add(message, options = {}) {
93
+ const tier = this.constructor.tier;
94
+ const item = document.createElement(`${tier}toast-item`);
95
+ item.textContent = message;
96
+ if (options.tone) {
97
+ item.setAttribute('tone', options.tone);
98
+ }
99
+ if (options.duration !== undefined) {
100
+ item.setAttribute('duration', String(options.duration));
101
+ }
102
+ if (options.dismissible ?? true) {
103
+ item.toggleAttribute('dismissible', true);
104
+ }
105
+ this.made.add(item);
106
+ this.append(item);
107
+ return item;
108
+ }
109
+ sweep() {
110
+ for (const one of [...this.children]) {
111
+ if (!(one instanceof HTMLElement) || !one.hidden || !this.made.has(one)) {
112
+ continue;
113
+ }
114
+ this.made.delete(one);
115
+ const running = typeof one.getAnimations === 'function' ? one.getAnimations({ subtree: true }) : [];
116
+ void Promise.allSettled(running.map((animation) => animation.finished)).then(() => one.remove());
117
+ }
118
+ }
119
+ get showing() {
120
+ return [...this.children].filter((one) => one instanceof HTMLElement && !one.hidden);
121
+ }
122
+ get region() {
123
+ return this.partNamed('region');
124
+ }
125
+ place(added = []) {
126
+ const region = this.region;
127
+ if (!region || typeof region.showPopover !== 'function') {
128
+ return;
129
+ }
130
+ const showing = this.showing;
131
+ const open = popoverOpen(region);
132
+ if (showing.length === 0) {
133
+ if (open) {
134
+ this.lowerWhenStill(region);
135
+ }
136
+ return;
137
+ }
138
+ const arrived = added.filter((one) => one instanceof HTMLElement && showing.includes(one));
139
+ const blocked = arrived.length > 0 && this.blocked();
140
+ if (open && blocked) {
141
+ region.hidePopover();
142
+ }
143
+ if (!open || blocked) {
144
+ region.showPopover();
145
+ }
146
+ if (blocked) {
147
+ this.notify(arrived);
148
+ }
149
+ }
150
+ lowerWhenStill(region) {
151
+ const running = typeof this.getAnimations === 'function' ? this.getAnimations({ subtree: true }) : [];
152
+ void Promise.allSettled(running.map((one) => one.finished)).then(() => {
153
+ if (this.showing.length === 0 && popoverOpen(region)) {
154
+ region.hidePopover();
155
+ }
156
+ });
157
+ }
158
+ blocked() {
159
+ const holders = scrollHolders();
160
+ try {
161
+ const native = document.querySelector('dialog:modal');
162
+ if (native) {
163
+ holders.push(native);
164
+ }
165
+ }
166
+ catch {
167
+ }
168
+ return holders.some((holder) => !deepContains(holder, this));
169
+ }
170
+ notify(arrived) {
171
+ const speaker = document;
172
+ if (typeof speaker.ariaNotify !== 'function') {
173
+ return;
174
+ }
175
+ for (const one of arrived) {
176
+ const words = (one.textContent ?? '').replace(/\s+/gu, ' ').trim();
177
+ if (words !== '') {
178
+ speaker.ariaNotify(words);
179
+ }
180
+ }
181
+ }
182
+ connectedCallback() {
183
+ super.connectedCallback();
184
+ this.addEventListener('focusin', this.handleFocusIn);
185
+ this.addEventListener('helix-dismiss', this.handleDismiss);
186
+ document.addEventListener('keydown', this.handleHotkey);
187
+ this.watcher = new MutationObserver((records) => {
188
+ this.place(records.flatMap((record) => [...record.addedNodes]));
189
+ this.sweep();
190
+ });
191
+ this.watcher.observe(this, {
192
+ attributeFilter: ['hidden'],
193
+ attributes: true,
194
+ childList: true,
195
+ subtree: true,
196
+ });
197
+ if (this.hasUpdated) {
198
+ this.place();
199
+ }
200
+ }
201
+ disconnectedCallback() {
202
+ super.disconnectedCallback();
203
+ this.removeEventListener('focusin', this.handleFocusIn);
204
+ this.removeEventListener('helix-dismiss', this.handleDismiss);
205
+ document.removeEventListener('keydown', this.handleHotkey);
206
+ this.watcher?.disconnect();
207
+ this.watcher = undefined;
208
+ }
209
+ firstUpdated(changed) {
210
+ super.firstUpdated(changed);
211
+ this.place();
212
+ }
22
213
  render() {
23
- return html `<div part="region"><slot></slot></div>`;
214
+ return html `<div part="region" popover="manual" tabindex="-1"><slot></slot></div>`;
24
215
  }
25
216
  };
217
+ __decorate([
218
+ property()
219
+ ], HelixHeadlessToast.prototype, "hotkey", void 0);
26
220
  __decorate([
27
221
  property()
28
222
  ], HelixHeadlessToast.prototype, "label", void 0);
@@ -33,3 +227,14 @@ HelixHeadlessToast = __decorate([
33
227
  customElement('helix-headless-toast')
34
228
  ], HelixHeadlessToast);
35
229
  export { HelixHeadlessToast };
230
+ const popoverOpen = (region) => {
231
+ try {
232
+ return region.matches(':popover-open');
233
+ }
234
+ catch {
235
+ return false;
236
+ }
237
+ };
238
+ const focusTarget = (item) => item.shadowRoot?.querySelector('[part="dismiss"]') ??
239
+ focusableElements(item)[0] ??
240
+ null;
@@ -14,6 +14,17 @@ declare const HelixHeadlessToggleGroup_base: (new (...args: any[]) => import("@f
14
14
  * Measurement happens on the client, so the server renders the indicator hidden
15
15
  * and it fades in once measured.
16
16
  *
17
+ * `selection-mode="multiple"` makes the group a toolbar of buttons that each
18
+ * say whether they are pressed with `aria-pressed`, as React Aria's
19
+ * ToggleButtonGroup and Radix's ToggleGroup do. A press presses or releases
20
+ * one item, the arrow keys move focus without pressing, and `value` is the
21
+ * pressed values, comma separated, which `values` also gives as a list. The
22
+ * form receives one entry per pressed value. The indicator stays hidden, and
23
+ * each pressed item is marked with `:state(pressed)`.
24
+ *
25
+ * `orientation="vertical"` runs the items down a column, says so to the
26
+ * accessibility tree, and the up and down arrows move between them.
27
+ *
17
28
  * @since 0.1.0
18
29
  * @status stable
19
30
  * @aliases SegmentedControl, Segmented, Navigation, Switcher
@@ -21,10 +32,13 @@ declare const HelixHeadlessToggleGroup_base: (new (...args: any[]) => import("@f
21
32
  *
22
33
  * @slot - The group's items.
23
34
  * @slot end - Content after the track, on the same line, such as a count or a reset.
35
+ * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
36
+ * @slot hint - Help under the control, for content richer than the `hint` text.
37
+ * @slot label - The label, for content richer than the `label` text.
24
38
  * @slot start - Content before the track, on the same line, such as an icon or a caption.
25
39
  *
26
40
  * @event {Event} change - Fired after the value changes.
27
- * @event {HelixBeforeChangeEvent} helix-before-change - Fired before the choice changes. Cancelable: call `preventDefault()` to keep the current choice. It is the only way to block a change.
41
+ * @event {HelixBeforeChangeEvent} helix-before-change - Fired before the choice changes. Cancelable: call `preventDefault()` to keep the current choice.
28
42
  * @event {HelixSelectEvent} helix-select - Fired when an item is chosen. The item sends it, and you listen for it on the group. Cancelable: call `preventDefault()` to keep the current choice.
29
43
  * @event {Event} input - Fired after the value changes.
30
44
  *
@@ -85,16 +99,91 @@ export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_b
85
99
  private adopt;
86
100
  /** Marks the chosen item, separately from placing the indicator. */
87
101
  private sync;
102
+ /**
103
+ * The item that holds the group's tab stop: the chosen one.
104
+ *
105
+ * With nothing chosen there is no selection to carry the tab stop, so the
106
+ * first item holds one until there is. Otherwise an unanswered group is a
107
+ * control the keyboard cannot enter, which is what the fallback was quietly
108
+ * also doing for it. A disabled answer takes no focus either, so the entry
109
+ * goes to the first item that can be chosen, as in `Radio.Group`.
110
+ */
111
+ private entry;
112
+ /** The item focus was last on, in a toolbar, which holds its tab stop. */
113
+ private lastFocused;
114
+ private readonly noteFocus;
115
+ /** Whether several items can be pressed at once. */
116
+ private get multiple();
117
+ /**
118
+ * Whether the group holds a tab stop of its own.
119
+ *
120
+ * A toolbar is one tab stop, so a toolbar the group sits in sets this to
121
+ * `false` while its stop is on another control, and every item, the chosen
122
+ * one too, is then out of the tab order. The arrow keys inside the group
123
+ * work as before once focus is in it.
124
+ *
125
+ * No attribute: it is coordination between elements, and a consumer has no
126
+ * reason to set it.
127
+ */
128
+ focusable: boolean;
129
+ /**
130
+ * Where the group's label sits. `start` puts it on the track's line.
131
+ *
132
+ * Absent is above, which is what a field looks like and what almost every
133
+ * group wants. `start` is for a row of controls that are labeled rather than
134
+ * a form of fields: a toolbar reads as one line, and a label stacked over
135
+ * every group in it turns one row into two.
136
+ *
137
+ * **A placement rather than a slot, and that is the whole point of it.** A
138
+ * `start` slot would let a consumer put the word there, and would also let
139
+ * them put it there *instead of* setting `label`, which is the group's
140
+ * accessible name: the set would then be named for nobody using a screen
141
+ * reader while looking correct to everybody else. Worse, setting both draws
142
+ * it twice. One property moves the label the consumer already has to give,
143
+ * and cannot be used to remove it.
144
+ */
145
+ labelPlacement?: 'start';
146
+ /**
147
+ * The direction the items run. `vertical` puts them in a column, and the up
148
+ * and down arrows move between them.
149
+ *
150
+ * Optional, so the attribute is absent until a consumer writes it.
151
+ */
152
+ orientation?: 'horizontal' | 'vertical';
153
+ /**
154
+ * Whether one item is chosen, as a radio group, or several can be pressed,
155
+ * as a toolbar of buttons with `aria-pressed`.
156
+ *
157
+ * Optional, so the attribute is absent until a consumer writes it.
158
+ */
159
+ selectionMode?: 'multiple' | 'single';
160
+ /**
161
+ * Focus goes to the item that holds the stop, and to the item that would
162
+ * hold it while a toolbar holds the stop instead, so a label press or a
163
+ * refused submission still lands in the group.
164
+ */
165
+ protected get focusTarget(): HTMLElement | null;
88
166
  private readonly indicator;
89
167
  protected readonly keys: KeyboardNavigationController;
90
168
  protected readonly cascade: DisabledCascadeController;
91
169
  private get items();
92
170
  private readonly handleSelection;
171
+ /**
172
+ * Whether the group holds an answer the form can submit: a value an item
173
+ * here carries. A chosen item that was removed is no longer an answer, and
174
+ * `value` keeps the name for an item that arrives later.
175
+ */
176
+ private get answered();
93
177
  connectedCallback(): void;
94
178
  disconnectedCallback(): void;
95
179
  protected firstUpdated(changed: PropertyValues): void;
96
180
  protected updated(changed: PropertyValues): void;
97
181
  protected willUpdate(changed: PropertyValues): void;
182
+ /**
183
+ * What the form receives: the value, or under `selection-mode="multiple"`
184
+ * one entry per pressed value, as a native `<select multiple>` sends.
185
+ */
186
+ private formValue;
98
187
  /**
99
188
  * The verdict on the answer `required` asks for.
100
189
  *
@@ -108,22 +197,12 @@ export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_b
108
197
  */
109
198
  private validate;
110
199
  /**
111
- * Where the group's label sits. `start` puts it on the track's line.
112
- *
113
- * Absent is above, which is what a field looks like and what almost every
114
- * group wants. `start` is for a row of controls that are labeled rather than
115
- * a form of fields: a toolbar reads as one line, and a label stacked over
116
- * every group in it turns one row into two.
117
- *
118
- * **A placement rather than a slot, and that is the whole point of it.** A
119
- * `start` slot would let a consumer put the word there, and would also let
120
- * them put it there *instead of* setting `label`, which is the group's
121
- * accessible name: the set would then be named for nobody using a screen
122
- * reader while looking correct to everybody else. Worse, setting both draws
123
- * it twice. One property moves the label the consumer already has to give,
124
- * and cannot be used to remove it.
200
+ * The pressed values as a list, under `selection-mode="multiple"`, in the
201
+ * order the items are written. Written, it presses exactly those items.
202
+ * Where one item is chosen, it is that one value, or none.
125
203
  */
126
- labelPlacement?: 'start';
204
+ get values(): string[];
205
+ set values(next: string[]);
127
206
  /**
128
207
  * The row the track sits in, so something can sit beside it.
129
208
  *