@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,11 +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 { commaList, customElement, HelixBeforeChangeEvent, HelixElement, hostAria, KeyboardNavigationController, requestChange, rovingStop, slottedWords, whenHydrated, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
7
+ import { commaList, customElement, deepActiveElement, deepContains, HelixBeforeChangeEvent, HelixElement, hostAria, KeyboardNavigationController, pressedValue, prunedValue, requestChange, rovingStop, slottedWords, typeaheadBy, whenHydrated, } from '@fusion.dev/helix-core';
8
+ import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessTag } from '../root/tag-root.js';
11
- import { pressedValue } from '../selection.js';
12
11
  let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
13
12
  constructor() {
14
13
  super(...arguments);
@@ -18,7 +17,7 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
18
17
  items: () => this.reachable.map((tag) => this.navigable(tag)),
19
18
  origin: 'focus',
20
19
  selects: () => false,
21
- typeahead: (item) => item.words,
20
+ typeahead: typeaheadBy((item) => item.words),
22
21
  wrap: false,
23
22
  });
24
23
  this.handleKeydown = (event) => {
@@ -53,6 +52,37 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
53
52
  this.press(tag);
54
53
  }
55
54
  };
55
+ this.handleDismiss = (event) => {
56
+ const tag = this.tagOf(event);
57
+ if (!tag) {
58
+ return;
59
+ }
60
+ const had = deepContains(this, deepActiveElement());
61
+ const settle = async () => {
62
+ if (event.defaultPrevented || tag.isConnected) {
63
+ return;
64
+ }
65
+ const left = this.tags.map((one) => this.valueFor(one));
66
+ const pruned = prunedValue(this.value, this.valueFor(tag), left);
67
+ if (pruned !== this.value) {
68
+ this.value = pruned;
69
+ this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
70
+ }
71
+ const active = document.activeElement;
72
+ if (!had || this.tags.length > 0 || (active !== null && active !== document.body)) {
73
+ return;
74
+ }
75
+ this.requestUpdate();
76
+ await this.updateComplete;
77
+ this.renderRoot.querySelector('[part~="empty"]')?.focus();
78
+ };
79
+ queueMicrotask(() => void settle());
80
+ requestAnimationFrame(() => void settle());
81
+ };
82
+ this.handleSlotchange = () => {
83
+ this.sync();
84
+ this.requestUpdate();
85
+ };
56
86
  this.handleFocusin = (event) => {
57
87
  const tag = this.tagOf(event);
58
88
  if (tag && tag !== this.last) {
@@ -122,24 +152,33 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
122
152
  this.addEventListener('keydown', this.handleKeydown);
123
153
  this.addEventListener('click', this.handleClick);
124
154
  this.addEventListener('focusin', this.handleFocusin);
155
+ this.addEventListener('helix-dismiss', this.handleDismiss);
125
156
  }
126
157
  disconnectedCallback() {
127
158
  super.disconnectedCallback();
128
159
  this.removeEventListener('keydown', this.handleKeydown);
129
160
  this.removeEventListener('click', this.handleClick);
130
161
  this.removeEventListener('focusin', this.handleFocusin);
162
+ this.removeEventListener('helix-dismiss', this.handleDismiss);
163
+ }
164
+ get empty() {
165
+ return this.childrenKnown && this.tags.length === 0;
131
166
  }
132
167
  willUpdate(changed) {
133
168
  super.willUpdate(changed);
134
- hostAria(this.internals, { label: this.label, role: 'grid' });
135
- this.internals.ariaMultiSelectable = this.selectionMode === 'multiple' ? 'true' : null;
169
+ const empty = this.empty;
170
+ hostAria(this.internals, { label: this.label, role: empty ? 'group' : 'grid' });
171
+ this.internals.ariaMultiSelectable = this.selectionMode === 'multiple' && !empty ? 'true' : null;
136
172
  }
137
173
  updated(changed) {
138
174
  super.updated(changed);
139
175
  this.sync();
140
176
  }
141
177
  render() {
142
- return html `<slot @slotchange=${() => this.sync()}></slot>`;
178
+ const empty = this.empty
179
+ ? html `<div part="empty" tabindex="-1"><slot name="empty"></slot></div>`
180
+ : nothing;
181
+ return html `<slot @slotchange=${this.handleSlotchange}></slot>${empty}`;
143
182
  }
144
183
  };
