@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,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, deepActiveElement, HelixElement, HelixSelectEvent, } from '@fusion.dev/helix-core';
7
+ import { customElement, deepActiveElement, HelixElement, HelixSelectEvent, slotFilled, } from '@fusion.dev/helix-core';
8
8
  import { caretRight } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -16,24 +16,41 @@ let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixE
16
16
  this.focusable = false;
17
17
  this.isDrilldownItem = true;
18
18
  this.onPage = true;
19
+ this.onPath = false;
19
20
  this.showingPage = false;
20
21
  this.value = '';
21
22
  this.page = false;
23
+ this.description = false;
24
+ this.end = false;
22
25
  this.readPage = () => {
23
26
  const slot = this.renderRoot.querySelector('slot[name="page"]');
24
27
  this.page = (slot?.assignedElements().length ?? 0) > 0;
25
28
  };
29
+ this.readWords = () => {
30
+ const slot = (name) => this.renderRoot.querySelector(`slot[name="${name}"]`);
31
+ this.description = slotFilled(slot('description'));
32
+ this.end = slotFilled(slot('end'));
33
+ };
26
34
  }
27
35
  willUpdate(changed) {
28
36
  super.willUpdate(changed);
29
37
  if (this.childrenKnown) {
30
- this.page = this.knownChildren.some((child) => child.slot === 'page');
38
+ const named = (name) => this.knownChildren.some((child) => child.slot === name);
39
+ this.page = named('page');
40
+ this.description = named('description');
41
+ this.end = named('end');
31
42
  }
32
43
  }
33
44
  firstUpdated(changed) {
34
45
  super.firstUpdated(changed);
35
46
  this.finishHydration();
36
47
  }
48
+ get describedBy() {
49
+ const ids = [this.description ? 'description' : '', this.end ? 'end' : '']
50
+ .filter((id) => id !== '')
51
+ .join(' ');
52
+ return ids === '' ? nothing : ids;
53
+ }
37
54
  focusItem() {
38
55
  this.partNamed('item')?.focus();
39
56
  }
@@ -58,7 +75,7 @@ let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixE
58
75
  >`;
59
76
  }
60
77
  render() {
61
- if (this.showingPage) {
78
+ if (this.showingPage || this.onPath) {
62
79
  return html `<slot name="page" @slotchange=${this.readPage}></slot>`;
63
80
  }
64
81
  if (!this.onPage) {
@@ -68,14 +85,23 @@ let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixE
68
85
  part="item"
69
86
  type="button"
70
87
  role="menuitem"
88
+ aria-describedby=${this.describedBy}
71
89
  aria-disabled=${this.disabled ? 'true' : nothing}
72
90
  aria-haspopup=${this.page ? 'menu' : nothing}
73
91
  tabindex=${this.focusable ? '0' : '-1'}
92
+ ?data-described=${this.description}
74
93
  @click=${() => this.select()}
75
94
  >
76
95
  <slot name="start"></slot>
77
96
  <span part="label"><slot></slot></span>
78
- <slot name="end"></slot>
97
+ <slot
98
+ id="description"
99
+ name="description"
100
+ aria-hidden="true"
101
+ ?data-empty=${!this.description}
102
+ @slotchange=${this.readWords}
103
+ ></slot>
104
+ <slot id="end" name="end" aria-hidden="true" @slotchange=${this.readWords}></slot>
79
105
  ${this.page ? this.renderMark() : nothing}
80
106
  </button>
81
107
  <div hidden><slot name="page" @slotchange=${this.readPage}></slot></div>`;
