@fusion.dev/helix-headless 0.142.1 → 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
@@ -8,6 +8,18 @@ import { CHART_PLOT, customElement, drawTooltip, HelixElement, MixinChartSource,
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
11
+ const PERCENTS = new Map();
12
+ const percentIn = (locale) => {
13
+ let found = PERCENTS.get(locale);
14
+ if (!found) {
15
+ found = new Intl.NumberFormat(locale || undefined, {
16
+ maximumFractionDigits: 0,
17
+ style: 'percent',
18
+ });
19
+ PERCENTS.set(locale, found);
20
+ }
21
+ return found;
22
+ };
11
23
  const CENTER = PLOT_WIDTH / 2;
12
24
  const EDGE = 40;
13
25
  const GAP = 9999;
@@ -36,34 +48,38 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
36
48
  }
37
49
  get shares() {
38
50
  const series = this.series;
39
- const values = (series[0]?.points ?? [])
40
- .map((point) => point.value)
41
- .map((value) => (Number.isFinite(value) && value > 0 ? value : 0));
51
+ const points = series[0]?.points ?? [];
52
+ const values = points.map((point) => Number.isFinite(point.value) && point.value > 0 ? point.value : 0);
42
53
  const total = values.reduce((sum, one) => sum + one, 0);
43
54
  if (total === 0) {
44
55
  return [];
45
56
  }
46
57
  let start = 0;
47
- return values.map((value) => {
58
+ return values.map((value, index) => {
48
59
  const fraction = value / total;
49
60
  const at = start;
50
61
  start += fraction;
51
- return { fraction, start: at };
62
+ return { fraction, row: points[index]?.at ?? index, start: at };
52
63
  });
53
64
  }
54
65
  render() {
55
66
  const shares = this.shares;
56
- return html `${this.renderPlot(shares)}
57
- `;
67
+ return html `${this.renderPlot(shares)}${shares.length === 0 ? nothing : this.renderSummary(shares)}`;
68
+ }
69
+ renderSummary(shares) {
70
+ const rows = this.sourceRows;
71
+ const parts = shares.map((share) => `${rows[share.row]?.label ?? ''} ${percentIn(this.locale).format(share.fraction)}`);
72
+ const named = this.named ? `${this.named}: ` : '';
73
+ return html `<p part="summary">${named}${parts.join(', ')}.</p>`;
58
74
  }
59
75
  renderPlot(shares) {
60
76
  return html `<div
61
- aria-hidden="true"
62
77
  id=${CHART_PLOT}
63
78
  part="plot"
64
79
  style="--donut-chart-stroke: ${this.thickness}"
65
80
  >
66
81
  <svg
82
+ aria-hidden="true"
67
83
  data-mirror=${this.sweep === 'half' ? '' : nothing}
68
84
  viewBox=${this.frame}
69
85
  xmlns="http://www.w3.org/2000/svg"
@@ -100,7 +116,11 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
100
116
  data-series=${seriesIndex(index)}
101
117
  fill="none"
102
118
  style="--chart-series: var(--chart-series-${seriesIndex(index)})"
103
- @pointerenter=${() => this.pointer.set(index)}
119
+ @pointerenter=${(event) => {
120
+ if (this.pointer.accepts(event)) {
121
+ this.pointer.set(index);
122
+ }
123
+ }}
104
124
  part="segment${active ? ' segment-active' : ''}"
105
125
  r=${this.radius}
106
126
  stroke-dasharray="${length} ${GAP}"
@@ -153,7 +173,7 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
153
173
  const y = ((CENTER + Math.sin(angle) * edge - top) / tall) * 100;
154
174
  const turned = this.sweep === 'half' && this.pointer.mirrored;
155
175
  const across = Math.cos(angle) > 0 !== turned;
156
- return drawTooltip(this.sourceRows, at, this.tag('tooltip'), {
176
+ return drawTooltip(this.sourceRows, share.row, this.tag('tooltip'), {
157
177
  fixed: this.sweep !== 'half',
158
178
  placement: Math.abs(Math.cos(angle)) > Math.abs(Math.sin(angle))
159
179
  ? across
@@ -1,4 +1,4 @@
1
- import { CommandController, HelixElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, CommandController, HelixElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessDrawer_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & typeof HelixElement;
4
4
  /**
@@ -9,11 +9,18 @@ declare const HelixHeadlessDrawer_base: (new (...args: any[]) => import("@fusion
9
9
  * `helix-before-open` and `helix-before-close` events work the same way as on
10
10
  * an accordion item.
11
11
  *
12
- * The drawer is modal by default, which means three things:
12
+ * The drawer is modal by default, which means four things:
13
13
  *
14
14
  * - focus is trapped inside
15
15
  * - Escape and a press outside close it
16
16
  * - the page behind is covered
17
+ * - the page behind does not scroll
18
+ *
19
+ * A drawer with `modal="false"` leaves the page usable and still closes on
20
+ * Escape.
21
+ *
22
+ * The panel's name is `label` when you set it. Otherwise it is the heading in
23
+ * the `header` slot, or the elements an `aria-labelledby` on the host names.
17
24
  *
18
25
  * Content stays mounted when the drawer is closed, so it is in the server
19
26
  * markup and can animate. A closed `<dialog>` is already out of the tab order
@@ -41,7 +48,7 @@ declare const HelixHeadlessDrawer_base: (new (...args: any[]) => import("@fusion
41
48
  * @event {Event} close - Sent from the host after the drawer closes, because the native `close` event does not leave the shadow root.
42
49
  * @event {HelixBeforeCloseEvent} helix-before-close - Fired before the panel closes, and cancelable.
43
50
  * @event {HelixBeforeOpenEvent} helix-before-open - Fired before the panel opens, and cancelable. Canceling it keeps the panel closed.
44
- * @event {Event} toggle - Sent when the drawer opens or closes, with `newState` and `oldState`.
51
+ * @event {ToggleEvent} toggle - Sent when the drawer opens or closes, with `newState` and `oldState`.
45
52
  *
46
53
  * @csspart content - The scrolling content region.
47
54
  * @csspart header - The header region.
@@ -65,9 +72,11 @@ export declare class HelixHeadlessDrawer extends HelixHeadlessDrawer_base {
65
72
  */
66
73
  closedBy?: 'any' | 'closerequest' | 'none';
67
74
  /**
68
- * Accessible name for the panel.
75
+ * Accessible name for the panel. When it is empty, the heading in the
76
+ * `header` slot names the panel.
69
77
  */
70
78
  label: string;
79
+ protected readonly aria: AriaForwardController;
71
80
  /**
72
81
  * Whether the drawer blocks the page behind it.
73
82
  *
@@ -126,11 +135,27 @@ export declare class HelixHeadlessDrawer extends HelixHeadlessDrawer_base {
126
135
  /**
127
136
  * A press on the backdrop, which lands on the dialog element itself.
128
137
  *
129
- * The panel fills only part of the dialog's box, so a press beside it hits
130
- * the dialog and not a child. A native `<dialog>` does not close on that, and
131
- * it is the gesture that most obviously means close.
138
+ * A native `<dialog>` does not close on that, and it is the gesture that most
139
+ * obviously means close.
140
+ *
141
+ * **The target alone is not enough.** The panel is the `<dialog>`, and a
142
+ * press in its own padding has the dialog as its target too, the same as a
143
+ * press on the backdrop. So the point is compared against the panel's box,
144
+ * as `Dialog` does.
132
145
  */
133
146
  private readonly handleBackdrop;
147
+ /** Whether the `header` slot's heading is what currently names the panel. */
148
+ private namedByHeader;
149
+ /**
150
+ * Names the panel by its heading, where nothing else names it.
151
+ *
152
+ * An element reference, because the heading is in the light DOM and the
153
+ * `<dialog>` is in this shadow root, which an IDREF cannot cross. `label` and
154
+ * an `aria-labelledby` on the host come first. A browser without element
155
+ * reflection keeps the fallback name.
156
+ */
157
+ private nameByHeader;
158
+ disconnectedCallback(): void;
134
159
  protected updated(changed: PropertyValues): void;
135
160
  protected render(): TemplateResult;
136
161
  }
@@ -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 { CommandController, customElement, HelixElement, MixinControllerOpen, onUnlessFalse, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, CommandController, customElement, HelixElement, lockScroll, MixinControllerOpen, onUnlessFalse, unlockScroll, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessDrawer = class HelixHeadlessDrawer extends MixinControllerOpen(HelixElement) {
@@ -18,6 +18,7 @@ let HelixHeadlessDrawer = class HelixHeadlessDrawer extends MixinControllerOpen(
18
18
  },
19
19
  });
20
20
  this.label = '';
21
+ this.aria = new AriaForwardController(this, { target: () => this.panel });
21
22
  this.modal = true;
22
23
  this.returned = '';
23
24
  this.handleCancel = (event) => {
@@ -31,10 +32,20 @@ let HelixHeadlessDrawer = class HelixHeadlessDrawer extends MixinControllerOpen(
31
32
  this.relayNativeEvent(event);
32
33
  };
33
34
  this.handleBackdrop = (event) => {
34
- if (event.target === this.panel && this.modal) {
35
- this.requestOpen(false);
35
+ const panel = this.panel;
36
+ if (!panel || event.target !== panel || !this.modal) {
37
+ return;
38
+ }
39
+ const box = panel.getBoundingClientRect();
40
+ if (event.clientX >= box.left &&
41
+ event.clientX <= box.right &&
42
+ event.clientY >= box.top &&
43
+ event.clientY <= box.bottom) {
44
+ return;
36
45
  }
46
+ this.requestOpen(false);
37
47
  };
48
+ this.namedByHeader = false;
38
49
  }
39
50
  get returnValue() {
40
51
  return this.panel?.returnValue ?? this.returned;
@@ -49,8 +60,35 @@ let HelixHeadlessDrawer = class HelixHeadlessDrawer extends MixinControllerOpen(
49
60
  get panel() {
50
61
  return this.partNamed('panel');
51
62
  }
63
+ nameByHeader() {
64
+ const panel = this.panel;
65
+ if (!panel || !('ariaLabelledByElements' in panel)) {
66
+ return;
67
+ }
68
+ const slot = this.renderRoot.querySelector('slot[name="header"]');
69
+ const heading = this.label || this.hasAttribute('aria-labelledby') ? [] : headingsIn(slot);
70
+ if (heading.length > 0) {
71
+ panel.ariaLabelledByElements = heading;
72
+ this.namedByHeader = true;
73
+ }
74
+ else if (this.namedByHeader) {
75
+ panel.ariaLabelledByElements = null;
76
+ this.namedByHeader = false;
77
+ }
78
+ }
79
+ disconnectedCallback() {
80
+ super.disconnectedCallback();
81
+ unlockScroll(this);
82
+ }
52
83
  updated(changed) {
53
84
  super.updated(changed);
85
+ this.nameByHeader();
86
+ if (this.open && this.modal) {
87
+ lockScroll(this);
88
+ }
89
+ else {
90
+ unlockScroll(this);
91
+ }
54
92
  const panel = this.panel;
55
93
  if (!panel) {
56
94
  return;
@@ -68,15 +106,16 @@ let HelixHeadlessDrawer = class HelixHeadlessDrawer extends MixinControllerOpen(
68
106
  }
69
107
  }
70
108
  render() {
109
+ const closedBy = this.closedBy ?? (this.modal ? undefined : 'closerequest');
71
110
  return html `<dialog
72
111
  part="panel"
73
- closedby=${this.closedBy ?? nothing}
112
+ closedby=${closedBy ?? nothing}
74
113
  aria-label=${this.label || 'Drawer'}
75
114
  @cancel=${this.handleCancel}
76
115
  @click=${this.handleBackdrop}
77
116
  @close=${this.handleClose}
78
117
  >
79
- <div part="header"><slot name="header"></slot></div>
118
+ <div part="header"><slot name="header" @slotchange=${this.nameByHeader}></slot></div>
80
119
  <div part="content"><slot></slot></div>
81
120
  </dialog>`;
82
121
  }
@@ -97,3 +136,9 @@ HelixHeadlessDrawer = __decorate([
97
136
  customElement('helix-headless-drawer')
98
137
  ], HelixHeadlessDrawer);
99
138
  export { HelixHeadlessDrawer };
139
+ const headingsIn = (slot) => {
140
+ const assigned = slot?.assignedElements({ flatten: true }) ?? [];
141
+ const heading = 'h1, h2, h3, h4, h5, h6, [role="heading"]';
142
+ const found = assigned.flatMap((one) => one.matches(heading) ? [one] : [...one.querySelectorAll(heading)].slice(0, 1));
143
+ return found.length > 0 ? found.slice(0, 1) : assigned;
144
+ };
@@ -112,21 +112,24 @@ export declare class HelixHeadlessDrilldownItem extends HelixElement {
112
112
  /** Moves focus onto the row itself. */
113
113
  focusItem(): void;
114
114
  /**
115
- * Whether this is the current row, which in a drill-down is the row with
116
- * focus.
115
+ * Whether the row has focus, which makes it the row the keyboard is on.
117
116
  *
118
- * A radio group's selection is an answer it holds; a page of rows has no
119
- * answer, only the row a reader has walked to, so focus is the whole of what
120
- * "the current one" can mean here. This is `Menu.Item`'s reading word for
121
- * word, and the two are one pattern: a menu that drills and a drill-down are
122
- * the same rows through the same controller.
117
+ * `Option` calls the same thing `active`. A page of rows holds no answer the
118
+ * way a radio group does, only the row a reader has walked to, so focus is
119
+ * the whole of what "the current one" can mean here. This is `Menu.Item`'s
120
+ * reading word for word, and the two are one pattern: a menu that drills and
121
+ * a drill-down are the same rows through the same controller.
123
122
  *
124
- * Asked of the document rather than of the shadow root. `focusItem` puts
125
- * focus on the control inside this row and the platform retargets it, so from
126
- * outside the shadow tree `activeElement` is this element already and nothing
127
- * has to walk a tree. `ShadowRoot.activeElement` is also the reading that does
128
- * not survive happy-dom, which climbs `getRootNode()` to the top and then
129
- * dereferences what it ran out of.
123
+ * The focused node is found through every shadow root and asked whether it
124
+ * is in this row's own, as `Menu.Item` does, so a drill-down inside another
125
+ * component's shadow root still knows its current row.
126
+ */
127
+ get active(): boolean;
128
+ /**
129
+ * Whether the row has focus, the same as `active`.
130
+ *
131
+ * @deprecated Read `active`. Everywhere else in the library `selected` means
132
+ * chosen out of a set, and a drill-down row is never chosen that way.
130
133
  */
131
134
  get selected(): boolean;
132
135
  /** Fires `helix-select` for this row. */
@@ -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, HelixSelectEvent } from '@fusion.dev/helix-core';
7
+ import { customElement, deepActiveElement, HelixElement, HelixSelectEvent, } from '@fusion.dev/helix-core';
8
8
  import { caretRight } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -37,8 +37,12 @@ let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixE
37
37
  focusItem() {
38
38
  this.partNamed('item')?.focus();
39
39
  }
40
+ get active() {
41
+ const active = deepActiveElement();
42
+ return active !== null && active.getRootNode() === this.renderRoot;
43
+ }
40
44
  get selected() {
41
- return this.ownerDocument.activeElement === this;
45
+ return this.active;
42
46
  }
43
47
  select() {
44
48
  if (this.disabled) {
@@ -1,4 +1,4 @@
1
- import { HelixElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessDrilldown_base: (new (...args: any[]) => import("@fusion.dev/helix-core").CollectionElement) & typeof HelixElement;
4
4
  /**
@@ -20,6 +20,11 @@ declare const HelixHeadlessDrilldown_base: (new (...args: any[]) => import("@fus
20
20
  * and reads the rest from the DOM. The behavior lives in `DrilldownController`,
21
21
  * which `Menu` shares.
22
22
  *
23
+ * The rows of the page showing are a `menu`, named by `label` on the top page
24
+ * and by the page's title below it. The back button and the title sit above
25
+ * the menu. Disabled rows stay reachable with the arrow keys, and typing a
26
+ * letter moves to the next row whose name starts with it.
27
+ *
23
28
  * @since 0.1.0
24
29
  * @status stable
25
30
  * @category navigation
@@ -36,14 +41,18 @@ declare const HelixHeadlessDrilldown_base: (new (...args: any[]) => import("@fus
36
41
  * @csspart header - The bar above a page, which holds the back button and the page's name.
37
42
  * @csspart live - A copy of the empty and loading text, in a region that screen readers announce when it changes. Visually hidden in the styled tier.
38
43
  * @csspart loading - Shown while the entries load.
39
- * @csspart page - The region the rows are in.
44
+ * @csspart page - The menu the rows are in.
40
45
  * @csspart title - The name of the page showing.
41
46
  *
42
47
  * @cssstate drilled - A row's page is showing, so a frame around the drilldown can style itself for that depth.
43
48
  */
44
49
  export declare class HelixHeadlessDrilldown extends HelixHeadlessDrilldown_base {
45
50
  /**
46
- * The region's name, read by screen readers.
51
+ * The name of the top page's menu, read by screen readers. A page below it is
52
+ * named by its title.
53
+ *
54
+ * An `aria-label` or `aria-labelledby` on the host names the top page too,
55
+ * and wins over this.
47
56
  */
48
57
  label: string;
49
58
  /**
@@ -66,8 +75,16 @@ export declare class HelixHeadlessDrilldown extends HelixHeadlessDrilldown_base
66
75
  */
67
76
  private direction?;
68
77
  private readonly drilldown;
69
- /** The rows on the page showing, which is what the keys move over. */
78
+ /**
79
+ * The rows on the page showing, which is what the keys move over.
80
+ *
81
+ * Disabled rows included. A row renders `aria-disabled` and keeps its
82
+ * `tabindex` so a reader can reach it and hear why it cannot be chosen, and
83
+ * `select()` is what refuses it.
84
+ */
70
85
  private get items();
86
+ /** Carries a host `aria-label` and `aria-labelledby` to the menu. */
87
+ protected readonly aria: AriaForwardController;
71
88
  /**
72
89
  * Moving between rows, which is `Menu`'s arrangement and not a second one.
73
90
  *
@@ -115,7 +132,6 @@ export declare class HelixHeadlessDrilldown extends HelixHeadlessDrilldown_base
115
132
  private readonly handleKeydown;
116
133
  connectedCallback(): void;
117
134
  disconnectedCallback(): void;
118
- protected willUpdate(changed: PropertyValues): void;
119
135
  protected updated(changed: PropertyValues): void;
120
136
  /** Walks the tree marking which rows are on the page showing. */
121
137
  private mark;
@@ -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, DrilldownController, HelixElement, hostAria, inlineStep, KeyboardNavigationController, keyClaimed, MixinCollection, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, DrilldownController, HelixElement, inlineStep, KeyboardNavigationController, keyClaimed, MixinCollection, slottedWords, } from '@fusion.dev/helix-core';
8
8
  import { caretLeft } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -20,14 +20,28 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
20
20
  root: () => [...this.children],
21
21
  slot: 'page',
22
22
  });
23
+ this.aria = new AriaForwardController(this, {
24
+ label: true,
25
+ target: () => this.partNamed('page'),
26
+ });
23
27
  this.keys = new KeyboardNavigationController(this, {
24
- items: () => this.items,
28
+ items: () => this.items.map((item) => ({
29
+ contains: (node) => item.contains(node),
30
+ disabled: false,
31
+ focusItem: () => item.focusItem(),
32
+ select: () => item.select(),
33
+ get selected() {
34
+ return item.active;
35
+ },
36
+ words: slottedWords(item, ['']),
37
+ })),
25
38
  reserved: () => ['ArrowLeft', 'ArrowRight'],
26
39
  selects: () => false,
40
+ typeahead: (item) => item.words,
27
41
  });
28
42
  this.handleFocusIn = (event) => {
29
43
  const item = this.drilldown.rows.find((row) => row.contains(event.target));
30
- if (item && !item.disabled) {
44
+ if (item) {
31
45
  for (const row of this.drilldown.rows) {
32
46
  row.focusable = row === item;
33
47
  }
@@ -71,7 +85,7 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
71
85
  };
72
86
  }
73
87
  get items() {
74
- return this.drilldown.rows.filter((item) => !item.disabled);
88
+ return this.drilldown.rows;
75
89
  }
76
90
  placeTabStop() {
77
91
  const items = this.items;
@@ -109,10 +123,6 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
109
123
  this.removeEventListener('keydown', this.handleKeydown);
110
124
  this.removeEventListener('focusin', this.handleFocusIn);
111
125
  }
112
- willUpdate(changed) {
113
- super.willUpdate(changed);
114
- hostAria(this.internals, { label: this.label, role: 'menu' });
115
- }
116
126
  updated(changed) {
117
127
  super.updated(changed);
118
128
  const page = this.drilldown.page;
@@ -149,9 +159,15 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
149
159
  ${unsafeSVG(caretLeft.content)}
150
160
  </svg>
151
161
  </button>
152
- <span part="title">${this.drilldown.label}</span>
162
+ <span id="title" part="title">${this.drilldown.label}</span>
153
163
  </div>`}
154
- <div aria-busy=${this.loading ? 'true' : nothing} part="page">
164
+ <div
165
+ aria-busy=${this.loading ? 'true' : nothing}
166
+ aria-label=${this.drilldown.atRoot ? this.label || nothing : nothing}
167
+ aria-labelledby=${this.drilldown.atRoot ? nothing : 'title'}
168
+ part="page"
169
+ role="menu"
170
+ >
155
171
  <slot @slotchange=${(event) => this.countRows(event)}></slot>
156
172
  ${this.renderStatus(this.hasRows)}
157
173
  </div>${this.renderLive(this.hasRows)}`;
@@ -19,9 +19,9 @@ registerElementEvents({
19
19
  'helix-remove',
20
20
  'helix-select',
21
21
  ],
22
- 'helix-headless-carousel': ['helix-select'],
23
- 'helix-headless-chat': ['helix-before-pin', 'helix-history', 'helix-send'],
24
- 'helix-headless-chat-composer': ['helix-send'],
22
+ 'helix-headless-carousel': ['helix-activate', 'helix-select'],
23
+ 'helix-headless-chat': ['helix-before-pin', 'helix-history', 'helix-pin', 'helix-send'],
24
+ 'helix-headless-chat-composer': ['helix-send', 'input'],
25
25
  'helix-headless-chat-message': ['helix-send'],
26
26
  'helix-headless-checkbox': ['change', 'helix-before-toggle', 'input'],
27
27
  'helix-headless-checkbox-group': ['change', 'helix-before-change', 'input'],
@@ -51,14 +51,25 @@ registerElementEvents({
51
51
  'input',
52
52
  'toggle',
53
53
  ],
54
- 'helix-headless-command': ['helix-before-close', 'helix-before-open', 'helix-select', 'toggle'],
54
+ 'helix-headless-command': [
55
+ 'helix-before-close',
56
+ 'helix-before-open',
57
+ 'helix-filter',
58
+ 'helix-select',
59
+ 'toggle',
60
+ ],
55
61
  'helix-headless-copy-button': ['helix-copy'],
56
62
  'helix-headless-data-table': [
63
+ 'helix-activate',
57
64
  'helix-filter',
65
+ 'helix-filtered',
58
66
  'helix-navigate',
59
67
  'helix-reorder',
68
+ 'helix-reordered',
60
69
  'helix-select',
70
+ 'helix-selected',
61
71
  'helix-sort',
72
+ 'helix-sorted',
62
73
  ],
63
74
  'helix-headless-date-input': [
64
75
  'change',
@@ -99,6 +110,7 @@ registerElementEvents({
99
110
  'helix-graph-zoom',
100
111
  ],
101
112
  'helix-headless-heatmap': ['helix-resize'],
113
+ 'helix-headless-icon': ['error'],
102
114
  'helix-headless-image': ['error', 'load'],
103
115
  'helix-headless-input': ['change', 'helix-before-change', 'input'],
104
116
  'helix-headless-kbd': ['helix-shortcut'],
@@ -112,7 +124,10 @@ registerElementEvents({
112
124
  ],
113
125
  'helix-headless-listbox': ['change', 'helix-before-change', 'helix-filter', 'input'],
114
126
  'helix-headless-map': [
127
+ 'change',
115
128
  'helix-activate',
129
+ 'helix-before-change',
130
+ 'helix-before-toggle',
116
131
  'helix-map-cluster',
117
132
  'helix-map-pan',
118
133
  'helix-map-part-change',
@@ -133,18 +148,19 @@ registerElementEvents({
133
148
  'helix-headless-map-terrain': ['helix-map-part-change'],
134
149
  'helix-headless-megamenu': ['helix-before-close', 'helix-before-open', 'toggle'],
135
150
  'helix-headless-menu': ['helix-before-close', 'helix-before-open', 'helix-select', 'toggle'],
136
- 'helix-headless-menu-item': ['helix-select'],
151
+ 'helix-headless-menu-item': ['helix-before-toggle', 'helix-select'],
137
152
  'helix-headless-notification': ['helix-dismiss', 'helix-read'],
138
- 'helix-headless-notification-group': ['helix-before-close', 'helix-before-open'],
153
+ 'helix-headless-notification-group': ['helix-before-close', 'helix-before-open', 'toggle'],
139
154
  'helix-headless-notifications': ['helix-dismiss', 'helix-read'],
140
155
  'helix-headless-number-input': ['change', 'helix-before-change', 'input'],
141
156
  'helix-headless-object-inspector': ['helix-copy'],
142
157
  'helix-headless-option': ['helix-select'],
143
158
  'helix-headless-outline': ['helix-activate'],
144
- 'helix-headless-pagination': ['helix-navigate'],
145
- 'helix-headless-pagination-size': ['helix-before-change'],
159
+ 'helix-headless-pagination': ['helix-activate', 'helix-navigate'],
160
+ 'helix-headless-pagination-size': ['change', 'helix-before-change', 'input'],
146
161
  'helix-headless-pin-input': ['change', 'helix-before-change', 'helix-complete', 'input'],
147
162
  'helix-headless-popover': ['helix-before-close', 'helix-before-open', 'toggle'],
163
+ 'helix-headless-qr-code': ['error'],
148
164
  'helix-headless-radio': ['helix-select'],
149
165
  'helix-headless-radio-group': ['change', 'helix-before-change', 'helix-select', 'input'],
150
166
  'helix-headless-rating': ['change', 'helix-before-change', 'input'],
@@ -153,6 +169,7 @@ registerElementEvents({
153
169
  'helix-before-change',
154
170
  'helix-before-close',
155
171
  'helix-before-open',
172
+ 'helix-filter',
156
173
  'input',
157
174
  'toggle',
158
175
  ],
@@ -164,11 +181,17 @@ registerElementEvents({
164
181
  'toggle',
165
182
  ],
166
183
  'helix-headless-speed-dial-item': ['helix-select'],
167
- 'helix-headless-splitter': ['helix-before-collapse', 'helix-before-resize'],
184
+ 'helix-headless-splitter': [
185
+ 'helix-before-collapse',
186
+ 'helix-before-resize',
187
+ 'helix-collapse',
188
+ 'helix-resized',
189
+ ],
168
190
  'helix-headless-step': ['helix-select'],
169
191
  'helix-headless-steps': ['helix-activate', 'helix-before-activate', 'helix-select'],
170
192
  'helix-headless-switch': ['change', 'helix-before-toggle', 'input'],
171
193
  'helix-headless-tab': ['helix-select'],
194
+ 'helix-headless-table': ['helix-sort'],
172
195
  'helix-headless-table-column': ['helix-before-resize'],
173
196
  'helix-headless-tabs': ['helix-activate', 'helix-before-activate', 'helix-select'],
174
197
  'helix-headless-tag': ['helix-dismiss'],
@@ -188,10 +211,13 @@ registerElementEvents({
188
211
  'helix-headless-toggle-group': ['change', 'helix-before-change', 'helix-select', 'input'],
189
212
  'helix-headless-tooltip': ['helix-before-close', 'helix-before-open', 'toggle'],
190
213
  'helix-headless-tour': [
214
+ 'helix-activate',
191
215
  'helix-before-activate',
192
216
  'helix-before-close',
217
+ 'helix-before-open',
193
218
  'helix-complete',
194
219
  'helix-tour-ask',
220
+ 'toggle',
195
221
  ],
196
222
  'helix-headless-tour-stop': ['helix-tour-ask'],
197
223
  'helix-headless-tree': ['helix-activate', 'helix-before-activate', 'helix-select'],
@@ -24,6 +24,7 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
24
24
  *
25
25
  * @slot feedback - The message that says what is wrong with the chosen files.
26
26
  * @slot hint - Help under the field.
27
+ * @slot label - The label, for content richer than the `label` text.
27
28
  * @slot trigger - The content that opens the picker when pressed.
28
29
  *
29
30
  * @event {Event} change - Fired after the value changes.
@@ -58,7 +59,10 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
58
59
  export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base {
59
60
  protected readonly aria: AriaForwardController;
60
61
  /**
61
- * Which files the picker offers, as a native `accept` list.
62
+ * Which files the picker offers and a drop takes, as a native `accept` list.
63
+ *
64
+ * A dropped file the list does not name is left out, because the picker's
65
+ * filter is the only check the platform makes and a drop never meets it.
62
66
  */
63
67
  accept: string;
64
68
  /**
@@ -185,8 +189,21 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
185
189
  private readonly handleDragOver;
186
190
  private readonly handleDragLeave;
187
191
  private readonly handleDrop;
192
+ /**
193
+ * Removes a file and keeps focus in the field.
194
+ *
195
+ * The pressed button goes with its file, so focus moves to the file that took
196
+ * its place, or to the one before it, or to the input once the list is empty:
197
+ * left alone, it fell to the page's body.
198
+ */
188
199
  private readonly handleRemove;
189
200
  protected firstUpdated(changed: PropertyValues): void;
201
+ /**
202
+ * Reports again on every update, so a new `name` or `required` reaches the
203
+ * form. Only a pick and the first render reported, so the form kept the old
204
+ * name and the old verdict.
205
+ */
206
+ protected updated(changed: PropertyValues): void;
190
207
  /** Moves focus to the native file input, as a label pointing at it would. */
191
208
  focus(options?: FocusOptions): void;
192
209
  /**