@fusion.dev/helix-headless 0.146.0 → 0.147.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/custom-elements.json +5266 -785
  2. package/dist/accordion/item/accordion-item.d.ts +38 -19
  3. package/dist/accordion/item/accordion-item.js +31 -9
  4. package/dist/accordion/root/accordion-root.d.ts +8 -2
  5. package/dist/accordion/root/accordion-root.js +3 -3
  6. package/dist/announcement/root/announcement-root.d.ts +13 -4
  7. package/dist/announcement/root/announcement-root.js +34 -6
  8. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  9. package/dist/area-chart/root/area-chart-root.js +11 -3
  10. package/dist/avatar/root/avatar-root.d.ts +31 -1
  11. package/dist/avatar/root/avatar-root.js +11 -1
  12. package/dist/badge/root/badge-root.d.ts +57 -6
  13. package/dist/badge/root/badge-root.js +88 -5
  14. package/dist/bar-chart/root/bar-chart-root.js +11 -20
  15. package/dist/bar-chart/stack.d.ts +10 -0
  16. package/dist/bar-chart/stack.js +8 -0
  17. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  18. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  19. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -2
  20. package/dist/button/root/button-root.d.ts +24 -38
  21. package/dist/button/root/button-root.js +6 -37
  22. package/dist/button/submitter.d.ts +27 -0
  23. package/dist/button/submitter.js +25 -0
  24. package/dist/card/root/card-root.d.ts +12 -1
  25. package/dist/card/root/card-root.js +11 -4
  26. package/dist/chat/root/chat-root.d.ts +15 -1
  27. package/dist/chat/root/chat-root.js +11 -2
  28. package/dist/checkbox/root/checkbox-root.js +5 -7
  29. package/dist/checkbox-group/root/checkbox-group-root.js +4 -3
  30. package/dist/checkbox-group/select-all.d.ts +0 -6
  31. package/dist/checkbox-group/select-all.js +0 -5
  32. package/dist/code-block/root/code-block-root.d.ts +35 -14
  33. package/dist/code-block/root/code-block-root.js +17 -3
  34. package/dist/color-input/root/color-input-root.d.ts +11 -0
  35. package/dist/color-input/root/color-input-root.js +12 -5
  36. package/dist/color-picker/channels.js +1 -2
  37. package/dist/color-picker/root/color-picker-root.d.ts +32 -24
  38. package/dist/color-picker/root/color-picker-root.js +101 -51
  39. package/dist/color-picker/wheel.d.ts +17 -0
  40. package/dist/color-picker/wheel.js +6 -0
  41. package/dist/combobox/root/combobox-root.d.ts +13 -0
  42. package/dist/combobox/root/combobox-root.js +14 -1
  43. package/dist/command/root/command-root.d.ts +3 -1
  44. package/dist/command/root/command-root.js +2 -19
  45. package/dist/copy-button/root/copy-button-root.d.ts +16 -1
  46. package/dist/copy-button/root/copy-button-root.js +7 -2
  47. package/dist/data-table/keys.d.ts +0 -18
  48. package/dist/data-table/keys.js +1 -11
  49. package/dist/data-table/root/data-table-root.js +13 -7
  50. package/dist/date-input/root/date-input-root.d.ts +8 -1
  51. package/dist/date-input/root/date-input-root.js +8 -5
  52. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  53. package/dist/date-picker/root/date-picker-root.js +21 -16
  54. package/dist/description-list/item/description-list-item.d.ts +18 -4
  55. package/dist/description-list/item/description-list-item.js +8 -4
  56. package/dist/donut-chart/root/donut-chart-root.js +13 -6
  57. package/dist/donut-chart/slice-label.d.ts +35 -0
  58. package/dist/donut-chart/slice-label.js +11 -0
  59. package/dist/drawer/root/drawer-root.d.ts +4 -2
  60. package/dist/drawer/root/drawer-root.js +15 -3
  61. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  62. package/dist/drilldown/item/drilldown-item.js +39 -4
  63. package/dist/drilldown/root/drilldown-root.js +3 -2
  64. package/dist/element-events.generated.js +4 -1
  65. package/dist/empty/root/empty-root.d.ts +31 -1
  66. package/dist/empty/root/empty-root.js +26 -5
  67. package/dist/file-input/root/file-input-root.d.ts +34 -6
  68. package/dist/file-input/root/file-input-root.js +54 -19
  69. package/dist/file-input/status.d.ts +24 -0
  70. package/dist/file-input/status.js +10 -0
  71. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  72. package/dist/filter-input/chip/filter-chip.js +11 -2
  73. package/dist/filter-input/root/filter-input-root.d.ts +8 -0
  74. package/dist/filter-input/root/filter-input-root.js +17 -27
  75. package/dist/format-date/options.d.ts +3 -4
  76. package/dist/format-date/options.js +2 -2
  77. package/dist/format-date/root/format-date-root.d.ts +18 -1
  78. package/dist/format-date/root/format-date-root.js +19 -23
  79. package/dist/heatmap/root/buckets.d.ts +21 -0
  80. package/dist/heatmap/root/buckets.js +34 -0
  81. package/dist/heatmap/root/heatmap-root.d.ts +64 -3
  82. package/dist/heatmap/root/heatmap-root.js +89 -19
  83. package/dist/icon/index.d.ts +1 -1
  84. package/dist/icon/index.js +1 -1
  85. package/dist/icon/registry.d.ts +26 -1
  86. package/dist/icon/registry.js +12 -1
  87. package/dist/icon/root/icon-root.d.ts +13 -0
  88. package/dist/icon/root/icon-root.js +23 -2
  89. package/dist/icon/root/index.d.ts +1 -1
  90. package/dist/icon/root/index.js +1 -1
  91. package/dist/image/root/image-root.d.ts +9 -0
  92. package/dist/image/root/image-root.js +6 -1
  93. package/dist/input/root/input-root.d.ts +23 -2
  94. package/dist/input/root/input-root.js +12 -8
  95. package/dist/kbd/keys.d.ts +29 -0
  96. package/dist/kbd/keys.js +52 -0
  97. package/dist/kbd/root/kbd-root.d.ts +20 -3
  98. package/dist/kbd/root/kbd-root.js +24 -50
  99. package/dist/legend/hidden-series.d.ts +10 -2
  100. package/dist/legend/hidden-series.js +5 -2
  101. package/dist/legend/root/legend-root.d.ts +25 -7
  102. package/dist/legend/root/legend-root.js +22 -10
  103. package/dist/lightbox/root/lightbox-root.d.ts +50 -5
  104. package/dist/lightbox/root/lightbox-root.js +76 -8
  105. package/dist/lightbox/zoom.d.ts +2 -0
  106. package/dist/lightbox/zoom.js +5 -3
  107. package/dist/line-chart/root/line-chart-root.d.ts +16 -0
  108. package/dist/line-chart/root/line-chart-root.js +10 -2
  109. package/dist/list/root/list-root.d.ts +10 -2
  110. package/dist/list/root/list-root.js +26 -5
  111. package/dist/list/selection.d.ts +17 -0
  112. package/dist/list/selection.js +16 -0
  113. package/dist/listbox/root/listbox-root.d.ts +9 -5
  114. package/dist/listbox/root/listbox-root.js +29 -37
  115. package/dist/megamenu/hand-over.d.ts +75 -0
  116. package/dist/megamenu/hand-over.js +67 -0
  117. package/dist/megamenu/root/megamenu-root.d.ts +38 -14
  118. package/dist/megamenu/root/megamenu-root.js +36 -50
  119. package/dist/menu/item/menu-item.js +4 -4
  120. package/dist/menu/root/menu-root.d.ts +3 -0
  121. package/dist/meter/root/meter-root.d.ts +42 -0
  122. package/dist/meter/root/meter-root.js +33 -7
  123. package/dist/notifications/root/notifications-root.d.ts +10 -1
  124. package/dist/notifications/root/notifications-root.js +11 -1
  125. package/dist/number-input/grain.d.ts +28 -0
  126. package/dist/number-input/grain.js +14 -0
  127. package/dist/number-input/root/number-input-root.d.ts +17 -1
  128. package/dist/number-input/root/number-input-root.js +29 -18
  129. package/dist/object-inspector/root/object-inspector-root.d.ts +13 -6
  130. package/dist/object-inspector/root/object-inspector-root.js +18 -17
  131. package/dist/option/index.d.ts +1 -0
  132. package/dist/option/index.js +1 -0
  133. package/dist/option/separator/option-separator.d.ts +30 -0
  134. package/dist/option/separator/option-separator.js +21 -0
  135. package/dist/outline/root/outline-root.d.ts +12 -0
  136. package/dist/outline/root/outline-root.js +5 -19
  137. package/dist/pagination/size/pagination-size.d.ts +5 -0
  138. package/dist/pagination/size/pagination-size.js +4 -2
  139. package/dist/pin-input/code.d.ts +28 -1
  140. package/dist/pin-input/code.js +12 -0
  141. package/dist/pin-input/root/pin-input-root.d.ts +98 -121
  142. package/dist/pin-input/root/pin-input-root.js +157 -148
  143. package/dist/popover/hide-outside.js +2 -9
  144. package/dist/popover/root/popover-root.d.ts +7 -1
  145. package/dist/popover/root/popover-root.js +9 -1
  146. package/dist/progress/root/progress-root.d.ts +27 -0
  147. package/dist/progress/root/progress-root.js +25 -7
  148. package/dist/qr-code/export.d.ts +19 -0
  149. package/dist/qr-code/export.js +14 -0
  150. package/dist/qr-code/name.d.ts +11 -0
  151. package/dist/qr-code/name.js +7 -0
  152. package/dist/qr-code/root/qr-code-root.d.ts +17 -6
  153. package/dist/qr-code/root/qr-code-root.js +10 -12
  154. package/dist/radio/group/radio-group.d.ts +15 -0
  155. package/dist/radio/root/radio-root.js +2 -2
  156. package/dist/rating/root/rating-root.d.ts +22 -9
  157. package/dist/rating/root/rating-root.js +15 -39
  158. package/dist/scatter-chart/root/scatter-chart-root.d.ts +30 -0
  159. package/dist/scatter-chart/root/scatter-chart-root.js +34 -7
  160. package/dist/scroll-area/scrollbar.d.ts +0 -8
  161. package/dist/scroll-area/scrollbar.js +1 -1
  162. package/dist/select/align.d.ts +47 -0
  163. package/dist/select/align.js +7 -0
  164. package/dist/select/root/select-root.d.ts +59 -3
  165. package/dist/select/root/select-root.js +72 -12
  166. package/dist/slider/root/slider-root.d.ts +18 -1
  167. package/dist/slider/root/slider-root.js +22 -0
  168. package/dist/sparkline/readings.d.ts +30 -12
  169. package/dist/sparkline/readings.js +32 -15
  170. package/dist/sparkline/root/sparkline-root.d.ts +46 -3
  171. package/dist/sparkline/root/sparkline-root.js +60 -21
  172. package/dist/speed-dial/item/speed-dial-item.d.ts +1 -1
  173. package/dist/speed-dial/item/speed-dial-item.js +2 -2
  174. package/dist/speed-dial/root/speed-dial-root.d.ts +6 -0
  175. package/dist/speed-dial/root/speed-dial-root.js +9 -1
  176. package/dist/splitter/root/splitter-root.d.ts +3 -0
  177. package/dist/splitter/root/splitter-root.js +2 -0
  178. package/dist/steps/root/steps-root.d.ts +8 -1
  179. package/dist/steps/root/steps-root.js +6 -1
  180. package/dist/steps/step/step.d.ts +1 -0
  181. package/dist/steps/step/step.js +1 -1
  182. package/dist/table/column/table-column.d.ts +6 -0
  183. package/dist/table/column/table-column.js +5 -1
  184. package/dist/table/root/table-root.js +13 -6
  185. package/dist/table/row/table-row.d.ts +24 -0
  186. package/dist/table/row/table-row.js +37 -1
  187. package/dist/tabs/root/tabs-root.d.ts +5 -3
  188. package/dist/tag/group/tag-group.d.ts +16 -4
  189. package/dist/tag/group/tag-group.js +19 -7
  190. package/dist/textarea/root/textarea-root.d.ts +14 -2
  191. package/dist/textarea/root/textarea-root.js +6 -7
  192. package/dist/time-input/root/time-input-root.d.ts +5 -1
  193. package/dist/time-input/root/time-input-root.js +10 -7
  194. package/dist/time-picker/root/time-picker-root.d.ts +11 -14
  195. package/dist/time-picker/root/time-picker-root.js +15 -23
  196. package/dist/toast/item/toast-item.d.ts +3 -3
  197. package/dist/toast/item/toast-item.js +0 -1
  198. package/dist/toast/root/toast-root.d.ts +28 -4
  199. package/dist/toast/root/toast-root.js +58 -12
  200. package/dist/toast/words.d.ts +12 -0
  201. package/dist/toast/words.js +25 -0
  202. package/dist/toggle/group/toggle-group.d.ts +29 -5
  203. package/dist/toggle/group/toggle-group.js +26 -41
  204. package/dist/toggle/root/toggle-root.js +2 -4
  205. package/dist/toolbar/root/toolbar-root.d.ts +21 -5
  206. package/dist/toolbar/root/toolbar-root.js +52 -8
  207. package/dist/tooltip/root/tooltip-root.d.ts +7 -1
  208. package/dist/tooltip/root/tooltip-root.js +10 -1
  209. package/dist/tour/root/tour-root.d.ts +15 -0
  210. package/dist/tour/root/tour-root.js +20 -1
  211. package/dist/tour/stop/tour-stop.d.ts +3 -0
  212. package/dist/tour/stop/tour-stop.js +3 -3
  213. package/dist/tree/root/tree-root.js +2 -2
  214. package/dist/trend/figure.d.ts +16 -0
  215. package/dist/trend/figure.js +8 -0
  216. package/dist/trend/root/trend-root.d.ts +41 -2
  217. package/dist/trend/root/trend-root.js +29 -3
  218. package/dist/video/root/extra-controls.d.ts +12 -0
  219. package/dist/video/root/extra-controls.js +6 -2
  220. package/dist/video/root/video-root.d.ts +5 -1
  221. package/dist/video-playlist/root/video-playlist-root.d.ts +11 -1
  222. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  223. package/dist/virtual-list/fill.d.ts +11 -0
  224. package/dist/virtual-list/fill.js +9 -0
  225. package/dist/virtual-list/keys.d.ts +16 -7
  226. package/dist/virtual-list/keys.js +17 -6
  227. package/dist/virtual-list/plan.d.ts +17 -1
  228. package/dist/virtual-list/plan.js +13 -2
  229. package/dist/virtual-list/root/virtual-list-root.d.ts +25 -3
  230. package/dist/virtual-list/root/virtual-list-root.js +48 -24
  231. package/lint.json +1 -1
  232. package/package.json +5 -5
  233. package/types/jsx.d.ts +2979 -738
  234. package/types/svelte.d.ts +1 -0
  235. package/types/vue.d.ts +619 -202
  236. package/dist/color-picker/swatches.d.ts +0 -12
  237. package/dist/color-picker/swatches.js +0 -26
  238. package/dist/rating/steps.d.ts +0 -7
  239. package/dist/rating/steps.js +0 -9