145
184
  __decorate([
@@ -36,7 +36,10 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
36
36
  }
37
37
  willUpdate(changed) {
38
38
  super.willUpdate(changed);
39
- hostAria(this.internals, { role: this.grouped ? 'row' : '' });
39
+ hostAria(this.internals, {
40
+ label: this.grouped ? this.words : '',
41
+ role: this.grouped ? 'row' : '',
42
+ });
40
43
  this.internals.ariaSelected = this.selected === undefined ? null : String(this.selected);
41
44
  this.internals.ariaDisabled = this.disabled ? 'true' : null;
42
45
  this.toggleState('selected', this.selected === true);
@@ -58,7 +61,11 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
58
61
  render() {
59
62
  const content = html `<slot name="start" part="start"></slot>${this.renderLabel()}${this.dismissible ? this.renderDismiss() : nothing}`;
60
63
  return this.grouped
61
- ? html `<span part="cell" role="gridcell" tabindex=${this.focusable ? '0' : '-1'}
64
+ ? html `<span
65
+ aria-label=${this.words || nothing}
66
+ part="cell"
67
+ role="gridcell"
68
+ tabindex=${this.focusable ? '0' : '-1'}
62
69
  >${content}</span
63
70
  >`
64
71
  : content;
@@ -17,6 +17,11 @@ declare const HelixHeadlessTextarea_base: (new (...args: any[]) => import("@fusi
17
17
  * `max-rows` stops the growth at that many lines, and the field scrolls past
18
18
  * them.
19
19
  *
20
+ * `clearable` draws a button that empties the field while it holds text, and
21
+ * sends `helix-clear`, as `Input` does. `validate` adds your own rule, run on
22
+ * every change once the browser finds nothing wrong, as React Aria's
23
+ * TextField `validate` does.
24
+ *
20
25
  * @since 0.1.0
21
26
  * @status stable
22
27
  * @category forms
@@ -31,15 +36,18 @@ declare const HelixHeadlessTextarea_base: (new (...args: any[]) => import("@fusi
31
36
  *
32
37
  * @event {Event} change - Sent from the textarea when the user finishes an edit.
33
38
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired as the text changes, and cancelable.
39
+ * @event {HelixClearEvent} helix-clear - Fired after the clear button has emptied the field, after `input` and `change`.
34
40
  * @event {Event} input - Fired after the value changes.
35
41
  *
42
+ * @csspart clear - The button that empties the field, under `clearable`.
36
43
  * @csspart control - The textarea itself.
37
44
  * @csspart feedback - The message that says what is wrong.
38
45
  * @csspart field - The box that holds the textarea.
39
46
  * @csspart hint - Help under the field, while nothing is wrong.
40
47
  * @csspart label - The label above the field.
41
48
  *
42
- * @cssstate invalid - The control has an error to show, from the browser's validation or from your `feedback`.
49
+ * @cssstate blank - The field holds no text.
50
+ * @cssstate invalid - The control has an error to show, from the browser's validation, `validate`, or your `feedback`.
43
51
  */
44
52
  export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
45
53
  protected readonly aria: AriaForwardController;
@@ -57,6 +65,14 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
57
65
  * Grows the box to fit its content, so it does not scroll.
58
66
  */
59
67
  autoGrow: boolean;
68
+ /**
69
+ * Draws a button that empties the field while it holds text.
70
+ */
71
+ clearable: boolean;
72
+ /**
73
+ * The accessible name of the clear button, which shows only an icon.
74
+ */
75
+ clearLabel: string;
60
76
  /**
61
77
  * How many characters wide the box is, when the layout does not set its
62
78
  * width.
@@ -194,6 +210,11 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
194
210
  focus(options?: FocusOptions): void;
195
211
  /** Reports the value and the validity the host stands behind. */
196
212
  private sync;
213
+ /**
214
+ * Empties the field and puts the caret back in it, through `requestValue`,
215
+ * so a consumer who owns the value can refuse. `Input` clears the same way.
216
+ */
217
+ private readonly handleClear;
197
218
  connectedCallback(): void;
198
219
  disconnectedCallback(): void;
199
220
  protected firstUpdated(changed: PropertyValues): void;
@@ -212,6 +233,12 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
212
233
  * How many lines the box is before anything is typed.
213
234
  */
214
235
  rows: number;
236
+ /**
237
+ * Your own rule for the value, run on every change once the browser finds
238
+ * nothing wrong. Return a message, or a list of them, to mark the field
239
+ * invalid, and `true`, `null`, or nothing to accept the value.
240
+ */
241
+ validate?: (value: string) => string | string[] | true | null | undefined;
215
242
  /**
216
243
  * How the browser wraps the text, and whether the breaks are submitted with
217
244
  * it.
@@ -4,7 +4,8 @@ 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, HelixFormElement, holdCaret, isRtl, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, drawIcon, HelixClearEvent, HelixFormElement, holdCaret, isRtl, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
8
+ import { x } from '@fusion.dev/helix-icons';
8
9
  import { html, nothing } from 'lit';
9
10
  import { property } from 'lit/decorators.js';
10
11
  import { grownHeight, lineMetrics } from '../grow.js';
@@ -17,6 +18,8 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
17
18
  this.announcesInput = true;
18
19
  this.autocomplete = '';
19
20
  this.autoGrow = false;
21
+ this.clearable = false;
22
+ this.clearLabel = 'Clear';
20
23
  this.dirName = '';
21
24
  this.placeholder = '';
22
25
  this.readOnly = false;
@@ -32,6 +35,17 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
32
35
  event.stopPropagation();
33
36
  this.announceChange();
34
37
  };
38
+ this.handleClear = () => {
39
+ if (this.value !== '' && this.requestValue('')) {
40
+ this.announceChange();
41
+ this.dispatchEvent(new HelixClearEvent());
42
+ }
43
+ const control = this.control;
44
+ if (control) {
45
+ restoreValue(control, this.value);
46
+ control.focus();
47
+ }
48
+ };
35
49
  this.rows = 3;
36
50
  this.wrap = 'soft';
37
51
  }
@@ -112,6 +126,11 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
112
126
  return;
113
127
  }
114
128
  this.copyValidity(control);
129
+ const own = control.validity.valid ? this.validate?.(this.value) : undefined;
130
+ const message = Array.isArray(own) ? own.join(' ') : typeof own === 'string' ? own : '';
131
+ if (message !== '') {
132
+ this.internals.setValidity({ customError: true }, message, control);
133
+ }
115
134
  this.refreshFeedback();
116
135
  }
117
136
  connectedCallback() {
@@ -140,6 +159,7 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
140
159
  }
141
160
  willUpdate(changed) {
142
161
  super.willUpdate(changed);
162
+ this.toggleState('blank', this.value === '');
143
163
  if (!this.hasUpdated) {
144
164
  return;
145
165
  }
@@ -180,6 +200,16 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
180
200
  @change=${this.handleChange}
181
201
  ></textarea>
182
202
  <slot name="end"></slot>
203
+ ${this.clearable && this.value !== '' && !this.isDisabled && !this.readOnly
204
+ ? html `<button
205
+ part="clear"
206
+ type="button"
207
+ aria-label=${this.clearLabel}
208
+ @click=${this.handleClear}
209
+ >
210
+ ${drawIcon(x)}
211
+ </button>`
212
+ : nothing}
183
213
  </div>
184
214
  ${this.renderHint()}${this.renderFeedback()}`;
185
215
  }
@@ -190,6 +220,12 @@ __decorate([
190
220
  __decorate([
191
221
  property({ attribute: 'auto-grow', reflect: true, type: Boolean })
192
222
  ], HelixHeadlessTextarea.prototype, "autoGrow", void 0);
223
+ __decorate([
224
+ property({ type: Boolean })
225
+ ], HelixHeadlessTextarea.prototype, "clearable", void 0);
226
+ __decorate([
227
+ property({ attribute: 'clear-label' })
228
+ ], HelixHeadlessTextarea.prototype, "clearLabel", void 0);
193
229
  __decorate([
194
230
  property({ type: Number })
195
231
  ], HelixHeadlessTextarea.prototype, "cols", void 0);
@@ -214,6 +250,9 @@ __decorate([
214
250
  __decorate([
215
251
  property({ type: Number })
216
252
  ], HelixHeadlessTextarea.prototype, "rows", void 0);
253
+ __decorate([
254
+ property({ attribute: false })
255
+ ], HelixHeadlessTextarea.prototype, "validate", void 0);
217
256
  __decorate([
218
257
  property()
219
258
  ], HelixHeadlessTextarea.prototype, "wrap", void 0);
@@ -42,6 +42,8 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
42
42
  *
43
43
  * @slot feedback - The message that says what is wrong with the value.
44
44
  * @slot hint - Help under the field.
45
+ * @slot panel-footer - Content in the panel under the times, such as a note or an action.
46
+ * @slot panel-header - Content in the panel above the times, such as a heading or a shortcut.
45
47
  * @slot trigger - The icon on the field that shows a list opens.
46
48
  *
47
49
  * @event {Event} change - Fired after the value changes.
@@ -60,6 +62,7 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
60
62
  * @csspart picker - The time picker inside the panel.
61
63
  * @csspart picker-column - One column of the picker.
62
64
  * @csspart picker-column-empty - The text shown when the filter hides every option.
65
+ * @csspart picker-column-error - A wheel's `error` part, which the list never sets: its times do not load.
63
66
  * @csspart picker-column-feedback - The column's message that says what is wrong with the value.
64
67
  * @csspart picker-column-filter-icon - The icon beside the filter field.
65
68
  * @csspart picker-column-filter-mark - The default icon beside the filter field, shown when none is slotted.
@@ -73,6 +76,7 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
73
76
  * @csspart picker-columns - The row of columns, in `wheels`.
74
77
  * @csspart picker-list - The listbox holding the times, in `list`.
75
78
  * @csspart picker-list-empty - The text shown when the filter hides every option.
79
+ * @csspart picker-list-error - The list's `error` part, which the list never sets: its times do not load.
76
80
  * @csspart picker-list-feedback - The inner list's message that says what is wrong with the value.
77
81
  * @csspart picker-list-filter-icon - The icon beside the filter field.
78
82
  * @csspart picker-list-filter-mark - The default icon beside the filter field, shown when none is slotted.
@@ -272,7 +276,7 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
272
276
  /** Each time, where `mode` holds several, sent one entry each. */
273
277
  protected get answers(): readonly string[] | undefined;
274
278
  /** What is wrong with the times, which the text input cannot say. */
275
- protected validate(): string;
279
+ protected validate(): string | readonly [keyof ValidityStateFlags, string];
276
280
  /**
277
281
  * Whether the panel shows one list of times or a column for each part of the
278
282
  * time.
@@ -152,14 +152,20 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
152
152
  const rules = { disableTimes: this.disableTimes, max: this.max, min: this.min };
153
153
  const faults = held.map((one) => timeFault(one, rules));
154
154
  if (faults.includes('rangeUnderflow')) {
155
- return fillTemplate(this.rangeUnderflowMessage || 'Choose a time at or after {min}.', {
156
- min: writeClock(this.min, clockLocale(this.locale, this.hourCycle), this.seconds),
157
- });
155
+ return [
156
+ 'rangeUnderflow',
157
+ fillTemplate(this.rangeUnderflowMessage || 'Choose a time at or after {min}.', {
158
+ min: writeClock(this.min, clockLocale(this.locale, this.hourCycle), this.seconds),
159
+ }),
160
+ ];
158
161
  }
159
162
  if (faults.includes('rangeOverflow')) {
160
- return fillTemplate(this.rangeOverflowMessage || 'Choose a time at or before {max}.', {
161
- max: writeClock(this.max, clockLocale(this.locale, this.hourCycle), this.seconds),
162
- });
163
+ return [
164
+ 'rangeOverflow',
165
+ fillTemplate(this.rangeOverflowMessage || 'Choose a time at or before {max}.', {
166
+ max: writeClock(this.max, clockLocale(this.locale, this.hourCycle), this.seconds),
167
+ }),
168
+ ];
163
169
  }
164
170
  if (faults.includes('unavailable')) {
165
171
  return this.unavailableMessage || 'Choose a time that is available.';
@@ -178,13 +184,13 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
178
184
  renderPicker() {
179
185
  return html `<${this.tag('time-picker')}
180
186
  part="picker"
181
- exportparts="column: picker-column, column-empty: picker-column-empty,
187
+ exportparts="column: picker-column, column-empty: picker-column-empty, column-error: picker-column-error,
182
188
  column-feedback: picker-column-feedback, column-filter-icon: picker-column-filter-icon,
183
189
  column-filter-mark: picker-column-filter-mark, column-hint: picker-column-hint,
184
190
  column-items: picker-column-items, column-label: picker-column-label,
185
191
  column-live: picker-column-live, column-loading: picker-column-loading, column-search: picker-column-search,
186
192
  column-search-field: picker-column-search-field, columns: picker-columns, list: picker-list,
187
- list-empty: picker-list-empty, list-feedback: picker-list-feedback,
193
+ list-empty: picker-list-empty, list-error: picker-list-error, list-feedback: picker-list-feedback,
188
194
  list-filter-icon: picker-list-filter-icon, list-filter-mark: picker-list-filter-mark,
189
195
  list-hint: picker-list-hint, list-items: picker-list-items, list-label: picker-list-label,
190
196
  list-live: picker-list-live, list-loading: picker-list-loading, list-search: picker-list-search,
@@ -285,7 +291,9 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
285
291
  ?hidden=${!this.open}
286
292
  @keydown=${this.handleWheelEnter}
287
293
  >
294
+ <slot name="panel-header"></slot>
288
295
  ${this.renderPicker()}
296
+ <slot name="panel-footer"></slot>
289
297
  </div>
290
298
  ${this.renderHint()}${this.renderFeedback()}`;
291
299
  }
@@ -47,6 +47,7 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
47
47
  *
48
48
  * @csspart column - One column of the wheels.
49
49
  * @csspart column-empty - The text shown when the filter hides every option.
50
+ * @csspart column-error - A wheel's `error` part, which Time Picker never sets: its times do not load.
50
51
  * @csspart column-feedback - The message that says what is wrong with the value.
51
52
  * @csspart column-filter-icon - The icon beside the filter field.
52
53
  * @csspart column-filter-mark - The default icon beside the filter field, shown when none is slotted.
@@ -60,6 +61,7 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
60
61
  * @csspart columns - The row of columns, in `wheels`.
61
62
  * @csspart list - The listbox holding the times, in `list`.
62
63
  * @csspart list-empty - The text shown when the filter hides every option.
64
+ * @csspart list-error - The list's `error` part, which Time Picker never sets: its times do not load.
63
65
  * @csspart list-feedback - The message that says what is wrong with the value.
64
66
  * @csspart list-filter-icon - The icon beside the filter field.
65
67
  * @csspart list-filter-mark - The default icon beside the filter field, shown when none is slotted.
@@ -112,8 +114,11 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
112
114
  hourCycle?: HourCycle;
113
115
  /**
114
116
  * What the wheel of hours is called, read by screen readers.
117
+ *
118
+ * Absent is the locale's own word, from `Intl.DisplayNames`: "Hour" in
119
+ * English, "Stunde" in German.
115
120
  */
116
- hourLabel: string;
121
+ hourLabel?: string;
117
122
  /**
118
123
  * Whether the current selection fails validation.
119
124
  *
@@ -151,16 +156,22 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
151
156
  /**
152
157
  * What the wheel of the half of the day is called, read by screen readers.
153
158
  * Its two options are written as the locale writes them, such as AM and PM.
159
+ *
160
+ * Absent is the locale's own word, from `Intl.DisplayNames`: "AM/PM" in
161
+ * English, "Tageshälfte" in German.
154
162
  */
155
- meridiemLabel: string;
163
+ meridiemLabel?: string;
156
164
  /**
157
165
  * Earliest time offered, as `HH:mm`.
158
166
  */
159
167
  min: string;
160
168
  /**
161
169
  * What the wheel of minutes is called, read by screen readers.
170
+ *
171
+ * Absent is the locale's own word, from `Intl.DisplayNames`: "Minute" in
172
+ * English and German, "分" in Japanese.
162
173
  */
163
- minuteLabel: string;
174
+ minuteLabel?: string;
164
175
  /**
165
176
  * How many times can be held at once.
166
177
  *
@@ -322,8 +333,11 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
322
333
  required: boolean;
323
334
  /**
324
335
  * What the wheel of seconds is called, read by screen readers.
336
+ *
337
+ * Absent is the locale's own word, from `Intl.DisplayNames`: "Second" in
338
+ * English, "Sekunde" in German.
325
339
  */
326
- secondLabel: string;
340
+ secondLabel?: string;
327
341
  /**
328
342
  * Whether times carry their seconds.
329
343
  *
@@ -8,21 +8,19 @@ import { clockLocale, commaList, customElement, dayPeriod, elementById, fillTemp
8
8
  import { nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
11
+ import { wheelName } from '../wheel-name.js';
11
12
  import '../../listbox/index.js';
12
13
  import '../../option/index.js';
13
14
  let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControllerValue(HelixFormElement) {
14
15
  constructor() {
15
16
  super(...arguments);
16
17
  this.disableTimes = '';
17
- this.hourLabel = 'Hour';
18
18
  this.invalid = false;
19
19
  this.label = 'Time';
20
20
  this.locale = '';
21
21
  this.manual = false;
22
22
  this.max = '23:59';
23
- this.meridiemLabel = 'Before or after noon';
24
23
  this.min = '00:00';
25
- this.minuteLabel = 'Minute';
26
24
  this.handleListInput = (event) => {
27
25
  event.stopPropagation();
28
26
  };
@@ -77,7 +75,6 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
77
75
  };
78
76
  this.readOnly = false;
79
77
  this.required = false;
80
- this.secondLabel = 'Second';
81
78
  this.seconds = false;
82
79
  this.step = 15;
83
80
  this.listRevealed = false;
@@ -286,7 +283,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
286
283
  part="column"
287
284
  data-column=${part}
288
285
  aria-invalid=${this.invalid ? 'true' : nothing}
289
- exportparts="empty: column-empty, feedback: column-feedback, filter-icon: column-filter-icon,
286
+ exportparts="empty: column-empty, error: column-error, feedback: column-feedback, filter-icon: column-filter-icon,
290
287
  filter-mark: column-filter-mark, hint: column-hint, items: column-items, label: column-label,
291
288
  live: column-live, loading: column-loading, search: column-search, search-field: column-search-field"
292
289
  aria-label=${label}
@@ -323,13 +320,13 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
323
320
  data-unset=${this.value === '' ? '' : nothing}
324
321
  @keydown=${this.handleColumnsKeydown}
325
322
  >
326
- ${this.renderColumn('hour', this.hourLabel, hours.map(String), (one) => (twelve ? one : one.padStart(2, '0')), String(shown), (one) => this.wholeHourBarred(Number(one), twelve ? meridiem : undefined))}
327
- ${this.renderColumn('minute', this.minuteLabel, this.minutes.map(String), (one) => one.padStart(2, '0'), String(minute), (one) => this.barredAt(hour * 3600 + Number(one) * 60))}
323
+ ${this.renderColumn('hour', this.hourLabel ?? wheelName('hour', this.locale), hours.map(String), (one) => (twelve ? one : one.padStart(2, '0')), String(shown), (one) => this.wholeHourBarred(Number(one), twelve ? meridiem : undefined))}
324
+ ${this.renderColumn('minute', this.minuteLabel ?? wheelName('minute', this.locale), this.minutes.map(String), (one) => one.padStart(2, '0'), String(minute), (one) => this.barredAt(hour * 3600 + Number(one) * 60))}
328
325
  ${this.seconds
329
- ? this.renderColumn('second', this.secondLabel, this.secondsShown.map(String), (one) => one.padStart(2, '0'), String(second), (one) => this.barredAt(hour * 3600 + minute * 60 + Number(one)))
326
+ ? this.renderColumn('second', this.secondLabel ?? wheelName('second', this.locale), this.secondsShown.map(String), (one) => one.padStart(2, '0'), String(second), (one) => this.barredAt(hour * 3600 + minute * 60 + Number(one)))
330
327
  : nothing}
331
328
  ${twelve
332
- ? this.renderColumn('meridiem', this.meridiemLabel, ['AM', 'PM'], (one) => dayPeriod(one, this.clock), meridiem, (one) => this.halfBarred(one))
329
+ ? this.renderColumn('meridiem', this.meridiemLabel ?? wheelName('dayPeriod', this.locale), ['AM', 'PM'], (one) => dayPeriod(one, this.clock), meridiem, (one) => this.halfBarred(one))
333
330
  : nothing}
334
331
  </div>`;
335
332
  }
@@ -337,7 +334,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
337
334
  return html `<${this.tag('listbox')}
338
335
  part="list"
339
336
  aria-invalid=${this.invalid ? 'true' : nothing}
340
- exportparts="empty: list-empty, feedback: list-feedback, filter-icon: list-filter-icon,
337
+ exportparts="empty: list-empty, error: list-error, feedback: list-feedback, filter-icon: list-filter-icon,
341
338
  filter-mark: list-filter-mark, hint: list-hint, items: list-items, label: list-label,
342
339
  live: list-live, loading: list-loading, search: list-search, search-field: list-search-field"
343
340
  aria-label=${this.listName}
@@ -0,0 +1,9 @@
1
+ /** A part of the time a Time Picker wheel sets. */
2
+ export type WheelField = 'dayPeriod' | 'hour' | 'minute' | 'second';
3
+ /**
4
+ * What a locale calls a wheel, core's `segmentName`, with its first letter in
5
+ * capitals as a name standing on its own: "Hour" in English, "Stunde" in
6
+ * German, "時" in Japanese, and "AM/PM" for the half of the day, React Aria's
7
+ * name for it.
8
+ */
9
+ export declare const wheelName: (field: WheelField, locale: string) => string;
@@ -0,0 +1,15 @@
1
+ import { segmentName } from '@fusion.dev/helix-core';
2
+ const ENGLISH = {
3
+ dayPeriod: 'AM/PM',
4
+ hour: 'Hour',
5
+ minute: 'Minute',
6
+ second: 'Second',
7
+ };
8
+ export const wheelName = (field, locale) => {
9
+ const name = segmentName(field, locale);
10
+ if (name === field || name === '') {
11
+ return ENGLISH[field];
12
+ }
13
+ const lead = String.fromCodePoint(name.codePointAt(0));
14
+ return `${lead.toLocaleUpperCase(locale || undefined)}${name.slice(lead.length)}`;
15
+ };
@@ -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 } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, headingLevel, hostAria } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  const HEADING_ID = 'heading';
@@ -18,7 +18,7 @@ let HelixHeadlessTimelineGroup = class HelixHeadlessTimelineGroup extends HelixE
18
18
  hostAria(this.internals, { role: 'listitem' });
19
19
  }
20
20
  render() {
21
- const level = Math.min(6, Math.max(1, Math.trunc(this.headingLevel ?? 3)));
21
+ const level = headingLevel(this.headingLevel);
22
22
  return html `<div part="heading" id=${HEADING_ID} role="heading" aria-level=${level}>
23
23
  <slot name="heading">${this.label}</slot>
24
24
  </div>
@@ -13,7 +13,9 @@ declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fus
13
13
  * seconds.
14
14
  *
15
15
  * **Each message is an `alertdialog` named by its words**, which is how a reader
16
- * who moves into the stack hears which message they are on. A `danger` message
16
+ * who moves into the stack hears which message they are on. Give it a
17
+ * `heading` and a `description`, and the heading names it and the description
18
+ * describes it, for a message with a title and a line of detail. A `danger` message
17
19
  * is its own assertive live region, so it is announced at once; every other one
18
20
  * is announced politely by the region.
19
21
  *
@@ -31,7 +33,9 @@ declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fus
31
33
  *
32
34
  * @slot - The message.
33
35
  * @slot actions - Actions for the message, usually a button or a link.
36
+ * @slot description - A line of detail under the heading, which describes the message.
34
37
  * @slot dismiss-icon - Replaces the glyph inside the dismiss control.
38
+ * @slot heading - The message's title, which names it.
35
39
  * @slot icon - Replaces the tone mark. The name matches `Callout`. The dismiss
36
40
  * control's glyph uses `dismiss-icon`, because two slots cannot share a name:
37
41
  * content goes to whichever slot comes first in the tree, and the tone mark is
@@ -41,8 +45,10 @@ declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fus
41
45
  *
42
46
  * @csspart actions - The region that holds the actions.
43
47
  * @csspart content - The message.
48
+ * @csspart description - The slot the description is in. It draws no box until you give it one.
44
49
  * @csspart dismiss - The control that dismisses the message.
45
50
  * @csspart dismiss-mark - The glyph on the dismiss control, where none is slotted.
51
+ * @csspart heading - The slot the heading is in. It draws no box until you give it one.
46
52
  * @csspart icon - The tone mark, where the message has one.
47
53
  * @csspart icon-mark - The tone glyph, where none is slotted.
48
54
  * @csspart item - The message's own surface.
@@ -114,6 +120,13 @@ export declare class HelixHeadlessToastItem extends HelixHeadlessToastItem_base
114
120
  connectedCallback(): void;
115
121
  protected willUpdate(changed: PropertyValues): void;
116
122
  protected updated(changed: PropertyValues): void;
123
+ /** The elements assigned to one of the message's named slots. */
124
+ private assigned;
125
+ /**
126
+ * Names the message by its heading and describes it by its description,
127
+ * where it has them, and otherwise names it by all its words.
128
+ */
129
+ private readonly name;
117
130
  protected firstUpdated(changed: PropertyValues): void;
118
131
  /**
119
132
  * The mark for the message's tone, behind a seam.
@@ -46,6 +46,17 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
46
46
  this.handleResume = () => {
47
47
  this.countdown.release();
48
48
  };
49
+ this.name = () => {
50
+ const words = this.partNamed('content');
51
+ if (!words) {
52
+ return;
53
+ }
54
+ const heading = this.assigned('heading');
55
+ hostAria(this.internals, {
56
+ describedBy: this.assigned('description'),
57
+ labelledBy: heading.length > 0 ? heading : [words],
58
+ });
59
+ };
49
60
  }
50
61
  get open() {
51
62
  return this.openState ?? true;
@@ -97,10 +108,12 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
97
108
  }
98
109
  updated(changed) {
99
110
  super.updated(changed);
100
- const words = this.partNamed('content');
101
- if (words) {
102
- hostAria(this.internals, { labelledBy: [words] });
103
- }
111
+ this.name();
112
+ }
113
+ assigned(name) {
114
+ return (this.renderRoot
115
+ ?.querySelector(`slot[name="${name}"]`)
116
+ ?.assignedElements({ flatten: true }) ?? []);
104
117
  }
105
118
  firstUpdated(changed) {
106
119
  super.firstUpdated(changed);
@@ -137,7 +150,11 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
137
150
  ${this.renderIcon(mark)}
138
151
  </span>`
139
152
  : nothing}
140
- <div part="content"><slot></slot></div>
153
+ <div part="content">
154
+ <slot name="heading" part="heading" @slotchange=${this.name}></slot>
155
+ <slot name="description" part="description" @slotchange=${this.name}></slot>
156
+ <slot></slot>
157
+ </div>
141
158
  <div part="actions"><slot name="actions"></slot></div>
142
159
  ${this.dismissible
143
160
  ? drawDismiss({