@fusion.dev/helix-headless 0.142.0 → 0.143.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 (217) hide show
  1. package/custom-elements.json +16710 -707
  2. package/dist/accordion/item/accordion-item.d.ts +38 -2
  3. package/dist/accordion/item/accordion-item.js +38 -6
  4. package/dist/accordion/root/accordion-root.d.ts +23 -1
  5. package/dist/accordion/root/accordion-root.js +39 -3
  6. package/dist/announcement/root/announcement-root.d.ts +11 -7
  7. package/dist/announcement/root/announcement-root.js +1 -1
  8. package/dist/app-header/root/app-header-root.d.ts +21 -0
  9. package/dist/app-header/root/app-header-root.js +31 -5
  10. package/dist/area-chart/root/area-chart-root.js +16 -5
  11. package/dist/avatar/group/avatar-group.d.ts +24 -3
  12. package/dist/avatar/group/avatar-group.js +34 -2
  13. package/dist/avatar/root/avatar-root.d.ts +3 -1
  14. package/dist/avatar/root/avatar-root.js +8 -1
  15. package/dist/badge/root/badge-root.d.ts +8 -0
  16. package/dist/bar-chart/root/bar-chart-root.d.ts +19 -3
  17. package/dist/bar-chart/root/bar-chart-root.js +43 -13
  18. package/dist/blockquote/root/blockquote-root.d.ts +12 -7
  19. package/dist/blockquote/root/blockquote-root.js +6 -4
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
  22. package/dist/button/root/button-root.d.ts +37 -0
  23. package/dist/button/root/button-root.js +44 -0
  24. package/dist/callout/root/callout-root.d.ts +14 -5
  25. package/dist/callout/root/callout-root.js +11 -3
  26. package/dist/canvas/root/canvas-root.d.ts +1 -0
  27. package/dist/canvas/root/canvas-root.js +10 -1
  28. package/dist/carousel/root/carousel-root.d.ts +47 -5
  29. package/dist/carousel/root/carousel-root.js +53 -21
  30. package/dist/chat/composer/chat-composer.d.ts +13 -11
  31. package/dist/chat/composer/chat-composer.js +6 -12
  32. package/dist/chat/message/chat-message.js +8 -1
  33. package/dist/chat/root/chat-root.d.ts +1 -0
  34. package/dist/chat/root/chat-root.js +2 -1
  35. package/dist/checkbox/root/checkbox-root.d.ts +7 -0
  36. package/dist/checkbox/root/checkbox-root.js +7 -3
  37. package/dist/checkbox-group/root/checkbox-group-root.d.ts +65 -32
  38. package/dist/checkbox-group/root/checkbox-group-root.js +85 -37
  39. package/dist/code/root/code-root.d.ts +1 -1
  40. package/dist/code-block/root/code-block-root.d.ts +19 -0
  41. package/dist/code-block/root/code-block-root.js +21 -1
  42. package/dist/color-input/root/color-input-root.d.ts +26 -1
  43. package/dist/color-input/root/color-input-root.js +32 -3
  44. package/dist/color-picker/root/color-picker-root.d.ts +37 -7
  45. package/dist/color-picker/root/color-picker-root.js +83 -6
  46. package/dist/combobox/root/combobox-root.d.ts +90 -2
  47. package/dist/combobox/root/combobox-root.js +160 -19
  48. package/dist/command/root/command-root.d.ts +17 -6
  49. package/dist/command/root/command-root.js +12 -1
  50. package/dist/copy-button/root/copy-button-root.d.ts +21 -6
  51. package/dist/copy-button/root/copy-button-root.js +11 -1
  52. package/dist/data-table/root/data-table-root.d.ts +164 -10
  53. package/dist/data-table/root/data-table-root.js +488 -57
  54. package/dist/date-input/root/date-input-root.d.ts +53 -8
  55. package/dist/date-input/root/date-input-root.js +70 -18
  56. package/dist/date-picker/root/date-picker-root.d.ts +84 -17
  57. package/dist/date-picker/root/date-picker-root.js +125 -20
  58. package/dist/dialog/root/dialog-root.d.ts +38 -10
  59. package/dist/dialog/root/dialog-root.js +55 -2
  60. package/dist/donut-chart/root/donut-chart-root.d.ts +39 -15
  61. package/dist/donut-chart/root/donut-chart-root.js +30 -10
  62. package/dist/drawer/root/drawer-root.d.ts +32 -7
  63. package/dist/drawer/root/drawer-root.js +50 -5
  64. package/dist/drilldown/item/drilldown-item.d.ts +16 -13
  65. package/dist/drilldown/item/drilldown-item.js +6 -2
  66. package/dist/drilldown/root/drilldown-root.d.ts +21 -5
  67. package/dist/drilldown/root/drilldown-root.js +26 -10
  68. package/dist/element-events.generated.js +35 -9
  69. package/dist/file-input/root/file-input-root.d.ts +18 -1
  70. package/dist/file-input/root/file-input-root.js +34 -8
  71. package/dist/filter-input/menu/filter-menu.d.ts +9 -0
  72. package/dist/filter-input/menu/filter-menu.js +8 -0
  73. package/dist/filter-input/root/filter-input-root.d.ts +5 -2
  74. package/dist/filter-input/root/filter-input-root.js +22 -7
  75. package/dist/format-date/root/format-date-root.d.ts +11 -3
  76. package/dist/format-date/root/format-date-root.js +26 -5
  77. package/dist/graph/root/graph-root.d.ts +24 -3
  78. package/dist/graph/root/graph-root.js +66 -12
  79. package/dist/heatmap/root/buckets.d.ts +2 -1
  80. package/dist/heatmap/root/buckets.js +5 -1
  81. package/dist/heatmap/root/heatmap-root.d.ts +27 -6
  82. package/dist/heatmap/root/heatmap-root.js +23 -6
  83. package/dist/icon/root/icon-root.d.ts +24 -3
  84. package/dist/icon/root/icon-root.js +34 -1
  85. package/dist/icon-button/root/icon-button-root.d.ts +1 -1
  86. package/dist/image/root/image-root.d.ts +9 -2
  87. package/dist/image/root/image-root.js +8 -2
  88. package/dist/input/root/input-root.d.ts +51 -9
  89. package/dist/input/root/input-root.js +46 -5
  90. package/dist/kbd/root/kbd-root.d.ts +12 -4
  91. package/dist/kbd/root/kbd-root.js +10 -11
  92. package/dist/legend/root/legend-root.d.ts +7 -0
  93. package/dist/legend/root/legend-root.js +7 -2
  94. package/dist/lightbox/item/lightbox-item.d.ts +2 -1
  95. package/dist/lightbox/root/lightbox-root.d.ts +16 -5
  96. package/dist/lightbox/root/lightbox-root.js +24 -3
  97. package/dist/line-chart/root/line-chart-root.d.ts +19 -2
  98. package/dist/line-chart/root/line-chart-root.js +40 -13
  99. package/dist/list/item/list-item.d.ts +11 -5
  100. package/dist/list/item/list-item.js +5 -6
  101. package/dist/listbox/root/listbox-root.d.ts +111 -2
  102. package/dist/listbox/root/listbox-root.js +144 -16
  103. package/dist/map/root/map-root.d.ts +19 -4
  104. package/dist/map/root/map-root.js +36 -7
  105. package/dist/megamenu/root/megamenu-root.d.ts +10 -1
  106. package/dist/megamenu/root/megamenu-root.js +10 -1
  107. package/dist/menu/item/menu-item.d.ts +41 -12
  108. package/dist/menu/item/menu-item.js +46 -3
  109. package/dist/menu/root/menu-root.d.ts +32 -2
  110. package/dist/menu/root/menu-root.js +48 -8
  111. package/dist/meter/root/meter-root.d.ts +6 -0
  112. package/dist/meter/root/meter-root.js +18 -2
  113. package/dist/navigation/root/navigation-root.d.ts +10 -2
  114. package/dist/navigation/root/navigation-root.js +13 -2
  115. package/dist/notifications/group/notification-group.d.ts +1 -0
  116. package/dist/notifications/notification/notification.d.ts +12 -0
  117. package/dist/notifications/notification/notification.js +31 -2
  118. package/dist/notifications/root/notifications-root.d.ts +7 -0
  119. package/dist/notifications/root/notifications-root.js +17 -2
  120. package/dist/number-input/root/number-input-root.d.ts +62 -7
  121. package/dist/number-input/root/number-input-root.js +68 -16
  122. package/dist/object-inspector/root/object-inspector-root.d.ts +48 -5
  123. package/dist/object-inspector/root/object-inspector-root.js +91 -11
  124. package/dist/option/root/option-root.d.ts +4 -1
  125. package/dist/option/root/option-root.js +1 -0
  126. package/dist/outline/root/outline-root.d.ts +13 -2
  127. package/dist/outline/root/outline-root.js +4 -2
  128. package/dist/pagination/root/pagination-root.d.ts +19 -1
  129. package/dist/pagination/root/pagination-root.js +49 -9
  130. package/dist/pagination/size/pagination-size.d.ts +13 -3
  131. package/dist/pagination/size/pagination-size.js +7 -0
  132. package/dist/pin-input/root/pin-input-root.d.ts +16 -0
  133. package/dist/pin-input/root/pin-input-root.js +19 -5
  134. package/dist/popover/root/popover-root.d.ts +19 -2
  135. package/dist/popover/root/popover-root.js +22 -1
  136. package/dist/progress/root/progress-root.d.ts +16 -1
  137. package/dist/progress/root/progress-root.js +31 -7
  138. package/dist/qr-code/root/qr-code-root.d.ts +19 -0
  139. package/dist/qr-code/root/qr-code-root.js +29 -1
  140. package/dist/radio/group/radio-group.d.ts +41 -0
  141. package/dist/radio/group/radio-group.js +50 -14
  142. package/dist/radio/root/radio-root.d.ts +8 -5
  143. package/dist/radio/root/radio-root.js +1 -2
  144. package/dist/rating/root/rating-root.d.ts +24 -1
  145. package/dist/rating/root/rating-root.js +24 -7
  146. package/dist/scatter-chart/root/scatter-chart-root.js +15 -7
  147. package/dist/select/root/select-root.d.ts +69 -3
  148. package/dist/select/root/select-root.js +92 -8
  149. package/dist/skeleton/root/skeleton-root.d.ts +14 -3
  150. package/dist/skeleton/root/skeleton-root.js +17 -3
  151. package/dist/skip-link/root/skip-link-root.d.ts +10 -0
  152. package/dist/skip-link/root/skip-link-root.js +15 -1
  153. package/dist/slider/root/slider-root.d.ts +20 -2
  154. package/dist/slider/root/slider-root.js +41 -3
  155. package/dist/sparkline/root/sparkline-root.d.ts +9 -10
  156. package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
  157. package/dist/speed-dial/item/speed-dial-item.js +7 -2
  158. package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
  159. package/dist/speed-dial/root/speed-dial-root.js +19 -5
  160. package/dist/splitter/root/splitter-root.d.ts +28 -6
  161. package/dist/splitter/root/splitter-root.js +35 -1
  162. package/dist/stat/root/stat-root.d.ts +7 -1
  163. package/dist/stat/root/stat-root.js +14 -1
  164. package/dist/steps/root/steps-root.js +1 -1
  165. package/dist/switch/root/switch-root.d.ts +2 -0
  166. package/dist/switch/root/switch-root.js +1 -0
  167. package/dist/table/cell/table-cell.d.ts +24 -4
  168. package/dist/table/cell/table-cell.js +21 -1
  169. package/dist/table/column/table-column.d.ts +32 -6
  170. package/dist/table/column/table-column.js +36 -6
  171. package/dist/table/root/table-root.d.ts +94 -3
  172. package/dist/table/root/table-root.js +128 -5
  173. package/dist/table/row/table-row.d.ts +7 -0
  174. package/dist/table/row/table-row.js +9 -1
  175. package/dist/tabs/root/tabs-root.d.ts +6 -1
  176. package/dist/tabs/root/tabs-root.js +6 -2
  177. package/dist/tag/root/tag-root.d.ts +23 -1
  178. package/dist/tag/root/tag-root.js +16 -2
  179. package/dist/textarea/root/textarea-root.d.ts +4 -0
  180. package/dist/textarea/root/textarea-root.js +1 -1
  181. package/dist/time-input/root/time-input-root.d.ts +28 -2
  182. package/dist/time-input/root/time-input-root.js +46 -10
  183. package/dist/time-picker/root/time-picker-root.d.ts +101 -18
  184. package/dist/time-picker/root/time-picker-root.js +115 -16
  185. package/dist/toast/root/toast-root.d.ts +64 -1
  186. package/dist/toast/root/toast-root.js +147 -2
  187. package/dist/toggle/group/toggle-group.d.ts +38 -1
  188. package/dist/toggle/group/toggle-group.js +34 -8
  189. package/dist/toggle/root/toggle-root.d.ts +14 -10
  190. package/dist/toggle/root/toggle-root.js +3 -3
  191. package/dist/toolbar/root/toolbar-root.d.ts +55 -15
  192. package/dist/toolbar/root/toolbar-root.js +99 -16
  193. package/dist/tooltip/root/tooltip-root.d.ts +37 -4
  194. package/dist/tooltip/root/tooltip-root.js +48 -0
  195. package/dist/tour/root/tour-root.d.ts +34 -5
  196. package/dist/tour/root/tour-root.js +51 -14
  197. package/dist/tour/stop/tour-stop.d.ts +18 -1
  198. package/dist/tour/stop/tour-stop.js +25 -5
  199. package/dist/tree/item/tree-item.d.ts +21 -5
  200. package/dist/tree/item/tree-item.js +14 -4
  201. package/dist/tree/root/tree-root.d.ts +29 -5
  202. package/dist/tree/root/tree-root.js +64 -8
  203. package/dist/trend/root/trend-root.d.ts +23 -1
  204. package/dist/trend/root/trend-root.js +28 -7
  205. package/dist/video/root/keys.d.ts +5 -0
  206. package/dist/video/root/keys.js +13 -1
  207. package/dist/video/root/video-root.d.ts +19 -0
  208. package/dist/video/root/video-root.js +48 -4
  209. package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
  210. package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
  211. package/dist/video-playlist/root/video-playlist-root.js +6 -3
  212. package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
  213. package/dist/virtual-list/root/virtual-list-root.js +103 -21
  214. package/lint.json +1 -1
  215. package/package.json +5 -5
  216. package/types/jsx.d.ts +4348 -965
  217. package/types/vue.d.ts +972 -232