@@ -4,14 +4,18 @@ 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 } from '@fusion.dev/helix-core';
7
+ import { customElement, fillTemplate, formatNumber, HelixElement } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.label = '';
14
+ this.locale = '';
14
15
  this.value = '';
16
+ this.handleSlotchange = () => {
17
+ this.describe();
18
+ };
15
19
  }
16
20
  get given() {
17
21
  return String(this.value ?? '');
@@ -19,18 +23,91 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
19
23
  get blank() {
20
24
  return this.given.trim() !== '' && Number(this.given) === 0;
21
25
  }
26
+ get capped() {
27
+ const count = Number(this.given);
28
+ return this.max !== undefined && Number.isFinite(count) && count > this.max;
29
+ }
22
30
  get text() {
31
+ const write = (count) => this.formatOptions ? formatNumber(count, this.locale, this.formatOptions) : String(count);
32
+ if (this.capped) {
33
+ return `${write(this.max)}+`;
34
+ }
23
35
  const count = Number(this.given);
24
- if (this.max !== undefined && Number.isFinite(count) && count > this.max) {
25
- return `${this.max}+`;
36
+ return this.formatOptions && this.given.trim() !== '' && Number.isFinite(count)
37
+ ? write(count)
38
+ : this.given;
39
+ }
40
+ get words() {
41
+ const text = this.text;
42
+ if (this.blank || (text === '' && this.label === '')) {
43
+ return '';
44
+ }
45
+ return fillTemplate(this.label, { count: text }) || text;
46
+ }
47
+ wrapped() {
48
+ const found = this.renderRoot
49
+ .querySelector('slot:not([name])')
50
+ ?.assignedElements({ flatten: true })
51
+ .find((element) => element instanceof HTMLElement &&
52
+ (element.tabIndex >= 0 || element.shadowRoot?.delegatesFocus === true));
53
+ const inner = found?.shadowRoot?.delegatesFocus
54
+ ? found.shadowRoot.querySelector('a[href], button, input, select, textarea')
55
+ : null;
56
+ return inner ?? found;
57
+ }
58
+ firstUpdated(changed) {
59
+ super.firstUpdated(changed);
60
+ requestAnimationFrame(() => this.describe());
61
+ }
62
+ describe() {
63
+ const words = this.words;
64
+ const control = words === '' ? undefined : this.wrapped();
65
+ if (this.described && this.described !== control) {
66
+ this.undescribe();
67
+ }
68
+ if (!control || !('ariaDescribedByElements' in control)) {
69
+ return;
70
+ }
71
+ const own = control.getAttribute('aria-describedby') || control.ariaDescribedByElements?.length;
72
+ if (control !== this.described && own) {
73
+ return;
26
74
  }
27
- return this.given;
75
+ this.internals.ariaLabel = words;
76
+ control.ariaDescribedByElements = [this];
77
+ this.described = control;
78
+ }
79
+ undescribe() {
80
+ if (this.described?.ariaDescribedByElements?.includes(this)) {
81
+ this.described.ariaDescribedByElements = null;
82
+ }
83
+ this.described = undefined;
84
+ this.internals.ariaLabel = null;
85
+ }
86
+ disconnectedCallback() {
87
+ super.disconnectedCallback();
88
+ this.undescribe();
89
+ }
90
+ connectedCallback() {
91
+ super.connectedCallback();
92
+ if (this.hasUpdated) {
93
+ this.describe();
94
+ }
95
+ }
96
+ willUpdate(changed) {
97
+ super.willUpdate(changed);
98
+ this.toggleState('capped', this.capped);
99
+ this.toggleState('dot', this.given.trim() === '');
100
+ this.toggleState('blank', this.blank);
101
+ }
102
+ updated(changed) {
103
+ super.updated(changed);
104
+ this.describe();
28
105
  }
29
106
  render() {
30
107
  const blank = this.blank;
31
108
  const text = this.text;
32
109
  const announced = !blank && (text !== '' || this.label !== '');
33
- return html `<slot></slot>
110
+ return html `<slot @slotchange=${this.handleSlotchange}></slot>
34
111
  <span
35
112
  part="indicator"
36
113
  data-blank=${blank ? '' : nothing}
@@ -41,9 +118,15 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
41
118
  >`;
42
119
  }
43
120
  };
121
+ __decorate([
122
+ property({ attribute: 'format-options', type: Object })
123
+ ], HelixHeadlessBadge.prototype, "formatOptions", void 0);
44
124
  __decorate([
45
125
  property()
46
126
  ], HelixHeadlessBadge.prototype, "label", void 0);
127
+ __decorate([
128
+ property()
129
+ ], HelixHeadlessBadge.prototype, "locale", void 0);
47
130
  __decorate([
48
131
  property({ type: Number })
49
132
  ], HelixHeadlessBadge.prototype, "max", void 0);
@@ -4,10 +4,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesNames, ticks, unbroken, } from '@fusion.dev/helix-core';
7
+ import { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, seriesNames, ticks, unbroken, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
- import { stackEnds, stackSpans } from '../stack.js';
10
+ import { stackEnds, stackedReadings, stackSpans } from '../stack.js';
11
11
  import '../../tooltip/index.js';
12
12
  let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource(HelixElement) {
13
13
  constructor() {
@@ -38,23 +38,13 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
38
38
  return html `<div id=${CHART_PLOT} part="plot"></div>`;
39
39
  }
40
40
  const scaled = this.scaled;
41
- const own = (one) => scaled.has(one.name.trim().toLowerCase());
41
+ const own = (one) => scaled.has(seriesKey(one.name));
42
42
  const shown = series.filter((one) => this.showsSeries(one.name));
43
43
  const values = shown.flatMap((one) => one.points.map((point) => point.value));
44
44
  const lined = this.lined;
45
- const readingsOf = (group) => {
46
- if (!this.stacked) {
47
- return group.flatMap((one) => one.points.map((point) => point.value));
48
- }
49
- const columns = group.filter((one) => !lined.has(one.name.trim().toLowerCase()));
50
- const slots = columns.map((one) => byRow(one, categories.length));
51
- return [
52
- ...group
53
- .filter((one) => lined.has(one.name.trim().toLowerCase()))
54
- .flatMap((one) => one.points.map((point) => point.value)),
55
- ...stackEnds(categories.map((_, at) => slots.map((one) => one[at]?.value))),
56
- ];
57
- };
45
+ const readingsOf = (group) => this.stacked
46
+ ? stackedReadings(group, categories.length, (one) => lined.has(seriesKey(one.name)))
47
+ : group.flatMap((one) => one.points.map((point) => point.value));
58
48
  const on = (which) => {
59
49
  const found = readingsOf(shown.filter(which));
60
50
  return found.length > 0 ? found : values.length > 0 ? values : [0];
@@ -96,11 +86,11 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
96
86
  const slot = 100 / categories.length;
97
87
  const lined = this.lined;
98
88
  const scaled = this.scaled;
99
- const own = (one) => scaled.has(one.name.trim().toLowerCase());
89
+ const own = (one) => scaled.has(seriesKey(one.name));
100
90
  const against = (one) => z !== undefined && own(one) ? z : y;
101
91
  const columns = series
102
92
  .map((one, index) => ({ one, slots: byRow(one, categories.length), tone: index }))
103
- .filter(({ one }) => !lined.has(one.name.trim().toLowerCase()) && this.showsSeries(one.name));
93
+ .filter(({ one }) => !lined.has(seriesKey(one.name)) && this.showsSeries(one.name));
104
94
  const abreast = this.stacked ? 1 : columns.length;
105
95
  const stride = (slot * 0.72) / Math.max(abreast, 1);
106
96
  const gap = abreast > 1 ? stride * 0.08 : 0;
@@ -178,7 +168,7 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
178
168
  : bar(point.value, index, depth, tone, against(one));
179
169
  })}
180
170
  </g>`)}
181
- ${series.map((one) => lined.has(one.name.trim().toLowerCase()) && this.showsSeries(one.name)
171
+ ${series.map((one) => lined.has(seriesKey(one.name)) && this.showsSeries(one.name)
182
172
  ? this.renderLine(one, series.indexOf(one), slot, against(one), sideways)
183
173
  : nothing)}
184
174
  </svg>
@@ -269,8 +259,9 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
269
259
  .map((one) => byRow(one, this.readings)[at]?.value)
270
260
  .filter((one) => one !== undefined);
271
261
  const values = this.stacked ? stackEnds([reached]) : reached;
262
+ const lined = this.lined;
272
263
  const all = this.stacked
273
- ? stackEnds(Array.from({ length: this.readings }, (_, row) => shown.map((one) => byRow(one, this.readings)[row]?.value)))
264
+ ? stackedReadings(shown, this.readings, (one) => lined.has(seriesKey(one.name)))
274
265
  : shown.flatMap((one) => one.points.map((point) => point.value));
275
266
  const [low, high] = extent(all.length > 0 ? all : [0]);
276
267
  const domain = [Math.min(0, low), Math.max(0, high)];
@@ -1,3 +1,4 @@
1
+ import { type Series } from '@fusion.dev/helix-core';
1
2
  /** Where a column starts and ends along the measure, in the data's own units. */
2
3
  export type Span = readonly [from: number, to: number];
3
4
  /**
@@ -13,3 +14,12 @@ export declare const stackSpans: (values: readonly (number | undefined)[]) => (S
13
14
  * of negatives. `categories` holds each category's readings, one per column.
14
15
  */
15
16
  export declare const stackEnds: (categories: readonly (readonly (number | undefined)[])[]) => number[];
17
+ /**
18
+ * The readings a stacked scale has to hold, over `count` categories: every
19
+ * reading of a series drawn as a line, which `lined` says, and the top and
20
+ * foot of each category's stack of the others.
21
+ *
22
+ * The plot's scale and the tooltip's placing ask the same question, and
23
+ * answered apart they disagreed about the lines.
24
+ */
25
+ export declare const stackedReadings: (group: readonly Series[], count: number, lined: (one: Series) => boolean) => number[];
@@ -1,3 +1,4 @@
1
+ import { byRow } from '@fusion.dev/helix-core';
1
2
  export const stackSpans = (values) => {
2
3
  let up = 0;
3
4
  let down = 0;
@@ -17,3 +18,10 @@ export const stackSpans = (values) => {
17
18
  });
18
19
  };
19
20
  export const stackEnds = (categories) => categories.flatMap((values) => stackSpans(values).flatMap((span) => (span === undefined ? [] : [span[1]])));
21
+ export const stackedReadings = (group, count, lined) => {
22
+ const slots = group.filter((one) => !lined(one)).map((one) => byRow(one, count));
23
+ return [
24
+ ...group.filter(lined).flatMap((one) => one.points.map((point) => point.value)),
25
+ ...stackEnds(Array.from({ length: count }, (_, at) => slots.map((one) => one[at]?.value))),
26
+ ];
27
+ };
@@ -37,6 +37,9 @@ import { type PropertyValues, type TemplateResult } from 'lit';
37
37
  * @csspart link - The crumb itself, which is an `<a>` where `href` is set.
38
38
  * @csspart separator - The separator preceding the item.
39
39
  * @csspart separator-mark - The chevron between crumbs, shown when no separator is slotted.
40
+ *
41
+ * @cssstate end - While the `end` slot holds something, so a stylesheet can join
42
+ * the label and what follows it into one control.
40
43
  */
41
44
  export declare class HelixHeadlessBreadcrumb extends HelixElement {
42
45
  /**
@@ -74,6 +77,17 @@ export declare class HelixHeadlessBreadcrumb extends HelixElement {
74
77
  */
75
78
  target: string;
76
79
  protected willUpdate(changed: PropertyValues): void;
80
+ protected updated(changed: PropertyValues): void;
81
+ /**
82
+ * Says whether anything sits after the label.
83
+ *
84
+ * A switcher's trigger in `end` makes the crumb a split control, and its
85
+ * label is drawn as the first half of it. No selector can ask a slot what was
86
+ * assigned to it, so this is script. Answered from a DOM, so the server
87
+ * writes the resting answer and the first client update corrects it.
88
+ */
89
+ private markEnd;
90
+ private readonly handleEnd;
77
91
  /**
78
92
  * The mark between one crumb and the next.
79
93
  *
@@ -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, hostAria, slotFilled } from '@fusion.dev/helix-core';
8
8
  import { caretRight } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
@@ -15,12 +15,22 @@ let HelixHeadlessBreadcrumb = class HelixHeadlessBreadcrumb extends HelixElement
15
15
  this.current = false;
16
16
  this.rel = '';
17
17
  this.target = '';
18
+ this.handleEnd = () => {
19
+ this.markEnd();
20
+ };
18
21
  }
19
22
  willUpdate(changed) {
20
23
  super.willUpdate(changed);
21
24
  hostAria(this.internals, { role: 'listitem' });
22
25
  this.internals.ariaCurrent = this.current ? 'page' : null;
23
26
  }
27
+ updated(changed) {
28
+ super.updated(changed);
29
+ this.markEnd();
30
+ }
31
+ markEnd() {
32
+ this.toggleState('end', slotFilled(this.renderRoot?.querySelector('slot[name="end"]')));
33
+ }
24
34
  renderSeparator() {
25
35
  return html `<slot name="separator"
26
36
  ><svg part="separator-mark" data-mirror fill="currentColor" viewBox=${caretRight.viewBox}>
@@ -41,7 +51,7 @@ let HelixHeadlessBreadcrumb = class HelixHeadlessBreadcrumb extends HelixElement
41
51
  rel=${this.rel || nothing}
42
52
  >${label}</a
43
53
  >`
44
- : html `<span part="link">${label}</span>`}<slot name="end"></slot>`;
54
+ : html `<span part="link">${label}</span>`}<slot name="end" @slotchange=${this.handleEnd}></slot>`;
45
55
  }
46
56
  };
47
57
  __decorate([
@@ -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, fillTemplate, HelixElement, hostAria } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { repeat } from 'lit/directives/repeat.js';
@@ -55,7 +55,7 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
55
55
  ><button
56
56
  part="expand"
57
57
  type="button"
58
- aria-label=${this.expandLabel.replace('{count}', String(folded))}
58
+ aria-label=${fillTemplate(this.expandLabel, { count: String(folded) })}
59
59
  @click=${this.expand}
60
60
  >
61
61
  …
@@ -18,13 +18,12 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
18
18
  *
19
19
  * The inner control is always `type="button"`, because a
20
20
  * `<button type="submit">` inside a shadow root cannot submit a form outside
21
- * it. To submit, the button calls `requestSubmit()` on its form, which runs
22
- * validation, unlike `submit()`.
23
- *
24
- * A `submit` listener reads `event.submitter` as `null`, because the platform
25
- * accepts only a native submit button there. The button's `name` and `value`
26
- * are in the form data, so read them from `new FormData(form)` to tell two
27
- * submit buttons apart.
21
+ * it. To submit, the button puts a hidden native submit button carrying its
22
+ * `name`, `value` and `form*` overrides into the form, calls
23
+ * `requestSubmit()` with it, which runs validation, unlike `submit()`, and
24
+ * takes it out again, as Shoelace and Web Awesome do. A `submit` listener
25
+ * reads that button as `event.submitter`, so `event.submitter.value` and
26
+ * `new FormData(form, event.submitter)` tell two submit buttons apart.
28
27
  *
29
28
  * As the first submit button in its form, it is the form's default button:
30
29
  * Enter in a text field of the form presses it, as it presses a native one.
@@ -54,6 +53,7 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
54
53
  *
55
54
  * @csspart button - The inner control.
56
55
  * @csspart live - The live region that says `pending-label` when the button becomes pending under focus. It is rendered from the first time the button is pending, and the styled button clips it out of view.
56
+ * @csspart spinner - The ring a pending button turns, an empty element the styled button draws over the words. It is rendered only while the button is pending.
57
57
  * @csspart text - The button's words, which the styled button trims to their capitals and baseline so the letters sit at the middle in any font.
58
58
  *
59
59
  * @cssstate pending - The button is pending: `pending` is set.
@@ -135,18 +135,13 @@ export declare class HelixHeadlessButton extends HelixFormElement {
135
135
  /**
136
136
  * Where this button's submission goes, overriding the form's own.
137
137
  *
138
- * **The five `form*` overrides work by writing the form and putting it back**,
139
- * which is the only mechanism available and is closer to the platform than it
140
- * sounds. `requestSubmit(submitter)` is how a native submitter carries these,
141
- * and it refuses ours twice over: the inner `<button>` is `type="button"` so
142
- * it is not a submit button, and it sits in this shadow root so its form owner
143
- * is not the consumer's form. Both throw. So the submission is started with no
144
- * submitter and the overrides are applied to the form for the duration of it.
145
- *
146
- * The whole submit algorithm runs synchronously inside `requestSubmit()`, so
147
- * the window is one call and a `finally` closes it whether or not a listener
148
- * refused. What a `submit` handler observes during that window is the form a
149
- * native submitter would have given it, which is the point.
138
+ * **The five `form*` overrides ride on a native submit button that stands in
139
+ * for this one**, because `requestSubmit(submitter)` refuses the inner
140
+ * `<button>` twice over: it is `type="button"`, and it sits in this shadow
141
+ * root, so its form owner is not the consumer's form. The stand-in is a
142
+ * hidden `<button type="submit">` in the form for the one synchronous call,
143
+ * so the platform applies the overrides to that submission alone and leaves
144
+ * the form as it was. See `submitAs`.
150
145
  */
151
146
  formAction: string;
152
147
  /**
@@ -249,13 +244,10 @@ export declare class HelixHeadlessButton extends HelixFormElement {
249
244
  * The value this button submits under its own `name`, as a native submit
250
245
  * button does.
251
246
  *
252
- * **Reported for exactly the submission this button starts and not otherwise.**
247
+ * **Sent with exactly the submission this button starts and not otherwise.**
253
248
  * A native submit button contributes `name=value` only when it is the one
254
- * that submitted, and every other button on the form contributes nothing. A
255
- * form-associated element reporting a value continuously would contribute on
256
- * every submission whoever started it, which is a different feature wearing
257
- * the same name, so `submit` below sets it, submits, and clears it inside one
258
- * synchronous call.
249
+ * that submitted, and every other button on the form contributes nothing,
250
+ * which is what the native button standing in for this one at submit does.
259
251
  *
260
252
  * `CopyButton` has a `value` of its own meaning the text to put on the
261
253
  * clipboard, which is not this and is why it is declared there rather than
@@ -263,19 +255,8 @@ export declare class HelixHeadlessButton extends HelixFormElement {
263
255
  */
264
256
  value: string;
265
257
  /**
266
- * Submits the form as this button, for the length of one call.
267
- *
268
- * Everything a native submitter carries is written onto the form, the
269
- * submission is started, and everything is put back. See `formAction` for why
270
- * `requestSubmit(submitter)` is not available to a control inside a shadow
271
- * root.
272
- *
273
- * `novalidate` is in the same list rather than reached by `form.submit()`,
274
- * which looks like the shortcut and is not: `submit()` skips the `submit`
275
- * event as well as the validation, so a consumer's handler would stop running
276
- * the moment somebody wrote `formnovalidate`. Turning the flag on and calling
277
- * `requestSubmit()` skips the validation alone, which is what the attribute
278
- * means.
258
+ * Submits the form as this button, through a native submit button carrying
259
+ * its name, value and overrides. See `formAction`.
279
260
  */
280
261
  private submit;
281
262
  /**
@@ -346,6 +327,11 @@ export declare class HelixHeadlessButton extends HelixFormElement {
346
327
  /** Called by the browser when the button's form changes. */
347
328
  formAssociatedCallback(form: HTMLFormElement | null): void;
348
329
  protected render(): TemplateResult;
330
+ /**
331
+ * The ring a pending button turns, behind a seam a tier can move: an empty
332
+ * element, hidden from a screen reader, which hears `pending-label` instead.
333
+ */
334
+ protected renderSpinner(): TemplateResult | typeof nothing;
349
335
  }
350
336
  declare global {
351
337
  interface HTMLElementTagNameMap {
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  import { AriaForwardController, customElement, defaultButton, drawStatus, elementById, HelixFallbackCommandEvent, HelixFormElement, HelixPressEvent, pressType, StatusController, submitsOnEnter, supportsInvokers, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ import { submitAs } from '../submitter.js';
10
11
  let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
11
12
  constructor() {
12
13
  super(...arguments);
@@ -103,42 +104,7 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
103
104
  }
104
105
  }
105
106
  submit(form) {
106
- const undo = [];
107
- const override = (attribute, value) => {
108
- if (value === null) {
109
- return;
110
- }
111
- const had = form.getAttribute(attribute);
112
- form.setAttribute(attribute, value);
113
- undo.push(() => {
114
- if (had === null) {
115
- form.removeAttribute(attribute);
116
- }
117
- else {
118
- form.setAttribute(attribute, had);
119
- }
120
- });
121
- };
122
- override('action', this.formAction || null);
123
- override('enctype', this.formEnctype || null);
124
- override('method', this.formMethod || null);
125
- override('novalidate', this.formNoValidate ? '' : null);
126
- override('target', this.formTarget || null);
127
- const reports = this.name !== '';
128
- if (reports) {
129
- this.internals.setFormValue(this.value);
130
- }
131
- try {
132
- form.requestSubmit();
133
- }
134
- finally {
135
- for (const put of undo) {
136
- put();
137
- }
138
- if (reports) {
139
- this.internals.setFormValue(null);
140
- }
141
- }
107
+ submitAs(form, this);
142
108
  }
143
109
  get commandForElement() {
144
110
  const button = this.renderRoot?.querySelector('button');
@@ -222,9 +188,12 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
222
188
  @keydown=${this.notePress}
223
189
  @pointerdown=${this.notePress}
224
190
  >
225
- ${label}
191
+ ${label}${this.renderSpinner()}
226
192
  </button>${this.renderLive()}`;
227
193
  }
194
+ renderSpinner() {
195
+ return this.pending ? html `<span part="spinner" aria-hidden="true"></span>` : nothing;
196
+ }
228
197
  };
229
198
  __decorate([
230
199
  property()
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Submitting a form through a native submit button that stands in for one in a
3
+ * shadow root.
4
+ *
5
+ * `requestSubmit(submitter)` takes only a native submit button whose form is
6
+ * the form, and a button inside a shadow root is neither. A hidden native
7
+ * `<button type="submit">` carrying the same name, value and `form*`
8
+ * overrides is put into the form for the one synchronous call, as Shoelace and
9
+ * Web Awesome do, so the platform does the rest: `event.submitter` is a
10
+ * button with the name and value, `new FormData(form, event.submitter)`
11
+ * includes them, and the overrides apply to that submission alone.
12
+ */
13
+ /** What a submit button carries into the submission it starts. */
14
+ export interface Carried {
15
+ formAction: string;
16
+ formEnctype: string;
17
+ formMethod: string;
18
+ formNoValidate: boolean;
19
+ formTarget: string;
20
+ name: string;
21
+ value: string;
22
+ }
23
+ /**
24
+ * Submits `form` as a native submit button carrying `carried` would, and takes
25
+ * that button out again whether or not a listener refused the submission.
26
+ */
27
+ export declare const submitAs: (form: HTMLFormElement, carried: Carried) => void;
@@ -0,0 +1,25 @@
1
+ export const submitAs = (form, carried) => {
2
+ const stand = form.ownerDocument.createElement('button');
3
+ stand.type = 'submit';
4
+ stand.hidden = true;
5
+ for (const [attribute, value] of [
6
+ ['formaction', carried.formAction],
7
+ ['formenctype', carried.formEnctype],
8
+ ['formmethod', carried.formMethod],
9
+ ['formtarget', carried.formTarget],
10
+ ['name', carried.name],
11
+ ]) {
12
+ if (value !== '') {
13
+ stand.setAttribute(attribute, value);
14
+ }
15
+ }
16
+ stand.value = carried.value;
17
+ stand.formNoValidate = carried.formNoValidate;
18
+ form.append(stand);
19
+ try {
20
+ form.requestSubmit(stand);
21
+ }
22
+ finally {
23
+ stand.remove();
24
+ }
25
+ };
@@ -31,6 +31,12 @@ import { type PropertyValues, type TemplateResult } from 'lit';
31
31
  * is no wrapper and every region is a bare slot. A server cannot see slotted
32
32
  * content, so served actions sit on their own line until the script runs.
33
33
  *
34
+ * **The card says which regions it holds**, as `:state(has-media)`,
35
+ * `:state(has-header)` and `:state(has-footer)`, so a sheet can draw a card
36
+ * with a header differently, as Web Awesome's `with-header` does, with no
37
+ * attribute written onto the card. Read in the browser, so a served card
38
+ * carries none until the script runs.
39
+ *
34
40
  * @since 0.1.0
35
41
  * @status stable
36
42
  * @category layout
@@ -52,6 +58,10 @@ import { type PropertyValues, type TemplateResult } from 'lit';
52
58
  * @csspart header-actions - The slot the header's controls arrive through, drawing no box, as `body` does.
53
59
  * @csspart header-row - The line holding `header` and `header-actions`, drawn only while `header-actions` holds something.
54
60
  * @csspart media - The slot the media arrives through, drawing no box, as `body` does.
61
+ *
62
+ * @cssstate has-footer - Something is slotted into `footer` or `footer-actions`.
63
+ * @cssstate has-header - Something is slotted into `header` or `header-actions`.
64
+ * @cssstate has-media - Something is slotted into `media`.
55
65
  */
56
66
  export declare class HelixHeadlessCard extends HelixElement {
57
67
  /**
@@ -92,7 +102,8 @@ export declare class HelixHeadlessCard extends HelixElement {
92
102
  private headerActions;
93
103
  protected willUpdate(changed: PropertyValues): void;
94
104
  protected updated(changed: PropertyValues): void;
95
- private readonly readActions;
105
+ protected firstUpdated(changed: PropertyValues): void;
106
+ private readonly readRegions;
96
107
  protected render(): TemplateResult;
97
108
  }
98
109
  declare global {
@@ -15,10 +15,13 @@ let HelixHeadlessCard = class HelixHeadlessCard extends HelixElement {
15
15
  this.namedBy = null;
16
16
  this.footerActions = false;
17
17
  this.headerActions = false;
18
- this.readActions = () => {
18
+ this.readRegions = () => {
19
19
  const filled = (name) => slotFilled(this.renderRoot?.querySelector(`slot[name="${name}"]`));
20
20
  this.headerActions = filled('header-actions');
21
21
  this.footerActions = filled('footer-actions');
22
+ this.toggleState('has-media', filled('media'));
23
+ this.toggleState('has-header', this.headerActions || filled('header'));
24
+ this.toggleState('has-footer', this.footerActions || filled('footer'));
22
25
  };
23
26
  }
24
27
  willUpdate(changed) {
@@ -28,20 +31,24 @@ let HelixHeadlessCard = class HelixHeadlessCard extends HelixElement {
28
31
  }
29
32
  updated(changed) {
30
33
  super.updated(changed);
31
- this.readActions();
34
+ this.readRegions();
35
+ }
36
+ firstUpdated(changed) {
37
+ super.firstUpdated(changed);
38
+ this.renderRoot.addEventListener('slotchange', this.readRegions);
32
39
  }
33
40
  render() {
34
41
  const header = html `<slot name="header" part="header"></slot
35
42
  ><slot
36
43
  name="header-actions"
37
44
  part="header-actions"
38
- @slotchange=${this.readActions}
45
+ @slotchange=${this.readRegions}
39
46
  ></slot>`;
40
47
  const footer = html `<slot name="footer" part="footer"></slot
41
48
  ><slot
42
49
  name="footer-actions"
43
50
  part="footer-actions"
44
- @slotchange=${this.readActions}
51
+ @slotchange=${this.readRegions}
45
52
  ></slot>`;
46
53
  return html `
47
54
  <slot name="media" part="media"></slot>