@fusion.dev/helix-headless 0.150.0 → 0.151.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 (40) hide show
  1. package/custom-elements.json +741 -66
  2. package/dist/area-chart/root/area-chart-root.d.ts +36 -0
  3. package/dist/area-chart/root/area-chart-root.js +17 -2
  4. package/dist/bar-chart/root/bar-chart-root.d.ts +28 -4
  5. package/dist/bar-chart/root/bar-chart-root.js +17 -4
  6. package/dist/callout/root/callout-root.d.ts +20 -0
  7. package/dist/callout/root/callout-root.js +19 -4
  8. package/dist/chat/composer/chat-composer.d.ts +30 -0
  9. package/dist/chat/composer/chat-composer.js +47 -2
  10. package/dist/chat/root/chat-root.d.ts +10 -0
  11. package/dist/chat/root/chat-root.js +14 -0
  12. package/dist/data-table/root/data-table-root.d.ts +34 -2
  13. package/dist/data-table/root/data-table-root.js +63 -2
  14. package/dist/divider/root/divider-root.d.ts +8 -6
  15. package/dist/divider/root/divider-root.js +10 -2
  16. package/dist/element-events.generated.js +4 -0
  17. package/dist/kbd/root/kbd-root.d.ts +7 -1
  18. package/dist/kbd/root/kbd-root.js +8 -4
  19. package/dist/line-chart/root/line-chart-root.d.ts +36 -0
  20. package/dist/line-chart/root/line-chart-root.js +15 -2
  21. package/dist/scatter-chart/root/scatter-chart-root.d.ts +18 -0
  22. package/dist/scatter-chart/root/scatter-chart-root.js +38 -15
  23. package/dist/table/column/table-column.js +3 -2
  24. package/dist/table/grid-controller.d.ts +14 -2
  25. package/dist/table/grid-controller.js +27 -1
  26. package/dist/table/root/table-root.d.ts +30 -4
  27. package/dist/table/root/table-root.js +45 -7
  28. package/dist/table/row/table-row.d.ts +12 -2
  29. package/dist/table/row/table-row.js +8 -2
  30. package/dist/video/root/video-root.d.ts +6 -0
  31. package/dist/video/root/video-root.js +5 -1
  32. package/dist/video-playlist/root/video-playlist-root.d.ts +4 -0
  33. package/dist/video-playlist/root/video-playlist-root.js +4 -1
  34. package/dist/virtual-list/root/virtual-list-root.d.ts +23 -1
  35. package/dist/virtual-list/root/virtual-list-root.js +21 -2
  36. package/lint.json +1 -1
  37. package/package.json +5 -5
  38. package/types/jsx.d.ts +648 -98
  39. package/types/vue.d.ts +195 -43
  40. package/types/words.d.ts +4 -2
@@ -40,6 +40,10 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
40
40
  * and shows a dash while only some are chosen.
41
41
  * - Shift with a press on a checkbox selects every row between it and the row
42
42
  * last chosen.
43
+ * - `selection-behavior="replace"` makes a press or a move select the row
44
+ * alone, as a file list does. Control or Command with a press adds or takes
45
+ * out one row, and Shift with a press selects every row from the one last
46
+ * chosen.
43
47
  *
44
48
  * To act on a single row, such as opening it, put a link or a button in a cell.
45
49
  *
@@ -56,7 +60,8 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
56
60
  * | Enter, F2 | Moves into the cell's controls. Escape moves back to the cell, and Tab moves between the controls of one cell. |
57
61
  * | Enter, Space on a sortable heading | Sorts by the column. |
58
62
  * | Enter, Space on the `load-more` row | Sends `helix-load-more`. |
59
- * | Space on a row | Selects the row, in a `selectable` table. |
63
+ * | Space on a row | Selects the row, in a `selectable` table. Under `selection-behavior="replace"`, selects it alone, and Control or Command with Space adds or takes out the row. |
64
+ * | Down, Up, Page Down, Page Up under `selection-behavior="replace"` | Selects the row it moves to alone. Control or Command with the key moves without selecting. |
60
65
  * | Shift with Down or Up | Selects the rows it moves across, in a `selectable` table. |
61
66
  * | Shift with Space | Selects every row from the one last chosen, in a `selectable` table. |
62
67
  * | Control or Command with A | Selects every row, in a `selectable` table. |
@@ -123,7 +128,9 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
123
128
  * @documentation https://fusion.dev/helix/components/data-table
124
129
  *
125
130
  * @slot - The columns, as `TableColumn` elements.