@@ -16,7 +16,18 @@ import { type PropertyValues, type TemplateResult } from 'lit';
16
16
  *
17
17
  * **Rotation stops on focus and on hover, and stays stopped** until the user
18
18
  * starts it again, as the APG requires. The rotation control is labeled with
19
- * the action it performs and is first in the tab order.
19
+ * the action it performs and is first in the tab order. Rotation goes on from
20
+ * the last slide to the first, with or without `loop`, which is about the
21
+ * reader's own moves.
22
+ *
23
+ * A move the reader makes is announced, the slide's position read from a
24
+ * polite live region. While the carousel rotates the region is off, so a
25
+ * screen reader is not interrupted every few seconds.
26
+ *
27
+ * The indicators are one tab stop. The arrow keys along the track move between
28
+ * them, Up and Down on a vertical carousel, and Home and End go to the first
29
+ * and the last. Without `loop`, Previous on the first slide and Next on the
30
+ * last are marked `aria-disabled`.
20
31
  *
21
32
  * @since 0.1.0
22
33
  * @status stable
@@ -26,12 +37,14 @@ import { type PropertyValues, type TemplateResult } from 'lit';
26
37
  *
27
38
  * @slot - The slides.
28
39
  *
40
+ * @event {HelixActivateEvent} helix-activate - Fired after the slide in view changes, with the new index as a string in `detail.value`.
29
41
  * @event {HelixSelectEvent} helix-select - Fired before the slide in view changes, with the requested index. Cancelable: cancel it to keep the current slide. Nothing moves, and a drag returns to the slide the index still holds. See `handleScroll`.
