@fusion.dev/helix-headless 0.145.0 → 0.146.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/custom-elements.json +7373 -3849
  2. package/dist/accordion/item/accordion-item.d.ts +14 -0
  3. package/dist/accordion/item/accordion-item.js +16 -6
  4. package/dist/accordion/root/accordion-root.d.ts +8 -5
  5. package/dist/announcement/root/announcement-root.d.ts +3 -2
  6. package/dist/announcement/root/announcement-root.js +3 -1
  7. package/dist/app-header/root/app-header-root.d.ts +7 -2
  8. package/dist/app-header/root/app-header-root.js +9 -1
  9. package/dist/app-header/sectioning.d.ts +3 -1
  10. package/dist/app-header/sectioning.js +3 -2
  11. package/dist/avatar/group/avatar-group.d.ts +20 -14
  12. package/dist/avatar/group/avatar-group.js +17 -19
  13. package/dist/badge/root/badge-root.d.ts +3 -1
  14. package/dist/badge/root/badge-root.js +2 -2
  15. package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
  16. package/dist/bar-chart/root/bar-chart-root.js +52 -15
  17. package/dist/bar-chart/stack.d.ts +15 -0
  18. package/dist/bar-chart/stack.js +19 -0
  19. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +25 -0
  20. package/dist/breadcrumbs/root/breadcrumbs-root.js +50 -3
  21. package/dist/button/root/button-root.d.ts +26 -0
  22. package/dist/button/root/button-root.js +38 -18
  23. package/dist/button-group/root/button-group-root.d.ts +2 -1
  24. package/dist/button-group/root/button-group-root.js +5 -1
  25. package/dist/canvas/node/canvas-node.d.ts +4 -3
  26. package/dist/canvas/node/canvas-node.js +3 -2
  27. package/dist/canvas/root/canvas-root.js +2 -2
  28. package/dist/card/root/card-root.d.ts +24 -3
  29. package/dist/card/root/card-root.js +20 -8
  30. package/dist/carousel/root/carousel-root.d.ts +43 -4
  31. package/dist/carousel/root/carousel-root.js +40 -12
  32. package/dist/carousel/stops.d.ts +20 -0
  33. package/dist/carousel/stops.js +30 -0
  34. package/dist/chat/message/chat-message.js +5 -4
  35. package/dist/chat/naming.d.ts +5 -1
  36. package/dist/chat/naming.js +4 -2
  37. package/dist/chat/root/chat-root.d.ts +25 -3
  38. package/dist/chat/root/chat-root.js +22 -5
  39. package/dist/checkbox/root/checkbox-root.d.ts +20 -0
  40. package/dist/checkbox/root/checkbox-root.js +14 -1
  41. package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
  42. package/dist/checkbox-group/root/checkbox-group-root.js +12 -10
  43. package/dist/checkbox-group/select-all.d.ts +27 -0
  44. package/dist/checkbox-group/select-all.js +12 -0
  45. package/dist/code-block/highlight.d.ts +15 -0
  46. package/dist/code-block/highlight.js +4 -0
  47. package/dist/code-block/root/code-block-root.d.ts +49 -1
  48. package/dist/code-block/root/code-block-root.js +58 -3
  49. package/dist/color-input/root/color-input-root.d.ts +3 -0
  50. package/dist/color-input/root/color-input-root.js +6 -3
  51. package/dist/color-picker/root/color-picker-root.d.ts +33 -4
  52. package/dist/color-picker/root/color-picker-root.js +80 -21
  53. package/dist/color-picker/swatches.d.ts +12 -0
  54. package/dist/color-picker/swatches.js +26 -0
  55. package/dist/combobox/root/combobox-root.d.ts +43 -0
  56. package/dist/combobox/root/combobox-root.js +79 -15
  57. package/dist/command/root/command-root.d.ts +16 -1
  58. package/dist/command/root/command-root.js +22 -4
  59. package/dist/copy-button/root/copy-button-root.d.ts +5 -0
  60. package/dist/copy-button/root/copy-button-root.js +10 -3
  61. package/dist/data-table/index.d.ts +1 -1
  62. package/dist/data-table/index.js +1 -1
  63. package/dist/data-table/keys.d.ts +24 -0
  64. package/dist/data-table/keys.js +24 -0
  65. package/dist/data-table/root/data-table-root.d.ts +27 -18
  66. package/dist/data-table/root/data-table-root.js +20 -52
  67. package/dist/data-table/tree.d.ts +16 -1
  68. package/dist/data-table/tree.js +2 -3
  69. package/dist/date-input/root/date-input-root.d.ts +3 -1
  70. package/dist/date-input/root/date-input-root.js +18 -7
  71. package/dist/date-picker/root/date-picker-root.js +12 -6
  72. package/dist/dialog/description.js +15 -1
  73. package/dist/dialog/root/dialog-root.d.ts +8 -2
  74. package/dist/dialog/root/dialog-root.js +16 -1
  75. package/dist/divider/root/divider-root.d.ts +3 -0
  76. package/dist/divider/root/divider-root.js +2 -1
  77. package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
  78. package/dist/donut-chart/root/donut-chart-root.js +48 -1
  79. package/dist/drawer/reveal.d.ts +8 -0
  80. package/dist/drawer/reveal.js +1 -0
  81. package/dist/drawer/root/drawer-root.d.ts +59 -0
  82. package/dist/drawer/root/drawer-root.js +63 -4
  83. package/dist/drilldown/item/drilldown-item.js +1 -1
  84. package/dist/drilldown/root/drilldown-root.d.ts +21 -3
  85. package/dist/drilldown/root/drilldown-root.js +21 -3
  86. package/dist/element-events.generated.js +31 -5
  87. package/dist/empty/root/empty-root.d.ts +3 -1
  88. package/dist/empty/root/empty-root.js +3 -2
  89. package/dist/file-input/root/file-input-root.d.ts +69 -6
  90. package/dist/file-input/root/file-input-root.js +82 -19
  91. package/dist/file-input/screen.d.ts +2 -1
  92. package/dist/filter-input/menu/filter-menu.d.ts +2 -0
  93. package/dist/filter-input/menu/filter-menu.js +12 -4
  94. package/dist/filter-input/root/filter-input-root.d.ts +30 -6
  95. package/dist/filter-input/root/filter-input-root.js +1 -1
  96. package/dist/filter-input/suggest.d.ts +28 -3
  97. package/dist/filter-input/suggest.js +18 -8
  98. package/dist/format-date/root/format-date-root.d.ts +9 -16
  99. package/dist/format-date/root/format-date-root.js +10 -27
  100. package/dist/heatmap/root/heatmap-root.d.ts +24 -13
  101. package/dist/heatmap/root/heatmap-root.js +34 -12
  102. package/dist/icon/root/icon-root.d.ts +12 -1
  103. package/dist/icon/root/icon-root.js +21 -10
  104. package/dist/icon/settle.d.ts +7 -0
  105. package/dist/icon/settle.js +10 -0
  106. package/dist/icon-button/root/icon-button-root.d.ts +2 -0
  107. package/dist/image/root/image-root.d.ts +5 -1
  108. package/dist/image/root/image-root.js +1 -1
  109. package/dist/input/root/input-root.d.ts +12 -2
  110. package/dist/input/root/input-root.js +11 -2
  111. package/dist/kbd/root/kbd-root.d.ts +35 -1
  112. package/dist/kbd/root/kbd-root.js +23 -4
  113. package/dist/legend/root/legend-root.d.ts +55 -8
  114. package/dist/legend/root/legend-root.js +55 -13
  115. package/dist/lightbox/root/lightbox-root.d.ts +49 -6
  116. package/dist/lightbox/root/lightbox-root.js +86 -19
  117. package/dist/lightbox/zoom-controller.d.ts +59 -0
  118. package/dist/lightbox/zoom-controller.js +128 -0
  119. package/dist/lightbox/zoom.d.ts +22 -0
  120. package/dist/lightbox/zoom.js +16 -0
  121. package/dist/line-chart/root/line-chart-root.d.ts +22 -1
  122. package/dist/line-chart/root/line-chart-root.js +21 -1
  123. package/dist/list/item/list-item.d.ts +2 -0
  124. package/dist/list/item/list-item.js +11 -2
  125. package/dist/list/root/list-root.d.ts +62 -13
  126. package/dist/list/root/list-root.js +70 -13
  127. package/dist/list/selection.d.ts +37 -7
  128. package/dist/list/selection.js +16 -7
  129. package/dist/listbox/root/listbox-root.d.ts +37 -1
  130. package/dist/listbox/root/listbox-root.js +49 -3
  131. package/dist/loader/busy.d.ts +10 -0
  132. package/dist/loader/busy.js +11 -0
  133. package/dist/loader/root/loader-root.d.ts +9 -2
  134. package/dist/loader/root/loader-root.js +23 -3
  135. package/dist/megamenu/root/megamenu-root.d.ts +51 -10
  136. package/dist/megamenu/root/megamenu-root.js +76 -5
  137. package/dist/menu/item/menu-item.d.ts +51 -2
  138. package/dist/menu/item/menu-item.js +47 -23
  139. package/dist/menu/root/menu-root.d.ts +14 -0
  140. package/dist/menu/root/menu-root.js +2 -2
  141. package/dist/meter/root/meter-root.d.ts +20 -1
  142. package/dist/meter/root/meter-root.js +11 -15
  143. package/dist/navigation/root/navigation-root.d.ts +2 -1
  144. package/dist/navigation/root/navigation-root.js +2 -2
  145. package/dist/notifications/root/notifications-root.d.ts +10 -3
  146. package/dist/notifications/root/notifications-root.js +19 -7
  147. package/dist/number-input/root/number-input-root.d.ts +17 -8
  148. package/dist/number-input/root/number-input-root.js +2 -7
  149. package/dist/object-inspector/root/object-inspector-root.d.ts +75 -27
  150. package/dist/object-inspector/root/object-inspector-root.js +117 -76
  151. package/dist/object-inspector/rows.d.ts +18 -0
  152. package/dist/object-inspector/rows.js +33 -0
  153. package/dist/option/root/option-root.d.ts +5 -3
  154. package/dist/outline/root/outline-root.d.ts +18 -0
  155. package/dist/outline/root/outline-root.js +23 -5
  156. package/dist/pagination/size/pagination-size.d.ts +1 -0
  157. package/dist/pagination/size/pagination-size.js +1 -1
  158. package/dist/pin-input/code.d.ts +10 -0
  159. package/dist/pin-input/code.js +13 -0
  160. package/dist/pin-input/root/pin-input-root.d.ts +25 -1
  161. package/dist/pin-input/root/pin-input-root.js +10 -11
  162. package/dist/popover/hide-outside.d.ts +14 -0
  163. package/dist/popover/hide-outside.js +41 -0
  164. package/dist/popover/root/popover-root.d.ts +57 -7
  165. package/dist/popover/root/popover-root.js +58 -3
  166. package/dist/progress/root/progress-root.d.ts +20 -0
  167. package/dist/progress/root/progress-root.js +15 -15
  168. package/dist/qr-code/export.d.ts +26 -1
  169. package/dist/qr-code/export.js +14 -0
  170. package/dist/qr-code/root/qr-code-root.d.ts +19 -5
  171. package/dist/qr-code/root/qr-code-root.js +58 -9
  172. package/dist/quote/marks.d.ts +8 -0
  173. package/dist/quote/marks.js +5 -0
  174. package/dist/quote/root/quote-root.d.ts +14 -2
  175. package/dist/quote/root/quote-root.js +23 -1
  176. package/dist/radio/root/radio-root.d.ts +6 -2
  177. package/dist/radio/root/radio-root.js +6 -1
  178. package/dist/rating/root/rating-root.d.ts +9 -4
  179. package/dist/rating/root/rating-root.js +31 -7
  180. package/dist/rating/steps.d.ts +7 -0
  181. package/dist/rating/steps.js +9 -0
  182. package/dist/scatter-chart/fit.d.ts +15 -0
  183. package/dist/scatter-chart/fit.js +35 -0
  184. package/dist/scatter-chart/root/scatter-chart-root.d.ts +21 -1
  185. package/dist/scatter-chart/root/scatter-chart-root.js +30 -17
  186. package/dist/scroll-area/root/scroll-area-root.d.ts +22 -51
  187. package/dist/scroll-area/root/scroll-area-root.js +57 -173
  188. package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
  189. package/dist/scroll-area/scrollbar-controller.js +172 -0
  190. package/dist/scroll-area/scrollbar.d.ts +27 -0
  191. package/dist/scroll-area/scrollbar.js +9 -0
  192. package/dist/select/root/select-root.d.ts +15 -0
  193. package/dist/select/root/select-root.js +18 -6
  194. package/dist/skeleton/root/skeleton-root.d.ts +3 -0
  195. package/dist/skeleton/root/skeleton-root.js +4 -2
  196. package/dist/skip-link/root/skip-link-root.js +2 -2
  197. package/dist/slider/root/slider-root.d.ts +41 -4
  198. package/dist/slider/root/slider-root.js +23 -3
  199. package/dist/sparkline/readings.d.ts +18 -0
  200. package/dist/sparkline/readings.js +19 -0
  201. package/dist/sparkline/root/sparkline-root.d.ts +78 -7
  202. package/dist/sparkline/root/sparkline-root.js +118 -23
  203. package/dist/speed-dial/root/speed-dial-root.js +2 -2
  204. package/dist/splitter/root/splitter-root.d.ts +7 -12
  205. package/dist/splitter/root/splitter-root.js +209 -204
  206. package/dist/stat/root/stat-root.d.ts +14 -5
  207. package/dist/stat/root/stat-root.js +21 -5
  208. package/dist/steps/reach.d.ts +5 -0
  209. package/dist/steps/reach.js +3 -0
  210. package/dist/steps/root/steps-root.d.ts +49 -3
  211. package/dist/steps/root/steps-root.js +44 -5
  212. package/dist/steps/step/step.d.ts +7 -0
  213. package/dist/steps/step/step.js +7 -2
  214. package/dist/switch/root/switch-root.d.ts +10 -0
  215. package/dist/table/column/table-column.d.ts +0 -10
  216. package/dist/table/column/table-column.js +3 -19
  217. package/dist/table/root/table-root.d.ts +31 -10
  218. package/dist/table/root/table-root.js +2 -13
  219. package/dist/tabs/panel/tab-panel.d.ts +16 -3
  220. package/dist/tabs/panel/tab-panel.js +34 -5
  221. package/dist/tabs/root/tabs-root.d.ts +12 -0
  222. package/dist/tabs/root/tabs-root.js +1 -1
  223. package/dist/tabs/tab/tab.d.ts +5 -0
  224. package/dist/tabs/tab/tab.js +5 -2
  225. package/dist/tag/group/tag-group.d.ts +31 -4
  226. package/dist/tag/group/tag-group.js +46 -7
  227. package/dist/tag/root/tag-root.js +9 -2
  228. package/dist/textarea/root/textarea-root.d.ts +28 -1
  229. package/dist/textarea/root/textarea-root.js +40 -1
  230. package/dist/time-input/root/time-input-root.d.ts +5 -1
  231. package/dist/time-input/root/time-input-root.js +16 -8
  232. package/dist/time-picker/root/time-picker-root.d.ts +18 -4
  233. package/dist/time-picker/root/time-picker-root.js +7 -10
  234. package/dist/time-picker/wheel-name.d.ts +9 -0
  235. package/dist/time-picker/wheel-name.js +15 -0
  236. package/dist/timeline/group/timeline-group.js +2 -2
  237. package/dist/toast/item/toast-item.d.ts +14 -1
  238. package/dist/toast/item/toast-item.js +22 -5
  239. package/dist/toast/root/toast-root.d.ts +6 -0
  240. package/dist/toast/root/toast-root.js +16 -1
  241. package/dist/toggle/group/toggle-group.d.ts +24 -3
  242. package/dist/toggle/group/toggle-group.js +34 -5
  243. package/dist/tooltip/root/tooltip-root.d.ts +13 -2
  244. package/dist/tooltip/root/tooltip-root.js +9 -1
  245. package/dist/tour/back.d.ts +10 -0
  246. package/dist/tour/back.js +8 -0
  247. package/dist/tour/root/tour-root.d.ts +30 -2
  248. package/dist/tour/root/tour-root.js +34 -8
  249. package/dist/tree/item/tree-item.d.ts +7 -3
  250. package/dist/tree/item/tree-item.js +3 -0
  251. package/dist/tree/root/tree-root.d.ts +24 -3
  252. package/dist/tree/root/tree-root.js +32 -13
  253. package/dist/tree/selection.js +2 -15
  254. package/dist/trend/root/trend-root.d.ts +43 -18
  255. package/dist/trend/root/trend-root.js +31 -6
  256. package/dist/video/media-events.d.ts +13 -18
  257. package/dist/video/media-events.js +1 -5
  258. package/dist/video/root/video-root.d.ts +18 -5
  259. package/dist/video/root/video-root.js +19 -10
  260. package/dist/video-playlist/menu/video-playlist-menu.d.ts +2 -0
  261. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  262. package/dist/video-playlist/root/video-playlist-root.d.ts +7 -1
  263. package/dist/virtual-list/fill.d.ts +0 -8
  264. package/dist/virtual-list/fill.js +1 -17
  265. package/dist/virtual-list/plan.d.ts +49 -0
  266. package/dist/virtual-list/plan.js +16 -0
  267. package/dist/virtual-list/root/virtual-list-root.d.ts +114 -13
  268. package/dist/virtual-list/root/virtual-list-root.js +183 -40
  269. package/lint.json +1 -1
  270. package/package.json +8 -8
  271. package/types/jsx.d.ts +4577 -835
  272. package/types/vue.d.ts +926 -168
  273. package/dist/chat/relative-clock.d.ts +0 -39
  274. package/dist/chat/relative-clock.js +0 -31
  275. package/dist/data-table/root/order.d.ts +0 -141
  276. package/dist/data-table/root/order.js +0 -76
  277. package/dist/tag/selection.d.ts +0 -10
  278. package/dist/tag/selection.js +0 -12