131
+ * @slot empty - Content shown when there are no rows to show and none are loading, such as a picture and a link. Falls back to `empty-text`, which the live region still says.
126
132
  * @slot footer - Content below the grid. It replaces the built-in pagination.
133
+ * @slot loading - Content shown while the rows load, such as a spinner. Falls back to `loading-text`, which the live region still says.
127
134
  * @slot toolbar - Content above the grid, such as a search field or actions.
128
135
  *
129
136
  * @event {HelixActivateEvent} helix-activate - Fired after the page changes, with the new page as a string in `detail.value`.
@@ -216,6 +223,8 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
216
223
  * `registerWords('de', { 'helix-data-table': { nextLabel: 'Nächste Seite' } })`.
217
224
  */
218
225
  static words: string;
226
+ /** The empty and loading lines are the `empty` and `loading` slots. */
227
+ protected statusSlots: boolean;
219
228
  /**
220
229
  * The word for an ascending sort, in a `sorted-label` that writes `{order}`.
221
230
  *
@@ -490,6 +499,15 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
490
499
  * {# wybranych} other {# wybranego}}`.
491
500
  */
492
501
  selectedLabel: string;
502
+ /**
503
+ * What a key or a press on a row does to the rows already selected, in a
504
+ * `selectable` table. `toggle`, the default, keeps them: Space adds or takes
505
+ * out the row. `replace` selects the row alone, as a file list does: Down and
506
+ * Up carry the selection with them, and a press on a row selects it. Control
507
+ * or Command with a press or Space adds or takes out one row, and with a move
508
+ * goes without selecting. The checkboxes add or take out one row either way.
509
+ */
510
+ selectionBehavior: 'replace' | 'toggle';
493
511
  /**
494
512
  * The name of a row's checkbox. `{row}` is replaced with the row's value in
495
513
  * the first column that has a field, or with its key.
@@ -954,7 +972,7 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
954
972
  */
955
973
  toggleSort(field: string): boolean;
956
974
  /**
957
- * The columns into the header row, everything else into the toolbar and footer.
975
+ * The columns into the header row, everything else into the slot it names.
958
976
  *
959
977
  * Manual assignment, so a consumer's own markup is never written to.
960
978
  */
@@ -1124,6 +1142,20 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
1124
1142
  * request carries only the state it asks for.
1125
1143
  */
1126
1144
  private readonly notePress;
