@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
@@ -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 { AriaForwardController, byRow, CHART_PLOT, customElement, drawTooltip, HelixElement, HelixResizeEvent, MixinChartSource, markRegion, searchableById, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, byRow, CHART_PLOT, customElement, drawTooltip, formatNumbers, HelixElement, HelixResizeEvent, MixinChartSource, markRegion, searchableById, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
@@ -12,12 +12,14 @@ import { fits, toneOf } from './buckets.js';
12
12
  let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(HelixElement) {
13
13
  constructor() {
14
14
  super(...arguments);
15
+ this.busiestLabel = 'Busiest';
15
16
  this.emptyText = 'No readings';
16
17
  this.gap = 2;
17
18
  this.minCell = 8;
18
19
  this.rows = 1;
19
20
  this.thresholds = '';
20
21
  this.tones = '';
22
+ this.totalLabel = 'Total';
21
23
  this.width = undefined;
22
24
  this.metrics = undefined;
23
25
  this.aria = new AriaForwardController(this, {
@@ -42,15 +44,6 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
42
44
  get visible() {
43
45
  return Math.min(this.points.length, this.room);
44
46
  }
45
- static get observedAttributes() {
46
- return [...super.observedAttributes, 'aria-label'];
47
- }
48
- attributeChangedCallback(name, old, value) {
49
- super.attributeChangedCallback(name, old, value);
50
- if (name === 'aria-label') {
51
- this.requestUpdate();
52
- }
53
- }
54
47
  get points() {
55
48
  const series = this.series[0];
56
49
  if (!series) {
@@ -151,7 +144,8 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
151
144
  const cells = this.drawn;
152
145
  const readable = cells.length > 0;
153
146
  return html `<div
154
- aria-label=${this.getAttribute?.('aria-label') || this.named || 'Heatmap'}
147
+ aria-describedby=${readable ? 'helix-chart-summary' : nothing}
148
+ aria-label=${this.named || 'Heatmap'}
155
149
  part="strip"
156
150
  id=${CHART_PLOT}
157
151
  role=${readable ? 'application' : 'img'}
@@ -184,7 +178,29 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
184
178
  <span part="start"><slot name="start"></slot></span>
185
179
  <span part="summary"><slot name="summary"></slot></span>
186
180
  <span part="end"><slot name="end"></slot></span>
187
- </div>`;
181
+ </div>${readable ? this.renderOverview(cells) : nothing}`;
182
+ }
183
+ renderOverview(cells) {
184
+ const rows = this.sourceRows;
185
+ const offset = this.points.length - cells.length;
186
+ const column = this.plottedColumns[0] ?? -1;
187
+ let busiest = -1;
188
+ let total = 0;
189
+ cells.forEach((cell, index) => {
190
+ if (cell.value === undefined) {
191
+ return;
192
+ }
193
+ total += cell.value;
194
+ if (busiest < 0 || cell.value > (cells[busiest]?.value ?? 0)) {
195
+ busiest = index;
196
+ }
197
+ });
198
+ const row = rows[offset + busiest];
199
+ const most = row
200
+ ? `${this.busiestLabel}: ${row.label} ${row.cells[column]?.text ?? ''}`.trim()
201
+ : '';
202
+ const sum = `${this.totalLabel} ${formatNumbers(this.locale)(total)}`;
203
+ return html `<p id="helix-chart-summary" part="overview">${most ? `${most}; ${sum}` : sum}.</p>`;
188
204
  }
189
205
  renderTooltip() {
190
206
  const at = this.pointer.at;
@@ -203,6 +219,9 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
203
219
  }, undefined, this.pointer);
204
220
  }
205
221
  };
222
+ __decorate([
223
+ property({ attribute: 'busiest-label' })
224
+ ], HelixHeadlessHeatmap.prototype, "busiestLabel", void 0);
206
225
  __decorate([
207
226
  property({ attribute: 'empty-text' })
208
227
  ], HelixHeadlessHeatmap.prototype, "emptyText", void 0);
@@ -221,6 +240,9 @@ __decorate([
221
240
  __decorate([
222
241
  property()
223
242
  ], HelixHeadlessHeatmap.prototype, "tones", void 0);
243
+ __decorate([
244
+ property({ attribute: 'total-label' })
245
+ ], HelixHeadlessHeatmap.prototype, "totalLabel", void 0);
224
246
  __decorate([
225
247
  state()
226
248
  ], HelixHeadlessHeatmap.prototype, "width", void 0);
@@ -28,7 +28,8 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
28
28
  * rendered is drawn then.
29
29
  *
30
30
  * A name with no match renders nothing, and the element fires `error`, so a
31
- * typo in a name can be heard.
31
+ * typo in a name can be heard. It waits until the page has loaded and painted
32
+ * first, so a library a later module registers is not taken for a typo.
32
33
  *
33
34
  * An icon is decorative by default and hidden from assistive technology. Set
34
35
  * `label` only when the icon is the only carrier of meaning, such as in a
@@ -120,6 +121,16 @@ export declare class HelixHeadlessIcon extends HelixElement {
120
121
  private lookup;
121
122
  protected willUpdate(changed: PropertyValues): void;
122
123
  protected updated(changed: PropertyValues): void;
124
+ /** The name a report is waiting on the page to settle for, so it is asked once. */
125
+ private pending;
126
+ /**
127
+ * Says a name drew nothing, once the page has had its chance to register
128
+ * it: a library registered by a module further down the page, or by the
129
+ * page's own script after this element, is not a missing glyph. Asked again
130
+ * then, and said only where the name still draws nothing and nothing is
131
+ * still on its way.
132
+ */
133
+ private reportMissing;
123
134
  protected render(): TemplateResult | typeof nothing;
124
135
  }
125
136
  declare global {
@@ -9,6 +9,7 @@ import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
11
11
  import { resolveIcon, watchIcons } from '../registry.js';
12
+ import { afterPageSettles } from '../settle.js';
12
13
  let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
13
14
  constructor() {
14
15
  super(...arguments);
@@ -18,6 +19,7 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
18
19
  this.missed = '';
19
20
  this.waiting = false;
20
21
  this.arrived = '';
22
+ this.pending = '';
21
23
  }
22
24
  static get observedAttributes() {
23
25
  return [...super.observedAttributes, 'aria-label'];
@@ -69,22 +71,31 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
69
71
  }
70
72
  this.arrived = '';
71
73
  if (this.drawn === undefined && this.name !== '' && !this.waiting) {
72
- if (this.missed !== this.name) {
73
- this.missed = this.name;
74
- this.dispatchEvent(new Event('error', { bubbles: false, cancelable: false, composed: false }));
75
- reportRuntime({
76
- kind: 'unknown-icon',
77
- message: `<${this.localName} name="${this.name}"> names no registered icon, so it draws ` +
78
- 'nothing. Check the name, register the glyph with `registerIcons`, or import it ' +
79
- 'and pass it as `icon`.',
80
- tag: this.localName,
81
- });
74
+ if (this.missed !== this.name && this.pending !== this.name) {
75
+ this.pending = this.name;
76
+ afterPageSettles(() => this.reportMissing());
82
77
  }
83
78
  }
84
79
  else {
85
80
  this.missed = '';
86
81
  }
87
82
  }
83
+ reportMissing() {
84
+ const name = this.pending;
85
+ this.pending = '';
86
+ if (this.name !== name || this.drawn !== undefined || this.waiting || !this.isConnected) {
87
+ return;
88
+ }
89
+ this.missed = name;
90
+ this.dispatchEvent(new Event('error', { bubbles: false, cancelable: false, composed: false }));
91
+ reportRuntime({
92
+ kind: 'unknown-icon',
93
+ message: `<${this.localName} name="${name}"> names no registered icon, so it draws ` +
94
+ 'nothing. Check the name, register the glyph with `registerIcons`, or import it ' +
95
+ 'and pass it as `icon`.',
96
+ tag: this.localName,
97
+ });
98
+ }
88
99
  render() {
89
100
  const icon = this.drawn;
90
101
  if (!icon) {
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Calls back once the page has loaded and painted a frame after it: every
3
+ * module a page lists has run by then, and with it every `registerIcons` and
4
+ * `registerIconLibrary` they hold. A page that has loaded already waits only
5
+ * the frame.
6
+ */
7
+ export declare const afterPageSettles: (callback: () => void) => void;
@@ -0,0 +1,10 @@
1
+ export const afterPageSettles = (callback) => {
2
+ const frame = () => {
3
+ requestAnimationFrame(() => callback());
4
+ };
5
+ if (document.readyState === 'complete') {
6
+ frame();
7
+ return;
8
+ }
9
+ window.addEventListener('load', frame, { once: true });
10
+ };
@@ -17,6 +17,8 @@ import { HelixHeadlessButton } from '../../button/root/button-root.js';
17
17
  * mean a part of a button, and an icon-only button is a complete button with
18
18
  * different content and a different shape.
19
19
  *
20
+ * **Keyboard.** Enter and Space press the button, as `Button`'s do.
21
+ *
20
22
  * @since 0.1.0
21
23
  * @status stable
22
24
  * @category actions
@@ -12,13 +12,17 @@ import { type PropertyValues, type TemplateResult } from 'lit';
12
12
  * The `placeholder` slot is empty by default, so the headless tier does not
13
13
  * depend on `Skeleton`, which it does not ship.
14
14
  *
15
+ * The placeholder is hidden from screen readers, the picture having a name of
16
+ * its own. The fallback is read once the picture fails, after the picture's
17
+ * `alt`, so a reader hears that it did not load, as a sighted reader sees.
18
+ *
15
19
  * @since 0.1.0
16
20
  * @status stable
17
21
  * @category media
18
22
  * @aliases Picture, Img, Figure
19
23
  * @documentation https://fusion.dev/helix/components/image
20
24
  *
21
- * @slot fallback - Shown in place of the image if it fails to load.
25
+ * @slot fallback - Shown in place of the image if it fails to load, and read by screen readers then, such as "Photo unavailable".
22
26
  * @slot placeholder - Shown until the image loads.
23
27
  *
24
28
  * @event {Event} error - The image failed to load. Relayed from the inner image, whose own event stays inside the shadow root.
@@ -147,7 +147,7 @@ let HelixHeadlessImage = class HelixHeadlessImage extends HelixElement {
147
147
  ></span>
148
148
  <span
149
149
  part="fallback"
150
- aria-hidden="true"
150
+ aria-hidden=${this.status === 'error' ? nothing : 'true'}
151
151
  ?hidden=${this.status !== 'error'}
152
152
  ><slot name="fallback"></slot
153
153
  ></span>`;
@@ -20,7 +20,10 @@ declare const HelixHeadlessInput_base: (new (...args: any[]) => import("@fusion.
20
20
  * which a reset restores.
21
21
  *
22
22
  * Validity comes from the browser. `type` and `required` are set on the real
23
- * input, and its `validity` is copied to the host.
23
+ * input, and its `validity` is copied to the host. `validate` adds your own
24
+ * rule, run on every change once the browser finds nothing wrong, as React
25
+ * Aria's TextField `validate` does. What it returns is the message the field
26
+ * shows once the user has edited it, and the message a submit raises.
24
27
  *
25
28
  * @since 0.1.0
26
29
  * @status stable
@@ -48,7 +51,8 @@ declare const HelixHeadlessInput_base: (new (...args: any[]) => import("@fusion.
48
51
  * @csspart reveal - The button that shows and hides a password.
49
52
  * @csspart reveal-pressed - The reveal button while the password is shown, added beside `reveal`.
50
53
  *
51
- * @cssstate invalid - The control has an error to show, from the browser's validation or from your `feedback`.
54
+ * @cssstate blank - The field holds no text.
55
+ * @cssstate invalid - The control has an error to show, from the browser's validation, `validate`, or your `feedback`.
52
56
  */
53
57
  export declare class HelixHeadlessInput extends HelixHeadlessInput_base {
54
58
  protected readonly aria: AriaForwardController;
@@ -281,6 +285,12 @@ export declare class HelixHeadlessInput extends HelixHeadlessInput_base {
281
285
  * validation follow.
282
286
  */
283
287
  type: InputType;
288
+ /**
289
+ * Your own rule for the value, run on every change once the browser finds
290
+ * nothing wrong. Return a message, or a list of them, to mark the field
291
+ * invalid, and `true`, `null`, or nothing to accept the value.
292
+ */
293
+ validate?: (value: string) => string | string[] | true | null | undefined;
284
294
  protected render(): TemplateResult;
285
295
  }
286
296
  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 { AriaForwardController, customElement, drawIcon, HelixClearEvent, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, drawIcon, HelixClearEvent, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, submitImplicitly, } from '@fusion.dev/helix-core';
8
8
  import { eye, eyeSlash, x } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -48,7 +48,7 @@ let HelixHeadlessInput = class HelixHeadlessInput extends MixinControllerValue(M
48
48
  };
49
49
  this.handleKeypress = (event) => {
50
50
  if (event.key === 'Enter' && !event.defaultPrevented) {
51
- this.submitImplicitly();
51
+ submitImplicitly(this.form);
52
52
  }
53
53
  };
54
54
  this.handleChange = (event) => {
@@ -112,6 +112,11 @@ let HelixHeadlessInput = class HelixHeadlessInput extends MixinControllerValue(M
112
112
  return;
113
113
  }
114
114
  this.copyValidity(control);
115
+ const own = control.validity.valid ? this.validate?.(this.value) : undefined;
116
+ const message = Array.isArray(own) ? own.join(' ') : typeof own === 'string' ? own : '';
117
+ if (message !== '') {
118
+ this.internals.setValidity({ customError: true }, message, control);
119
+ }
115
120
  this.refreshFeedback();
116
121
  }
117
122
  focus(options) {
@@ -136,6 +141,7 @@ let HelixHeadlessInput = class HelixHeadlessInput extends MixinControllerValue(M
136
141
  }
137
142
  willUpdate(changed) {
138
143
  super.willUpdate(changed);
144
+ this.toggleState('blank', this.value === '');
139
145
  if (!this.hasUpdated) {
140
146
  return;
141
147
  }
@@ -268,6 +274,9 @@ __decorate([
268
274
  __decorate([
269
275
  property()
270
276
  ], HelixHeadlessInput.prototype, "type", void 0);
277
+ __decorate([
278
+ property({ attribute: false })
279
+ ], HelixHeadlessInput.prototype, "validate", void 0);
271
280
  HelixHeadlessInput = __decorate([
272
281
  customElement('helix-headless-input')
273
282
  ], HelixHeadlessInput);
@@ -23,6 +23,11 @@ import { type PropertyValues, type TemplateResult } from 'lit';
23
23
  * the shortcut with the control. A chord the page wrote there itself is left
24
24
  * alone, and the key takes back only what it added.
25
25
  *
26
+ * `caps` draws a chord as a key per key: each is a `key` part and the `+`
27
+ * between two a `separator` part, so a sheet can draw a cap for each.
28
+ * `key-labels` renames any key, on every platform, such as
29
+ * `key-labels='{"mod":"Command"}'`.
30
+ *
26
31
  * @since 0.1.0
27
32
  * @status stable
28
33
  * @category typography
@@ -33,9 +38,20 @@ import { type PropertyValues, type TemplateResult } from 'lit';
33
38
  *
34
39
  * @event {HelixShortcutEvent} helix-shortcut - The chord was pressed. Cancelable: cancel it to let the key press keep its usual effect.
35
40
  *
36
- * @csspart kbd - The key.
41
+ * @csspart kbd - The key, or the chord under `keys`.
42
+ * @csspart key - One key of a chord under `keys` and `caps`, such as `Ctrl` in `Ctrl+K`.
43
+ * @csspart separator - The `+` between two keys of a chord under `keys` and `caps`, drawn where the platform writes one: on Windows and Linux. An Apple keyboard writes none.
37
44
  */
38
45
  export declare class HelixHeadlessKbd extends HelixElement {
46
+ /**
47
+ * Draws a chord under `keys` as one key per key, each a `key` part with a
48
+ * `separator` part between two where the platform writes a `+`, as GitHub
49
+ * and most docs draw `Ctrl` `K`.
50
+ *
51
+ * Absent, a chord is one key holding the whole label, as it always was.
52
+ * Reflected, because the styled tier draws the caps on the keys under it.
53
+ */
54
+ caps: boolean;
39
55
  /**
40
56
  * The id of the control the shortcut works, which is given the chord as its
41
57
  * `aria-keyshortcuts`.
@@ -45,6 +61,18 @@ export declare class HelixHeadlessKbd extends HelixElement {
45
61
  * shortcut the page binds itself.
46
62
  */
47
63
  for: string;
64
+ /**
65
+ * Names for keys in place of the platform's own, by the key's name in
66
+ * `keys`, written as JSON in the attribute: `key-labels='{"mod":"Command",
67
+ * "enter":"Return"}'` shows `Command` and `Return` wherever `mod` and
68
+ * `enter` are written.
69
+ *
70
+ * For a page in another language, or a product with its own word for a key.
71
+ * Absent, the platform's glyphs and words are used. Only what is shown
72
+ * changes: the chord written onto `aria-keyshortcuts` keeps the names a
73
+ * screen reader knows.
74
+ */
75
+ keyLabels?: Record<string, string>;
48
76
  /**
49
77
  * The key or chord to show, such as `mod+k`, `?`, or `/`.
50
78
  *
@@ -93,6 +121,12 @@ export declare class HelixHeadlessKbd extends HelixElement {
93
121
  private readonly handleKeydown;
94
122
  protected firstUpdated(changed: PropertyValues): void;
95
123
  protected render(): TemplateResult;
124
+ /**
125
+ * The chord under `caps`, a part for each key and for each `+` between two,
126
+ * so a sheet can draw a cap per key. Written with no space between, so a
127
+ * chord reads as the one label it was.
128
+ */
129
+ protected renderChord(): TemplateResult;
96
130
  }
97
131
  declare global {
98
132
  interface HTMLElementTagNameMap {
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { ariaChord, chordParts, customElement, elementById, HelixElement, HelixShortcutEvent, matchesChord, onApple, printableChord, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
8
+ import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { claimShortcut, releaseShortcut } from '../shortcut.js';
11
11
  const typing = (node) => {
@@ -70,6 +70,7 @@ const ELSEWHERE = {
70
70
  let HelixHeadlessKbd = class HelixHeadlessKbd extends HelixElement {
71
71
  constructor() {
72
72
  super(...arguments);
73
+ this.caps = false;
73
74
  this.for = '';
74
75
  this.keys = '';
75
76
  this.handleKeydown = (event) => {
@@ -134,8 +135,12 @@ let HelixHeadlessKbd = class HelixHeadlessKbd extends HelixElement {
134
135
  get label() {
135
136
  const apple = !this.hydrating && onApple();
136
137
  const names = apple ? APPLE : ELSEWHERE;
137
- const parts = chordParts(this.keys).map((part) => names[part.toLowerCase()] ?? (part.length === 1 ? part.toUpperCase() : part));
138
- return parts.join(apple ? '' : '+');
138
+ const own = this.keyLabels ?? {};
139
+ const keys = chordParts(this.keys).map((part) => {
140
+ const name = part.toLowerCase();
141
+ return own[name] ?? names[name] ?? (part.length === 1 ? part.toUpperCase() : part);
142
+ });
143
+ return { keys, separator: apple ? '' : '+' };
139
144
  }
140
145
  listen() {
141
146
  if (!this.within || this.keys === '') {
@@ -157,12 +162,26 @@ let HelixHeadlessKbd = class HelixHeadlessKbd extends HelixElement {
157
162
  this.finishHydration();
158
163
  }
159
164
  render() {
160
- return html `<kbd part="kbd">${this.keys === '' ? html `<slot></slot>` : this.label}</kbd>`;
165
+ if (this.keys === '') {
166
+ return html `<kbd part="kbd"><slot></slot></kbd>`;
167
+ }
168
+ const { keys, separator } = this.label;
169
+ return html `<kbd part="kbd">${this.caps ? this.renderChord() : keys.join(separator)}</kbd>`;
170
+ }
171
+ renderChord() {
172
+ const { keys, separator } = this.label;
173
+ return html `${keys.map((key, index) => html `${index > 0 && separator !== '' ? html `<span part="separator">${separator}</span>` : nothing}<span part="key">${key}</span>`)}`;
161
174
  }
162
175
  };
176
+ __decorate([
177
+ property({ reflect: true, type: Boolean })
178
+ ], HelixHeadlessKbd.prototype, "caps", void 0);
163
179
  __decorate([
164
180
  property()
165
181
  ], HelixHeadlessKbd.prototype, "for", void 0);
182
+ __decorate([
183
+ property({ attribute: 'key-labels', type: Object })
184
+ ], HelixHeadlessKbd.prototype, "keyLabels", void 0);
166
185
  __decorate([
167
186
  property()
168
187
  ], HelixHeadlessKbd.prototype, "keys", void 0);
@@ -1,4 +1,4 @@
1
- import { HelixElement, SourceController } from '@fusion.dev/helix-core';
1
+ import { HelixElement, KeyboardNavigationController, SourceController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * @summary A list of the series in a chart, each name beside the color it is drawn in.
@@ -21,8 +21,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
21
21
  * **`toggles` makes each entry a button that hides its series.** Every chart
22
22
  * reading the same table draws without a hidden series and fits its scale to
23
23
  * the rest, and the series keeps its color. The button says whether its
24
- * series is shown with `aria-pressed`. `hidden-series` holds the hidden names,
25
- * so a page can start with some hidden or read which are.
24
+ * series is shown with `aria-pressed`. The buttons are one tab stop: the
25
+ * arrow keys move between them, Home and End go to the first and the last,
26
+ * and a typed letter goes to the next name starting with it.
27
+ * `hidden-series` holds the hidden names,
28
+ * so a page can start with some hidden or read which are. Two legends on one
29
+ * table show the same hidden series: the list written last wins, and the
30
+ * other legend takes it for its own entries.
26
31
  *
27
32
  * @since 0.1.0
28
33
  * @status stable
@@ -99,16 +104,56 @@ export declare class HelixHeadlessLegend extends HelixElement {
99
104
  */
100
105
  toggles: boolean;
101
106
  protected readonly source: SourceController;
102
- /** Whether this legend has written the table's hidden series, so leaving undoes it. */
103
- private wrote;
107
+ /**
108
+ * The toggle that holds the one tab stop, by place: the one a reader was
109
+ * last on, or the first.
110
+ */
111
+ private stop;
112
+ /**
113
+ * The arrow keys, Home, End and typed letters among the toggles, so a
114
+ * legend of twelve series is one tab stop, as a toolbar of
115
+ * toggle buttons is. Nothing without `toggles`, where there is nothing to
116
+ * press.
117
+ */
118
+ protected readonly keys: KeyboardNavigationController;
119
+ /** The table this legend last wrote its `hidden-series` to, so leaving undoes it. */
120
+ private wroteTo;
121
+ /** Whether this update writes `hidden-series` to the table, decided before the render. */
122
+ private writes;
123
+ /**
124
+ * Decides who holds the hidden series for this update: this legend, or the
125
+ * table.
126
+ *
127
+ * **This legend, where its own `hidden-series` moved**, by a press, by the
128
+ * page, or by its first value, and where it has a list the table it now
129
+ * reads has not had from it yet. **The table otherwise**, which is a second
130
+ * legend on the same table having hidden something: this one takes the
131
+ * table's list for its own entries and writes nothing back.
132
+ *
133
+ * Writing back on every update is what hung a page with two legends whose
134
+ * lists differed: each write asks every reader of the table to update,
135
+ * which included the other legend, which wrote its own list back. Now the
136
+ * last list written wins and the other legend shows it, as two switches on
137
+ * one lamp do.
138
+ */
104
139
  protected willUpdate(changed: PropertyValues<this>): void;
140
+ /**
141
+ * Takes the table's hidden series as this legend's own, for its entries.
142
+ *
143
+ * A name hidden on the table that is not one of this legend's entries stays
144
+ * out of its list: a legend of the rows and one of the columns share a
145
+ * table, and each keeps a list of its own.
146
+ */
147
+ private follow;
105
148
  /**
106
149
  * Hands `hidden-series` to the table, where every chart reading it finds
107
150
  * it.
108
151
  *
109
152
  * After the update rather than before, because a server has no table and a
110
153
  * hydrating render must draw what the server drew: the charts follow a frame
111
- * after the legend, which is when they draw at all.
154
+ * after the legend, which is when they draw at all. Only this legend's own
155
+ * entries are written, so a series another legend hid among other names
156
+ * stays hidden.
112
157
  */
113
158
  protected updated(changed: PropertyValues<this>): void;
114
159
  disconnectedCallback(): void;
@@ -135,9 +180,11 @@ export declare class HelixHeadlessLegend extends HelixElement {
135
180
  * The button around a name under `toggles`, behind a seam a tier can move.
136
181
  *
137
182
  * Pressed while its series is shown, which is the state a reader changes by
138
- * pressing it: a toggle button says whether its thing is on.
183
+ * pressing it: a toggle button says whether its thing is on. `holds` says
184
+ * whether it is the one tab stop, which is the last toggle a reader was on,
185
+ * the toggle at `index`.
139
186
  */
140
- protected renderToggle(name: string, off: boolean): TemplateResult;
187
+ protected renderToggle(name: string, off: boolean, index?: number, holds?: boolean): TemplateResult;
141
188
  }
142
189
  declare global {
143
190
  interface HTMLElementTagNameMap {