@@ -18,6 +18,10 @@ declare const HelixHeadlessAccordionItem_base: (new (...args: any[]) => import("
18
18
  * out of the tab order and the accessibility tree after the styled tier's
19
19
  * `display` overrides `hidden`.
20
20
  *
21
+ * While it is open, the content part carries the content's height as
22
+ * `--accordion-content-height`, as Radix's accordion does, for a stylesheet
23
+ * that animates the section open with keyframes of its own.
24
+ *
21
25
  * Set `hidden-until-found` and the browser's find in page searches a closed
22
26
  * section too: a match opens it, with `helix-before-open` first, as React
23
27
  * Aria's disclosure does. The content is then `hidden="until-found"` while
@@ -43,6 +47,8 @@ declare const HelixHeadlessAccordionItem_base: (new (...args: any[]) => import("
43
47
  * @csspart heading - The heading that holds the trigger.
44
48
  * @csspart trigger - The button that opens and closes the section, holding the summary.
45
49
  *
50
+ * @cssproperty --accordion-content-height - The height of the content, in pixels, written on the `content` part while the section is open.
51
+ *
46
52
  * @cssstate open - While the item is open.
47
53
  */
48
54
  export declare class HelixHeadlessAccordionItem extends HelixHeadlessAccordionItem_base {
@@ -166,6 +172,12 @@ export declare class HelixHeadlessAccordionItem extends HelixHeadlessAccordionIt
166
172
  * settle, once every item is there to compare.
167
173
  */
168
174
  protected updated(changed: PropertyValues): void;
175
+ /**
176
+ * Writes the content's height onto the content part while the section is
177
+ * open. `scrollHeight`, so a sheet that clips the box while it grows still
178
+ * reads the whole height.
179
+ */
180
+ private readonly measure;
169
181
  /**
170
182
  * Moves focus to this item's trigger.
171
183
  *
@@ -203,6 +215,8 @@ export declare class HelixHeadlessAccordionItem extends HelixHeadlessAccordionIt
203
215
  * overrides `display`, `inert` is the only one of the two still in force.
204
216
  * `content-visibility: hidden` keeps skipped content out of the tab order and
205
217
  * the accessibility tree by itself, and an inert region is never searched.
218
+ * It leaves the region's own box in the tree, so a closed findable section
219
+ * drops `role="region"` and its name until it opens.
206
220
  */
207
221
  protected render(): TemplateResult;
208
222
  }
@@ -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, MixinControllerOpen, } from '@fusion.dev/helix-core';
7
+ import { closestInstance, customElement, HelixElement, headingLevel, MixinControllerOpen, } 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 } from 'lit/decorators.js';
@@ -43,12 +43,20 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
43
43
  this.closePeers();
44
44
  }
45
45
  };