1145
+ /** Whether a press or a move on a row selects it alone. */
1146
+ private get replaces();
1147
+ /**
1148
+ * Selects the row at `row` on the page alone, and makes it where a range
1149
+ * runs from. Asks nothing where it is already the only one selected.
1150
+ */
1151
+ private chooseOnly;
1152
+ /**
1153
+ * A press on a row under `selection-behavior="replace"`: it selects the row
1154
+ * alone, Control or Command adds or takes out the row, and Shift selects
1155
+ * every row from the one last chosen. A press on a control in a cell, or on
1156
+ * the checkbox or grip column, is the control's.
1157
+ */
1158
+ private readonly handleRowClick;
1127
1159
  /**
1128
1160
  * A click on a sortable heading, anywhere but its grip or its edge.
1129
1161
  *
@@ -30,6 +30,7 @@ const isControl = (element) => element.matches(CONTROL) || element.shadowRoot?.d
30
30
  let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollection(HelixElement) {
31
31
  constructor() {
32
32
  super(...arguments);
33
+ this.statusSlots = true;
33
34
  this.ascendingLabel = 'ascending';
34
35
  this.canceledLabel = 'Canceled';
35
36
  this.defaultExpanded = '';
@@ -64,6 +65,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
64
65
  this.selectable = false;
65
66
  this.selectAllLabel = 'Select every row';
66
67
  this.selectedLabel = '{count} selected';
68
+ this.selectionBehavior = 'toggle';
67
69
  this.selectRowLabel = 'Select {row}';
68
70
  this.sortedLabel = '{direction, select, ascending {Sorted by {column}, ascending} descending {Sorted by {column}, descending} other {Sorted by {column}}}';
69
71
  this.total = 0;
@@ -178,6 +180,30 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
178
180
  this.notePress = (event) => {
179
181
  this.shiftPressed = event.shiftKey;
180
182
  };
183
+ this.handleRowClick = (event) => {
184
+ if (!this.replaces || event.defaultPrevented) {
185
+ return;
186
+ }
187
+ const place = this.placeOf(event);
188
+ if (!place || place.row < 0 || place.row >= this.drawnPage.length || place.column < this.lead) {
189
+ return;
190
+ }
191
+ const origin = event.composedPath()[0];
192
+ if (origin instanceof Node && this.controlsIn(place).some((one) => deepContains(one, origin))) {
193
+ return;
194
+ }
195
+ if (event.shiftKey && this.anchorKey !== undefined) {
196
+ this.selectRange(place.row);
197
+ return;
198
+ }
199
+ if (event.ctrlKey || event.metaKey) {
200
+ const data = this.drawnPage[place.row];
201
+ const key = this.keyOf(data, this.rowIndex(place.row) - 2);
202
+ this.chooseRow(key, !this.chosen.has(key));
203
+ return;
204
+ }
205
+ this.chooseOnly(place.row);
206
+ };
181
207
  this.handleHeadClick = (event) => {
182
208
  if (this.partIn(event, 'grip') || this.partIn(event, 'resizer')) {
183
209
  return;
@@ -251,14 +277,22 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
251
277
  }
252
278
  const { column, row } = to;
253
279
  event.preventDefault();
280
+ const onRow = row >= 0 && row < this.drawnPage.length;
254
281
  if (event.shiftKey &&
255
282
  this.selectable &&
256
283
  (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&
257
284
  place.row >= 0 &&
258
- row >= 0 &&
259
- row < this.drawnPage.length) {
285
+ onRow) {
260
286
  this.selectRange(row, place.row);
261
287
  }
288
+ else if (this.replaces &&
289
+ onRow &&
290
+ row !== place.row &&
291
+ !event.shiftKey &&
292
+ !event.ctrlKey &&
293
+ !event.metaKey) {
294
+ this.chooseOnly(row);
295
+ }
262
296
  if (row !== place.row || column !== place.column) {
263
297
  this.moveTo(row, column);
264
298
  }
@@ -878,6 +912,8 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
878
912
  for (const [name, wanted] of [
879
913
  ['toolbar', true],
880
914
  ['footer', true],
915
+ ['empty', true],
916
+ ['loading', true],
881
917
  ]) {
882
918
  const slot = root.querySelector(`slot[name="${name}"]`);
883
919
  if (slot?.assign && wanted) {
@@ -1201,6 +1237,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1201
1237
  connectedCallback() {
1202
1238
  super.connectedCallback();
1203
1239
  this.addEventListener('click', this.handleHeadClick);
1240
+ this.addEventListener('click', this.handleRowClick);
1204
1241
  this.addEventListener('pointerdown', this.notePress, true);
1205
1242
  this.addEventListener('keydown', this.notePress, true);
1206
1243
  this.addEventListener('focusin', this.handleFocusIn);
@@ -1212,6 +1249,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1212
1249
  disconnectedCallback() {
1213
1250
  super.disconnectedCallback();
1214
1251
  this.removeEventListener('click', this.handleHeadClick);
1252
+ this.removeEventListener('click', this.handleRowClick);
1215
1253
  this.removeEventListener('pointerdown', this.notePress, true);
1216
1254
  this.removeEventListener('keydown', this.notePress, true);
1217
1255
  this.removeEventListener('focusin', this.handleFocusIn);
@@ -1220,6 +1258,22 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1220
1258
  this.removeEventListener('keydown', this.handleGripKey);
1221
1259
  this.removeEventListener('helix-before-resize', this.handleResize);
1222
1260
  }
1261
+ get replaces() {
1262
+ return this.selectable && this.selectionBehavior === 'replace';
1263
+ }
1264
+ chooseOnly(row) {
1265
+ const data = this.drawnPage[row];
1266
+ if (!data) {
1267
+ return;
1268
+ }
1269
+ const key = this.keyOf(data, this.rowIndex(row) - 2);
1270
+ this.anchorKey = key;
1271
+ const now = this.chosen;
1272
+ if (now.size === 1 && now.has(key)) {
1273
+ return;
1274
+ }
1275
+ this.requestSelected(new Set([key]));
1276
+ }
1223
1277
  cellAt(row, column) {
1224
1278
  const offset = this.lead;
1225
1279
  if (row === -1) {
@@ -1402,6 +1456,10 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1402
1456
  event.preventDefault();
1403
1457
  this.selectRange(place.row);
1404
1458
  }
1459
+ else if (this.replaces && row && !event.ctrlKey && !event.metaKey) {
1460
+ event.preventDefault();
1461
+ this.chooseOnly(place.row);
1462
+ }
1405
1463
  else if (this.selectable && row) {
1406
1464
  event.preventDefault();
1407
1465
  const key = this.keyOf(row, this.rowIndex(place.row) - 2);
@@ -1595,6 +1653,9 @@ __decorate([
1595
1653
  words,
1596
1654
  property({ attribute: 'selected-label' })
1597
1655
  ], HelixHeadlessDataTable.prototype, "selectedLabel", void 0);
1656
+ __decorate([
1657
+ property({ attribute: 'selection-behavior' })
1658
+ ], HelixHeadlessDataTable.prototype, "selectionBehavior", void 0);
1598
1659
  __decorate([
1599
1660
  words,
1600
1661
  property({ attribute: 'select-row-label' })
@@ -12,9 +12,10 @@ import { type PropertyValues, type TemplateResult } from 'lit';
12
12
  * **The line is a separator**, as `<hr>` is, and as Radix Separator, React
13
13
  * Aria Separator, and Web Awesome's divider are. It is named by any content
14
14
  * set into it, so a screen reader says "Or, separator" where you wrote "Or" on
15
- * the line. Set `decorative` on a rule that separates nothing, such as one
16
- * under a heading or between the facts of a byline, and it leaves the
17
- * accessibility tree.
15
+ * the line, and the words are read once, as that name. Set words into a
16
+ * separator and no controls, which are hidden with them. Set `decorative` on
17
+ * a rule that separates nothing, such as one under a heading or between the
18
+ * facts of a byline, and it leaves the accessibility tree.
18
19
  *
19
20
  * Use `orientation="vertical"` between items on one line. A vertical divider
20
21
  * stretches to fit its flex parent and sets no height of its own.
@@ -44,9 +45,10 @@ export declare class HelixHeadlessDivider extends HelixElement {
44
45
  * Whether screen readers announce the line as a separator, which they now do
45
46
  * by default.
46
47
  *
47
- * It dates from when the line was decorative by default. It is still read,
48
- * and it wins over `decorative`, so a divider written with it stays a
49
- * separator.
48
+ * @deprecated Remove it: a divider is a separator unless `decorative` is
49
+ * set. It dates from when the line was decorative by default, and it is
50
+ * still read and wins over `decorative`, so a divider written with it stays
51
+ * a separator.
50
52
  */