@@ -90,6 +116,9 @@ __decorate([
90
116
  __decorate([
91
117
  property({ attribute: false })
92
118
  ], HelixHeadlessDrilldownItem.prototype, "onPage", void 0);
119
+ __decorate([
120
+ property({ attribute: false })
121
+ ], HelixHeadlessDrilldownItem.prototype, "onPath", void 0);
93
122
  __decorate([
94
123
  property({ attribute: false })
95
124
  ], HelixHeadlessDrilldownItem.prototype, "showingPage", void 0);
@@ -99,6 +128,12 @@ __decorate([
99
128
  __decorate([
100
129
  state()
101
130
  ], HelixHeadlessDrilldownItem.prototype, "page", void 0);
131
+ __decorate([
132
+ state()
133
+ ], HelixHeadlessDrilldownItem.prototype, "description", void 0);
134
+ __decorate([
135
+ state()
136
+ ], HelixHeadlessDrilldownItem.prototype, "end", void 0);
102
137
  HelixHeadlessDrilldownItem = __decorate([
103
138
  customElement('helix-headless-drilldown-item')
104
139
  ], HelixHeadlessDrilldownItem);
@@ -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, DrilldownController, HelixElement, inlineStep, KeyboardNavigationController, keyClaimed, MixinCollection, slottedWords, typeaheadBy, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, DrilldownController, HelixElement, inlineStep, KeyboardNavigationController, keyClaimed, MixinCollection, slottedWords, typeaheadByWords, } from '@fusion.dev/helix-core';
8
8
  import { caretLeft } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -38,7 +38,7 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
38
38
  })),
39
39
  reserved: () => ['ArrowLeft', 'ArrowRight'],
40
40
  selects: () => false,
41
- typeahead: typeaheadBy((item) => item.words),
41
+ typeahead: typeaheadByWords(),
42
42
  });