46
+ this.measure = () => {
47
+ const content = this.partNamed('content');
48
+ const inner = this.partNamed('content-inner');
49
+ if (!content || !inner || !this.open) {
50
+ return;
51
+ }
52
+ content.style.setProperty('--accordion-content-height', `${inner.scrollHeight}px`);
53
+ };
46
54
  }
47
55
  renderChevron() {
48
56
  if (this.chevronPlacement === 'none') {
49
57
  return nothing;
50
58
  }
51
- return html `<slot name="chevron"
59
+ return html `<slot name="chevron" aria-hidden="true"
52
60
  ><svg part="chevron" fill="currentColor" viewBox=${caretDown.viewBox} aria-hidden="true">
53
61
  ${unsafeSVG(caretDown.content)}
54
62
  </svg></slot
@@ -89,13 +97,15 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
89
97
  this.closePeers();
90
98
  }
91
99
  this.wasOpen = this.open;
100
+ this.measure();
92
101
  }
93
102
  focusTrigger() {
94
103
  this.renderRoot.querySelector(`#${TRIGGER_ID}`)?.focus();
95
104
  }
96
105
  render() {
97
106
  const open = this.open;
98
- const level = Math.min(6, Math.max(1, this.headingLevel ?? this.groupHeadingLevel ?? 3));
107
+ const landmark = open || !this.findable;
108
+ const level = headingLevel(this.headingLevel ?? this.groupHeadingLevel);
99
109
  return html `
100
110
  <div part="container">
101
111
  <div part="heading" role="heading" aria-level=${level}>
@@ -115,15 +125,15 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
115
125
  </div>
116
126
  <div
117
127
  id=${CONTENT_ID}
118
- aria-labelledby=${TRIGGER_ID}
128
+ aria-labelledby=${landmark ? TRIGGER_ID : nothing}
119
129
  part="content"
120
130
  hidden=${open ? nothing : this.findable ? 'until-found' : ''}
121
131
  ?inert=${!open && !this.findable}
122
- role="region"
132
+ role=${landmark ? 'region' : nothing}
123
133
  @beforematch=${this.handleBeforeMatch}
124
134
  >
125
135
  <div part="content-inner">
126
- <slot></slot>
136
+ <slot @slotchange=${this.measure}></slot>
127
137
  </div>
128
138
  </div>
129
139
  </div>
@@ -17,12 +17,15 @@ import { HelixHeadlessAccordionItem } from '../item/accordion-item.js';
17
17
  * matched with `instanceof HelixHeadlessAccordionItem`, so the headless tier
18
18
  * does not depend on the styled tier's tag names.
19
19
  *
20
- * The keyboard listener belongs to the controller and is attached on connect,
21
- * so it does not run on the server.
20
+ * Keys, on a trigger: Up and Down Arrow move to the previous and the next
21
+ * trigger, and so do Left and Right, wrapping at the ends; Home and End go to
22
+ * the first and the last. Enter and Space open and close the section. Disabled
23
+ * sections are passed over.
22
24
  *
23
- * Each trigger sits inside a heading, level 3 unless `heading-level` says
24
- * otherwise, so the sections are in a screen reader's list of headings. Set the
25
- * level that fits the page's outline.
25
+ * Each trigger is a button with `aria-expanded` and `aria-controls`, inside a
26
+ * heading, level 3 unless `heading-level` says otherwise, so the sections are
27
+ * in a screen reader's list of headings. An open section is a region named by
28
+ * its trigger. Set the level that fits the page's outline.
26
29
  *
27
30
  * @since 0.1.0
28
31
  * @status stable
@@ -16,8 +16,9 @@ declare const HelixHeadlessAnnouncement_base: (new (...args: any[]) => import("@
16
16
  *
17
17
  * The live region is its own part. When the announcement appears after the page
18
18
  * has loaded, the message is written into the region one frame later, so screen
19
- * readers read it reliably, and the region is emptied seven seconds later, so
20
- * the message is not in the page twice. When the announcement is in the served
19
+ * readers read it reliably, and the region is emptied seven seconds later.
20
+ * While the region holds the copy, the message itself is hidden from screen
21
+ * readers, so it is in the page once. When the announcement is in the served
21
22
  * HTML, the region stays empty and nothing is read out.
22
23
  *
23
24
  * The dismiss button fires `helix-dismiss`, and the announcement stays in the
@@ -64,7 +64,9 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
64
64
  </slot>`;
65
65
  }