51
53
  announced: boolean;
52
54
  /**
@@ -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, markRegion, toolbarAround, toolbarAxis, } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, markRegion, reportRuntime, toolbarAround, toolbarAxis, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessDivider = class HelixHeadlessDivider extends HelixElement {
@@ -38,6 +38,14 @@ let HelixHeadlessDivider = class HelixHeadlessDivider extends HelixElement {
38
38
  updated(changed) {
39
39
  super.updated(changed);
40
40
  this.markEmpty();
41
+ if (changed.has('announced') && this.announced && changed.get('announced') === undefined) {
42
+ reportRuntime({
43
+ kind: 'deprecated',
44
+ message: `<${this.localName} announced> is deprecated: remove it. A divider is a separator ` +
45
+ 'unless `decorative` is set.',
46
+ tag: this.localName,
47
+ });
48
+ }
41
49
  }
42
50
  render() {
43
51
  const separator = this.announced || !this.decorative;
@@ -47,7 +55,7 @@ let HelixHeadlessDivider = class HelixHeadlessDivider extends HelixElement {
47
55
  aria-labelledby=${separator ? 'content' : nothing}
48
56
  aria-orientation=${separator ? this.drawn : nothing}
49
57
  ></div>
50
- <span id="content" part="content"><slot @slotchange=${() => this.markEmpty()}></slot></span>
58
+ <span id="content" part="content" aria-hidden=${separator ? 'true' : nothing}><slot @slotchange=${() => this.markEmpty()}></slot></span>
51
59
  <div part="line" role="presentation"></div>`;
52
60
  }
53
61
  };
@@ -3,7 +3,9 @@ registerElementEvents({
3
3
  'helix-headless-accordion-item': ['helix-before-close', 'helix-before-open', 'toggle'],
4
4
  'helix-headless-announcement': ['helix-dismiss'],
5
5
  'helix-headless-app-header': ['helix-scrolled'],
6
+ 'helix-headless-area-chart': ['helix-reading-change', 'helix-reading-press'],
6
7
  'helix-headless-avatar': ['error', 'load'],
8
+ 'helix-headless-bar-chart': ['helix-reading-change', 'helix-reading-press'],
7
9
  'helix-headless-button': ['helix-press'],
8
10
  'helix-headless-button-group': ['change', 'helix-before-change'],
9
11
  'helix-headless-callout': ['helix-dismiss'],
@@ -132,6 +134,7 @@ registerElementEvents({
132
134
  'helix-before-open',
133
135
  'toggle',
134
136
  ],
137
+ 'helix-headless-line-chart': ['helix-reading-change', 'helix-reading-press'],
135
138
  'helix-headless-list': [
136
139
  'change',
137
140
  'helix-action',
@@ -208,6 +211,7 @@ registerElementEvents({
208
211
  'helix-preview',
209
212
  'input',
210
213
  ],
214
+ 'helix-headless-scatter-chart': ['helix-reading-change', 'helix-reading-press'],
211
215
  'helix-headless-scroll-area': ['scroll', 'scrollend'],
212
216
  'helix-headless-select': [
213
217
  'change',
@@ -23,6 +23,11 @@ import { type PropertyValues, type TemplateResult } from 'lit';
23
23
  * the shortcut with the control. A chord the page wrote there itself is left
24
24
  * alone, and the key takes back only what it added.
25
25
  *
26
+ * A chord is drawn with the platform's modifiers first, in its order, so one
27
+ * `keys` value is right everywhere: `mod+shift+p` is `⇧⌘P` on an Apple
28
+ * keyboard and `Ctrl+Shift+P` elsewhere. Where it is drawn in glyphs, it is
29
+ * read in words, such as `Shift Command P`.
30
+ *
26
31
  * `caps` draws a chord as a key per key: each is a `key` part and the `+`
27
32
  * between two a `separator` part, so a sheet can draw a cap for each.
28
33
  * `key-labels` renames any key, on every platform, such as
@@ -141,7 +146,8 @@ export declare class HelixHeadlessKbd extends HelixElement {
141
146
  /**
142
147
  * The chord under `caps`, a part for each key and for each `+` between two,
143
148
  * so a sheet can draw a cap per key. Written with no space between, so a
144
- * chord reads as the one label it was.
149
+ * chord reads as the one label it was. Where the chord is said in words,
150
+ * the parts are hidden from a screen reader, which reads the words.
145
151
  */