30
42
  *
31
43
  * @csspart controls - The row of controls.
32
44
  * @csspart indicator - One indicator. Pressing it moves to its slide.
33
45
  * @csspart indicator-current - The indicator for the slide in view, alongside `indicator`.
34
46
  * @csspart indicators - The row of indicators that shows which slide is in view.
47
+ * @csspart live - The region that announces the slide a move lands on. Visually hidden in the styled tier.
35
48
  * @csspart next - The control that moves to the next slide.
36
49
  * @csspart next-mark - The glyph on the next control.
37
50
  * @csspart previous - The control that moves to the previous slide.
@@ -131,6 +144,8 @@ export declare class HelixHeadlessCarousel extends HelixElement {
131
144
  private indexState;
132
145
  /** How many panes were drawn at the last update, to scroll when the first are. */
133
146
  private scrolledFor;
147
+ /** What the live region says: the position of the slide the last move landed on. */
148
+ private said;
134
149
  /**
135
150
  * The carousel's name, read by screen readers.
136
151
  */
@@ -336,14 +351,24 @@ export declare class HelixHeadlessCarousel extends HelixElement {
336
351
  * **A fitted carousel's last position moves with the window**, which a
337
352
  * declared one's never does: eight slides at one across has eight places to
338
353
  * stand and four across has five, so a track widened while the reader sits on
339
- * the last of them is a track resting somewhere it can no longer be. Through
340
- * `goTo`, so the correction goes out as the same cancelable announcement
341
- * every other index change does and a consumer holding `index` hears it
342
- * rather than being overwritten.
354
+ * the last of them is a track resting somewhere it can no longer be. See
355
+ * `holdIndex` for why the correction is not a request.
343
356
  *
344
357
  * Only on a change, so the observer's own re-render cannot feed itself.
345
358
  */
346
359
  private readonly readFit;
360
+ /**
361
+ * Puts the index back on the last place the track can stand, where fewer
362
+ * places are left than it counted.
363
+ *
364
+ * **Written, with no request.** Nobody asked for this move: a slide was
365
+ * removed, the window widened, or `per-view` went up. It went out as a
366
+ * cancelable `helix-select`, which a consumer reading the event as a reader's
367
+ * request had no reason to expect, and refusing it left the index past the
368
+ * last slide, with no current indicator and no tab stop, and asked again on
369
+ * every update. An index that cannot be refused is not a request.
370
+ */
371
+ private holdIndex;
347
372
  /**
348
373
  * The count handed to the styles, and nothing at all where they work it out.
349
374
  *
@@ -413,6 +438,14 @@ export declare class HelixHeadlessCarousel extends HelixElement {
413
438
  * the right, the left arrow is the one that moves on.
414
439
  */
415
440
  private readonly handleIndicatorKeydown;
441
+ /**
442
+ * Puts a requested slide in view, and says so.
443
+ *
444
+ * `helix-activate` is the notification after `helix-select`, the one Tour
445
+ * and Tabs send once a different member is current. A clamp in `holdIndex`
446
+ * sends neither, being a correction rather than a move anybody asked for.
447
+ */
448
+ private applyIndex;
416
449
  /** Moves to a slide. It wraps past either end only when `loop` is set. */
417
450
  goTo(index: number): void;
418
451
  /**
@@ -663,6 +696,15 @@ export declare class HelixHeadlessCarousel extends HelixElement {
663
696
  * floor.
664
697
  */
665
698
  private runEnd;
699
+ /**
700
+ * Whether Previous or Next has nowhere to go, which is only at an end and
701
+ * without `loop`.
702
+ *
703
+ * Said with `aria-disabled` and not `disabled`, so a reader who pressed Next
704
+ * onto the last slide keeps focus on the control they are on. Not on the
705
+ * server, which knows no slides and so no ends.
706
+ */
707
+ private atEnd;
666
708
  protected render(): TemplateResult;
667
709
  }
668
710
  declare global {
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, fillTemplate, HelixElement, HelixSelectEvent, hostAria, inlineStep, isRtl, requestChange, } from '@fusion.dev/helix-core';
7
+ import { customElement, fillTemplate, HelixActivateEvent, HelixElement, HelixSelectEvent, hostAria, inlineStep, isRtl, requestChange, } from '@fusion.dev/helix-core';
8
8
  import { caretLeft, caretRight, play, stop } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -28,6 +28,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
28
28
  this.interval = 5000;
29
29
  this.indexState = undefined;
30
30
  this.scrolledFor = 0;
31
+ this.said = '';
31
32
  this.label = 'Carousel';
32
33
  this.loop = false;
33
34
  this.nextLabel = 'Next slide';
@@ -43,17 +44,29 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
43
44
  return;
44
45
  }
