@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
@@ -148,8 +148,11 @@ export declare class HelixHeadlessAccordionItem extends HelixHeadlessAccordionIt
148
148
  /**
149
149
  * Opens the section find in page has matched in, before the match is shown.
150
150
  *
151
- * The browser removes `hidden` itself. A refusal puts it back, so the trigger
152
- * and the region still agree.
151
+ * A refusal puts `hidden` back, so the trigger and the region still agree.
152
+ * The browser removes the attribute after this event's listeners have
153
+ * returned, and after the microtasks they queued, so writing it here or in a
154
+ * microtask is undone at once. A mutation observer hears the removal itself
155
+ * and runs before the next paint or key press.
153
156
  */
154
157
  private readonly handleBeforeMatch;
155
158
  /** Whether the item was open at the last update, and `undefined` before one. */
@@ -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 { closestInstance, customElement, HelixElement, headingLevel, MixinControllerOpen, onUnlessFalse, } from '@fusion.dev/helix-core';
7
+ import { animationsFinished, closestInstance, customElement, HelixElement, headingLevel, MixinControllerOpen, onUnlessFalse, } from '@fusion.dev/helix-core';
8
8
  import { caretDown } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -23,8 +23,14 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
23
23
  if (this.open || this.requestOpen(true)) {
24
24
  return;
25
25
  }
26
- ;
27
- event.currentTarget.setAttribute('hidden', 'until-found');
26
+ const content = event.currentTarget;
27
+ const observer = new MutationObserver(() => {
28
+ observer.disconnect();
29
+ if (!this.open && !this.closing && !content.hasAttribute('hidden')) {
30
+ content.setAttribute('hidden', 'until-found');
31
+ }
32
+ });
33
+ observer.observe(content, { attributeFilter: ['hidden'] });
28
34
  };
29
35
  this.wasOpen = undefined;
30
36
  this.handleToggle = () => {
@@ -72,13 +78,11 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
72
78
  }
73
79
  }
74
80
  finishClosing() {
75
- const running = this.partNamed('content')?.getAnimations?.({ subtree: true }) ?? [];
76
- const done = () => {
81
+ void animationsFinished(this.partNamed('content')).then(() => {
77
82
  if (!this.open) {
78
83
  this.closing = false;
79
84
  }
80
- };
81
- Promise.all(running.map((animation) => animation.finished)).then(done, done);
85
+ });
82
86
  }