146
152
  protected renderChord(): TemplateResult;
147
153
  }
@@ -135,12 +135,16 @@ let HelixHeadlessKbd = class HelixHeadlessKbd extends HelixElement {
135
135
  if (this.keys === '') {
136
136
  return html `<kbd part="kbd"><slot></slot></kbd>`;
137
137
  }
138
- const { keys, separator } = this.label;
139
- return html `<kbd part="kbd">${this.caps ? this.renderChord() : keys.join(separator)}</kbd>`;
138
+ const { keys, separator, spoken } = this.label;
139
+ if (spoken === '') {
140
+ return html `<kbd part="kbd">${this.caps ? this.renderChord() : keys.join(separator)}</kbd>`;
141
+ }
142
+ return html `<kbd part="kbd">${this.caps ? this.renderChord() : html `<span aria-hidden="true">${keys.join(separator)}</span>`}<span data-spoken>${spoken}</span></kbd>`;
140
143
  }
141
144
  renderChord() {
142
- const { keys, separator } = this.label;
143
- return html `${keys.map((key, index) => html `${index > 0 && separator !== '' ? html `<span part="separator">${separator}</span>` : nothing}<span part="key">${key}</span>`)}`;
145
+ const { keys, separator, spoken } = this.label;
146
+ const hidden = spoken === '' ? nothing : 'true';
147
+ return html `${keys.map((key, index) => html `${index > 0 && separator !== '' ? html `<span part="separator" aria-hidden=${hidden}>${separator}</span>` : nothing}<span part="key" aria-hidden=${hidden}>${key}</span>`)}`;
144
148
  }
145
149
  };