45
46
  this.fitted = fitted;
46
- if (this.index > this.positions - 1) {
47
- this.goTo(this.positions - 1);
48
- }
47
+ this.holdIndex();
49
48
  };
50
49
  this.handleIndicatorKeydown = (event) => {
51
- const step = inlineStep(this, event.key);
52
- if (step === 0) {
50
+ const vertical = this.orientation === 'vertical';
51
+ const step = vertical
52
+ ? event.key === 'ArrowDown'
53
+ ? 1
54
+ : event.key === 'ArrowUp'
55
+ ? -1
56
+ : 0
57
+ : inlineStep(this, event.key);
58
+ let to = this.index + step;
59
+ if (event.key === 'Home') {
60
+ to = 0;
61
+ }
62
+ else if (event.key === 'End') {
63
+ to = this.positions - 1;
64
+ }
65
+ else if (step === 0) {
53
66
  return;
54
67
  }
55
68
  event.preventDefault();
56
- this.goTo(this.index + step);
69
+ this.goTo(to);
57
70
  this.updateComplete.then(() => {
58
71
  this.renderRoot.querySelector('[part~="indicator-current"]')?.focus();
59
72
  });
@@ -88,11 +101,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
88
101
  return;
89
102
  }
90
103
  const applied = requestChange({
91
- apply: (value) => {
92
- const previous = this.index;
93
- this.indexState = value;
94
- this.requestUpdate('index', previous);
95
- },
104
+ apply: (value) => this.applyIndex(value),
96
105
  event: (value) => new HelixSelectEvent({ value: String(value) }),
97
106
  host: this,
98
107
  next: nearest,
@@ -149,6 +158,14 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
149
158
  }
150
159
  return Math.max(1, Math.round((room + gap) / (pane + gap)));
151
160
  }
161
+ holdIndex() {
162
+ const last = this.positions - 1;
163
+ if (this.slides.length > 0 && this.index > last) {
164
+ const previous = this.index;
165
+ this.indexState = Math.max(0, last);
166
+ this.requestUpdate('index', previous);
167
+ }
168
+ }
152
169
  handed() {
153
170
  return this.fits ? nothing : `--carousel-per-view: ${this.clamped}`;
154
171
  }
@@ -178,6 +195,12 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
178
195
  get slides() {
179
196
  return this.knownChildren.filter((child) => child instanceof HTMLElement);
180
197
  }
198
+ applyIndex(value) {
199
+ const previous = this.index;
200
+ this.indexState = value;
201
+ this.requestUpdate('index', previous);
202
+ this.dispatchEvent(new HelixActivateEvent({ value: String(value) }));
203
+ }
181
204
  goTo(index) {
182
205
  if (this.slides.length === 0) {
183
206
  return;
@@ -188,11 +211,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
188
211
  return;
189
212
  }
190
213
  requestChange({
191
- apply: (value) => {
192
- const previous = this.index;
193
- this.indexState = value;
194
- this.requestUpdate('index', previous);
195
- },
214
+ apply: (value) => this.applyIndex(value),
196
215
  event: (value) => new HelixSelectEvent({ value: String(value) }),
197
216
  host: this,
198
217
  next,
@@ -246,7 +265,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
246
265
  if (!this.rotating || this.slides.length < 2) {
247
266
  return;
248
267
  }
249
- this.timer = setInterval(() => this.goTo(this.index + 1), this.interval);
268
+ this.timer = setInterval(() => this.goTo(this.index >= this.positions - 1 ? 0 : this.index + 1), this.interval);
250
269
  }
251
270
  nearest() {
252
271
  const track = this.partNamed('track');
@@ -294,6 +313,12 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
294
313
  }
295
314
  if (changed.has('index')) {
296
315
  this.showing = undefined;
316
+ if (this.hasUpdated) {
317
+ this.said = fillTemplate(this.slideLabel, {
318
+ count: this.slides.length,
319
+ index: this.index + 1,
320
+ });
321
+ }
297
322
  }
298
323
  if (changed.has('perView')) {
299
324
  this.fitted = undefined;
@@ -314,9 +339,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
314
339
  }
315
340
  this.scrolledFor = drawn;
316
341
  this.runTimer();
317
- if (this.slides.length > 0 && this.index > this.positions - 1) {
318
- this.goTo(this.positions - 1);
319
- }
342
+ this.holdIndex();
320
343
  this.watchPane();
321
344
  this.assign();
322
345
  }
@@ -343,6 +366,12 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
343
366
  ' - var(--carousel-gap) - 100%';
344
367
  return `--carousel-run-end: calc(round(${run}, 1px) - (${run}))`;
345
368
  }
369
+ atEnd(side) {
370
+ if (this.loop || this.slides.length < 2) {
371
+ return false;
372
+ }
373
+ return side === 'start' ? this.index <= 0 : this.index >= this.positions - 1;
374
+ }
346
375
  render() {
347
376
  const rotating = this.rotating;
348
377
  const last = this.slides.length - 1;
@@ -369,6 +398,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
369
398
  part="previous"
370
399
  type="button"
371
400
  aria-label=${this.previousLabel}
401
+ aria-disabled=${this.atEnd('start') ? 'true' : nothing}
372
402
  @click=${() => this.goTo(this.index - 1)}
373
403
  >
374
404
  <svg part="previous-mark" data-mirror fill="currentColor" viewBox=${caretLeft.viewBox} aria-hidden="true">
@@ -379,6 +409,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
379
409
  part="next"
380
410
  type="button"
381
411
  aria-label=${this.nextLabel}
412
+ aria-disabled=${this.atEnd('end') ? 'true' : nothing}
382
413
  @click=${() => this.goTo(this.index + 1)}
383
414
  >
384
415
  <svg part="next-mark" data-mirror fill="currentColor" viewBox=${caretRight.viewBox} aria-hidden="true">
@@ -409,6 +440,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
409
440
  ></slot>
410
441
  </div>`)}
411
442
  </div>
443
+ ${''}<span part="live" aria-live=${rotating ? 'off' : 'polite'}>${this.said}</span>
412
444
  ${this.slides.length > 1 && this.across !== undefined
413
445
  ? html `<div part="indicators" role="group" aria-label=${fillTemplate(this.indicatorsLabel, { label: this.label })}>
414
446
  ${''}
@@ -25,6 +25,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
25
25
  * @slot start - Content under the field at the leading edge, such as an attachment button or a model picker.
26
26
  *
27
27
  * @event {HelixSendEvent} helix-send - Fired when the user sends their turn, and cancelable.
28
+ * @event {Event} input - The user changed the text. Read it from `value`, for a typing indicator or a saved draft. Sent on the composer and does not bubble.
28
29
  *
29
30
  * @csspart composer - The whole composer.
30
31
  * @csspart end - The box around the `end` slot.
@@ -69,16 +70,14 @@ export declare class HelixHeadlessChatComposer extends HelixElement {
69
70
  /**
70
71
  * The text the user has written.
71
72
  *
72
- * Read it, or set it to seed a draft.
73
+ * It follows typing, so reading it gives the draft. Set it to seed a draft or
74
+ * to replace the one in the field, and set it to `''` to clear it.
73
75
  */
74
76
  value: string;
75
- private draft;
76
77
  /**
77
78
  * @internal Read by the transcript to find the composer among its children.
78
79
  */
79
80
  readonly isChatComposer = true;
80
- /** What is in the field, whoever last wrote it. */
81
- private get text();
82
81
  /**
83
82
  * Sends what is written, and returns whether the send went through.
84
83
  *
@@ -129,14 +128,17 @@ export declare class HelixHeadlessChatComposer extends HelixElement {
129
128
  */
130
129
  protected render(): TemplateResult;
131
130
  /**
132
- * Records the draft, and stops the textarea speaking in this element's name.
131
+ * Takes what was typed into `value`, and says so from this element.
133
132
  *
134
- * **The stop is not incidental.** `input` is composed by specification, so
135
- * the `<textarea>` in this shadow root sent its own past this host and on
136
- * through whatever `<helix-headless-chat>` it is slotted into, and neither
137
- * element declares an `input`: a consumer binding one on either received an
138
- * undeclared event whose target was a control they cannot reach. What a
139
- * composer offers is `helix-send`, once, when the reader is done.
133
+ * **The textarea's own `input` is stopped and this element sends one.**
134
+ * `input` is composed by specification, so the `<textarea>` in this shadow
135
+ * root sent its own past this host with a target a consumer cannot reach.
136
+ * The one sent here comes from the composer, whose `value` already holds
137
+ * the text, which is what a typing indicator or a saved draft reads.
138
+ *
139
+ * **The text is `value` and nothing beside it.** A private draft read before
140
+ * `value` sent a seed the reader had deleted, since an empty draft fell back
141
+ * to it, and left `value` reading empty while the field held a whole draft.
140
142
  */
141
143
  private readonly handleInput;
142
144
  private readonly handleSend;
@@ -7,7 +7,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  import { customElement, HelixElement, HelixSendEvent, hostAria, markRegion, } from '@fusion.dev/helix-core';
8
8
  import { arrowElbowDownLeft } from '@fusion.dev/helix-icons';
9
9
  import { html } from 'lit';
10
- import { property, state } from 'lit/decorators.js';
10
+ import { property } from 'lit/decorators.js';
11
+ import { live } from 'lit/directives/live.js';
11
12
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
13
  let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixElement {
13
14
  constructor() {
@@ -19,11 +20,11 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixEle
19
20
  this.sendLabel = 'Send';
20
21
  this.submitOn = 'enter';
21
22
  this.value = '';
22
- this.draft = '';
23
23
  this.isChatComposer = true;
24
24
  this.handleInput = (event) => {
25
25
  event.stopPropagation();
26
- this.draft = event.target.value;
26
+ this.value = event.target.value;
27
+ this.dispatchEvent(new Event('input'));
27
28
  };
28
29
  this.handleSend = () => {
29
30
  this.send();
@@ -39,18 +40,14 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixEle
39
40
  this.send();
40
41
  };
41
42
  }
42
- get text() {
43
- return this.draft || this.value;
44
- }
45
43
  send() {
46
- const value = this.text.trim();
44
+ const value = this.value.trim();
47
45
  if (value === '' || this.disabled) {
48
46
  return false;
49
47
  }
50
48
  if (!this.dispatchEvent(new HelixSendEvent({ value }))) {
51
49
  return false;
52
50
  }
53
- this.draft = '';
54
51
  this.value = '';
55
52
  return true;
56
53
  }
@@ -89,7 +86,7 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixEle
89
86
  return html `<div part="composer">
90
87
  <textarea
91
88
  part="field"
92
- .value=${this.text}
89
+ .value=${live(this.value)}
93
90
  aria-label=${this.label}
94
91
  placeholder=${this.placeholder}
95
92
  rows=${this.rows}
@@ -126,9 +123,6 @@ __decorate([
126
123
  __decorate([
127
124
  property()
128
125
  ], HelixHeadlessChatComposer.prototype, "value", void 0);
129
- __decorate([
130
- state()
131
- ], HelixHeadlessChatComposer.prototype, "draft", void 0);
132
126
  HelixHeadlessChatComposer = __decorate([
133
127
  customElement('helix-headless-chat-composer')
134
128
  ], HelixHeadlessChatComposer);
@@ -28,7 +28,14 @@ let HelixHeadlessChatMessage = class HelixHeadlessChatMessage extends HelixEleme
28
28
  apply: (next) => {
29
29
  this.state = next;
30
30
  },
31
- event: () => new HelixSendEvent({ value: this.textContent?.trim() ?? '' }),
31
+ event: () => new HelixSendEvent({
32
+ value: [...this.childNodes]
33
+ .filter((node) => !(node.nodeType === 1 && node.hasAttribute('slot')))
34
+ .map((node) => node.textContent ?? '')
35
+ .join('')
36
+ .replace(/\s+/g, ' ')
37
+ .trim(),
38
+ }),
32
39
  host: this,
33
40
  next: 'sending',
34
41
  });
@@ -65,6 +65,7 @@ declare const HelixHeadlessChat_base: (new (...args: any[]) => import("@fusion.d
65
65
  *
66
66
  * @event {HelixBeforePinEvent} helix-before-pin - Fired when the user pins or unpins the transcript by scrolling. Cancelable.
67
67
  * @event {HelixHistoryEvent} helix-history - Fired when the user reaches the oldest turn while `history` is `more`. It reports the position and cannot be canceled.
68
+ * @event {HelixPinEvent} helix-pin - Fired after the transcript is pinned or unpinned, with `true` or `false` in `detail.value`.
68
69
  * @event {HelixSendEvent} helix-send - Fired when the user sends a turn from the composer or retries a failed one. It is fired inside and handled here, so you can listen for it on the conversation. Cancelable: canceling it sends nothing and leaves the decision to you.
69
70
  *
70
71
  * @csspart actions - The controls for the conversation, at the header's trailing edge.
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  var HelixHeadlessChat_1;
8
- import { customElement, HelixBeforePinEvent, HelixElement, HelixHistoryEvent, hostAria, MixinCollection, onUnlessFalse, requestChange, VirtualizeController, } from '@fusion.dev/helix-core';
8
+ import { customElement, HelixBeforePinEvent, HelixElement, HelixHistoryEvent, HelixPinEvent, hostAria, MixinCollection, onUnlessFalse, requestChange, VirtualizeController, } from '@fusion.dev/helix-core';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { keyed } from 'lit/directives/keyed.js';
@@ -181,6 +181,7 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
181
181
  const previous = this.pinned;
182
182
  this.pinnedState = value;
183
183
  this.requestUpdate('pinned', previous);
184
+ this.dispatchEvent(new HelixPinEvent({ value: String(value) }));
184
185
  },
185
186
  event: (value) => new HelixBeforePinEvent({ value: String(value) }),
186
187
  host: this,
@@ -28,6 +28,8 @@ declare const HelixHeadlessCheckbox_base: (new (...args: any[]) => import("@fusi
28
28
  * @documentation https://fusion.dev/helix/components/checkbox
29
29
  *
30
30
  * @slot - The label.
31
+ * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
32
+ * @slot hint - Help under the control, for content richer than the `hint` text.
31
33
  *
32
34
  * @event {Event} change - The box was checked or unchecked. Relayed from the native input.
33
35
  * @event {HelixBeforeToggleEvent} helix-before-toggle - The box is about to be checked or unchecked. Cancelable: cancel it to keep its state.
@@ -46,6 +48,7 @@ declare const HelixHeadlessCheckbox_base: (new (...args: any[]) => import("@fusi
46
48
  *
47
49
  * @cssstate checked - While the box is checked.
48
50
  * @cssstate has-label - The label slot has content, so the label is drawn.
51
+ * @cssstate indeterminate - While the box shows the dash, neither checked nor unchecked.
49
52
  * @cssstate invalid - The control has an error to show, from the browser's validation or from your `feedback`.
50
53
  */
51
54
  export declare class HelixHeadlessCheckbox extends HelixHeadlessCheckbox_base {
@@ -56,6 +59,10 @@ export declare class HelixHeadlessCheckbox extends HelixHeadlessCheckbox_base {
56
59
  * Shows the box as neither checked nor unchecked.
57
60
  *
58
61
  * Presentation only. It never submits, and the first change resolves it.
62
+ *
63
+ * Not reflected, as no state is: the element resolves it itself, and a
64
+ * reflected one removed the attribute the consumer wrote. A stylesheet
65
+ * reads it as `:state(indeterminate)`.
59
66
  */
60
67
  indeterminate: boolean;
61
68
  /**
@@ -20,11 +20,14 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
20
20
  this.handleChange = (event) => {
21
21
  const input = event.target;
22
22
  const next = input.checked;
23
- if (this.requestChecked(next)) {
23
+ const taken = this.requestChecked(next);
24
+ if (taken) {
24
25
  this.indeterminate = false;
25
26
  }
26
27
  input.checked = this.checked;
27
- this.relayNativeEvent(event);
28
+ if (taken) {
29
+ this.relayNativeEvent(event);
30
+ }
28
31
  };
29
32
  this.handleLabelChange = () => {
30
33
  this.syncLabelPresence();
@@ -46,6 +49,7 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
46
49
  willUpdate(changed) {
47
50
  super.willUpdate(changed);
48
51
  this.internals.ariaRequired = this.required ? 'true' : null;
52
+ this.toggleState('indeterminate', this.indeterminate);
49
53
  if (this.indeterminate) {
50
54
  this.dashed = true;
51
55
  }
@@ -98,7 +102,7 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
98
102
  }
99
103
  };
100
104
  __decorate([
101
- property({ reflect: true, type: Boolean })
105
+ property({ type: Boolean })
102
106
  ], HelixHeadlessCheckbox.prototype, "indeterminate", void 0);
103
107
  __decorate([
104
108
  property()
@@ -16,8 +16,13 @@ declare const HelixHeadlessCheckboxGroup_base: (new (...args: any[]) => import("
16
16
  * `name` that you write once, and one place to read and write the whole set.
17
17
  *
18
18
  * `value` is the checked values, separated by commas, in the order the
19
- * checkboxes are written. Setting it checks exactly those values and unchecks
20
- * the rest.
19
+ * checkboxes are written, read from the boxes themselves. Setting it checks
20
+ * exactly those values and unchecks the rest.
21
+ *
22
+ * A checkbox is a member wherever it sits inside the group, in a row or a grid
23
+ * cell of your own, unless another group inside this one holds it. A field of
24
+ * another kind inside the group, such as an "Other" text field, sends its own
25
+ * `input` and `change` past the group.
21
26
  *
22
27
  * **The group shows the message you set in `feedback`.** It is form-associated
23
28
  * only for validity and never calls `setFormValue`, so the browser does not
@@ -29,7 +34,8 @@ declare const HelixHeadlessCheckboxGroup_base: (new (...args: any[]) => import("
29
34
  * @aliases Checkbox list, Multi-select
30
35
  * @documentation https://fusion.dev/helix/components/checkbox-group
31
36
  *
32
- * @slot - The checkboxes.
37
+ * @slot - The checkboxes, and anything laid out around them.
38
+ * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
33
39
  * @slot hint - Help under the checkboxes, for help that needs more than plain text.
34
40
  * @slot label - The group's label, for a label that needs more than plain text.
35
41
  *
@@ -47,37 +53,63 @@ declare const HelixHeadlessCheckboxGroup_base: (new (...args: any[]) => import("
47
53
  export declare class HelixHeadlessCheckboxGroup extends HelixHeadlessCheckboxGroup_base {
48
54
  /**
49
55
  * The checked values, comma separated.
56
+ *
57
+ * Read from the boxes once they are known, so a box a script ticks directly
58
+ * is in it at once. Written, it is pushed onto the boxes.
59
+ */
60
+ get value(): string;
61
+ set value(next: string);
62
+ /** The value last written, which the boxes are given at the next update. */
63
+ private declared;
64
+ /**
65
+ * Whether `declared` has yet to reach the boxes.
66
+ *
67
+ * **Pushed once and then left alone**, which is the fix for a box a script
68
+ * ticked being unticked by the next unrelated update: every update used to
69
+ * push the group's value onto every box, whatever the boxes said since.
70
+ */
71
+ private pending;
72
+ /** Watches for boxes arriving inside a wrapper, which no slot reports. */
73
+ private observer;
74
+ /** The value the required check last judged. */
75
+ private judged;
76
+ /** The boxes `member` is added to. */
77
+ private readonly watched;
78
+ /**
79
+ * Asks for an update when a box's own update moved the set.
80
+ *
81
+ * **A controller on each box, because a box changed by a script says
82
+ * nothing.** `box.checked = false` sends no event, as on a native checkbox,
83
+ * so the group heard of it only at its next unrelated update: a required
84
+ * group whose last box a script unchecked still passed `checkValidity()` and
85
+ * let its form submit. A box's update is the one thing every change to its
86
+ * state goes through, whatever made it.
50
87
  */
51
- value: string;
88
+ private readonly member;
52
89
  protected readonly cascade: DisabledCascadeController;
53
- /** The checkboxes in the group, in the order they are written. Browser only. */
90
+ /**
91
+ * The checkboxes in the group, in the order they are written. Browser only.
92
+ *
93
+ * Descendants rather than children, so a box inside a layout wrapper of the
94
+ * consumer's is a member: only direct children counted, and a box in a row
95
+ * with a description got no name and no place in the set. A box held by a
96
+ * group nested inside this one is that group's.
97
+ */
54
98
  get boxes(): HelixHeadlessCheckbox[];
99
+ /** Whether this is the nearest group around `box`. */
100
+ private holds;
55
101
  /**
56
- * Pushes the group's name and value onto the boxes.
102
+ * Pushes the group's name onto the boxes, and its value where one is waiting.
57
103
  *
58
104
  * As properties rather than attributes. A `name` written onto a box is a
59
105
  * change to markup the consumer rendered, which a React consumer's hydration
60
106
  * reports and does not patch up. A box that names itself keeps its own.
61
- */
62
- private sync;
63
- /** Whether `seed` has had its one chance. */
64
- private seeded;
65
- /**
66
- * Reads the boxes' own state into `value`, once, before anything is pushed.
67
107
  *
68
- * Seeding, and never a guard in `sync` refusing to write while `value` is
69
- * empty. The guard would answer the same case, a group written with no `value`
70
- * whose boxes declare their own `checked` being cleared on the first update,
71
- * and it also makes `group.value = ''` do nothing at all: the obvious way to
72
- * clear a group would silently not, and the last box could not be unticked
73
- * through the group either.
74
- *
75
- * The markup decides the starting set, and from then on `value` is what the
76
- * boxes follow, including when it is empty. The group's `value` is a derived
77
- * read of the boxes rather than state it owns, so reading it out of them at
78
- * the start is what it already means.
108
+ * **Nothing is pushed for a value nobody wrote**, so a group written with no
109
+ * `value` keeps what its boxes declare, and `group.value = ''` still clears
110
+ * every box, being a value somebody wrote.
79
111
  */
80
- private seed;
112
+ private sync;
81
113
  /**
82
114
  * Reads a box's requested change into `value`, and says so once.
83
115
  *
@@ -114,16 +146,17 @@ export declare class HelixHeadlessCheckboxGroup extends HelixHeadlessCheckboxGro
114
146
  private readonly handleMemberNative;
115
147
  connectedCallback(): void;
116
148
  disconnectedCallback(): void;
149
+ /** Stops listening to a box that left the group. */
150
+ private unwatch;
117
151
  protected willUpdate(changed: PropertyValues): void;
118
- protected firstUpdated(changed: PropertyValues): void;
119
152
  /**
120
153
  * Goes back to the set the markup started with.
121
154
  *
122
- * **Seeded again rather than cleared.** The form resets each box too, in the
123
- * same task and after this, so by the next update the boxes are back to
124
- * what they declared and the seed reads them. Without this the group kept
125
- * the reader's set, reported valid over boxes the reset had emptied, and
126
- * ticked them again the next time anything updated it.
155
+ * The form resets each box too, in the same task and after this, so a group
156
+ * whose markup wrote no `value` has nothing to do: its boxes return to what
157
+ * they declared, and its value is read from them. A `value` the markup wrote
158
+ * is pushed again at the next update, after the boxes have reset, and an
159
+ * update is asked for, since an unchanged value schedules none.
127
160
  */
128
161
  formResetCallback(): void;
129
162
  /**
@@ -141,8 +174,8 @@ export declare class HelixHeadlessCheckboxGroup extends HelixHeadlessCheckboxGro
141
174
  * `invalid`, which is what marks the field touched and puts the message on
142
175
  * screen.
143
176
  *
144
- * Anchored on the list, because the browser needs somewhere to point the
145
- * bubble.
177
+ * Anchored on the list, which takes no focus, so the base points the bubble
178
+ * at the first box instead.
146
179
  */
147
180
  private validate;
148
181
  protected updated(changed: PropertyValues): void;