@fusion.dev/helix-headless 0.146.0 → 0.147.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 (239) hide show
  1. package/custom-elements.json +5266 -785
  2. package/dist/accordion/item/accordion-item.d.ts +38 -19
  3. package/dist/accordion/item/accordion-item.js +31 -9
  4. package/dist/accordion/root/accordion-root.d.ts +8 -2
  5. package/dist/accordion/root/accordion-root.js +3 -3
  6. package/dist/announcement/root/announcement-root.d.ts +13 -4
  7. package/dist/announcement/root/announcement-root.js +34 -6
  8. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  9. package/dist/area-chart/root/area-chart-root.js +11 -3
  10. package/dist/avatar/root/avatar-root.d.ts +31 -1
  11. package/dist/avatar/root/avatar-root.js +11 -1
  12. package/dist/badge/root/badge-root.d.ts +57 -6
  13. package/dist/badge/root/badge-root.js +88 -5
  14. package/dist/bar-chart/root/bar-chart-root.js +11 -20
  15. package/dist/bar-chart/stack.d.ts +10 -0
  16. package/dist/bar-chart/stack.js +8 -0
  17. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  18. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  19. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -2
  20. package/dist/button/root/button-root.d.ts +24 -38
  21. package/dist/button/root/button-root.js +6 -37
  22. package/dist/button/submitter.d.ts +27 -0
  23. package/dist/button/submitter.js +25 -0
  24. package/dist/card/root/card-root.d.ts +12 -1
  25. package/dist/card/root/card-root.js +11 -4
  26. package/dist/chat/root/chat-root.d.ts +15 -1
  27. package/dist/chat/root/chat-root.js +11 -2
  28. package/dist/checkbox/root/checkbox-root.js +5 -7
  29. package/dist/checkbox-group/root/checkbox-group-root.js +4 -3
  30. package/dist/checkbox-group/select-all.d.ts +0 -6
  31. package/dist/checkbox-group/select-all.js +0 -5
  32. package/dist/code-block/root/code-block-root.d.ts +35 -14
  33. package/dist/code-block/root/code-block-root.js +17 -3
  34. package/dist/color-input/root/color-input-root.d.ts +11 -0
  35. package/dist/color-input/root/color-input-root.js +12 -5
  36. package/dist/color-picker/channels.js +1 -2
  37. package/dist/color-picker/root/color-picker-root.d.ts +32 -24
  38. package/dist/color-picker/root/color-picker-root.js +101 -51
  39. package/dist/color-picker/wheel.d.ts +17 -0
  40. package/dist/color-picker/wheel.js +6 -0
  41. package/dist/combobox/root/combobox-root.d.ts +13 -0
  42. package/dist/combobox/root/combobox-root.js +14 -1
  43. package/dist/command/root/command-root.d.ts +3 -1
  44. package/dist/command/root/command-root.js +2 -19
  45. package/dist/copy-button/root/copy-button-root.d.ts +16 -1
  46. package/dist/copy-button/root/copy-button-root.js +7 -2
  47. package/dist/data-table/keys.d.ts +0 -18
  48. package/dist/data-table/keys.js +1 -11
  49. package/dist/data-table/root/data-table-root.js +13 -7
  50. package/dist/date-input/root/date-input-root.d.ts +8 -1
  51. package/dist/date-input/root/date-input-root.js +8 -5
  52. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  53. package/dist/date-picker/root/date-picker-root.js +21 -16
  54. package/dist/description-list/item/description-list-item.d.ts +18 -4
  55. package/dist/description-list/item/description-list-item.js +8 -4
  56. package/dist/donut-chart/root/donut-chart-root.js +13 -6
  57. package/dist/donut-chart/slice-label.d.ts +35 -0
  58. package/dist/donut-chart/slice-label.js +11 -0
  59. package/dist/drawer/root/drawer-root.d.ts +4 -2
  60. package/dist/drawer/root/drawer-root.js +15 -3
  61. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  62. package/dist/drilldown/item/drilldown-item.js +39 -4
  63. package/dist/drilldown/root/drilldown-root.js +3 -2
  64. package/dist/element-events.generated.js +4 -1
  65. package/dist/empty/root/empty-root.d.ts +31 -1
  66. package/dist/empty/root/empty-root.js +26 -5
  67. package/dist/file-input/root/file-input-root.d.ts +34 -6
  68. package/dist/file-input/root/file-input-root.js +54 -19
  69. package/dist/file-input/status.d.ts +24 -0
  70. package/dist/file-input/status.js +10 -0
  71. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  72. package/dist/filter-input/chip/filter-chip.js +11 -2
  73. package/dist/filter-input/root/filter-input-root.d.ts +8 -0
  74. package/dist/filter-input/root/filter-input-root.js +17 -27
  75. package/dist/format-date/options.d.ts +3 -4
  76. package/dist/format-date/options.js +2 -2
  77. package/dist/format-date/root/format-date-root.d.ts +18 -1
  78. package/dist/format-date/root/format-date-root.js +19 -23
  79. package/dist/heatmap/root/buckets.d.ts +21 -0
  80. package/dist/heatmap/root/buckets.js +34 -0
  81. package/dist/heatmap/root/heatmap-root.d.ts +64 -3
  82. package/dist/heatmap/root/heatmap-root.js +89 -19
  83. package/dist/icon/index.d.ts +1 -1
  84. package/dist/icon/index.js +1 -1
  85. package/dist/icon/registry.d.ts +26 -1
  86. package/dist/icon/registry.js +12 -1
  87. package/dist/icon/root/icon-root.d.ts +13 -0
  88. package/dist/icon/root/icon-root.js +23 -2
  89. package/dist/icon/root/index.d.ts +1 -1
  90. package/dist/icon/root/index.js +1 -1
  91. package/dist/image/root/image-root.d.ts +9 -0
  92. package/dist/image/root/image-root.js +6 -1
  93. package/dist/input/root/input-root.d.ts +23 -2
  94. package/dist/input/root/input-root.js +12 -8
  95. package/dist/kbd/keys.d.ts +29 -0
  96. package/dist/kbd/keys.js +52 -0
  97. package/dist/kbd/root/kbd-root.d.ts +20 -3
  98. package/dist/kbd/root/kbd-root.js +24 -50
  99. package/dist/legend/hidden-series.d.ts +10 -2
  100. package/dist/legend/hidden-series.js +5 -2
  101. package/dist/legend/root/legend-root.d.ts +25 -7
  102. package/dist/legend/root/legend-root.js +22 -10
  103. package/dist/lightbox/root/lightbox-root.d.ts +50 -5
  104. package/dist/lightbox/root/lightbox-root.js +76 -8
  105. package/dist/lightbox/zoom.d.ts +2 -0
  106. package/dist/lightbox/zoom.js +5 -3
  107. package/dist/line-chart/root/line-chart-root.d.ts +16 -0
  108. package/dist/line-chart/root/line-chart-root.js +10 -2
  109. package/dist/list/root/list-root.d.ts +10 -2
  110. package/dist/list/root/list-root.js +26 -5
  111. package/dist/list/selection.d.ts +17 -0
  112. package/dist/list/selection.js +16 -0
  113. package/dist/listbox/root/listbox-root.d.ts +9 -5
  114. package/dist/listbox/root/listbox-root.js +29 -37
  115. package/dist/megamenu/hand-over.d.ts +75 -0
  116. package/dist/megamenu/hand-over.js +67 -0
  117. package/dist/megamenu/root/megamenu-root.d.ts +38 -14
  118. package/dist/megamenu/root/megamenu-root.js +36 -50
  119. package/dist/menu/item/menu-item.js +4 -4
  120. package/dist/menu/root/menu-root.d.ts +3 -0
  121. package/dist/meter/root/meter-root.d.ts +42 -0
  122. package/dist/meter/root/meter-root.js +33 -7
  123. package/dist/notifications/root/notifications-root.d.ts +10 -1
  124. package/dist/notifications/root/notifications-root.js +11 -1
  125. package/dist/number-input/grain.d.ts +28 -0
  126. package/dist/number-input/grain.js +14 -0
  127. package/dist/number-input/root/number-input-root.d.ts +17 -1
  128. package/dist/number-input/root/number-input-root.js +29 -18
  129. package/dist/object-inspector/root/object-inspector-root.d.ts +13 -6
  130. package/dist/object-inspector/root/object-inspector-root.js +18 -17
  131. package/dist/option/index.d.ts +1 -0
  132. package/dist/option/index.js +1 -0
  133. package/dist/option/separator/option-separator.d.ts +30 -0
  134. package/dist/option/separator/option-separator.js +21 -0
  135. package/dist/outline/root/outline-root.d.ts +12 -0
  136. package/dist/outline/root/outline-root.js +5 -19
  137. package/dist/pagination/size/pagination-size.d.ts +5 -0
  138. package/dist/pagination/size/pagination-size.js +4 -2
  139. package/dist/pin-input/code.d.ts +28 -1
  140. package/dist/pin-input/code.js +12 -0
  141. package/dist/pin-input/root/pin-input-root.d.ts +98 -121
  142. package/dist/pin-input/root/pin-input-root.js +157 -148
  143. package/dist/popover/hide-outside.js +2 -9
  144. package/dist/popover/root/popover-root.d.ts +7 -1
  145. package/dist/popover/root/popover-root.js +9 -1
  146. package/dist/progress/root/progress-root.d.ts +27 -0
  147. package/dist/progress/root/progress-root.js +25 -7
  148. package/dist/qr-code/export.d.ts +19 -0
  149. package/dist/qr-code/export.js +14 -0
  150. package/dist/qr-code/name.d.ts +11 -0
  151. package/dist/qr-code/name.js +7 -0
  152. package/dist/qr-code/root/qr-code-root.d.ts +17 -6
  153. package/dist/qr-code/root/qr-code-root.js +10 -12
  154. package/dist/radio/group/radio-group.d.ts +15 -0
  155. package/dist/radio/root/radio-root.js +2 -2
  156. package/dist/rating/root/rating-root.d.ts +22 -9
  157. package/dist/rating/root/rating-root.js +15 -39
  158. package/dist/scatter-chart/root/scatter-chart-root.d.ts +30 -0
  159. package/dist/scatter-chart/root/scatter-chart-root.js +34 -7
  160. package/dist/scroll-area/scrollbar.d.ts +0 -8
  161. package/dist/scroll-area/scrollbar.js +1 -1
  162. package/dist/select/align.d.ts +47 -0
  163. package/dist/select/align.js +7 -0
  164. package/dist/select/root/select-root.d.ts +59 -3
  165. package/dist/select/root/select-root.js +72 -12
  166. package/dist/slider/root/slider-root.d.ts +18 -1
  167. package/dist/slider/root/slider-root.js +22 -0
  168. package/dist/sparkline/readings.d.ts +30 -12
  169. package/dist/sparkline/readings.js +32 -15
  170. package/dist/sparkline/root/sparkline-root.d.ts +46 -3
  171. package/dist/sparkline/root/sparkline-root.js +60 -21
  172. package/dist/speed-dial/item/speed-dial-item.d.ts +1 -1
  173. package/dist/speed-dial/item/speed-dial-item.js +2 -2
  174. package/dist/speed-dial/root/speed-dial-root.d.ts +6 -0
  175. package/dist/speed-dial/root/speed-dial-root.js +9 -1
  176. package/dist/splitter/root/splitter-root.d.ts +3 -0
  177. package/dist/splitter/root/splitter-root.js +2 -0
  178. package/dist/steps/root/steps-root.d.ts +8 -1
  179. package/dist/steps/root/steps-root.js +6 -1
  180. package/dist/steps/step/step.d.ts +1 -0
  181. package/dist/steps/step/step.js +1 -1
  182. package/dist/table/column/table-column.d.ts +6 -0
  183. package/dist/table/column/table-column.js +5 -1
  184. package/dist/table/root/table-root.js +13 -6
  185. package/dist/table/row/table-row.d.ts +24 -0
  186. package/dist/table/row/table-row.js +37 -1
  187. package/dist/tabs/root/tabs-root.d.ts +5 -3
  188. package/dist/tag/group/tag-group.d.ts +16 -4
  189. package/dist/tag/group/tag-group.js +19 -7
  190. package/dist/textarea/root/textarea-root.d.ts +14 -2
  191. package/dist/textarea/root/textarea-root.js +6 -7
  192. package/dist/time-input/root/time-input-root.d.ts +5 -1
  193. package/dist/time-input/root/time-input-root.js +10 -7
  194. package/dist/time-picker/root/time-picker-root.d.ts +11 -14
  195. package/dist/time-picker/root/time-picker-root.js +15 -23
  196. package/dist/toast/item/toast-item.d.ts +3 -3
  197. package/dist/toast/item/toast-item.js +0 -1
  198. package/dist/toast/root/toast-root.d.ts +28 -4
  199. package/dist/toast/root/toast-root.js +58 -12
  200. package/dist/toast/words.d.ts +12 -0
  201. package/dist/toast/words.js +25 -0
  202. package/dist/toggle/group/toggle-group.d.ts +29 -5
  203. package/dist/toggle/group/toggle-group.js +26 -41
  204. package/dist/toggle/root/toggle-root.js +2 -4
  205. package/dist/toolbar/root/toolbar-root.d.ts +21 -5
  206. package/dist/toolbar/root/toolbar-root.js +52 -8
  207. package/dist/tooltip/root/tooltip-root.d.ts +7 -1
  208. package/dist/tooltip/root/tooltip-root.js +10 -1
  209. package/dist/tour/root/tour-root.d.ts +15 -0
  210. package/dist/tour/root/tour-root.js +20 -1
  211. package/dist/tour/stop/tour-stop.d.ts +3 -0
  212. package/dist/tour/stop/tour-stop.js +3 -3
  213. package/dist/tree/root/tree-root.js +2 -2
  214. package/dist/trend/figure.d.ts +16 -0
  215. package/dist/trend/figure.js +8 -0
  216. package/dist/trend/root/trend-root.d.ts +41 -2
  217. package/dist/trend/root/trend-root.js +29 -3
  218. package/dist/video/root/extra-controls.d.ts +12 -0
  219. package/dist/video/root/extra-controls.js +6 -2
  220. package/dist/video/root/video-root.d.ts +5 -1
  221. package/dist/video-playlist/root/video-playlist-root.d.ts +11 -1
  222. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  223. package/dist/virtual-list/fill.d.ts +11 -0
  224. package/dist/virtual-list/fill.js +9 -0
  225. package/dist/virtual-list/keys.d.ts +16 -7
  226. package/dist/virtual-list/keys.js +17 -6
  227. package/dist/virtual-list/plan.d.ts +17 -1
  228. package/dist/virtual-list/plan.js +13 -2
  229. package/dist/virtual-list/root/virtual-list-root.d.ts +25 -3
  230. package/dist/virtual-list/root/virtual-list-root.js +48 -24
  231. package/lint.json +1 -1
  232. package/package.json +5 -5
  233. package/types/jsx.d.ts +2979 -738
  234. package/types/svelte.d.ts +1 -0
  235. package/types/vue.d.ts +619 -202
  236. package/dist/color-picker/swatches.d.ts +0 -12
  237. package/dist/color-picker/swatches.js +0 -26
  238. package/dist/rating/steps.d.ts +0 -7
  239. package/dist/rating/steps.js +0 -9