146
150
  __decorate([
@@ -27,6 +27,9 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
27
27
  * `<svg>` that fills its box scales with the box, so the same `font-size` can
28
28
  * render at 10px in a narrow chart and 43px in a wide one.
29
29
  *
30
+ * `value-axis` draws the scale's numbers beside the plot, at the levels
31
+ * `gridlines` rules, written in `locale`.
32
+ *
30
33
  * The table is the accessible representation, and the drawing is hidden from
31
34
  * screen readers, who get the numbers. The `summary` part names the chart, from
32
35
  * `label` or the table's caption, and gives each series' latest reading.
@@ -37,13 +40,22 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
37
40
  * a reading. The tooltip follows, the `live` part says the reading in words,
38
41
  * and Escape puts the tooltip away.
39
42
  *
43
+ * **A tap shows the reading under it**, and keeps it until a tap lands
44
+ * elsewhere. `helix-reading-change` says each move of the reading, by pointer,
45
+ * tap or key, and `helix-reading-press` says a reading was clicked, tapped, or
46
+ * pressed with Enter or Space, for a page that opens what a reading stands for.
47
+ *
40
48
  * @since 0.1.0
41
49
  * @status stable
42
50
  * @category charts
43
51
  * @aliases Time Series, Trend Chart, Multi-line Chart
44
52
  * @documentation https://fusion.dev/helix/components/line-chart
45
53
  *
54
+ * @event {HelixReadingChangeEvent} helix-reading-change - The reading shown moved, or was put away: `index` is the row, or `-1` for none, with its `label`, the `series` the keys rest on, and the `pointerType` that moved it.
55
+ * @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
56
+ *
46
57
  * @csspart area - The wash under one line, where `area` is set.
58
+ * @csspart axis - The numbers of the scale beside the plot, under `value-axis`.
47
59
  * @csspart band - The band around the reading under the pointer.
48
60
  * @csspart baseline - The rule at zero, where zero is inside the range.
49
61
  * @csspart gridline - One rule across the plot, under `gridlines`.
@@ -60,6 +72,7 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
60
72
  * @csspart summary - The chart's name and each series' latest reading, written as a sentence for screen readers. Hide it visually.
61
73
  * @csspart svg - The drawing surface the lines and areas sit on.
62
74
  * @csspart terminus - The mark on one series' last reading.
75
+ * @csspart tick - One number on the axis, placed with `inset-block-start` at its level.
63
76
  * @csspart tooltip - The bubble, forwarded from `Tooltip`.
64
77
  * @csspart tooltip-anchor - The `Tooltip` element itself.
65
78
  * @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
@@ -79,6 +92,7 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
79
92
  *
80
93
  * @cssproperty --chart-terminus-x - Where a series' last reading sits horizontally, as a percentage. Written by the component.
81
94
  * @cssproperty --chart-terminus-y - Where a series' last reading sits vertically, as a percentage. Written by the component.
95
+ * @cssproperty --line-chart-axis - The width of the axis, in `ch`, set from the widest number on it. Written by the component on the axis.
82
96
  */
83
97
  export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base {
84
98
  /**
@@ -86,6 +100,13 @@ export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base
86
100
  * `registerWords('de', { 'helix-line-chart': { positionLabel: '{position} von {count}' } })`.
87
101
  */
88
102
  static words: string;
103
+ /**
104
+ * Sends the reading's moves and presses as `helix-reading-change` and
105
+ * `helix-reading-press`, which the pointer hears. Here and not in a
106
+ * constructor, where the manifest's analyzer reads the assignment as a
107
+ * member of this class.
108
+ */
109
+ connectedCallback(): void;
89
110
  /**
90
111
  * Draws a wash under each line, down to the floor of the plot. The washes
91
112
  * overlap and do not add up, so for parts that make a total, use
@@ -202,6 +223,15 @@ export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base
202
223
  * layers; a single `mask-image` would only ever cut the last one.
203
224
  */
204
225
  terminus?: 'none' | 'ring' | 'square' | 'triangle';
226
+ /**
227
+ * Draws the scale's numbers beside the plot, the `axis` part, at the levels
228
+ * `gridlines` rules, so a reading can be measured off the drawing as well
229
+ * as read from the tooltip.
230
+ *
231
+ * Absent draws none: a line read for its shape needs no numbers beside it,
232
+ * and the tooltip and the table hold every reading.
233
+ */
234
+ valueAxis: boolean;
205
235
  /** The domain every series is drawn against, which the tooltip also needs. */
206
236
  private range;
207
237
  /**
@@ -223,6 +253,12 @@ export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base
223
253
  */
224
254
  private ends;
225
255
  protected render(): TemplateResult;
256
+ /**
257
+ * The scale's numbers beside the plot, under `value-axis`, written in the
258
+ * chart's locale. A seam, so a subclass can write the numbers its own way,
259
+ * such as a currency.
260
+ */
261
+ protected renderAxis(y: (value: number) => number, domain: [number, number]): TemplateResult;
226
262
  /**
227
263
  * What a reader who cannot see the lines is told: the chart's name and the
228
264
  * latest reading of each series.
@@ -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 { byRow, CHART_PLOT, commaList, customElement, drawTooltip, extent, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, ticks, unbroken, wordsLocale, } from '@fusion.dev/helix-core';
7
+ import { byRow, CHART_PLOT, commaList, customElement, drawTooltip, drawValueAxis, extent, formatNumbers, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, ticks, unbroken, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
@@ -14,9 +14,16 @@ let HelixHeadlessLineChart = class HelixHeadlessLineChart extends MixinChartSour
14
14
  this.area = false;
15
15
  this.dashed = '';
16
16
  this.markers = false;
17
+ this.valueAxis = false;
17
18
  this.zero = false;
18
19
  }
19
20
  static { this.words = 'helix-line-chart'; }
21
+ connectedCallback() {
22
+ super.connectedCallback();
23
+ this.pointer.onReading = (heard, detail) => this.dispatchEvent(heard === 'press'
24
+ ? new HelixReadingPressEvent(detail)
25
+ : new HelixReadingChangeEvent(detail));
26
+ }
20
27
  get rulesValue() {
21
28
  return this.gridlines !== undefined && this.gridlines !== 'category';
22
29
  }
@@ -58,7 +65,10 @@ let HelixHeadlessLineChart = class HelixHeadlessLineChart extends MixinChartSour
58
65
  const y = linear(domain, [PLOT_HEIGHT, 0]);
59
66
  const slot = PLOT_WIDTH / readings.length;
60
67
  const x = linear([0, Math.max(readings.length - 1, 1)], [slot / 2, PLOT_WIDTH - slot / 2]);
61
- return html `${this.renderPlot(series, x, y, domain)} ${this.renderLabels(readings)}${this.renderSummary(series)}`;
68
+ return html `${this.valueAxis ? this.renderAxis(y, domain) : nothing}${this.renderPlot(series, x, y, domain)} ${this.renderLabels(readings)}${this.renderSummary(series)}`;
69
+ }
70
+ renderAxis(y, domain) {
71
+ return drawValueAxis(domain, (value) => (y(value) / PLOT_HEIGHT) * 100, formatNumbers(wordsLocale(this)), (widest) => `--line-chart-axis: ${widest}ch`);
62
72
  }
63
73
  renderSummary(series) {
64
74
  const rows = this.sourceRows;
@@ -254,6 +264,9 @@ __decorate([
254
264
  __decorate([
255
265
  property({ reflect: true })
256
266
  ], HelixHeadlessLineChart.prototype, "terminus", void 0);
267
+ __decorate([
268
+ property({ attribute: 'value-axis', reflect: true, type: Boolean })
269
+ ], HelixHeadlessLineChart.prototype, "valueAxis", void 0);
257
270
  __decorate([
258
271
  property({ reflect: true, type: Boolean })
259
272
  ], HelixHeadlessLineChart.prototype, "zero", void 0);
@@ -66,12 +66,20 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
66
66
  * reads in its own direction. The summary and the tooltip bubble follow the
67
67
  * page's direction.
68
68
  *
69
+ * **A tap shows the reading under it**, and keeps it until a tap lands
70
+ * elsewhere. `helix-reading-change` says each move of the reading, by pointer,
71
+ * tap or key, and `helix-reading-press` says a reading was clicked, tapped, or
72
+ * pressed with Enter or Space, for a page that opens what a reading stands for.
73
+ *
69
74
  * @since 0.1.0
70
75
  * @status stable
71
76
  * @category charts
72
77
  * @aliases Scatter Plot, XY Chart, Correlation Chart, Dot Plot
73
78
  * @documentation https://fusion.dev/helix/components/scatter-chart
74
79
  *
80
+ * @event {HelixReadingChangeEvent} helix-reading-change - The reading shown moved, or was put away: `index` is the row, or `-1` for none, with its `label`, the `series` the keys rest on, and the `pointerType` that moved it.
81
+ * @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
82
+ *
75
83
  * @csspart gridline - One line across the plot, drawn when `gridlines` is set.
76
84
  * @csspart live - The readout: a polite live region that says the row the keys moved to. Hide it visually.
77
85
  * @csspart mark - The invisible point the tooltip is anchored to.
@@ -238,6 +246,16 @@ export declare class HelixHeadlessScatterChart extends HelixHeadlessScatterChart
238
246
  * rest on a point with.
239
247
  */
240
248
  private readonly handleMove;
249
+ /**
250
+ * The point a tap lands nearest, within twice a pointer's reach, a finger
251
+ * being broader than a cursor. It stays shown until a tap lands elsewhere.
252
+ */
253
+ private readonly handleTap;
254
+ /**
255
+ * The drawn point nearest a pointer, within `reach` pixels, `undefined` for
256
+ * none in reach, and `null` with no plot to measure.
257
+ */
258
+ private pointNear;
241
259
  private readonly handleLeave;
242
260
  }
243
261
  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 { CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, formatWords, HelixElement, isRtl, linear, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, ticks, words, wordsLocale, } from '@fusion.dev/helix-core';
7
+ import { CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, isRtl, linear, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, ticks, words, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
@@ -47,28 +47,27 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
47
47
  if (event.pointerType !== 'mouse' && event.pointerType !== 'pen') {
48
48
  return;
49
49
  }
50
- const box = this.partNamed('plot')?.getBoundingClientRect();
51
- if (!box || box.width === 0 || box.height === 0) {
50
+ const found = this.pointNear(event, REACH);
51
+ if (found === null) {
52
52
  return;
53
53
  }
54
- const across = event.clientX - box.left;
55
- const up = event.clientY - box.top;
56
- let found;
57
- let best = REACH ** 2;
58
- for (const point of this.drawn) {
59
- const distance = ((point.x / PLOT_WIDTH) * box.width - across) ** 2 +
60
- ((point.y / PLOT_HEIGHT) * box.height - up) ** 2;
61
- if (distance <= best) {
62
- best = distance;
63
- found = point;
64
- }
65
- }
66
54
  if (found !== this.nearest) {
67
55
  this.nearest = found;
68
56
  this.pointer.set(found?.row ?? -1);
69
57
  this.requestUpdate();
70
58
  }
71
59
  };
60
+ this.handleTap = (event) => {
61
+ if (event.pointerType !== 'touch') {
62
+ return;
63
+ }
64
+ const found = this.pointNear(event, REACH * 2);
65
+ if (found) {
66
+ this.nearest = found;
67
+ this.pointer.tapTo(found.row);
68
+ this.requestUpdate();
69
+ }
70
+ };
72
71
  this.handleLeave = () => {
73
72
  this.nearest = undefined;
74
73
  };
@@ -77,13 +76,18 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
77
76
  connectedCallback() {
78
77
  super.connectedCallback();
79
78
  this.pointer.axis = 'none';
79
+ this.pointer.onReading = (heard, detail) => this.dispatchEvent(heard === 'press'
80
+ ? new HelixReadingPressEvent(detail)
81
+ : new HelixReadingChangeEvent(detail));
80
82
  this.addEventListener('pointermove', this.handleMove);
81
83
  this.addEventListener('pointerleave', this.handleLeave);
84
+ this.addEventListener('pointerup', this.handleTap);
82
85
  }
83
86
  disconnectedCallback() {
84
87
  super.disconnectedCallback();
85
88
  this.removeEventListener('pointermove', this.handleMove);
86
89
  this.removeEventListener('pointerleave', this.handleLeave);
90
+ this.removeEventListener('pointerup', this.handleTap);
87
91
  }
88
92
  seriesOf(cell) {
89
93
  const depth = this.measures.ups.indexOf(cell);
@@ -253,6 +257,25 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
253
257
  y: `${point.y}%`,
254
258
  }, (cell) => this.seriesOf(cell), this.pointer);
255
259
  }
260
+ pointNear(event, reach) {
261
+ const box = this.partNamed('plot')?.getBoundingClientRect();
262
+ if (!box || box.width === 0 || box.height === 0) {
263
+ return null;
264
+ }
265
+ const across = event.clientX - box.left;
266
+ const up = event.clientY - box.top;
267
+ let found;
268
+ let best = reach ** 2;
269
+ for (const point of this.drawn) {
270
+ const distance = ((point.x / PLOT_WIDTH) * box.width - across) ** 2 +
271
+ ((point.y / PLOT_HEIGHT) * box.height - up) ** 2;
272
+ if (distance <= best) {
273
+ best = distance;
274
+ found = point;
275
+ }
276
+ }
277
+ return found;
278
+ }
256
279
  };
257
280
  __decorate([
258
281
  words,
@@ -174,6 +174,7 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
174
174
  const named = { name, named: name ? 'yes' : 'no' };
175
175
  const grid = this.gridCell;
176
176
  const control = grid ? '-1' : nothing;
177
+ const width = Math.min(4096, Math.max(HelixHeadlessTableColumn_1.NARROWEST, this.drawn));
177
178
  return html `<span
178
179
  part="column"
179
180
  role=${grid ? 'columnheader' : nothing}
@@ -197,8 +198,8 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
197
198
  aria-orientation="vertical"
198
199
  aria-valuemin=${HelixHeadlessTableColumn_1.NARROWEST}
199
200
  aria-valuemax="4096"
200
- aria-valuenow=${Math.max(HelixHeadlessTableColumn_1.NARROWEST, this.drawn)}
201
- aria-valuetext=${formatWords(this, 'widthLabel', { width: this.drawn })}
201
+ aria-valuenow=${width}
202
+ aria-valuetext=${formatWords(this, 'widthLabel', { width })}
202
203
  @keydown=${this.handleResizeKey}
203
204
  @pointerdown=${this.handleResizeStart}
204
205
  @pointermove=${this.handleResizeMove}