43
43
  this.handleFocusIn = (event) => {
44
44
  const item = this.drilldown.rows.find((row) => row.contains(event.target));
@@ -162,6 +162,7 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
162
162
  mark(item, page, onPage) {
163
163
  item.onPage = onPage;
164
164
  item.showingPage = item === page;
165
+ item.onPath = page !== null && item !== page && item.contains(page);
165
166
  for (const child of item.children) {
166
167
  if (child instanceof HelixHeadlessDrilldownItem && child.slot === 'page') {
167
168
  this.mark(child, page, item === page);
@@ -115,7 +115,7 @@ registerElementEvents({
115
115
  'helix-graph-signal',
116
116
  'helix-graph-zoom',
117
117
  ],
118
- 'helix-headless-heatmap': ['helix-resize'],
118
+ 'helix-headless-heatmap': ['change', 'helix-before-change', 'helix-resize'],
119
119
  'helix-headless-icon': ['error', 'load'],
120
120
  'helix-headless-icon-button': ['helix-press'],
121
121
  'helix-headless-image': ['error', 'load'],
@@ -166,6 +166,7 @@ registerElementEvents({
166
166
  'helix-headless-megamenu': ['helix-before-close', 'helix-before-open', 'toggle'],
167
167
  'helix-headless-menu': ['helix-before-close', 'helix-before-open', 'helix-select', 'toggle'],
168
168
  'helix-headless-menu-item': ['helix-before-toggle', 'helix-select'],
169
+ 'helix-headless-meter': ['helix-band'],
169
170
  'helix-headless-notification': ['helix-dismiss', 'helix-read'],
170
171
  'helix-headless-notification-group': ['helix-before-close', 'helix-before-open', 'toggle'],
171
172
  'helix-headless-notifications': ['helix-dismiss', 'helix-load-more', 'helix-read'],
@@ -186,6 +187,7 @@ registerElementEvents({
186
187
  'helix-headless-pagination-size': ['change', 'helix-before-change', 'input'],
187
188
  'helix-headless-pin-input': ['change', 'helix-before-change', 'helix-complete', 'input'],
188
189
  'helix-headless-popover': ['helix-before-close', 'helix-before-open', 'toggle'],
190
+ 'helix-headless-progress': ['helix-complete'],
189
191
  'helix-headless-qr-code': ['error'],
190
192
  'helix-headless-radio': ['helix-select'],
191
193
  'helix-headless-radio-group': ['change', 'helix-before-change', 'helix-select', 'input'],
@@ -220,6 +222,7 @@ registerElementEvents({
220
222
  'helix-before-resize',
221
223
  'helix-collapse',
222
224
  'helix-resized',
225
+ 'input',
223
226
  ],
224
227
  'helix-headless-step': ['helix-select'],
225
228
  'helix-headless-steps': ['helix-activate', 'helix-before-activate', 'helix-select'],
@@ -19,6 +19,11 @@ import { type TemplateResult } from 'lit';
19
19
  * `empty-state-actions` element: assign your own heading and actions to the
20
20
  * slots.
21
21
  *
22
+ * **Or give the heading as words.** `heading` draws them in a `heading` part
23
+ * that is a heading at `heading-level`, 3 where it is not set, so a page
24
+ * rendering from data or on a server gets a heading a reader finds in the
25
+ * outline without writing the element. A heading slotted in replaces it.
26
+ *
22
27
  * @since 0.1.0
23
28
  * @status stable
24
29
  * @category feedback
@@ -27,12 +32,27 @@ import { type TemplateResult } from 'lit';
27
32
  *
28
33
  * @slot - The message: why the region is empty, and what to do about it.
29
34
  * @slot actions - The action that would put something in the region.
30
- * @slot heading - The heading, which names what is missing. Slot a heading element, such as an `<h3>`, so a reader can find it in the page's outline.
35
+ * @slot heading - The heading, which names what is missing. Slot a heading element, such as an `<h3>`, so a reader can find it in the page's outline. Replaces the one `heading` draws.
31
36
  * @slot icon - An icon above the heading.
32
37
  *
38
+ * @csspart heading - The heading `heading` draws, a heading at `heading-level`. Drawn only while `heading` holds words and nothing is slotted into `heading`.
33
39
  * @csspart live - A copy of the heading and the message, in a live region that screen readers announce when the view appears. Visually hidden in the styled tier.
34
40
  */
35
41
  export declare class HelixHeadlessEmpty extends HelixElement {
42
+ /**
43
+ * The heading as words, such as "No results", drawn as a heading when
44
+ * nothing is slotted into `heading`.
45
+ *
46
+ * Absent draws none, and the `heading` slot is the only heading.
47
+ */
48
+ heading: string;
49
+ /**
50
+ * The level of the heading `heading` draws, from 1 to 6.
51
+ *
52
+ * Absent is 3, the level of a heading inside a page's section. Set it to fit
53
+ * the outline the empty view sits in. A slotted heading keeps its own.
54
+ */
55
+ headingLevel?: number;
36
56
  /**
37
57
  * Whether the state is announced when it appears.
38
58
  *
@@ -42,6 +62,16 @@ export declare class HelixHeadlessEmpty extends HelixElement {
42
62
  */
43
63
  noAnnounce: boolean;
44
64
  protected readonly arrival: ArrivalController;
65
+ /**
66
+ * The `heading` slot, with the heading `heading` draws as its fallback, so
67
+ * one slotted in replaces it; no fallback while `heading` holds no words.
68
+ *
69
+ * A method, so a tier above can draw its own, as the slot lets a page.
70
+ *
71
+ * @param hidden Whether the live copy holds the heading's words, which
72
+ * hides them from readers until it empties.
73
+ */
74
+ protected renderHeading(hidden: boolean): TemplateResult;
45
75
  protected render(): TemplateResult;
46
76
  }
47
77
  declare global {
@@ -4,30 +4,51 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { ArrivalController, customElement, HelixElement, slottedWords, } from '@fusion.dev/helix-core';
7
+ import { ArrivalController, customElement, HelixElement, headingLevel, slottedWords, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessEmpty = class HelixHeadlessEmpty extends HelixElement {
11
11
  constructor() {
12
12
  super(...arguments);
13
+ this.heading = '';
13
14
  this.noAnnounce = false;
14
15
  this.arrival = new ArrivalController(this, {
15
16
  arriving: () => !this.serverRendered && document.readyState === 'complete',
16
- text: () => slottedWords(this, ['heading', '']),
17
+ text: () => [slottedWords(this, ['heading']) || this.heading.trim(), slottedWords(this, [''])]
18
+ .filter(Boolean)
19
+ .join(' '),
17
20
  });
18
21
  }
22
+ renderHeading(hidden) {
23
+ return html `<slot
24
+ aria-hidden=${hidden ? 'true' : nothing}
25
+ name="heading"
26
+ @slotchange=${() => this.arrival.say()}
27
+ >${this.heading.trim()
28
+ ? html `<div aria-level=${headingLevel(this.headingLevel)} part="heading" role="heading">
29
+ ${this.heading}
30
+ </div>`
31
+ : nothing}</slot
32
+ >`;
33
+ }
19
34
  render() {
20
35
  const say = () => this.arrival.say();
21
- const copied = this.arrival.said !== '' && !this.noAnnounce ? 'true' : nothing;
36
+ const held = this.arrival.said !== '' && !this.noAnnounce;
22
37
  return html `<slot name="icon"></slot>
23
- <slot aria-hidden=${copied} name="heading" @slotchange=${say}></slot>
24
- <slot aria-hidden=${copied} @slotchange=${say}></slot>
38
+ ${this.renderHeading(held)}
39
+ <slot aria-hidden=${held ? 'true' : nothing} @slotchange=${say}></slot>
25
40
  <slot name="actions"></slot>
26
41
  ${this.noAnnounce
27
42
  ? nothing
28
43
  : html `<span part="live" role="status">${this.arrival.said}</span>`}`;
29
44
  }
30
45
  };
46
+ __decorate([
47
+ property()
48
+ ], HelixHeadlessEmpty.prototype, "heading", void 0);
49
+ __decorate([
50
+ property({ attribute: 'heading-level', type: Number })
51
+ ], HelixHeadlessEmpty.prototype, "headingLevel", void 0);
31
52
  __decorate([
32
53
  property({ attribute: 'no-announce', reflect: true, type: Boolean })
33
54
  ], HelixHeadlessEmpty.prototype, "noAnnounce", void 0);
@@ -1,5 +1,7 @@
1
1
  import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
2
- import { type PropertyValues, type TemplateResult } from 'lit';
2
+ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
+ import { type FileStatus } from '../status.js';
4
+ export type { FileStatus } from '../status.js';
3
5
  import '../../tag/index.js';
4
6
  declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
5
7
  /**
@@ -59,6 +61,9 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
59
61
  * @csspart feedback - The message that says what is wrong.
60
62
  * @csspart field - The box you press or drop files on.
61
63
  * @csspart file - One chosen file.
64
+ * @csspart file-done - A file whose upload `setFileStatus` says is done. Set together with `file`.
65
+ * @csspart file-error - A file whose upload `setFileStatus` says failed. Set together with `file`.
66
+ * @csspart file-uploading - A file whose upload `setFileStatus` says is under way. Set together with `file`.
62
67
  * @csspart hint - Help under the field, while nothing is wrong.
63
68
  * @csspart kind - A file's type.
64
69
  * @csspart kind-cell - The grid cell around the tag, rendered only when the tag sits in a `Tag.Group`.
@@ -72,6 +77,7 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
72
77
  * @csspart live - The live region that says `added-text`, `rejected-text`, and `removed-text`. It is rendered from the first time it has something to say.
73
78
  * @csspart meta - The row that holds a file's size and type.
74
79
  * @csspart name - A file's name.
80
+ * @csspart progress - A file's upload bar, a `<progress>`, while `setFileStatus` gives it a measure or says it is under way.
75
81
  * @csspart remove - The control that removes a file.
76
82
  * @csspart size - A file's size.
77
83
  * @csspart size-cell - The grid cell around the tag, rendered only when the tag sits in a `Tag.Group`.
@@ -80,6 +86,8 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
80
86
  * @csspart size-label - The label, trimmed to its capitals and baseline.
81
87
  * @csspart size-link - The link around the label, when the tag has an `href`.
82
88
  * @csspart size-start - The category before the label.
89
+ * @csspart status - How a file's upload is going, from `setFileStatus`: the bar and the words.
90
+ * @csspart status-text - The words `setFileStatus` gives a file, such as the reason it failed.
83
91
  * @csspart trigger - The label that opens the picker.
84
92
  *
85
93
  * @cssstate dropping - Files are being dragged over the field.
@@ -128,6 +136,11 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
128
136
  * Whether more than one file can be chosen.
129
137
  */
130
138
  multiple: boolean;
139
+ /**
140
+ * The name of a file's progress bar, read by screen readers, with `{name}`
141
+ * filled in. A label with no `{name}` has it added after it.
142
+ */
143
+ progressLabel: string;
131
144
  /**
132
145
  * What a reader hears when files are refused, with `{count}` and `{names}`
133
146
  * filled in.
@@ -170,6 +183,19 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
170
183
  validate?: (file: File) => string | null | undefined;
171
184
  /** The live region, rendered from the first time it has something to say. */
172
185
  private readonly status;
186
+ /** What the page has said of each file's upload, by file. */
187
+ private readonly statuses;
188
+ /**
189
+ * Shows how a file's upload is going, in its row: a bar for `progress`,
190
+ * from 0 to 1, or a bar without a measure while `state` is `uploading`, and
191
+ * `text` beside it. `state` names the row `file-uploading`, `file-done` or
192
+ * `file-error` beside `file`, and a reader hears `text` when the state
193
+ * becomes `done` or `error`. `null` clears it.
194
+ *
195
+ * The field uploads nothing, as Zag's file upload does not: your page sends
196
+ * the file and says here how it is going.
197
+ */
198
+ setFileStatus(file: File, status: FileStatus | null): void;
173
199
  /**
174
200
  * Screens arriving files and commits what is taken, after the files the
175
201
  * field keeps, through `transformFiles` where it is set.
@@ -193,10 +219,8 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
193
219
  * count is written as the language writes a number.
194
220
  */
195
221
  private listed;
196
- /** The element slotted into `remove-icon`, which each remove button copies. */
197
- private removeIcon;
198
- /** One copy of `removeIcon` per row, kept so a render does not replace it. */
199
- private readonly removeIcons;
222
+ /** The element slotted into `remove-icon`, one copy per row's remove button. */
223
+ private readonly removeIcon;
200
224
  private readonly handleRemoveIcon;
201
225
  /** The icon on one row's remove button: a copy of `remove-icon`, or the bin. */
202
226
  private removeMark;
@@ -342,6 +366,11 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
342
366
  * renders the styled one from the same template.
343
367
  */
344
368
  protected renderMeta(file: File): TemplateResult;
369
+ /**
370
+ * How a file's upload is going, from `setFileStatus`, behind a seam a tier
371
+ * can move: a `<progress>` named by `progress-label` and the status words.
372
+ */
373
+ protected renderFileStatus(file: File): TemplateResult | typeof nothing;
345
374
  protected render(): TemplateResult;
346
375
  }
347
376
  declare global {
@@ -349,4 +378,3 @@ declare global {
349
378
  'helix-headless-file-input': HelixHeadlessFileInput;
350
379
  }
351
380
  }
352
- export {};
@@ -4,12 +4,13 @@ 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, drawStatus, fillTemplate, formatNumbers, HelixBeforeChangeEvent, HelixFormElement, HelixRejectEvent, MixinField, requestChange, restoreValue, StatusController, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, drawIcon, drawStatus, fillTemplate, formatNumbers, HelixBeforeChangeEvent, HelixFormElement, HelixRejectEvent, MixinField, requestChange, restoreValue, SlottedGlyph, StatusController, } from '@fusion.dev/helix-core';
8
8
  import { trash } from '@fusion.dev/helix-icons';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { html } from 'lit/static-html.js';
12
12
  import { screenFiles } from '../screen.js';
13
+ import { barOf, saysStatus } from '../status.js';
13
14
  import '../../tag/index.js';
14
15
  const named = (label, name) => label.includes('{name}') ? fillTemplate(label, { name }) : `${label} ${name}`;
15
16
  const size = (bytes, locale) => {
@@ -46,16 +47,17 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
46
47
  this.directory = false;
47
48
  this.locale = '';
48
49
  this.multiple = false;
50
+ this.progressLabel = 'Uploading {name}';
49
51
  this.rejectedText = 'Not added: {names}';
50
52
  this.removedText = 'Removed: {name}';
51
53
  this.removeLabel = 'Remove {name}';
52
54
  this.triggerLabel = 'Choose files';
53
55
  this.status = new StatusController(this);
54
- this.removeIcon = undefined;
55
- this.removeIcons = new Map();
56
+ this.statuses = new WeakMap();
57
+ this.removeIcon = new SlottedGlyph();
56
58
  this.handleRemoveIcon = (event) => {
57
- this.removeIcons.clear();
58
- this.removeIcon = event.target.assignedElements({ flatten: true })[0];
59
+ this.removeIcon.read(event.target);
60
+ this.requestUpdate();
59
61
  };
60
62
  this.handlePaste = (event) => {
61
63
  const pasted = [...(event.clipboardData?.files ?? [])];
@@ -106,6 +108,19 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
106
108
  });
107
109
  };
108
110
  }
111
+ setFileStatus(file, status) {
112
+ const was = this.statuses.get(file);
113
+ if (status) {
114
+ this.statuses.set(file, status);
115
+ if (saysStatus(status, was)) {
116
+ this.status.say(status.text ?? '');
117
+ }
118
+ }
119
+ else {
120
+ this.statuses.delete(file);
121
+ }
122
+ this.requestUpdate();
123
+ }
109
124
  take(arriving, keeping, picked = false) {
110
125
  const { kept, refused } = screenFiles(arriving, keeping, {
111
126
  accept: this.accept,
@@ -152,12 +167,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
152
167
  });
153
168
  }
154
169
  removeMark(at) {
155
- if (!this.removeIcon) {
156
- return drawIcon(trash);
157
- }
158
- const held = this.removeIcons.get(at) ?? this.removeIcon.cloneNode(true);
159
- this.removeIcons.set(at, held);
160
- return held;
170
+ return this.removeIcon.copyFor(at) ?? drawIcon(trash);
161
171
  }
162
172
  get files() {
163
173
  return [...(this.control?.files ?? [])];
@@ -258,6 +268,23 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
258
268
  part="kind"
259
269
  exportparts="cell: kind-cell, dismiss: kind-dismiss, dismiss-mark: kind-dismiss-mark, label: kind-label, link: kind-link, start: kind-start">${kind(file)}</${this.tag('tag')}>`;
260
270
  }
271
+ renderFileStatus(file) {
272
+ const status = this.statuses.get(file);
273
+ if (!status) {
274
+ return nothing;
275
+ }
276
+ const bar = barOf(status);
277
+ return html `<span part="status"
278
+ >${bar === undefined
279
+ ? nothing
280
+ : html `<progress
281
+ part="progress"
282
+ max="1"
283
+ value=${bar === 'unmeasured' ? nothing : bar}
284
+ aria-label=${named(this.progressLabel, file.name)}
285
+ ></progress>`}${status.text ? html `<span part="status-text">${status.text}</span>` : nothing}</span
286
+ >`;
287
+ }
261
288
  render() {
262
289
  const files = this.files;
263
290
  return html `${this.renderLabel()}
@@ -300,14 +327,21 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
300
327
  ${files.length === 0
301
328
  ? nothing
302
329
  : html `<ul part="list">
303
- ${files.map((file, at) => html `<li part="file">
330
+ ${files.map((file, at) => {
331
+ const state = this.statuses.get(file)?.state;
332
+ const done = state === 'done';
333
+ const failed = state === 'error';
334
+ const sending = state === 'uploading';
335
+ return html `<li
336
+ part="file${done ? ' file-done' : ''}${failed ? ' file-error' : ''}${sending ? ' file-uploading' : ''}"
337
+ >
304
338
  <span part="detail">
305
339
  ${this.renderFile
306
- ? this.renderFile(file, at)
307
- : html `<span part="name">${file.name}</span>
340
+ ? this.renderFile(file, at)
341
+ : html `<span part="name">${file.name}</span>
308
342
  <span part="meta">
309
343
  ${this.renderMeta(file)}
310
- </span>`}
344
+ </span>`}${this.renderFileStatus(file)}
311
345
  </span>
312
346
  <button
313
347
  part="remove"
@@ -319,7 +353,8 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
319
353
  >
320
354
  ${this.removeMark(at)}
321
355
  </button>
322
- </li>`)}
356
+ </li>`;
357
+ })}
323
358
  </ul>`}`;
324
359
  }
325
360
  };
@@ -347,6 +382,9 @@ __decorate([
347
382
  __decorate([
348
383
  property({ reflect: true, type: Boolean })
349
384
  ], HelixHeadlessFileInput.prototype, "multiple", void 0);
385
+ __decorate([
386
+ property({ attribute: 'progress-label' })
387
+ ], HelixHeadlessFileInput.prototype, "progressLabel", void 0);
350
388
  __decorate([
351
389
  property({ attribute: 'rejected-text' })
352
390
  ], HelixHeadlessFileInput.prototype, "rejectedText", void 0);
@@ -368,9 +406,6 @@ __decorate([
368
406
  __decorate([
369
407
  property({ attribute: false })
370
408
  ], HelixHeadlessFileInput.prototype, "validate", void 0);
371
- __decorate([
372
- state()
373
- ], HelixHeadlessFileInput.prototype, "removeIcon", void 0);
374
409
  __decorate([
375
410
  state()
376
411
  ], HelixHeadlessFileInput.prototype, "dropping", void 0);
@@ -0,0 +1,24 @@
1
+ /**
2
+ * How one file's upload is going, as a page reports it to File Input. The
3
+ * field does not upload anything: the page does, and says how it is going.
4
+ */
5
+ export interface FileStatus {
6
+ /** How much is done, from 0 to 1. Left out, the bar moves without a measure while `state` is `uploading`. */
7
+ readonly progress?: number | undefined;
8
+ /** Where the upload is: under way, finished, or failed. */
9
+ readonly state?: 'done' | 'error' | 'uploading' | undefined;
10
+ /** Words beside the bar, such as "Uploaded" or the reason it failed. */
11
+ readonly text?: string | undefined;
12
+ }
13
+ /**
14
+ * The bar's value, held between 0 and 1, or nothing for a bar without a
15
+ * measure, and no bar at all for a status that is neither measured nor under
16
+ * way.
17
+ */
18
+ export declare const barOf: (status: FileStatus) => number | 'unmeasured' | undefined;
19
+ /**
20
+ * Whether a new status is worth saying: a finish or a failure with words, the
21
+ * first time the file reaches it. Progress is shown and not said, since a
22
+ * reader hearing every percent hears nothing else.
23
+ */
24
+ export declare const saysStatus: (next: FileStatus, was: FileStatus | undefined) => boolean;
@@ -0,0 +1,10 @@
1
+ export const barOf = (status) => {
2
+ const { progress, state } = status;
3
+ if (typeof progress === 'number' && Number.isFinite(progress)) {
4
+ return Math.min(1, Math.max(0, progress));
5
+ }
6
+ return state === 'uploading' ? 'unmeasured' : undefined;
7
+ };
8
+ export const saysStatus = (next, was) => Boolean(next.text) &&
9
+ (next.state === 'done' || next.state === 'error') &&
10
+ next.state !== was?.state;
@@ -27,6 +27,15 @@ declare const HelixHeadlessFilterChip_base: (new (...args: any[]) => import("@fu
27
27
  * dismissal, which is also how you take control of the interaction for one
28
28
  * press. `requestChange` documents the pattern.
29
29
  *
30
+ * **Keyboard.** The remove button is a tab stop of its own, and Enter or Space
31
+ * on it asks to remove the filter. In a `FilterInput`, Backspace in the empty
32
+ * field asks to remove the last chip.
33
+ *
34
+ * The remove button is named `dismiss-label` followed by the chip's words,
35
+ * such as "Remove filter Status is not open", so a row of chips says which
36
+ * filter each button removes. Write `{label}` in `dismiss-label` to put the
37
+ * words elsewhere.
38
+ *
30
39
  * To let the user change the operator, slot a menu trigger into `operator`.
31
40
  * With the text fallback, the operator is a label. The chip does not interpret
32
41
  * operators: their meaning belongs to your domain, as `FilterOperator`
@@ -100,6 +109,20 @@ export declare class HelixHeadlessFilterChip extends HelixHeadlessFilterChip_bas
100
109
  * makes an unusual filter look unusual.
101
110
  */
102
111
  operatorLabel: string;
112
+ /**
113
+ * The remove button's name: `dismissLabel` with the chip's words, its field,
114
+ * its comparison and its values, such as "Remove filter Status is not open".
115
+ *
116
+ * **Named for its chip**, as File Input names each remove button for its
117
+ * file: a row of buttons each called "Remove filter" says nothing about
118
+ * which filter each one takes away. `{label}` in `dismiss-label` says where
119
+ * the words go, and without it they follow the label.
120
+ *
121
+ * Each word is read from what is slotted and otherwise from `label` and
122
+ * `operator-label`, which is all a server and a hydrating render can read,
123
+ * so the values join the update after.
124
+ */
125
+ private get removeName();
103
126
  protected firstUpdated(changed: PropertyValues): void;
104
127
  protected render(): TemplateResult;
105
128
  }
@@ -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, MixinDismissible } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, MixinDismissible, slottedWords } from '@fusion.dev/helix-core';
8
8
  import { x } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
@@ -16,8 +16,17 @@ let HelixHeadlessFilterChip = class HelixHeadlessFilterChip extends MixinDismiss
16
16
  this.label = '';
17
17
  this.operatorLabel = '';
18
18
  }
19
+ get removeName() {
20
+ const read = (slot, fallback) => (this.childrenKnown && slottedWords(this, [slot])) || fallback;
21
+ const words = [read('field', this.label), read('operator', this.operatorLabel), read('', '')]
22
+ .filter(Boolean)
23
+ .join(' ');
24
+ const label = this.dismissLabel;
25
+ return label.includes('{label}') ? label.replace('{label}', words) : `${label} ${words}`.trim();
26
+ }
19
27
  firstUpdated(changed) {
20
28
  super.firstUpdated(changed);
29
+ this.renderRoot.addEventListener('slotchange', () => this.requestUpdate());
21
30
  this.finishHydration();
22
31
  }
23
32
  render() {
@@ -33,7 +42,7 @@ let HelixHeadlessFilterChip = class HelixHeadlessFilterChip extends MixinDismiss
33
42
  ? html `<button
34
43
  part="dismiss"
35
44
  type="button"
36
- aria-label=${this.dismissLabel}
45
+ aria-label=${this.removeName}
37
46
  @click=${() => this.dismiss()}
38
47
  >
39
48
  <svg part="dismiss-mark" fill="currentColor" viewBox=${x.viewBox} aria-hidden="true">