66
66
  render() {
67
- return html `<div part="content"><slot @slotchange=${() => this.arrival.say()}></slot></div>
67
+ return html `<div aria-hidden=${this.arrival.said === '' ? nothing : 'true'} part="content"
68
+ ><slot @slotchange=${() => this.arrival.say()}></slot
69
+ ></div>
68
70
  <span part="live" role="status">${this.arrival.said}</span>
69
71
  <div part="actions"><slot name="actions" @slotchange=${() => this.markEmpty()}></slot></div>
70
72
  ${this.dismissible
@@ -22,8 +22,11 @@ import { type PropertyValues, type TemplateResult } from 'lit';
22
22
  * The host is the page's `banner` landmark. Placed inside `main`, `article`,
23
23
  * `aside`, `nav` or `section`, it is a plain box instead, as a native
24
24
  * `<header>` is there, so a header inside a card or a pane does not add a
25
- * second banner. A server cannot see the ancestors, so it serves the banner and
26
- * the browser corrects it when the element connects.
25
+ * second banner. The ancestors are read as the accessibility tree reads them,
26
+ * through the slots that take the header in, so one slotted into a component
27
+ * that draws a `<nav>`, such as `Navigation`, is not a banner either. A server
28
+ * cannot see the ancestors, so it serves the banner and the browser corrects
29
+ * it when the element connects.
27
30
  *
28
31
  * @since 0.1.0
29
32
  * @status stable
@@ -105,6 +108,8 @@ export declare class HelixHeadlessAppHeader extends HelixElement {
105
108
  private readonly handleScroll;
106
109
  protected willUpdate(changed: PropertyValues): void;
107
110
  connectedCallback(): void;
111
+ /** Whether this header is the page's banner, read from where it sits. */
112
+ private readLandmark;
108
113
  disconnectedCallback(): void;
109
114
  protected updated(changed: PropertyValues): void;
110
115
  protected render(): TemplateResult;
@@ -47,9 +47,17 @@ let HelixHeadlessAppHeader = class HelixHeadlessAppHeader extends HelixElement {
47
47
  }
48
48
  connectedCallback() {
49
49
  super.connectedCallback();
50
+ this.readLandmark();
51
+ setTimeout(() => {
52
+ if (this.isConnected) {
53
+ this.readLandmark();
54
+ }
55
+ });
56
+ this.listen();
57
+ }
58
+ readLandmark() {
50
59
  this.landmark = inSectioningContent(this) ? '' : 'banner';
51
60
  hostAria(this.internals, { role: this.landmark });
52
- this.listen();
53
61
  }
54
62
  disconnectedCallback() {
55
63
  super.disconnectedCallback();
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Whether an element sits inside sectioning content, read across shadow
3
- * boundaries as the accessibility tree reads it.
3
+ * boundaries as the accessibility tree reads it: through the slot an element
4
+ * is assigned to, so a header slotted into a component that draws a `<nav>`
5
+ * around its slot, as `Navigation` does, is inside that `<nav>`.
4
6
  *
5
7
  * Client only: a server has no ancestors to read.
6
8
  */
@@ -11,15 +11,16 @@ const SECTIONING = [
11
11
  '[role="region"]',
12
12
  ].join(', ');
13
13
  export const inSectioningContent = (element) => {
14
- let node = element.parentElement ?? hostOf(element);
14
+ let node = above(element);
15
15
  while (node) {
16
16
  if (node.matches(SECTIONING)) {
17
17
  return true;
18
18
  }
19
- node = node.parentElement ?? hostOf(node);
19
+ node = above(node);
20
20
  }
21
21
  return false;
22
22
  };
23
+ const above = (element) => element.assignedSlot ?? element.parentElement ?? hostOf(element);
23
24
  const hostOf = (element) => {
24
25
  const root = element.getRootNode();
25
26
  return root instanceof ShadowRoot ? root.host : null;
@@ -1,13 +1,13 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
- * The largest `max` a stylesheet can hide past.
4
+ * The largest `max` a stylesheet folds a server's markup at.
5
5
  *
6
- * The fold is `:host([max="N"]) ::slotted(:nth-child(n + N+1))`, one selector
6
+ * The rule is `:host([max="N"]) ::slotted(:nth-child(n + N+1))`, one selector
7
7
  * per value, because `An+B` takes a literal and no custom property can be read
8
- * into it. So the range is finite, and it is written down beside the count that
9
- * has to agree with it: a count that folds and a stylesheet that does not would
10
- * put a "+5" beside five avatars that are still on screen.
8
+ * into it. So the range a stylesheet covers is finite. The group folds any
9
+ * `max` itself once it runs, with the platform's `hidden`; past this one, a
10
+ * server's markup shows every avatar until the page hydrates.
11
11
  */
12
12
  export declare const FOLDS_UP_TO = 12;
13
13
  /**
@@ -21,9 +21,10 @@ export declare const FOLDS_UP_TO = 12;
21
21
  * hidden.
22
22
  *
23
23
  * Avatars past `max` stay in the DOM and are hidden. The group writes `hidden`
24
- * on each of them, so they are hidden on the headless tier as well, and the
25
- * count is read out as `overflow-label`, such as "3 more", so a screen reader
26
- * hears that there are more people than the ones it can reach.
24
+ * on each of them, so they are hidden on the headless tier as well, and at any
25
+ * `max`. The count is read out as `overflow-label`, such as "3 more: Grace
26
+ * Hopper, Alan Turing, Barbara Liskov", so a screen reader hears who is there
27
+ * past the ones it can reach.
27
28
  *
28
29
  * @since 0.1.0
29
30
  * @status stable
@@ -59,10 +60,11 @@ export declare class HelixHeadlessAvatarGroup extends HelixElement {
59
60
  */
60
61
  max?: number;
61
62
  /**
62
- * What a screen reader is told the count is, with `{count}` for the number.
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`.
63
65
  *
64
66
  * The count shows "+3", which reads as "plus three" and says nothing about
65
- * what was counted.
67
+ * what was counted, nor who.
66
68
  */
67
69
  overflowLabel: string;
68
70
  /** The avatars this group hid, so it unhides only those. */
@@ -78,8 +80,6 @@ export declare class HelixHeadlessAvatarGroup extends HelixElement {
78
80
  * the attribute that hides the whole group.
79
81
  */
80
82
  get overflow(): number;
81
- /** Said once per element, because a re-render is not a second mistake. */
82
- private warned;
83
83
  protected willUpdate(changed: PropertyValues): void;
84
84
  protected firstUpdated(changed: PropertyValues): void;
85
85
  protected updated(changed: PropertyValues): void;
@@ -89,10 +89,16 @@ export declare class HelixHeadlessAvatarGroup extends HelixElement {
89
89
  * **State in markup, so the headless tier folds too.** The fold was a styled
90
90
  * rule alone, and on the headless tier every avatar was drawn beside a "+3".
91
91
  * The styled rule stays, because it is what folds a server's markup before
92
- * this runs. Written through `whenHydrated`, so an avatar's hydrating render
93
- * is the server's, and undone only on the avatars this group hid.
92
+ * this runs, up to `FOLDS_UP_TO`, and this folds at any `max`. Written
93
+ * through `whenHydrated`, so an avatar's hydrating render is the server's,
94
+ * and undone only on the avatars this group hid.
94
95
  */
95
96
  private fold;
97
+ /**
98
+ * The names of the people past `max`, by each avatar's `name`, then its
99
+ * `aria-label`, in the order they are read.
100
+ */
101
+ private foldedNames;
96
102
  protected render(): TemplateResult;
97
103
  }
98
104
  declare global {
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement, hostAria, reportRuntime, whenHydrated, } from '@fusion.dev/helix-core';
7
+ import { customElement, fillTemplate, HelixElement, hostAria, whenHydrated, } 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,17 +12,15 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
12
12
  constructor() {
13
13
  super(...arguments);
14
14
  this.label = '';
15
- this.overflowLabel = '{count} more';
15
+ this.overflowLabel = '{count} more: {names}';
16
16
  this.folded = new WeakSet();
17
- this.warned = false;
18
17
  }
19
18
  get avatars() {
20
19
  return this.knownChildren.filter((child) => child instanceof HTMLElement);
21
20
  }
22
21
  get overflow() {
23
22
  const limit = this.max ?? 0;
24
- const limited = limit > 0 && limit <= FOLDS_UP_TO;
25
- return limited ? Math.max(this.avatars.length - limit, 0) : 0;
23
+ return limit > 0 ? Math.max(this.avatars.length - limit, 0) : 0;
26
24
  }
27
25
  willUpdate(changed) {
28
26
  super.willUpdate(changed);
@@ -35,23 +33,11 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
35
33
  updated(changed) {
36
34
  super.updated(changed);
37
35
  this.fold();
38
- const limit = this.max ?? 0;
39
- if (limit > FOLDS_UP_TO && this.avatars.length > limit && !this.warned) {
40
- this.warned = true;
41
- reportRuntime({
42
- kind: 'max-past-fold',
43
- message: `<${this.localName}> has max="${this.max}", past the ${FOLDS_UP_TO} the fold is written for, ` +
44
- 'so every avatar is shown and none are counted. Lower `max`, or hand the group the ' +
45
- 'avatars you want shown.',
46
- tag: this.localName,
47
- });
48
- }
49
36
  }
50
37
  fold() {
51
38
  const limit = this.max ?? 0;
52
- const limited = limit > 0 && limit <= FOLDS_UP_TO;
53
39
  for (const [index, avatar] of this.avatars.entries()) {
54
- const past = limited && index >= limit;
40
+ const past = limit > 0 && index >= limit;
55
41
  if (past && !avatar.hidden) {
56
42
  this.folded.add(avatar);
57
43
  whenHydrated(avatar, () => {
@@ -66,6 +52,15 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
66
52
  }
67
53
  }
68
54
  }
55
+ foldedNames() {
56
+ const limit = this.max ?? 0;
57
+ return this.avatars
58
+ .slice(limit)
59
+ .map((avatar) => avatar.name ||
60
+ avatar.getAttribute('aria-label') ||
61
+ '')
62
+ .filter(Boolean);
63
+ }
69
64
  render() {
70
65
  const past = this.overflow;
71
66
  return html `<slot @slotchange=${() => this.requestUpdate()}></slot>
@@ -73,7 +68,10 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
73
68
  ? html `<span
74
69
  part="overflow"
75
70
  role="img"
76
- aria-label=${this.overflowLabel.replace('{count}', String(past))}
71
+ aria-label=${fillTemplate(this.overflowLabel, {
72
+ count: String(past),
73
+ names: this.foldedNames().join(', '),
74
+ }).replace(/[\s:,]+$/, '')}
77
75
  >+${past}</span
78
76
  >`
79
77
  : ''}`;
@@ -44,7 +44,9 @@ export declare class HelixHeadlessBadge extends HelixElement {
44
44
  /**
45
45
  * The badge's name, read by screen readers.
46
46
  *
47
- * Required for a dot, which has no text to read.
47
+ * Required for a dot, which has no text to read. `{count}` is replaced with
48
+ * what the badge shows, so `label="{count} unread"` reads "8 unread" and
49
+ * stays current as `value` changes, with no label of yours to keep in step.
48
50
  */
49
51
  label: string;
50
52
  /**
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement } from '@fusion.dev/helix-core';
7
+ import { customElement, fillTemplate, HelixElement } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
@@ -36,7 +36,7 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
36
36
  data-blank=${blank ? '' : nothing}
37
37
  data-single=${text.length <= 1 ? '' : nothing}
38
38
  role="status"
39
- aria-label=${announced ? this.label || text : nothing}
39
+ aria-label=${announced ? fillTemplate(this.label, { count: text }) || text : nothing}
40
40
  >${blank ? html `<span aria-hidden="true">${text}</span>` : text}</span
41
41
  >`;
42
42
  }
@@ -176,6 +176,17 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
176
176
  * of any scale question the picture raises.
177
177
  */
178
178
  secondary?: string;
179
+ /**
180
+ * Stacks a category's columns into one, each series standing on the ones
181
+ * before it, so the column's height is the category's total.
182
+ *
183
+ * Absent stands them side by side, as it always did. A negative reading
184
+ * hangs below zero on the negatives before it, so neither direction hides
185
+ * the other. The scale fits the stacks. A series
186
+ * in `lines` is still a line, measured on its own reading; columns on the
187
+ * second scale stack among themselves.
188
+ */
189
+ stacked: boolean;
179
190
  /** The series names on the second scale, lowercased and trimmed. */
180
191
  private get scaled();
181
192
  /** The series names drawn as a line, lowercased and trimmed for matching. */
@@ -204,7 +215,8 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
204
215
  *
205
216
  * The highest, because which category leads is the question a bar chart is
206
217
  * read for, and the table beside it has every other number. The reading is
207
- * written the way the table writes it, such as `$128k` for `128000`.
218
+ * written the way the table writes it, such as `$128k` for `128000`. A
219
+ * series a legend hid is not drawn and is not said.
208
220
  */
209
221
  protected renderSummary(series: Series[]): TemplateResult;
210
222
  /**
@@ -214,6 +226,11 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
214
226
  * and a screen reader hearing both hears the same numbers twice.
215
227
  */
216
228
  protected renderPlot(series: Series[], categories: string[], y: (value: number) => number, domain: [number, number], floor: number, z?: (value: number) => number): TemplateResult;
229
+ /**
230
+ * One category's columns stacked into one, each on the ones before it on
231
+ * its own scale, through the pure `stackSpans`.
232
+ */
233
+ private renderStack;
217
234
  /**
218
235
  * The numbers along one scale, drawn only where there are two of them.
219
236
  *
@@ -7,11 +7,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  import { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesNames, ticks, unbroken, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ import { stackEnds, stackSpans } from '../stack.js';
10
11
  import '../../tooltip/index.js';
11
12
  let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource(HelixElement) {
12
13
  constructor() {
13
14
  super(...arguments);
14
15
  this.highestLabel = 'Highest';
16
+ this.stacked = false;
15
17
  }
16
18
  get scaled() {
17
19
  return new Set(seriesNames(this.secondary));
@@ -39,8 +41,22 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
39
41
  const own = (one) => scaled.has(one.name.trim().toLowerCase());
40
42
  const shown = series.filter((one) => this.showsSeries(one.name));
41
43
  const values = shown.flatMap((one) => one.points.map((point) => point.value));
44
+ const lined = this.lined;
45
+ const readingsOf = (group) => {
46
+ if (!this.stacked) {
47
+ return group.flatMap((one) => one.points.map((point) => point.value));
48
+ }
49
+ const columns = group.filter((one) => !lined.has(one.name.trim().toLowerCase()));
50
+ const slots = columns.map((one) => byRow(one, categories.length));
51
+ return [
52
+ ...group
53
+ .filter((one) => lined.has(one.name.trim().toLowerCase()))
54
+ .flatMap((one) => one.points.map((point) => point.value)),
55
+ ...stackEnds(categories.map((_, at) => slots.map((one) => one[at]?.value))),
56
+ ];
57
+ };
42
58
  const on = (which) => {
43
- const found = shown.filter(which).flatMap((one) => one.points.map((point) => point.value));
59
+ const found = readingsOf(shown.filter(which));
44
60
  return found.length > 0 ? found : values.length > 0 ? values : [0];
45
61
  };
46
62
  const [low, high] = extent(scaled.size === 0 ? on(() => true) : on((one) => !own(one)));
@@ -66,7 +82,7 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
66
82
  const columns = this.plottedColumns;
67
83
  const highest = series.flatMap((one, depth) => {
68
84
  const top = one.points.reduce((best, point) => (best === undefined || point.value > best.value ? point : best), undefined);
69
- if (top === undefined) {
85
+ if (top === undefined || !this.showsSeries(one.name)) {
70
86
  return [];
71
87
  }
72
88
  const text = rows[top.at]?.cells[columns[depth] ?? -1]?.text ?? String(top.value);
@@ -85,13 +101,15 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
85
101
  const columns = series
86
102
  .map((one, index) => ({ one, slots: byRow(one, categories.length), tone: index }))
87
103
  .filter(({ one }) => !lined.has(one.name.trim().toLowerCase()) && this.showsSeries(one.name));
88
- const stride = (slot * 0.72) / Math.max(columns.length, 1);
89
- const gap = columns.length > 1 ? stride * 0.08 : 0;
104
+ const abreast = this.stacked ? 1 : columns.length;
105
+ const stride = (slot * 0.72) / Math.max(abreast, 1);
106
+ const gap = abreast > 1 ? stride * 0.08 : 0;
90
107
  const thickness = stride - gap;
91
- const bar = (value, index, depth, tone, scale) => {
108
+ const bar = (value, index, depth, tone, scale, from) => {
92
109
  const at = scale(value);
93
- const along = Math.min(floor, at);
94
- const size = Math.abs(floor - at);
110
+ const base = from === undefined ? floor : scale(from);
111
+ const along = Math.min(base, at);
112
+ const size = Math.abs(base - at);
95
113
  const across = index * slot + slot * 0.14 + depth * stride + gap / 2;
96
114
  return sideways
97
115
  ? svg `<rect
@@ -151,12 +169,14 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
151
169
  data-active=${this.pointer.at === index ? '' : nothing}
152
170
  part="group${this.pointer.at === index ? ' group-active' : ''}"
153
171
  >
154
- ${columns.map(({ slots, one, tone }, depth) => {
155
- const point = slots[index];
156
- return point === undefined
157
- ? nothing
158
- : bar(point.value, index, depth, tone, against(one));
159
- })}
172
+ ${this.stacked
173
+ ? this.renderStack(columns, index, against, bar)
174
+ : columns.map(({ slots, one, tone }, depth) => {
175
+ const point = slots[index];
176
+ return point === undefined
177
+ ? nothing
178
+ : bar(point.value, index, depth, tone, against(one));
179
+ })}
160
180
  </g>`)}
161
181
  ${series.map((one) => lined.has(one.name.trim().toLowerCase()) && this.showsSeries(one.name)
162
182
  ? this.renderLine(one, series.indexOf(one), slot, against(one), sideways)
@@ -165,6 +185,17 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
165
185
  ${this.renderTooltip()}
166
186
  </div>`;
167
187
  }
188
+ renderStack(columns, index, against, bar) {
189
+ const scales = [...new Set(columns.map(({ one }) => against(one)))];
190
+ return scales.flatMap((scale) => {
191
+ const mine = columns.filter(({ one }) => against(one) === scale);
192
+ const spans = stackSpans(mine.map(({ slots }) => slots[index]?.value));
193
+ return mine.flatMap(({ tone }, place) => {
194
+ const span = spans[place];
195
+ return span === undefined ? [] : [bar(span[1], index, 0, tone, scale, span[0])];
196
+ });
197
+ });
198
+ }
168
199
  renderAxis(scale, domain, which, sideways, widest, tint) {
169
200
  const sizing = sideways ? undefined : `--bar-chart-axis: ${widest}ch`;
170
201
  return html `<div
@@ -234,10 +265,13 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
234
265
  return drawTooltip(this.sourceRows, at, this.tag('tooltip'), undefined, undefined, this.pointer);
235
266
  }
236
267
  const shown = series.filter((one) => this.showsSeries(one.name));
237
- const values = shown
268
+ const reached = shown
238
269
  .map((one) => byRow(one, this.readings)[at]?.value)
239
270
  .filter((one) => one !== undefined);
240
- const all = shown.flatMap((one) => one.points.map((point) => point.value));
271
+ const values = this.stacked ? stackEnds([reached]) : reached;
272
+ const all = this.stacked
273
+ ? stackEnds(Array.from({ length: this.readings }, (_, row) => shown.map((one) => byRow(one, this.readings)[row]?.value)))
274
+ : shown.flatMap((one) => one.points.map((point) => point.value));
241
275
  const [low, high] = extent(all.length > 0 ? all : [0]);
242
276
  const domain = [Math.min(0, low), Math.max(0, high)];
243
277
  const sideways = this.orientation === 'horizontal';
@@ -268,6 +302,9 @@ __decorate([
268
302
  __decorate([
269
303
  property({ reflect: true })
270
304
  ], HelixHeadlessBarChart.prototype, "secondary", void 0);
305
+ __decorate([
306
+ property({ reflect: true, type: Boolean })
307
+ ], HelixHeadlessBarChart.prototype, "stacked", void 0);
271
308
  HelixHeadlessBarChart = __decorate([
272
309
  customElement('helix-headless-bar-chart')
273
310
  ], HelixHeadlessBarChart);
@@ -0,0 +1,15 @@
1
+ /** Where a column starts and ends along the measure, in the data's own units. */
2
+ export type Span = readonly [from: number, to: number];
3
+ /**
4
+ * Each column's span in one category, in the order given: a positive reading
5
+ * stacks up from zero on the positives before it, and a negative one down
6
+ * from zero on the negatives before it, so neither hides the other. A blank
7
+ * has no span.
8
+ */
9
+ export declare const stackSpans: (values: readonly (number | undefined)[]) => (Span | undefined)[];
10
+ /**
11
+ * Every end a stack reaches across the categories, which is what its scale
12
+ * has to hold: the top of each stack of positives and the foot of each stack
13
+ * of negatives. `categories` holds each category's readings, one per column.
14
+ */
15
+ export declare const stackEnds: (categories: readonly (readonly (number | undefined)[])[]) => number[];
@@ -0,0 +1,19 @@
1
+ export const stackSpans = (values) => {
2
+ let up = 0;
3
+ let down = 0;
4
+ return values.map((value) => {
5
+ if (value === undefined) {
6
+ return undefined;
7
+ }
8
+ const from = value >= 0 ? up : down;
9
+ const to = from + value;
10
+ if (value >= 0) {
11
+ up = to;
12
+ }
13
+ else {
14
+ down = to;
15
+ }
16
+ return [from, to];
17
+ });
18
+ };
19
+ export const stackEnds = (categories) => categories.flatMap((values) => stackSpans(values).flatMap((span) => (span === undefined ? [] : [span[1]])));