@@ -4,10 +4,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { CHART_PLOT, ChartPointerController, customElement, extent, formatNumbers, HelixElement, linear, linePath, PLOT_WIDTH, SourceController, unbroken, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
7
+ import { CHART_PLOT, ChartPointerController, customElement, extent, extremesOf, formatNumbers, HelixElement, linear, linePath, PLOT_WIDTH, SourceController, unbroken, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
- import { extremesOf, readingWords } from '../readings.js';
10
+ import { areaPath, overviewWords, readingWords, slotsOf } from '../readings.js';
11
11
  const STRIP_HEIGHT = 32;
12
12
  let HelixHeadlessSparkline = class HelixHeadlessSparkline extends HelixElement {
13
13
  constructor() {
@@ -16,11 +16,31 @@ let HelixHeadlessSparkline = class HelixHeadlessSparkline extends HelixElement {
16
16
  this.label = '';
17
17
  this.locale = '';
18
18
  this.marks = '';
19
+ this.overviewLabel = 'From {first} to {last}, highest {high}, lowest {low}';
19
20
  this.positionLabel = '{position} of {count}';
21
+ this.range = '';
20
22
  this.values = '';
21
23
  this.source = new SourceController(this);
22
24
  this.pointer = new ChartPointerController(this);
23
25
  }
26
+ static get observedAttributes() {
27
+ return [...super.observedAttributes, 'aria-label'];
28
+ }
29
+ attributeChangedCallback(name, old, value) {
30
+ super.attributeChangedCallback(name, old, value);
31
+ if (name === 'aria-label') {
32
+ this.requestUpdate();
33
+ }
34
+ }
35
+ get named() {
36
+ return this.getAttribute?.('aria-label') || this.label;
37
+ }
38
+ get band() {
39
+ const [one, two] = writtenNumbers(this.range);
40
+ return one === undefined || two === undefined
41
+ ? undefined
42
+ : [Math.min(one, two), Math.max(one, two)];
43
+ }
24
44
  willUpdate(changed) {
25
45
  if (changed.has('for')) {
26
46
  this.source.watch();
@@ -54,15 +74,11 @@ let HelixHeadlessSparkline = class HelixHeadlessSparkline extends HelixElement {
54
74
  }
55
75
  render() {
56
76
  const { count, series } = this.drawn();
57
- const slots = Array.from({ length: count }, () => undefined);
58
- for (const point of series.points) {
59
- if (point.at >= 0 && point.at < count) {
60
- slots[point.at] = point.value;
61
- }
62
- }
77
+ const slots = slotsOf(series.points, count);
63
78
  const points = slots.filter((value) => value !== undefined);
79
+ const band = this.band;
64
80
  const x = linear([0, Math.max(count - 1, 1)], [0, PLOT_WIDTH]);
65
- const [low, high] = extent(points.length > 0 ? points : [0]);
81
+ const [low, high] = extent(points.length > 0 ? [...points, ...(band ?? [])] : [0]);
66
82
  const y = this.plot === 'bar'
67
83
  ? linear([Math.min(0, low), Math.max(0, high)], [STRIP_HEIGHT, 0])
68
84
  : linear([low, high], [STRIP_HEIGHT, 0]);
@@ -73,15 +89,7 @@ let HelixHeadlessSparkline = class HelixHeadlessSparkline extends HelixElement {
73
89
  .map((run) => ({ path: linePath(run, this.curve ?? 'smooth'), run }))
74
90
  .filter((one) => one.path !== '');
75
91
  const path = runs.map((one) => one.path).join('');
76
- const area = this.plot !== 'bar' && this.plot !== 'line' && path !== ''
77
- ? runs
78
- .map((one) => {
79
- const first = one.run[0];
80
- const last = one.run.at(-1);
81
- return `${one.path}L${last[0]},${STRIP_HEIGHT}L${first[0]},${STRIP_HEIGHT}Z`;
82
- })
83
- .join('')
84
- : '';
92
+ const area = this.plot !== 'bar' && this.plot !== 'line' && path !== '' ? areaPath(runs, STRIP_HEIGHT) : '';
85
93
  const slot = PLOT_WIDTH / Math.max(count, 1);
86
94
  const floor = y(0);
87
95
  const bars = this.plot === 'bar'
@@ -104,10 +112,15 @@ let HelixHeadlessSparkline = class HelixHeadlessSparkline extends HelixElement {
104
112
  const across = this.plot === 'bar' ? index * slot + slot / 2 : x(index);
105
113
  return `inset-inline-start: ${(across / PLOT_WIDTH) * 100}%; inset-block-start: ${(y(value) / STRIP_HEIGHT) * 100}%`;
106
114
  };
107
- const named = this.label !== '';
115
+ const name = this.named;
116
+ const named = name !== '';
117
+ const overview = named
118
+ ? overviewWords(this.overviewLabel, slots, (index) => this.reading(index, slots[index]).text)
119
+ : '';
108
120
  return html `<svg
121
+ aria-describedby=${overview === '' ? nothing : 'sparkline-overview'}
109
122
  aria-hidden=${named ? nothing : 'true'}
110
- aria-label=${named ? this.label : nothing}
123
+ aria-label=${named ? name : nothing}
111
124
  data-mirror
112
125
  id=${CHART_PLOT}
113
126
  part="svg"
@@ -123,6 +136,15 @@ let HelixHeadlessSparkline = class HelixHeadlessSparkline extends HelixElement {
123
136
  <stop offset="1" part="wash-end" />
124
137
  </linearGradient>
125
138
  </defs>`}
139
+ ${band === undefined
140
+ ? nothing
141
+ : svg `<rect
142
+ height=${Math.abs(y(band[0]) - y(band[1]))}
143
+ part="range"
144
+ width=${PLOT_WIDTH}
145
+ x="0"
146
+ y=${Math.min(y(band[0]), y(band[1]))}
147
+ />`}
126
148
  ${path === '' ? nothing : svg `<path d=${path} part="line" />`}
127
149
  ${area === '' ? nothing : svg `<path d=${area} part="area" />`}
128
150
  ${bars.map((bar) => svg `<rect
@@ -135,7 +157,18 @@ let HelixHeadlessSparkline = class HelixHeadlessSparkline extends HelixElement {
135
157
  </svg>
136
158
  ${points.length < 2 || this.plot === 'bar'
137
159
  ? nothing
138
- : html `<span aria-hidden="true" part="terminus" style=${end ?? nothing}></span>`}${this.renderMarks(slots, at)}${this.renderReading(slots, at)}${named && count > 0 ? this.renderLive(slots) : nothing}`;
160
+ : html `<span aria-hidden="true" part="terminus" style=${end ?? nothing}></span>`}${this.renderLone(points.length < 2 ? undefined : series, at)}${this.renderMarks(slots, at)}${this.renderReading(slots, at)}${named && count > 0 ? this.renderLive(slots) : nothing}${overview === ''
161
+ ? nothing
162
+ : html `<span hidden id="sparkline-overview" part="overview">${overview}</span>`}`;
163
+ }
164
+ renderLone(series, at) {
165
+ if (!series || this.plot === 'bar') {
166
+ return nothing;
167
+ }
168
+ const alone = unbroken(series).filter((run) => run.length === 1);
169
+ return alone.length === 0
170
+ ? nothing
171
+ : html `${alone.map(([point]) => html `<span aria-hidden="true" part="lone" style=${at(point?.at ?? 0, point?.value ?? 0)}></span>`)}`;
139
172
  }
140
173
  renderMarks(slots, at) {
141
174
  const asked = writtenList(this.marks);
@@ -184,12 +217,18 @@ __decorate([
184
217
  __decorate([
185
218
  property()
186
219
  ], HelixHeadlessSparkline.prototype, "marks", void 0);
220
+ __decorate([
221
+ property({ attribute: 'overview-label' })
222
+ ], HelixHeadlessSparkline.prototype, "overviewLabel", void 0);
187
223
  __decorate([
188
224
  property({ reflect: true })
189
225
  ], HelixHeadlessSparkline.prototype, "plot", void 0);
190
226
  __decorate([
191
227
  property({ attribute: 'position-label' })
192
228
  ], HelixHeadlessSparkline.prototype, "positionLabel", void 0);
229
+ __decorate([
230
+ property()
231
+ ], HelixHeadlessSparkline.prototype, "range", void 0);
193
232
  __decorate([
194
233
  property()
195
234
  ], HelixHeadlessSparkline.prototype, "values", void 0);
@@ -43,7 +43,7 @@ export declare class HelixHeadlessSpeedDialItem extends HelixElement {
43
43
  */
44
44
  readonly isSpeedDialItem = true;
45
45
  /** Puts focus on the control inside the item. */
46
- focus(): void;
46
+ focus(options?: FocusOptions): void;
47
47
  /**
48
48
  * Moves focus to the action. Part of `NavigableItem`, which the shared
49
49
  * keyboard controller uses for the arrow keys.
@@ -17,8 +17,8 @@ let HelixHeadlessSpeedDialItem = class HelixHeadlessSpeedDialItem extends HelixE
17
17
  this.choose();
18
18
  };
19
19
  }
20
- focus() {
21
- this.partNamed('item')?.focus();
20
+ focus(options) {
21
+ this.partNamed('item')?.focus(options);
22
22
  }
23
23
  focusItem() {
24
24
  this.focus();
@@ -76,6 +76,12 @@ export declare class HelixHeadlessSpeedDial extends HelixHeadlessSpeedDial_base
76
76
  private get trigger();
77
77
  /** The actions, which are this element's own rows and nothing else slotted. */
78
78
  private get items();
79
+ /**
80
+ * Puts focus on the trigger, the slotted one or the button drawn in its
81
+ * place, as a `focus()` on a native control would. The host itself takes no
82
+ * focus, so a page focusing it after a dialog closes landed nowhere.
83
+ */
84
+ focus(options?: FocusOptions): void;
79
85
  /** Shuts it and puts the reader back on the trigger. */
80
86
  private close;
81
87
  private readonly handleTriggerClick;
@@ -66,11 +66,19 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
66
66
  const slot = this.renderRoot?.querySelector('slot:not([name])');
67
67
  return (slot?.assignedElements?.() ?? []).filter((one) => one.isSpeedDialItem === true);
68
68
  }
69
+ focus(options) {
70
+ const trigger = this.trigger;
71
+ if (trigger === this) {
72
+ super.focus(options);
73
+ return;
74
+ }
75
+ trigger.focus(options);
76
+ }
69
77
  close() {
70
78
  if (!this.requestOpen(false)) {
71
79
  return;
72
80
  }
73
- this.trigger.focus();
81
+ this.focus();
74
82
  }
75
83
  connectedCallback() {
76
84
  super.connectedCallback();
@@ -42,6 +42,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
42
42
  * @event {HelixBeforeResizeEvent} helix-before-resize - Fired before the divider settles, and only when the gesture moved it. A press that moved nothing, a drag that came back, an arrow key with no room left, and a corner that moved only the other divider do not fire it. Cancelable: canceling it leaves the position to you and keeps the rest of the gesture, so a collapse the gesture reached still fires `helix-before-collapse`, which you can also cancel. A dragged divider stays where it was dropped until you set your own `position`.
43
43
  * @event {HelixCollapseEvent} helix-collapse - Fired after a pane shuts or reopens, with the side that is shut, or empty where both are open.
44
44
  * @event {HelixResizedEvent} helix-resized - Fired after the divider settles somewhere new, with the new position.
45
+ * @event {Event} input - Fired each time a drag moves the divider, before it settles, as a range input does while it is dragged. Read `position` for where it is now.
45
46
  *
46
47
  * @csspart corner - The handle where two dividers cross, added on the client.
47
48
  * @csspart end - The box that holds the second pane.
@@ -50,6 +51,8 @@ import { type PropertyValues, type TemplateResult } from 'lit';
50
51
  *
51
52
  * @cssproperty --splitter-corner-at - Where a nested splitter crosses this one, as a percentage.
52
53
  * @cssproperty --splitter-position - Where the divider sits, as a percentage of the box.
54
+ *
55
+ * @cssstate dragging - While the pointer or a corner holds the divider.
53
56
  */
54
57
  export declare class HelixHeadlessSplitter extends HelixElement {
55
58
  /**
@@ -127,6 +127,7 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
127
127
  this.positionState = wanted;
128
128
  this.collapsedState = 'none';
129
129
  this.requestUpdate('position', previous);
130
+ this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
130
131
  }
131
132
  settle(next) {
132
133
  const wanted = this.clamp(next);
@@ -471,6 +472,7 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
471
472
  }
472
473
  willUpdate(changed) {
473
474
  super.willUpdate(changed);
475
+ this.toggleState('dragging', this.dragging);
474
476
  }
475
477
  render() {
476
478
  const position = this.showing;
@@ -31,7 +31,9 @@ declare const HelixHeadlessSteps_base: (new (...args: any[]) => import("@fusion.
31
31
  * its stages that are `complete`. The `progress` part carries it as a
32
32
  * `progressbar` and as `--steps-progress`, and is hidden until a stylesheet
33
33
  * gives it a `display`, so a bar of your own costs nothing until you draw it.
34
- * The sequence is `:state(complete)` once every stage is.
34
+ * A screen reader hears it as `progress-label` says, "33% complete" unless
35
+ * set, as Ark UI's steps write it. The sequence is `:state(complete)` once
36
+ * every stage is.
35
37
  *
36
38
  * The sequence is a list named by `label`, and each step a list item. The
37
39
  * current stage's button says `aria-current="step"`, and a finished stage's
@@ -82,6 +84,11 @@ export declare class HelixHeadlessSteps extends HelixHeadlessSteps_base {
82
84
  * catches people out.
83
85
  */
84
86
  orientation?: 'vertical';
87
+ /**
88
+ * What the `progress` part says to a screen reader, with `{percent}` filled
89
+ * in with `progress`.
90
+ */
91
+ progressLabel: string;
85
92
  /**
86
93
  * A function that answers whether a stage is valid, asked about the stage a
87
94
  * reader is on before they move forward from it. Return `false` to keep them
@@ -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, MixinControllerActive, whenHydrated, } from '@fusion.dev/helix-core';
7
+ import { customElement, fillTemplate, HelixElement, hostAria, MixinControllerActive, whenHydrated, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { canReach, progressOf } from '../reach.js';
@@ -13,6 +13,7 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
13
13
  super(...arguments);
14
14
  this.label = 'Steps';
15
15
  this.linear = false;
16
+ this.progressLabel = '{percent}% complete';
16
17
  this.handleSelection = (event) => {
17
18
  if (event.defaultPrevented || !this.steps.includes(event.target)) {
18
19
  return;
@@ -140,6 +141,7 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
140
141
  aria-valuemin="0"
141
142
  aria-valuemax="100"
142
143
  aria-valuenow=${progress}
144
+ aria-valuetext=${fillTemplate(this.progressLabel, { percent: String(progress) })}
143
145
  style="--steps-progress: ${progress}%"
144
146
  hidden
145
147
  ></div>`;
@@ -154,6 +156,9 @@ __decorate([
154
156
  __decorate([
155
157
  property({ reflect: true })
156
158
  ], HelixHeadlessSteps.prototype, "orientation", void 0);
159
+ __decorate([
160
+ property({ attribute: 'progress-label' })
161
+ ], HelixHeadlessSteps.prototype, "progressLabel", void 0);
157
162
  __decorate([
158
163
  property({ attribute: false })
159
164
  ], HelixHeadlessSteps.prototype, "validate", void 0);
@@ -45,6 +45,7 @@ import '../../button/index.js';
45
45
  * @csspart label - The step's name, which is a control.
46
46
  * @csspart label-button - The button inside the label.
47
47
  * @csspart label-live - The live region inside that button, rendered only from the first time the button is pending.
48
+ * @csspart label-spinner - The ring that button turns while it is pending.
48
49
  * @csspart label-text - The words on that button, trimmed to their capitals and baseline.
49
50
  * @csspart live - The live region that says a stage was completed, rendered from the first time one is.
50
51
  * @csspart mark - The glyph inside the marker: a tick, a dot, or an empty ring.
@@ -53,7 +53,7 @@ let HelixHeadlessStep = class HelixHeadlessStep extends HelixElement {
53
53
  renderLabel() {
54
54
  return html `<${this.tag('button')}
55
55
  part="label"
56
- exportparts="button: label-button, live: label-live, text: label-text"
56
+ exportparts="button: label-button, live: label-live, spinner: label-spinner, text: label-text"
57
57
  emphasis="ghost"
58
58
  aria-current=${this.current ? 'step' : nothing}
59
59
  aria-describedby=${this.complete ? 'state' : nothing}
@@ -271,6 +271,12 @@ export declare class HelixHeadlessTableColumn extends HelixElement {
271
271
  * table that sets `gridCell`. Not a consumer's to set.
272
272
  */
273
273
  tabStop: boolean;
274
+ /**
275
+ * @internal The sort said in words on the sort button, written by the table
276
+ * on Android, where TalkBack reads no `aria-sort`. Empty elsewhere. Not a
277
+ * consumer's to set: the table's `sorted-label` holds the words.
278
+ */
279
+ sortWords: string;
274
280
  /** How wide the heading drew, for the separator to report. */
275
281
  private drawn;
276
282
  /** Where a drag of the separator started, while one is running. */
@@ -30,6 +30,7 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
30
30
  this.movable = false;
31
31
  this.gridCell = false;
32
32
  this.tabStop = false;
33
+ this.sortWords = '';
33
34
  this.drawn = 0;
34
35
  this.handleResizeStart = (event) => {
35
36
  if (event.button !== 0) {
@@ -184,7 +185,7 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
184
185
  </svg>
185
186
  </button>`
186
187
  : nothing}${this.canSort
187
- ? html `<button part="sort" type="button" tabindex=${control}><slot></slot></button>`
188
+ ? html `<button part="sort" type="button" tabindex=${control} aria-description=${this.sortWords || nothing}><slot></slot></button>`
188
189
  : html `<slot></slot>`}${this.resizable
189
190
  ? html `<span
190
191
  part="resizer"
@@ -250,6 +251,9 @@ __decorate([
250
251
  __decorate([
251
252
  state()
252
253
  ], HelixHeadlessTableColumn.prototype, "tabStop", void 0);
254
+ __decorate([
255
+ state()
256
+ ], HelixHeadlessTableColumn.prototype, "sortWords", void 0);
253
257
  __decorate([
254
258
  state()
255
259
  ], HelixHeadlessTableColumn.prototype, "drawn", void 0);
@@ -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, commaList, customElement, cycle, fillTemplate, format, HelixElement, HelixSelectEvent, HelixSelectedEvent, HelixSortEvent, HelixSortedEvent, MixinCollection, parse, reflectElements, requestChange, sort, splitValues, toggleEvery, whenHydrated, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, commaList, customElement, cycle, everyState, fillTemplate, format, HelixElement, HelixSelectEvent, HelixSelectedEvent, HelixSortEvent, HelixSortedEvent, inRowOrder, MixinCollection, onAndroid, parse, reflectElements, requestChange, sort, splitValues, toggleEvery, whenHydrated, } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
@@ -204,7 +204,7 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
204
204
  },
205
205
  event: (value) => new HelixSelectEvent({ value }),
206
206
  host: this,
207
- next: [...keys].join(','),
207
+ next: inRowOrder(keys, this.keyed.map(({ key }) => key)).join(','),
208
208
  });
209
209
  }
210
210
  markSelected() {
@@ -230,6 +230,7 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
230
230
  }
231
231
  markSorted() {
232
232
  const current = parse(this.sort);
233
+ const android = onAndroid();
233
234
  for (const column of this.tableColumns) {
234
235
  column.sorted =
235
236
  current && column.canSort && column.field === current.field
@@ -237,6 +238,13 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
237
238
  ? 'ascending'
238
239
  : 'descending'
239
240
  : undefined;
241
+ column.sortWords =
242
+ android && column.sorted
243
+ ? fillTemplate(this.sortedLabel, {
244
+ column: column.textContent?.trim() || column.field,
245
+ order: column.sorted === 'ascending' ? this.ascendingLabel : this.descendingLabel,
246
+ })
247
+ : '';
240
248
  }
241
249
  }
242
250
  get tableColumns() {
@@ -366,8 +374,7 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
366
374
  return nothing;
367
375
  }
368
376
  const every = this.childrenKnown ? this.keyed.map(({ key }) => key) : [];
369
- const keys = this.chosen;
370
- const count = every.filter((key) => keys.has(key)).length;
377
+ const { checked, indeterminate } = everyState(this.chosen, every);
371
378
  const checkbox = this.tag('checkbox');
372
379
  return html `<span part="select-all" role="columnheader"
373
380
  ><${checkbox}
@@ -377,8 +384,8 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
377
384
  mark-checked: checkbox-mark-checked, mark-dash: checkbox-mark-dash,
378
385
  mark-indeterminate: checkbox-mark-indeterminate"
379
386
  aria-label=${this.selectAllLabel}
380
- .checked=${every.length > 0 && count === every.length}
381
- .indeterminate=${count > 0 && count < every.length}
387
+ .checked=${checked}
388
+ .indeterminate=${indeterminate}
382
389
  ></${checkbox}
383
390
  ></span>`;
384
391
  }
@@ -13,6 +13,13 @@ import '../../checkbox/index.js';
13
13
  * In a `selectable` table the row draws a checkbox cell before its own cells,
14
14
  * and `value` is the key the table's `selected` lists it by.
15
15
  *
16
+ * **`href` makes the whole row a link**, as a row action does in React Aria's
17
+ * table: a press anywhere on the row follows it, except on a link or a control
18
+ * of the row's own, and Control, Command or Shift open it the way they open a
19
+ * link. A drag that selects text follows nothing. A row in a table is not a
20
+ * stop of its own, so put the same link in a cell, usually the row header,
21
+ * which is what the keyboard and a screen reader reach.
22
+ *
16
23
  * @since 0.1.0
17
24
  * @status stable
18
25
  * @aliases Row, TableRow
@@ -34,6 +41,7 @@ import '../../checkbox/index.js';
34
41
  * @csspart select - The cell that holds the row's checkbox, in a `selectable` table.
35
42
  * @csspart select-selected - Added to `select` while the row is selected.
36
43
  *
44
+ * @cssstate link - While the row has `href`, so a press on it follows the link.
37
45
  * @cssstate selected - While the row is selected, in a `selectable` table.
38
46
  */
39
47
  export declare class HelixHeadlessTableRow extends HelixElement {
@@ -42,6 +50,15 @@ export declare class HelixHeadlessTableRow extends HelixElement {
42
50
  * finds its columns. Not a consumer's to read or set.
43
51
  */
44
52
  readonly isTableRow = true;
53
+ /**
54
+ * The URL a press on the row follows, which makes the whole row a link.
55
+ * Empty, the default, leaves presses to the row's cells.
56
+ */
57
+ href: string;
58
+ /** The `rel` of the row's link. You set it yourself, as on an anchor. */
59
+ rel: string;
60
+ /** Where the row's link opens, such as `_blank`. */
61
+ target: string;
45
62
  /**
46
63
  * The key the table's `selected` lists this row by. Left empty, the row is
47
64
  * known by its place among the rows as written.
@@ -69,6 +86,13 @@ export declare class HelixHeadlessTableRow extends HelixElement {
69
86
  */
70
87
  alignCells(rules: CSSStyleSheet): void;
71
88
  protected willUpdate(changed: PropertyValues): void;
89
+ connectedCallback(): void;
90
+ disconnectedCallback(): void;
91
+ /**
92
+ * A press on the row follows `href`, unless it landed on a link or control
93
+ * of the row's own, or ended a drag that selected text.
94
+ */
95
+ private readonly handleClick;
72
96
  /**
73
97
  * Asks each cell to read its role again once the cells change.
74
98
  *
@@ -4,20 +4,38 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement, hostAria } from '@fusion.dev/helix-core';
7
+ import { customElement, followLink, HelixElement, hostAria } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
11
11
  import '../../checkbox/index.js';
12
+ const PRESSABLE = 'a[href], button, input, label, select, summary, textarea, [tabindex]';
12
13
  let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
13
14
  constructor() {
14
15
  super(...arguments);
15
16
  this.isTableRow = true;
17
+ this.href = '';
18
+ this.rel = '';
19
+ this.target = '';
16
20
  this.value = '';
17
21
  this.selectable = false;
18
22
  this.selected = false;
19
23
  this.selectLabel = '';
20
24
  this.columnRules = undefined;
25
+ this.handleClick = (event) => {
26
+ if (this.href === '' || event.button !== 0 || event.defaultPrevented) {
27
+ return;
28
+ }
29
+ const path = event.composedPath();
30
+ const inner = path.slice(0, Math.max(0, path.indexOf(this)));
31
+ if (inner.some((node) => node instanceof Element && node.matches(PRESSABLE))) {
32
+ return;
33
+ }
34
+ if (globalThis.getSelection?.()?.toString()) {
35
+ return;
36
+ }
37
+ followLink(this.ownerDocument, { href: this.href, rel: this.rel, target: this.target }, event);
38
+ };
21
39
  this.handleSlotChange = () => {
22
40
  for (const cell of this.children) {
23
41
  if ('isTableCell' in cell) {
@@ -42,6 +60,15 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
42
60
  super.willUpdate(changed);
43
61
  hostAria(this.internals, { role: 'row' });
44
62
  this.toggleState('selected', this.selectable && this.selected);
63
+ this.toggleState('link', this.href !== '');
64
+ }
65
+ connectedCallback() {
66
+ super.connectedCallback();
67
+ this.addEventListener('click', this.handleClick);
68
+ }
69
+ disconnectedCallback() {
70
+ super.disconnectedCallback();
71
+ this.removeEventListener('click', this.handleClick);
45
72
  }
46
73
  renderSelect() {
47
74
  if (!this.selectable) {
@@ -64,6 +91,15 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
64
91
  return html `${this.renderSelect()}<slot @slotchange=${this.handleSlotChange}></slot>`;
65
92
  }
66
93
  };
94
+ __decorate([
95
+ property()
96
+ ], HelixHeadlessTableRow.prototype, "href", void 0);
97
+ __decorate([
98
+ property()
99
+ ], HelixHeadlessTableRow.prototype, "rel", void 0);
100
+ __decorate([
101
+ property()
102
+ ], HelixHeadlessTableRow.prototype, "target", void 0);
67
103
  __decorate([
68
104
  property()
69
105
  ], HelixHeadlessTableRow.prototype, "value", void 0);
@@ -21,9 +21,11 @@ declare const HelixHeadlessTabs_base: (new (...args: any[]) => import("@fusion.d
21
21
  * between tabs either way.
22
22
  *
23
23
  * A tab with an `href` is a link, for tabs that are pages of a site. Pressing
24
- * it follows the link, and the arrow keys move focus without choosing, so
25
- * arrowing past a tab never leaves the page. Mark the tab of the page you are
26
- * on with `selected`, or set `active` to its value.
24
+ * it follows the link. Where any tab is a link, the arrow keys move focus
25
+ * without choosing on every tab of the list, as under `manual`, so arrowing
26
+ * past a tab never leaves the page and the list answers the keys one way.
27
+ * Mark the tab of the page you are on with `selected`, or set `active` to its
28
+ * value.
27
29
  *
28
30
  * Each tab says which panel it controls through `aria-controls`, set as an
29
31
  * element reference to the panel's host, where the engine has reflection.
@@ -22,12 +22,14 @@ export type TagSelectionMode = SelectionMode;
22
22
  * that is removed leaves `value`, which then sends `change`.
23
23
  *
24
24
  * Name the set with `label`. Each tag is named by its words alone, without
25
- * its remove button.
25
+ * its remove button. `value` takes an array of values on the property as
26
+ * well, as React sets `value={['Apple']}`; a press writes it back as a
27
+ * string.
26
28
  *
27
29
  * **Removing the last tag keeps focus in the group.** With no tags left the
28
30
  * set is a `group`, and focus moves to its `empty` part,
29
31
  * which holds the `empty` slot: put words there such as "No filters" for a
30
- * reader to hear.
32
+ * reader to hear. With nothing slotted it says `empty-label`, "No tags".
31
33
  *
32
34
  * @since 0.1.0
33
35
  * @status stable
@@ -35,7 +37,7 @@ export type TagSelectionMode = SelectionMode;
35
37
  * @documentation https://fusion.dev/helix/components/tag
36
38
  *
37
39
  * @slot - The tags, in the order they are read.
38
- * @slot empty - Shown in place of the tags while there are none, such as "No filters".
40
+ * @slot empty - Shown in place of the tags while there are none, such as "No filters". Its fallback is `empty-label`.
39
41
  *
40
42
  * @event {Event} change - Fired once a press has changed which tags are selected.
41
43
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired before a press changes which tags are selected, with the `value` it would leave. Cancelable.
@@ -44,6 +46,11 @@ export type TagSelectionMode = SelectionMode;
44
46
  * @csspart empty - Holds the `empty` slot while the group has no tags, rendered only then. Takes focus when the last tag is removed.
45
47
  */
46
48
  export declare class HelixHeadlessTagGroup extends HelixElement {
49
+ /**
50
+ * The words the group shows and says once its last tag is removed, where
51
+ * nothing is in the `empty` slot.
52
+ */
53
+ emptyLabel: string;
47
54
  /**
48
55
  * The name of the set, such as "Filters", which a screen reader announces
49
56
  * before its tags.
@@ -62,7 +69,7 @@ export declare class HelixHeadlessTagGroup extends HelixElement {
62
69
  * Set it to start with tags selected. A tag's value is its `value`, or its
63
70
  * words where it has none.
64
71
  */
65
- value: string;
72
+ value: string | readonly string[];
66
73
  /** The tag the reader was last on, which keeps the tab stop. */
67
74
  private last;
68
75
  /**
@@ -72,6 +79,11 @@ export declare class HelixHeadlessTagGroup extends HelixElement {
72
79
  * a list of tags has a first and a last.
73
80
  */
74
81
  protected readonly keys: KeyboardNavigationController;
82
+ /**
83
+ * `value` as one written list, separated by commas, however it was set:
84
+ * an array of values is joined.
85
+ */
86
+ private get written();
75
87
  /** The tags the group holds, in order. */
76
88
  get tags(): HelixHeadlessTag[];
77
89
  /** The tags a key can land on, which are the ones that are not disabled. */