83
87
  get accordion() {
84
88
  return closestInstance(this, HelixHeadlessAccordion);
@@ -4,16 +4,14 @@ 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, KeyboardNavigationController, onUnlessFalse, whenHydrated, } from '@fusion.dev/helix-core';
7
+ import { customElement, focusItemOf, HelixElement, KeyboardNavigationController, onUnlessFalse, whenHydrated, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessAccordionItem } from '../item/accordion-item.js';
11
- const navigable = (item) => ({
12
- contains: (node) => node === item,
11
+ const navigable = (item) => focusItemOf(item, {
13
12
  disabled: item.disabled,
14
- focusItem: () => item.focusTrigger(),
15
- select: () => { },
16
- selected: false,
13
+ focus: () => item.focusTrigger(),
14
+ owns: (node) => node === item,
17
15
  });
18
16
  let HelixHeadlessAccordion = class HelixHeadlessAccordion extends HelixElement {
19
17
  constructor() {
@@ -1,5 +1,5 @@
1
1
  import { ArrivalController, HelixElement } from '@fusion.dev/helix-core';
2
- import { type PropertyValues, type TemplateResult } from 'lit';
2
+ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessAnnouncement_base: (new (...args: any[]) => import("@fusion.dev/helix-core").DismissibleElement) & typeof HelixElement;
4
4
  /**
5
5
  * @summary A message for the whole page, shown above its header, that can be dismissed.
@@ -29,6 +29,23 @@ declare const HelixHeadlessAnnouncement_base: (new (...args: any[]) => import("@
29
29
  * announcement beside it, move focus yourself, for example to the page's main
30
30
  * heading.
31
31
  *
32
+ * **The announcement is a landmark region**, named by `label`, "Announcement"
33
+ * unless you set it, so a screen reader lists it among the page's regions.
34
+ * `hotkey`, F6 unless you set it, moves focus to the announcement's first
35
+ * control, an action or the dismiss button, and pressed there moves focus
36
+ * back to where it was, as React Aria's toast region does. An announcement
37
+ * with no control leaves the key to the browser. When an announcement that
38
+ * the key moved focus into is removed with focus still inside, focus goes
39
+ * back to where it was before the key.
40
+ *
41
+ * **Set `queued` to show several announcements one at a time.** A queued
42
+ * announcement waits while a queued announcement before it in the same parent
43
+ * is still there, and shows when that one is removed: its message is then
44
+ * read out, and its `duration` starts. While it waits it renders nothing,
45
+ * is no region, and carries `:state(waiting)`. A server cannot see the
46
+ * announcements beside it, so a served page shows every one until the script
47
+ * runs.
48
+ *
32
49
  * Set `duration` to dismiss it after a time. The clock stops while the pointer
33
50
  * or focus is on the announcement, and continues from what was left, so a
34
51
  * reader partway through is not cut off. It stops while the page is hidden too,
@@ -57,8 +74,14 @@ declare const HelixHeadlessAnnouncement_base: (new (...args: any[]) => import("@
57
74
  * @csspart live - A copy of the message in a live region, read by screen readers when the announcement appears after the page has loaded, and emptied seven seconds later. Visually hidden in the styled tier.
58
75
  *
59
76
  * @cssstate paused - The clock `duration` started is stopped, while the pointer or focus is on the announcement or the page is hidden.
77
+ * @cssstate waiting - The announcement is `queued` behind another, and renders nothing until that one is removed.
60
78
  */
61
79
  export declare class HelixHeadlessAnnouncement extends HelixHeadlessAnnouncement_base {
80
+ /**
81
+ * The key this family's words are registered under, its styled tag:
82
+ * `registerWords('de', { 'helix-announcement': { label: 'Mitteilung' } })`.
83
+ */
84
+ static words: string;
62
85
  /**
63
86
  * How long the announcement stays, in milliseconds, before it sends
64
87
  * `helix-dismiss`. When it is not set, it stays until it is dismissed.
@@ -67,6 +90,28 @@ export declare class HelixHeadlessAnnouncement extends HelixHeadlessAnnouncement
67
90
  * own. The clock stops while the pointer or focus is on the announcement.
68
91
  */
69
92
  duration?: number;
93
+ /**
94
+ * The key that moves focus to the announcement's first control, and back
95
+ * to where it was, written as keys joined by `+`, such as `F6` or `Alt+A`.
96
+ *
97
+ * F6 by default, the key React Aria's regions answer to. Empty turns it
98
+ * off. With several announcements, the first one in the page takes focus.
99
+ */
100
+ hotkey: string;
101
+ /**
102
+ * The name of the announcement's region, which screen readers list among
103
+ * the page's landmarks. Translate it with the rest of your interface.
104
+ *
105
+ * Empty leaves the announcement no region.
106
+ */
107
+ label: string;
108
+ /**
109
+ * Whether the announcement waits its turn behind the queued announcements
110
+ * before it in the same parent, showing when the last of them is removed.
111
+ *
112
+ * Off by default, so every announcement on a page shows at once.
113
+ */
114
+ queued: boolean;
70
115
  /**
71
116
  * The band's color.
72
117
  *
@@ -90,18 +135,29 @@ export declare class HelixHeadlessAnnouncement extends HelixHeadlessAnnouncement
90
135
  * axes, which is the test the registry answers to.
91
136
  */
92
137
  tone?: 'accent' | 'danger' | 'info' | 'neutral' | 'primary' | 'success' | 'warning';
138
+ /** Whether a queued announcement before this one is still there. */
139
+ private waiting;
140
+ /** Watches the parent for the announcements before this one leaving. */
141
+ private queue;
142
+ /** Whether a queued announcement comes before this one in its parent. */
143
+ private behind;
93
144
  protected readonly arrival: ArrivalController;
94
145
  /** The time `duration` gives it, held while a reader is on it. */
95
146
  private readonly countdown;
96
- /** Whether the page being hidden is one of the reasons the clock is held. */
97
- private heldHidden;
98
- private readonly handleVisibility;
147
+ private readonly scope;
148
+ /** Where focus was when `hotkey` moved it in. */
149
+ private origin;
150
+ /** Whether focus is inside the announcement, kept as it moves. */
151
+ private focused;
152
+ private readonly handleHotkey;
153
+ protected willUpdate(changed: PropertyValues): void;
99
154
  private readonly handleHold;
100
155
  private readonly handleRelease;
101
156
  connectedCallback(): void;
102
157
  disconnectedCallback(): void;
103
158
  protected firstUpdated(changed: PropertyValues): void;
104
159
  protected updated(changed: PropertyValues): void;
160
+ private watchQueue;
105
161
  /**
106
162
  * Says on the `actions` region whether anything is in it.
107
163
  *
@@ -126,7 +182,7 @@ export declare class HelixHeadlessAnnouncement extends HelixHeadlessAnnouncement
126
182
  * draws a tone mark. An announcement has no tone mark at all.
127
183
  */
128
184
  protected renderDismissIcon(): TemplateResult;
129
- protected render(): TemplateResult;
185
+ protected render(): TemplateResult | typeof nothing;
130
186
  }
131
187
  declare global {
132
188
  interface HTMLElementTagNameMap {
@@ -4,54 +4,111 @@ 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 { ArrivalController, CountdownController, customElement, HelixElement, MixinDismissible, markRegion, reportRuntime, slottedWords, } from '@fusion.dev/helix-core';
7
+ var HelixHeadlessAnnouncement_1;
8
+ import { ArrivalController, ariaChord, CountdownController, customElement, deepActiveElement, FocusScopeController, HelixElement, hostAria, MixinDismissible, markRegion, matchesChord, onApple, reportRuntime, slottedWords, words, } from '@fusion.dev/helix-core';
8
9
  import { x } from '@fusion.dev/helix-icons';
9
10
  import { html, nothing } from 'lit';
10
- import { property } from 'lit/decorators.js';
11
+ import { property, state } from 'lit/decorators.js';
11
12
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
13
  let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDismissible(HelixElement) {
13
14
  constructor() {
14
15
  super(...arguments);
16
+ this.hotkey = 'F6';
17
+ this.label = 'Announcement';
18
+ this.queued = false;
19
+ this.waiting = false;
15
20
  this.arrival = new ArrivalController(this, {
16
21
  arriving: () => !this.serverRendered && document.readyState === 'complete',
17
- text: () => slottedWords(this, ['']),
22
+ text: () => (this.waiting ? '' : slottedWords(this, [''])),
18
23
  });
19
24
  this.countdown = new CountdownController(this, {
20
25
  done: () => {
21
26
  this.dismiss();
22
27
  },
28
+ holdWhileHidden: true,
23
29
  paused: (paused) => {
24
30
  this.toggleState('paused', paused);
25
31
  },
26
32
  });
27
- this.heldHidden = false;
28
- this.handleVisibility = () => {
29
- if (document.hidden && !this.heldHidden) {
30
- this.heldHidden = true;
31
- this.countdown.hold();
33
+ this.scope = new FocusScopeController(this, {
34
+ contents: () => [
35
+ ...(this.renderRoot
36
+ ?.querySelector('slot[name="actions"]')
37
+ ?.assignedElements({ flatten: true }) ?? []),
38
+ ...[...(this.renderRoot?.querySelectorAll('[part="dismiss"]') ?? [])],
39
+ ],
40
+ surface: () => null,
41
+ });
42
+ this.focused = false;
43
+ this.handleHotkey = (event) => {
44
+ if (event.defaultPrevented || !matchesChord(event, this.hotkey, onApple())) {
45
+ return;
46
+ }
47
+ if (this.scope.holdsFocus) {
48
+ if (this.scope.leave()) {
49
+ event.preventDefault();
50
+ }
51
+ this.origin = undefined;
52
+ return;
32
53
  }
33
- else if (!document.hidden && this.heldHidden) {
34
- this.heldHidden = false;
35
- this.countdown.release();
54
+ const other = event
55
+ .composedPath()
56
+ .some((node) => node !== this && node instanceof HelixHeadlessAnnouncement_1);
57
+ if (other || this.scope.controls.length === 0) {
58
+ return;
36
59
  }
60
+ event.preventDefault();
61
+ const active = deepActiveElement();
62
+ this.origin = active instanceof HTMLElement ? active : undefined;
63
+ this.scope.enter();
37
64
  };
38
- this.handleHold = () => {
65
+ this.handleHold = (event) => {
66
+ this.focused ||= event.type === 'focusin';
39
67
  this.countdown.hold();
40
68
  };
41
- this.handleRelease = () => {
69
+ this.handleRelease = (event) => {
70
+ if (event.type === 'focusout') {
71
+ queueMicrotask(() => {
72
+ this.focused = this.matches(':focus-within');
73
+ });
74
+ }
42
75
  this.countdown.release();
43
76
  };
44
77
  }
78
+ static { HelixHeadlessAnnouncement_1 = this; }
79
+ static { this.words = 'helix-announcement'; }
80
+ behind() {
81
+ if (!this.queued || (this.shadowRoot !== null && !this.hasUpdated)) {
82
+ return false;
83
+ }
84
+ for (let at = this.previousElementSibling; at; at = at.previousElementSibling) {
85
+ if (at instanceof HelixHeadlessAnnouncement_1 && at.queued) {
86
+ return true;
87
+ }
88
+ }
89
+ return false;
90
+ }
91
+ willUpdate(changed) {
92
+ super.willUpdate(changed);
93
+ hostAria(this.internals, {
94
+ label: this.label,
95
+ role: this.label.trim() && !this.waiting ? 'region' : '',
96
+ });
97
+ if ('ariaKeyShortcuts' in this.internals) {
98
+ this.internals.ariaKeyShortcuts = ariaChord(this.hotkey, onApple()) || null;
99
+ }
100
+ }
45
101
  connectedCallback() {
102
+ this.waiting = this.behind();
46
103
  super.connectedCallback();
104
+ this.watchQueue();
47
105
  this.addEventListener('mouseenter', this.handleHold);
48
106
  this.addEventListener('mouseleave', this.handleRelease);
49
107
  this.addEventListener('focusin', this.handleHold);
50
108
  this.addEventListener('focusout', this.handleRelease);
51
- document.addEventListener('visibilitychange', this.handleVisibility);
109
+ document.addEventListener('keydown', this.handleHotkey);
52
110
  if (this.hasUpdated) {
53
111
  this.countdown.resume();
54
- this.handleVisibility();
55
112
  }
56
113
  }
57
114
  disconnectedCallback() {
@@ -60,13 +117,26 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
60
117
  this.removeEventListener('mouseleave', this.handleRelease);
61
118
  this.removeEventListener('focusin', this.handleHold);
62
119
  this.removeEventListener('focusout', this.handleRelease);
63
- document.removeEventListener('visibilitychange', this.handleVisibility);
64
- this.heldHidden = false;
120
+ document.removeEventListener('keydown', this.handleHotkey);
121
+ this.queue?.disconnect();
122
+ const origin = this.focused ? this.origin : undefined;
123
+ this.focused = false;
124
+ this.origin = undefined;
125
+ if (origin) {
126
+ requestAnimationFrame(() => {
127
+ const active = deepActiveElement();
128
+ if (origin.isConnected && (active === null || active === document.body)) {
129
+ origin.focus({ preventScroll: true });
130
+ }
131
+ });
132
+ }
65
133
  }
66
134
  firstUpdated(changed) {
67
135
  super.firstUpdated(changed);
68
- this.countdown.start(this.duration);
69
- this.handleVisibility();
136
+ this.watchQueue();
137
+ if (!this.waiting) {
138
+ this.countdown.start(this.duration);
139
+ }
70
140
  if (this.querySelector(':scope > [slot="icon"]')) {
71
141
  reportRuntime({
72
142
  kind: 'deprecated',
@@ -78,6 +148,26 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
78
148
  updated(changed) {
79
149
  super.updated(changed);
80
150
  this.markEmpty();
151
+ if (changed.has('queued')) {
152
+ this.watchQueue();
153
+ }
154
+ this.toggleState('waiting', this.waiting);
155
+ if (changed.has('waiting') && this.waiting) {
156
+ this.arrival.say();
157
+ }
158
+ else if (changed.get('waiting') === true) {
159
+ this.countdown.start(this.duration);
160
+ }
161
+ }
162
+ watchQueue() {
163
+ this.queue?.disconnect();
164
+ this.waiting = this.behind();
165
+ if (this.queued && this.parentElement && typeof MutationObserver !== 'undefined') {
166
+ this.queue ??= new MutationObserver(() => {
167
+ this.waiting = this.behind();
168
+ });
169
+ this.queue.observe(this.parentElement, { childList: true });
170
+ }
81
171
  }
82
172
  markEmpty() {
83
173
  markRegion(this.renderRoot, 'actions', ['actions']);
@@ -92,6 +182,9 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
92
182
  >`;
93
183
  }
94
184
  render() {
185
+ if (this.waiting) {
186
+ return nothing;
187
+ }
95
188
  return html `<div aria-hidden=${this.arrival.said === '' ? nothing : 'true'} part="content"
96
189
  ><slot @slotchange=${() => this.arrival.say()}></slot
97
190
  ></div>
@@ -112,10 +205,23 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
112
205
  __decorate([
113
206
  property({ type: Number })
114
207
  ], HelixHeadlessAnnouncement.prototype, "duration", void 0);
208
+ __decorate([
209
+ property()
210
+ ], HelixHeadlessAnnouncement.prototype, "hotkey", void 0);
211
+ __decorate([
212
+ words,
213
+ property()
214
+ ], HelixHeadlessAnnouncement.prototype, "label", void 0);
215
+ __decorate([
216
+ property({ reflect: true, type: Boolean })
217
+ ], HelixHeadlessAnnouncement.prototype, "queued", void 0);
115
218
  __decorate([
116
219
  property({ reflect: true })
117
220
  ], HelixHeadlessAnnouncement.prototype, "tone", void 0);
118
- HelixHeadlessAnnouncement = __decorate([
221
+ __decorate([
222
+ state()
223
+ ], HelixHeadlessAnnouncement.prototype, "waiting", void 0);
224
+ HelixHeadlessAnnouncement = HelixHeadlessAnnouncement_1 = __decorate([
119
225
  customElement('helix-headless-announcement')
120
226
  ], HelixHeadlessAnnouncement);
121
227
  export { HelixHeadlessAnnouncement };
@@ -101,6 +101,11 @@ declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args:
101
101
  * @csspart tooltip-value - A column's value inside the tooltip.
102
102
  */
103
103
  export declare class HelixHeadlessAreaChart extends HelixHeadlessAreaChart_base {
104
+ /**
105
+ * The key this family's words are registered under, its styled tag:
106
+ * `registerWords('de', { 'helix-area-chart': { totalLabel: 'Summe' } })`.
107
+ */
108
+ static words: string;
104
109
  /**
105
110
  * How a band's edge gets from one reading to the next.
106
111
  *
@@ -146,6 +151,12 @@ export declare class HelixHeadlessAreaChart extends HelixHeadlessAreaChart_base
146
151
  * fills the height and the bands are shares of it.
147
152
  */
148
153
  share: boolean;
154
+ /**
155
+ * The summary's sentence. `{reading}` is the latest reading's name,
156
+ * `{totalLabel}` the name for the sum, `{total}` the sum, written in the
157
+ * locale, and `{shares}` each part's share, listed as the locale lists.
158
+ */
159
+ summaryLabel: string;
149
160
  /**
150
161
  * The name for the sum of the parts, shown in the tooltip and the summary.
151
162
  */
@@ -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 { CHART_PLOT, customElement, drawTooltip, formatNumbers, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, reportRuntime, seriesIndex, ticks, } from '@fusion.dev/helix-core';
7
+ import { CHART_PLOT, customElement, drawTooltip, formatNumbers, formatWords, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, reportRuntime, seriesIndex, ticks, words, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
@@ -25,9 +25,11 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
25
25
  super(...arguments);
26
26
  this.overlap = false;
27
27
  this.share = false;
28
+ this.summaryLabel = '{reading}: {totalLabel} {total}; {shares}.';
28
29
  this.totalLabel = 'Total';
29
30
  this.warnedAboutNegatives = false;
30
31
  }
32
+ static { this.words = 'helix-area-chart'; }
31
33
  get rulesValue() {
32
34
  return this.gridlines !== undefined && this.gridlines !== 'category';
33
35
  }
@@ -37,7 +39,7 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
37
39
  get stack() {
38
40
  const rows = this.sourceRows;
39
41
  const columns = this.plottedColumns;
40
- const names = columns.map((column) => rows.find((row) => row.cells[column])?.cells[column]?.name ?? '');
42
+ const names = columns.map((column) => this.columnName(column));
41
43
  const values = columns.map((column, depth) => rows.map((row) => {
42
44
  if (!this.showsSeries(names[depth] ?? '')) {
43
45
  return 0;
@@ -179,19 +181,24 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
179
181
  renderSummary(stack, rows) {
180
182
  const last = rows.length - 1;
181
183
  const total = stack.totals[last] ?? 0;
182
- const format = formatNumbers(this.locale);
184
+ const locale = wordsLocale(this);
183
185
  const shares = stack.names.flatMap((name, depth) => this.showsSeries(name)
184
186
  ? [
185
- `${name} ${percentIn(this.locale).format(total > 0 ? (stack.values[depth]?.[last] ?? 0) / total : 0)}`,
187
+ `${name} ${percentIn(locale).format(total > 0 ? (stack.values[depth]?.[last] ?? 0) / total : 0)}`,
186
188
  ]
187
189
  : []);
188
190
  const named = this.named ? `${this.named}, ` : '';
189
- return html `<p id="helix-chart-summary" part="summary">
190
- ${named}${rows[last]?.label ?? ''}: ${this.totalLabel} ${format(total)}; ${shares.join(', ')}.
191
- </p>`;
191
+ const said = `${named}${formatWords(this, 'summaryLabel', {
192
+ reading: rows[last]?.label ?? '',
193
+ shares: new Intl.ListFormat(locale, { style: 'short', type: 'unit' }).format(shares),
194
+ total,
195
+ totalLabel: this.totalLabel,
196
+ })}`;
197
+ return html `<p id="helix-chart-summary" part="summary">${said}</p>`;
192
198
  }
193
199
  tallied(rows, stack) {
194
- const format = formatNumbers(this.locale);
200
+ const locale = wordsLocale(this);
201
+ const format = formatNumbers(locale);
195
202
  return rows.map((row, index) => {
196
203
  const total = stack.totals[index] ?? 0;
197
204
  return {
@@ -200,7 +207,7 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
200
207
  ...row.cells.map((cell, column) => this.share && cell.value !== undefined && stack.columns.includes(column) && total > 0
201
208
  ? {
202
209
  ...cell,
203
- text: `${cell.text} (${percentIn(this.locale).format(Math.max(0, cell.value) / total)})`,
210
+ text: `${cell.text} (${percentIn(locale).format(Math.max(0, cell.value) / total)})`,
204
211
  }
205
212
  : cell),
206
213
  { name: this.totalLabel, text: format(total) },
@@ -235,6 +242,11 @@ __decorate([
235
242
  property({ reflect: true, type: Boolean })
236
243
  ], HelixHeadlessAreaChart.prototype, "share", void 0);
237
244
  __decorate([
245
+ words,
246
+ property({ attribute: 'summary-label' })
247
+ ], HelixHeadlessAreaChart.prototype, "summaryLabel", void 0);
248
+ __decorate([
249
+ words,
238
250
  property({ attribute: 'total-label' })
239
251
  ], HelixHeadlessAreaChart.prototype, "totalLabel", void 0);
240
252
  HelixHeadlessAreaChart = __decorate([
@@ -36,6 +36,11 @@ export declare const FOLDS_UP_TO = 12;
36
36
  * @csspart overflow - The count of avatars past `max`, an image named by `overflow-label`.
37
37
  */
38
38
  export declare class HelixHeadlessAvatarGroup extends HelixElement {
39
+ /**
40
+ * The key this part's words are registered under, its styled tag:
41
+ * `registerWords('de', { 'helix-avatar-group': { overflowLabel: '…' } })`.
42
+ */
43
+ static words: string;
39
44
  /**
40
45
  * Accessible name for the set, such as "Reviewers".
41
46
  */
@@ -60,8 +65,11 @@ export declare class HelixHeadlessAvatarGroup extends HelixElement {
60
65
  */
61
66
  max?: number;
62
67
  /**
63
- * What a screen reader is told the count is, with `{count}` for the number
64
- * and `{names}` for the people it stands for, by each avatar's `name`.
68
+ * What a screen reader is told the count is, with `{count}` for the number,
69
+ * written in the locale, and `{names}` for the people it stands for, by each
70
+ * avatar's `name`, listed as the locale lists. A language that needs plural
71
+ * forms writes them: `{count, plural, one {# osoba więcej: {names}} few
72
+ * {# osoby więcej: {names}} other {# osób więcej: {names}}}`.
65
73
  *
66
74
  * The count shows "+3", which reads as "plus three" and says nothing about
67
75
  * what was counted, nor who.
@@ -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, fillTemplate, HelixElement, hostAria, whenHydrated, } from '@fusion.dev/helix-core';
7
+ import { customElement, formatWords, HelixElement, hostAria, whenHydrated, words, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  export const FOLDS_UP_TO = 12;
@@ -12,9 +12,10 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
12
12
  constructor() {
13
13
  super(...arguments);
14
14
  this.label = '';
15
- this.overflowLabel = '{count} more: {names}';
15
+ this.overflowLabel = '{count, plural, one {# more: {names}} other {# more: {names}}}';
16
16
  this.folded = new WeakSet();
17
17
  }
18
+ static { this.words = 'helix-avatar-group'; }
18
19
  get avatars() {
19
20
  return this.knownChildren.filter((child) => child instanceof HTMLElement);
20
21
  }
@@ -68,9 +69,12 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
68
69
  ? html `<span
69
70
  part="overflow"
70
71
  role="img"
71
- aria-label=${fillTemplate(this.overflowLabel, {
72
- count: String(past),
73
- names: this.foldedNames().join(', '),
72
+ aria-label=${formatWords(this, 'overflowLabel', {
73
+ count: past,
74
+ names: new Intl.ListFormat(wordsLocale(this), {
75
+ style: 'short',
76
+ type: 'unit',
77
+ }).format(this.foldedNames()),
74
78
  }).replace(/[\s:,]+$/, '')}
75
79
  >+${past}</span
76
80
  >`
@@ -84,6 +88,7 @@ __decorate([
84
88
  property({ reflect: true, type: Number })
85
89
  ], HelixHeadlessAvatarGroup.prototype, "max", void 0);
86
90
  __decorate([
91
+ words,
87
92
  property({ attribute: 'overflow-label' })
88
93
  ], HelixHeadlessAvatarGroup.prototype, "overflowLabel", void 0);
89
94
  HelixHeadlessAvatarGroup = __decorate([
@@ -0,0 +1,19 @@
1
+ /** Whether the value counts nothing, in which case there is no mark. */
2
+ export declare const badgeBlank: (given: string) => boolean;
3
+ /** Whether the count is past `max`, which the mark shows as `{max}+`. */
4
+ export declare const badgeCapped: (given: string, max: number | undefined) => boolean;
5
+ /**
6
+ * What the mark reads once the cap has been applied: the value as given, or
7
+ * written with `options` in `locale` where they are set and it is a number.
8
+ */
9
+ export declare const badgeText: (given: string, max: number | undefined, locale: string, options: Intl.NumberFormatOptions | undefined) => string;
10
+ /**
11
+ * What a reader hears for the mark: `label` with `{count}` filled, or the text
12
+ * alone, and nothing for a blank mark or an unnamed dot.
13
+ *
14
+ * `label` is a message, so it can hold plural forms: `{count, plural, one
15
+ * {{count} unread} other {{count} unread}}`. `{count}` is the text as the mark
16
+ * shows it, such as `99+`, and the form is picked by its number in `locale`'s
17
+ * rules, `other` where the text is no number.
18
+ */
19
+ export declare const badgeWords: (text: string, label: string, blank: boolean, locale?: string) => string;
@@ -0,0 +1,20 @@
1
+ import { formatMessage, formatNumber } from '@fusion.dev/helix-core';
2
+ export const badgeBlank = (given) => given.trim() !== '' && Number(given) === 0;
3
+ export const badgeCapped = (given, max) => {
4
+ const count = Number(given);
5
+ return max !== undefined && Number.isFinite(count) && count > max;
6
+ };
7
+ export const badgeText = (given, max, locale, options) => {
8
+ const write = (count) => options ? formatNumber(count, locale, options) : String(count);
9
+ if (badgeCapped(given, max)) {
10
+ return `${write(max)}+`;
11
+ }
12
+ const count = Number(given);
13
+ return options && given.trim() !== '' && Number.isFinite(count) ? write(count) : given;
14
+ };
15
+ export const badgeWords = (text, label, blank, locale = '') => {
16
+ if (blank || (text === '' && label === '')) {
17
+ return '';
18
+ }
19
+ return formatMessage(label, { count: text }, locale) || text;
20
+ };
@@ -64,12 +64,16 @@ export declare class HelixHeadlessBadge extends HelixElement {
64
64
  * Required for a dot, which has no text to read. `{count}` is replaced with
65
65
  * what the badge shows, so `label="{count} unread"` reads "8 unread" and
66
66
  * stays current as `value` changes, with no label of yours to keep in step.
67
+ * A language with plural forms writes them, `{count, plural, one {{count}
68
+ * nieprzeczytana} few {{count} nieprzeczytane} other {{count}
69
+ * nieprzeczytanych}}`, picked by the count in `locale`.
67
70
  */
68
71
  label: string;
69
72
  /**
70
- * The locale a count is written in under `format-options`, such as `de`.
73
+ * The locale a count is written in under `format-options`, and whose plural
74
+ * rules `label` picks a form by, such as `de`.
71
75
  *
72
- * Absent uses the runtime's own.
76
+ * Absent reads the nearest `lang`, then the runtime's own.
73
77
  */
74
78
  locale: string;
75
79
  /**