@fusion.dev/helix-headless 0.22.0 → 0.24.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 (103) hide show
  1. package/custom-elements.json +48397 -26631
  2. package/dist/announcement/root/announcement-root.d.ts +19 -5
  3. package/dist/area-chart/index.d.ts +1 -0
  4. package/dist/area-chart/index.js +1 -0
  5. package/dist/area-chart/root/area-chart-root.d.ts +182 -0
  6. package/dist/area-chart/root/area-chart-root.js +205 -0
  7. package/dist/carousel/root/carousel-root.d.ts +1 -0
  8. package/dist/carousel/root/carousel-root.js +18 -2
  9. package/dist/chat/index.d.ts +1 -1
  10. package/dist/chat/message/chat-message.d.ts +7 -0
  11. package/dist/chat/message/chat-message.js +2 -1
  12. package/dist/chat/root/chat-root.d.ts +195 -4
  13. package/dist/chat/root/chat-root.js +316 -23
  14. package/dist/checkbox-group/root/checkbox-group-root.d.ts +2 -0
  15. package/dist/checkbox-group/root/checkbox-group-root.js +12 -2
  16. package/dist/code-block/index.d.ts +1 -0
  17. package/dist/code-block/index.js +1 -0
  18. package/dist/code-block/patch.d.ts +62 -0
  19. package/dist/code-block/patch.js +93 -0
  20. package/dist/code-block/root/code-block-root.d.ts +82 -17
  21. package/dist/code-block/root/code-block-root.js +83 -6
  22. package/dist/color-picker/root/color-picker-root.d.ts +4 -3
  23. package/dist/color-picker/root/color-picker-root.js +4 -1
  24. package/dist/data-table/root/data-table-root.d.ts +195 -23
  25. package/dist/data-table/root/data-table-root.js +308 -27
  26. package/dist/description-list/index.d.ts +2 -0
  27. package/dist/description-list/index.js +2 -0
  28. package/dist/description-list/item/description-list-item.d.ts +39 -0
  29. package/dist/description-list/item/description-list-item.js +26 -0
  30. package/dist/description-list/root/description-list-root.d.ts +56 -0
  31. package/dist/description-list/root/description-list-root.js +21 -0
  32. package/dist/element-events.generated.js +27 -6
  33. package/dist/filter-input/chip/filter-chip.d.ts +114 -0
  34. package/dist/filter-input/chip/filter-chip.js +61 -0
  35. package/dist/filter-input/field/filter-field.d.ts +128 -0
  36. package/dist/filter-input/field/filter-field.js +70 -0
  37. package/dist/filter-input/grammar.d.ts +96 -0
  38. package/dist/filter-input/grammar.js +133 -0
  39. package/dist/filter-input/index.d.ts +5 -0
  40. package/dist/filter-input/index.js +5 -0
  41. package/dist/filter-input/menu/filter-menu.d.ts +153 -0
  42. package/dist/filter-input/menu/filter-menu.js +164 -0
  43. package/dist/filter-input/operator/filter-operator.d.ts +105 -0
  44. package/dist/filter-input/operator/filter-operator.js +39 -0
  45. package/dist/filter-input/root/filter-input-root.d.ts +118 -9
  46. package/dist/filter-input/root/filter-input-root.js +96 -2
  47. package/dist/format-date/root/format-date-root.js +1 -1
  48. package/dist/index.d.ts +5 -0
  49. package/dist/index.js +5 -0
  50. package/dist/line-chart/root/line-chart-root.d.ts +9 -2
  51. package/dist/listbox/root/listbox-root.d.ts +8 -7
  52. package/dist/listbox/root/listbox-root.js +7 -2
  53. package/dist/menu/item/menu-item.d.ts +54 -6
  54. package/dist/menu/item/menu-item.js +31 -6
  55. package/dist/menu/root/menu-root.d.ts +23 -0
  56. package/dist/menu/root/menu-root.js +13 -2
  57. package/dist/notifications/group/notification-group.d.ts +129 -0
  58. package/dist/notifications/group/notification-group.js +106 -0
  59. package/dist/notifications/index.d.ts +1 -0
  60. package/dist/notifications/index.js +1 -0
  61. package/dist/notifications/notification/notification.d.ts +39 -13
  62. package/dist/notifications/notification/notification.js +11 -8
  63. package/dist/notifications/root/notifications-root.d.ts +57 -1
  64. package/dist/notifications/root/notifications-root.js +35 -4
  65. package/dist/number-input/root/number-input-root.d.ts +21 -1
  66. package/dist/number-input/root/number-input-root.js +18 -5
  67. package/dist/object-inspector/index.d.ts +1 -0
  68. package/dist/object-inspector/index.js +1 -0
  69. package/dist/object-inspector/root/object-inspector-root.d.ts +183 -0
  70. package/dist/object-inspector/root/object-inspector-root.js +348 -0
  71. package/dist/outline/root/outline-root.d.ts +16 -1
  72. package/dist/outline/root/outline-root.js +27 -9
  73. package/dist/radio/group/radio-group.d.ts +1 -0
  74. package/dist/scatter-chart/index.d.ts +1 -0
  75. package/dist/scatter-chart/index.js +1 -0
  76. package/dist/scatter-chart/root/scatter-chart-root.d.ts +144 -0
  77. package/dist/scatter-chart/root/scatter-chart-root.js +205 -0
  78. package/dist/skip-link/index.d.ts +1 -0
  79. package/dist/skip-link/index.js +1 -0
  80. package/dist/skip-link/root/skip-link-root.d.ts +81 -0
  81. package/dist/skip-link/root/skip-link-root.js +58 -0
  82. package/dist/speed-dial/root/speed-dial-root.d.ts +1 -0
  83. package/dist/speed-dial/root/speed-dial-root.js +6 -2
  84. package/dist/steps/root/steps-root.d.ts +1 -0
  85. package/dist/table/column/table-column.d.ts +79 -0
  86. package/dist/table/column/table-column.js +125 -5
  87. package/dist/table/root/table-root.d.ts +2 -0
  88. package/dist/table/root/table-root.js +12 -3
  89. package/dist/time-picker/root/time-picker-root.d.ts +9 -0
  90. package/dist/time-picker/root/time-picker-root.js +5 -0
  91. package/dist/toast/root/toast-root.d.ts +2 -0
  92. package/dist/tooltip/root/tooltip-root.js +1 -1
  93. package/dist/tour/root/tour-root.d.ts +1 -0
  94. package/dist/tree/item/tree-item.d.ts +7 -4
  95. package/dist/tree/item/tree-item.js +4 -1
  96. package/dist/video/root/video-root.d.ts +23 -1
  97. package/dist/video/root/video-root.js +42 -22
  98. package/dist/virtual-list/root/virtual-list-root.d.ts +10 -6
  99. package/dist/virtual-list/root/virtual-list-root.js +10 -4
  100. package/package.json +5 -5
  101. package/types/jsx.d.ts +4535 -346
  102. package/types/svelte.d.ts +11 -0
  103. package/types/vue.d.ts +737 -39
@@ -1,5 +1,5 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
- import { type TemplateResult } from 'lit';
2
+ import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../copy-button/index.js';
4
4
  /**
5
5
  * @summary A block of code, with somewhere to put its name and a way to take it.
@@ -23,6 +23,24 @@ import '../../copy-button/index.js';
23
23
  * A `Code` for a fragment inside a sentence. Separate families, because one
24
24
  * flows with prose and the other is a figure.
25
25
  *
26
+ * **`diff` reads the code as a change.** The content is then a unified diff,
27
+ * what `git diff` and `diff -u` write, in the same slot ordinary code goes in,
28
+ * and it is drawn as the lines it added and removed. The block reads a diff and
29
+ * never computes one, so the algorithm stays wherever the two versions are.
30
+ * Numbers come from the hunk headers, which makes them the file's; changed lines
31
+ * are `<ins>` and `<del>` with the `+` and `-` kept as text, so the change is not
32
+ * carried by color; the numbers are hidden from the accessibility tree. Each
33
+ * file in the patch is a block of its own under its own name, and its copy
34
+ * button takes the file as it now reads. `variant="split"` sets the old version
35
+ * beside the new, each removal opposite its replacement.
36
+ *
37
+ * **Before the script runs, a diff is its own text.** A server has no children
38
+ * to read, so it renders the slot as ordinary code, which for a diff is the
39
+ * patch in a `<pre>`: a legible diff, and the first render the client makes
40
+ * too, which is what keeps hydration clean. The drawn diff replaces it once the
41
+ * content can be read. Diff mode is experimental while the rest of the family
42
+ * is stable.
43
+ *
26
44
  * @since 0.1.0
27
45
  * @status stable
28
46
  * @category typography
@@ -31,20 +49,42 @@ import '../../copy-button/index.js';
31
49
  *
32
50
  * @slot - The code.
33
51
  *
52
+ * @csspart added - In a diff, a line only the new version has, an `<ins>`.
34
53
  * @csspart block - The scrolling `<pre>`.
35
54
  * @csspart code - The `<code>` inside it.
55
+ * @csspart context - In a diff, a line both versions share.
36
56
  * @csspart copy - The copy button.
37
57
  * @csspart copy-button - The inner button.
38
58
  * @csspart copy-mark - The clipboard glyph, and the confirmation that replaces it.
39
59
  * @csspart copy-tooltip - The bubble, forwarded from the tooltip inside.
40
60
  * @csspart copy-tooltip-arrow - The point, resting against the trigger.
61
+ * @csspart file - In a diff, one file's header and block.
62
+ * @csspart filler - In a split diff, the empty row opposite a line the other side does not have.
41
63
  * @csspart frame - The code and the copy button beside it.
42
64
  * @csspart header - The bar carrying the name, where there is one.
65
+ * @csspart hunk - In a diff, a `@@` line saying where the lines under it are.
43
66
  * @csspart label - The file name or language, where one is given.
67
+ * @csspart marker - In a diff, the `+`, `-` or space that opens a line.
68
+ * @csspart new - In a split diff, the new version's `<pre>`.
69
+ * @csspart number - In a diff, a line number.
70
+ * @csspart old - In a split diff, the old version's `<pre>`.
71
+ * @csspart removed - In a diff, a line only the old version has, a `<del>`.
72
+ * @csspart split - In a split diff, the two versions side by side.
73
+ * @csspart text - In a diff, a line's code.
44
74
  */
45
75
  export declare class HelixHeadlessCodeBlock extends HelixElement {
76
+ /**
77
+ * Whether the content is a unified diff, drawn as the lines it changed.
78
+ *
79
+ * Reflected, because the stylesheet paints a diff differently from code, and
80
+ * `false` reflects as no attribute at all.
81
+ */
82
+ diff: boolean;
46
83
  /**
47
84
  * What to call this block, usually a file name or a language.
85
+ *
86
+ * In a diff, each file is named from the patch, and this names a patch that
87
+ * carries no name of its own.
48
88
  */
49
89
  label: string;
50
90
  /**
@@ -70,27 +110,34 @@ export declare class HelixHeadlessCodeBlock extends HelixElement {
70
110
  */
71
111
  noCopy: boolean;
72
112
  /**
73
- * The code as text, read from the slot rather than stored.
113
+ * How a diff's two versions are arranged; nothing without `diff`.
114
+ *
115
+ * Absent is unified, each removal above its replacement, which fits any
116
+ * width. `split` sets the old version beside the new one. Optional rather
117
+ * than defaulted, so the host carries no attribute the consumer did not write.
118
+ */
119
+ variant?: 'split';
120
+ /** The last text read as a diff, and what it read as, so a render does not parse twice. */
121
+ private parsed;
122
+ /** The files in the content, when it is a diff and it can be read yet. */
123
+ private get files();
124
+ /**
125
+ * The code as text, read from what the default slot is given rather than
126
+ * stored.
127
+ *
128
+ * **The host's own children rather than the slot's assigned nodes**, which
129
+ * are the same nodes: text, and every element without a `slot`. A slot exists
130
+ * only once a render has made it, so reading through one left the first render
131
+ * with nothing to copy.
74
132
  *
75
133
  * Client only, and it has to be: the server has no children to read. The
76
134
  * copy button is a client-side affordance anyway, so nothing is lost, and
77
135
  * taking the text from a property instead would be a second copy of the code
78
- * that goes stale the moment the slot changes.
136
+ * that goes stale the moment the slot changes. Empty while hydrating what a
137
+ * server sent, which rendered it empty.
79
138
  */
80
139
  private get text();
81
- /**
82
- * One more render, once there is a slot to read.
83
- *
84
- * The first render has no shadow children yet, so `text` is empty and the
85
- * copy button is bound to nothing. `slotchange` normally schedules the render
86
- * that fixes it, and leaning on that alone was measured to leave the button
87
- * empty through four update cycles: the event is not guaranteed to have
88
- * arrived, and in one environment never arrives at all. A component should not
89
- * need an event to show its own content, so it asks for the second render
90
- * itself and `slotchange` is left to handle the case it is actually for, which
91
- * is content changing later.
92
- */
93
- protected firstUpdated(): void;
140
+ protected firstUpdated(changed: PropertyValues): void;
94
141
  protected render(): TemplateResult;
95
142
  /**
96
143
  * The button that takes the code, behind a seam the styled tier can move.
@@ -102,7 +149,25 @@ export declare class HelixHeadlessCodeBlock extends HelixElement {
102
149
  * base button, and a styled subclass that sets `tier` renders the styled one
103
150
  * from the same template.
104
151
  */
105
- protected renderCopyButton(): TemplateResult;
152
+ protected renderCopyButton(value?: string): TemplateResult;
153
+ /** A number padded to the widest in its file, so the columns line up as text. */
154
+ private renderNumber;
155
+ private renderLine;
156
+ private renderHunk;
157
+ /**
158
+ * The old version beside the new one, row for row.
159
+ *
160
+ * The new side's hunk headers are hidden from the accessibility tree, being
161
+ * the old side's text again and there only to keep the rows level.
162
+ */
163
+ private renderSplit;
164
+ /**
165
+ * Each file as a block of its own, the way one block of code is drawn.
166
+ *
167
+ * The slot stays in the tree, hidden, because it is still where the content
168
+ * comes from: a diff edited later arrives by `slotchange` like any other.
169
+ */
170
+ private renderDiff;
106
171
  }
107
172
  declare global {
108
173
  interface HTMLElementTagNameMap {
@@ -9,42 +9,119 @@ import { nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
11
11
  import '../../copy-button/index.js';
12
+ import { pairLines, parsePatch } from '../patch.js';
12
13
  let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
13
14
  constructor() {
14
15
  super(...arguments);
16
+ this.diff = false;
15
17
  this.label = '';
16
18
  this.noCopy = false;
19
+ this.parsed = { files: [], text: '' };
20
+ }
21
+ get files() {
22
+ if (!this.diff) {
23
+ return [];
24
+ }
25
+ const text = this.text;
26
+ if (text !== this.parsed.text) {
27
+ this.parsed = { files: parsePatch(text), text };
28
+ }
29
+ return this.parsed.files;
17
30
  }
18
31
  get text() {
19
- const slot = this.renderRoot?.querySelector('slot');
20
- return (slot?.assignedNodes() ?? []).map((node) => node.textContent ?? '').join('');
32
+ if (this.hydrating) {
33
+ return '';
34
+ }
35
+ return [...(this.childNodes ?? [])]
36
+ .filter((node) => node.nodeType === Node.TEXT_NODE ||
37
+ (node.nodeType === Node.ELEMENT_NODE && node.slot === ''))
38
+ .map((node) => node.textContent ?? '')
39
+ .join('');
21
40
  }
22
- firstUpdated() {
23
- this.requestUpdate();
41
+ firstUpdated(changed) {
42
+ super.firstUpdated(changed);
43
+ this.finishHydration();
24
44
  }
25
45
  render() {
46
+ const files = this.files;
47
+ if (files.length > 0) {
48
+ return this.renderDiff(files);
49
+ }
26
50
  return html `${this.label ? html `<div part="header"><span part="label">${this.label}</span></div>` : nothing}
27
51
  <div part="frame">
28
52
  <pre part="block"><code part="code"><slot @slotchange=${() => this.requestUpdate()}></slot></code></pre>
29
53
  ${this.noCopy ? nothing : this.renderCopyButton()}
30
54
  </div>`;
31
55
  }
32
- renderCopyButton() {
56
+ renderCopyButton(value = this.text) {
33
57
  return html `<${this.tag('copy-button')}
34
58
  part="copy"
35
59
  exportparts="button: copy-button, mark: copy-mark, tooltip: copy-tooltip,
36
60
  tooltip-arrow: copy-tooltip-arrow"
37
61
  label="Copy code"
38
- .value=${this.text}
62
+ .value=${value}
39
63
  ></${this.tag('copy-button')}>`;
40
64
  }
65
+ renderNumber(value, width) {
66
+ return html `<span part="number" aria-hidden="true">${String(value ?? '').padStart(width)} </span>`;
67
+ }
68
+ renderLine(line, width, side) {
69
+ const numbers = side
70
+ ? this.renderNumber(side === 'old' ? line.old : line.new, width)
71
+ : html `${this.renderNumber(line.old, width)}${this.renderNumber(line.new, width)}`;
72
+ const text = html `<span part="text">${line.text}\n</span>`;
73
+ if (line.kind === 'added') {
74
+ return html `<ins part="added">${numbers}<span part="marker">+</span>${text}</ins>`;
75
+ }
76
+ if (line.kind === 'removed') {
77
+ return html `<del part="removed">${numbers}<span part="marker">-</span>${text}</del>`;
78
+ }
79
+ return html `<span part="context">${numbers}<span part="marker"> </span>${text}</span>`;
80
+ }
81
+ renderHunk(hunk, hidden = false) {
82
+ return html `<span part="hunk" aria-hidden=${hidden ? 'true' : nothing}>${hunk.header}\n</span>`;
83
+ }
84
+ renderSplit(file, width) {
85
+ const hunks = file.hunks.map((hunk) => ({ hunk, rows: pairLines(hunk.lines) }));
86
+ const filler = html `<span part="filler" aria-hidden="true">\n</span>`;
87
+ return html `<div part="split">
88
+ <pre part="old"><code part="code">${hunks.map(({ hunk, rows }) => html `${this.renderHunk(hunk)}${rows.map(({ left }) => left ? this.renderLine(left, width, 'old') : filler)}`)}</code></pre>
89
+ <pre part="new"><code part="code">${hunks.map(({ hunk, rows }) => html `${this.renderHunk(hunk, true)}${rows.map(({ right }) => right ? this.renderLine(right, width, 'new') : filler)}`)}</code></pre>
90
+ </div>`;
91
+ }
92
+ renderDiff(files) {
93
+ return html `${files.map((file) => {
94
+ const name = file.name || this.label;
95
+ const widest = Math.max(0, ...file.hunks.flatMap((hunk) => hunk.lines.map((line) => Math.max(line.old ?? 0, line.new ?? 0))));
96
+ const width = String(widest).length;
97
+ const now = file.hunks
98
+ .flatMap((hunk) => hunk.lines.filter((line) => line.kind !== 'removed'))
99
+ .map((line) => line.text)
100
+ .join('\n');
101
+ return html `<div part="file">
102
+ ${name ? html `<div part="header"><span part="label">${name}</span></div>` : nothing}
103
+ <div part="frame">
104
+ ${this.variant === 'split'
105
+ ? this.renderSplit(file, width)
106
+ : html `<pre part="block"><code part="code">${file.hunks.map((hunk) => html `${this.renderHunk(hunk)}${hunk.lines.map((line) => this.renderLine(line, width))}`)}</code></pre>`}
107
+ ${this.noCopy ? nothing : this.renderCopyButton(now)}
108
+ </div>
109
+ </div>`;
110
+ })}<slot hidden @slotchange=${() => this.requestUpdate()}></slot>`;
111
+ }
41
112
  };
113
+ __decorate([
114
+ property({ reflect: true, type: Boolean })
115
+ ], HelixHeadlessCodeBlock.prototype, "diff", void 0);
42
116
  __decorate([
43
117
  property()
44
118
  ], HelixHeadlessCodeBlock.prototype, "label", void 0);
45
119
  __decorate([
46
120
  property({ attribute: 'no-copy', reflect: true, type: Boolean })
47
121
  ], HelixHeadlessCodeBlock.prototype, "noCopy", void 0);
122
+ __decorate([
123
+ property({ reflect: true })
124
+ ], HelixHeadlessCodeBlock.prototype, "variant", void 0);
48
125
  HelixHeadlessCodeBlock = __decorate([
49
126
  customElement('helix-headless-code-block')
50
127
  ], HelixHeadlessCodeBlock);
@@ -143,9 +143,10 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
143
143
  /**
144
144
  * Whether this browser can sample a color off the screen.
145
145
  *
146
- * Settled after the first render rather than at construction, because there is
147
- * no `window` on the server, and a button that ships in the markup and does
148
- * nothing in Firefox is worse than one that is never offered.
146
+ * Settled before each render rather than at construction, because a server
147
+ * has no `EyeDropper` and a button that ships in the markup and does nothing
148
+ * in Firefox is worse than one that is never offered. Not while hydrating a
149
+ * server's markup, which was rendered without it.
149
150
  */
150
151
  private sampling;
151
152
  /**
@@ -103,7 +103,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
103
103
  }
104
104
  firstUpdated(changed) {
105
105
  super.firstUpdated(changed);
106
- this.sampling = 'EyeDropper' in globalThis;
106
+ this.finishHydration();
107
107
  }
108
108
  updated(changed) {
109
109
  super.updated(changed);
@@ -151,6 +151,9 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
151
151
  willUpdate(changed) {
152
152
  super.willUpdate(changed);
153
153
  this.internals.ariaInvalid = this.invalid ? 'true' : null;
154
+ if (!this.hydrating) {
155
+ this.sampling = 'EyeDropper' in globalThis;
156
+ }
154
157
  }
155
158
  renderTools() {
156
159
  return html `<${this.tag('button-group')} part="actions" label="Color tools">
@@ -1,6 +1,7 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
- import type { HelixHeadlessTableColumn } from '../../table/column/table-column.js';
3
+ import { type HelixHeadlessTableColumn } from '../../table/column/table-column.js';
4
+ import '../../checkbox/index.js';
4
5
  import '../../pagination/index.js';
5
6
  import { type DataRow } from './order.js';
6
7
  declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fusion.dev/helix-core").CollectionElement) & typeof HelixElement;
@@ -32,6 +33,25 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
32
33
  * right shape with nothing in it. That is the cost of the first paragraph: use
33
34
  * `helix-table` where the served HTML has to carry the values.
34
35
  *
36
+ * **`selectable` adds a column of checkboxes, keyed on `row-key`.** A row is
37
+ * selected by its identity rather than its position, so a selection survives a
38
+ * sort, a filter and a page. `selected` is the chosen keys, comma separated, the
39
+ * shape Listbox uses for a multiple choice, and like `sort` it is controlled
40
+ * when set and seeded by `default-selected`. The box in the heading selects every
41
+ * row in the result, across pages, and shows a dash while only some are chosen.
42
+ * Choosing one of many, rather than several, is opening a row, and that is a
43
+ * link or a button in a cell rather than a selection.
44
+ *
45
+ * **A reader can size and order the columns.** `resizable` on a column puts a
46
+ * separator on its edge; `reorderable` on the table puts a grip on each heading
47
+ * that has a `field`, moved by dragging or with the arrow keys. The order lands
48
+ * in `order` as fields, and the consumer's own markup is never rewritten: the
49
+ * table assigns the headings to its header in that order instead.
50
+ *
51
+ * **`csv()` hands back the result as text**: every row the filter lets through,
52
+ * in the order on screen, in the columns on screen. Saving it is the page's
53
+ * business, the same as any other file.
54
+ *
35
55
  * @since 0.1.0
36
56
  * @status experimental
37
57
  * @category data
@@ -44,17 +64,29 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
44
64
  *
45
65
  * @event {HelixFilterEvent} helix-filter - Fired when the query changes, and cancelable.
46
66
  * @event {HelixNavigateEvent} helix-navigate - Fired when a reader asks for another page, and cancelable.
67
+ * @event {HelixReorderEvent} helix-reorder - Fired when a reader asks for the columns in another order, with every field in the new one, and cancelable.
68
+ * @event {HelixSelectEvent} helix-select - Fired when a reader changes which rows are selected, with every selected key comma separated, and cancelable.
47
69
  * @event {HelixSortEvent} helix-sort - Fired when a reader asks for a different order, and cancelable.
48
70
  *
49
71
  * @csspart after - The room the rows below the window would have taken. Windowed only.
50
72
  * @csspart before - The room the rows above the window would have taken. Windowed only.
51
73
  * @csspart cell - One cell.
74
+ * @csspart checkbox - A row's checkbox, or the one in the heading that selects them all.
75
+ * @csspart checkbox-box - The frame the checkbox's input is painted as.
76
+ * @csspart checkbox-control - The checkbox's input.
77
+ * @csspart checkbox-feedback - Forwarded from the checkbox, and never drawn here.
78
+ * @csspart checkbox-field - The row the checkbox's box sits in.
79
+ * @csspart checkbox-hint - Forwarded from the checkbox, and never drawn here.
80
+ * @csspart checkbox-label - Forwarded from the checkbox; the label is spoken, not shown.
81
+ * @csspart checkbox-mark - The tick, or the dash while some rows are selected.
52
82
  * @csspart empty - Shown when no rows match.
53
83
  * @csspart footer - The region below the grid.
54
84
  * @csspart head - The row the column headings sit in. `display: contents`, so it takes no space.
55
85
  * @csspart loading - Shown while the rows are still on their way.
56
86
  * @csspart pagination - The pagination, where this draws one.
57
87
  * @csspart row - One row of data.
88
+ * @csspart select - The cell holding a row's checkbox.
89
+ * @csspart select-all - The heading holding the checkbox that selects every row.
58
90
  * @csspart table - The grid.
59
91
  * @csspart toolbar - The region above the grid.
60
92
  *
@@ -75,6 +107,10 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
75
107
  * Query to open with, where nothing declared one.
76
108
  */
77
109
  defaultFilter: string;
110
+ /**
111
+ * Column order to open with, where nothing declared one.
112
+ */
113
+ defaultOrder: string;
78
114
  /**
79
115
  * Page to open on, where nothing declared one.
80
116
  */
@@ -83,6 +119,10 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
83
119
  * Page size to open with, where nothing declared one.
84
120
  */
85
121
  defaultPageSize: number;
122
+ /**
123
+ * Keys to open with selected, comma separated, where nothing declared any.
124
+ */
125
+ defaultSelected: string;
86
126
  /**
87
127
  * Order to open on, where nothing declared one.
88
128
  */
@@ -107,16 +147,9 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
107
147
  get sort(): string;
108
148
  set sort(next: string);
109
149
  /**
110
- * The field naming a row's stable identity.
111
- *
112
- * **Needed before selection exists, not after.** Lit keys the rendered rows by
113
- * it, so a sort moves a row's element rather than rewriting every cell in
114
- * place, which is what keeps focus inside a cell and keeps a CSS transition
115
- * attached to the row it belongs to. Left unset the index is the key, which is
116
- * correct only while nothing reorders: it is why this defaults to something
117
- * that works and is worth setting anyway.
150
+ * Accessible name for the grid.
118
151
  */
119
- key: string;
152
+ label: string;
120
153
  /**
121
154
  * What the rows are narrowed to, as one query across every column.
122
155
  *
@@ -127,9 +160,16 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
127
160
  get filter(): string;
128
161
  set filter(next: string);
129
162
  /**
130
- * Accessible name for the grid.
163
+ * Whether this element does the work or asks the consumer to.
164
+ *
165
+ * `client` filters, sorts and pages `rows` itself. `server` applies none of
166
+ * them: `rows` is the page, `total` is the size of the whole result, and the
167
+ * three events are requests the consumer answers by fetching. Past some size
168
+ * the client cannot hold the data, so this is not an optimization, it is the
169
+ * only mode that works, and it is here from the start because adding it later
170
+ * inverts the data flow.
131
171
  */
132
- label: string;
172
+ mode: 'client' | 'server';
133
173
  /**
134
174
  * The page on show, from 1.
135
175
  *
@@ -140,16 +180,38 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
140
180
  get page(): number;
141
181
  set page(next: number);
142
182
  /**
143
- * Whether this element does the work or asks the consumer to.
183
+ * Whether a reader may move the columns.
144
184
  *
145
- * `client` filters, sorts and pages `rows` itself. `server` applies none of
146
- * them: `rows` is the page, `total` is the size of the whole result, and the
147
- * three events are requests the consumer answers by fetching. Past some size
148
- * the client cannot hold the data, so this is not an optimization, it is the
149
- * only mode that works, and it is here from the start because adding it later
150
- * inverts the data flow.
185
+ * Each heading with a `field` gets a grip, dragged along the header or moved a
186
+ * place at a time with the arrow keys once it has focus.
151
187
  */
152
- mode: 'client' | 'server';
188
+ reorderable: boolean;
189
+ /**
190
+ * The columns' order, as their fields separated by spaces.
191
+ *
192
+ * A reader's move fires `helix-reorder` first, and refusing it leaves the
193
+ * columns where they were, which is how a consumer who stores the order takes
194
+ * the decision. `default-order` seeds an order this element then owns. A field not named
195
+ * keeps its place after the named ones, and a column with no field never
196
+ * moves.
197
+ */
198
+ get order(): string;
199
+ set order(next: string);
200
+ /**
201
+ * The field naming a row's stable identity.
202
+ *
203
+ * **Needed before selection exists, not after.** Lit keys the rendered rows by
204
+ * it, so a sort moves a row's element rather than rewriting every cell in
205
+ * place, which is what keeps focus inside a cell and keeps a CSS transition
206
+ * attached to the row it belongs to. Left unset the index is the key, which is
207
+ * correct only while nothing reorders: it is why this defaults to something
208
+ * that works and is worth setting anyway.
209
+ *
210
+ * **`row-key` and not `key`**, which is what it was called until selection
211
+ * needed it from React: `key` is reserved there, and a JSX `key` never reaches
212
+ * the element, so a React page could not tell the table which field a row is.
213
+ */
214
+ rowKey: string;
153
215
  /**
154
216
  * The whole result, in the order it arrived.
155
217
  *
@@ -162,6 +224,24 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
162
224
  * holds and reordering it under them is a mutation they did not ask for.
163
225
  */
164
226
  rows: readonly DataRow[];
227
+ /**
228
+ * Whether a reader may select rows, with a column of checkboxes.
229
+ *
230
+ * Wants `row-key`: a selection is a set of identities, and without one the rows
231
+ * are known by their place in the result, which a sort changes.
232
+ */
233
+ selectable: boolean;
234
+ /**
235
+ * The keys of the selected rows, comma separated.
236
+ *
237
+ * A string rather than an array, so it is an attribute a server writes and a
238
+ * framework's hydration keeps, and the shape a multiple Listbox already uses.
239
+ * A key holding a comma cannot be told apart from two keys, which is the
240
+ * price of the shape. A reader's choice fires `helix-select` first, and
241
+ * refusing it leaves the selection as it was.
242
+ */
243
+ get selected(): string;
244
+ set selected(next: string);
165
245
  /**
166
246
  * How many rows a page holds. Zero is every row, which is what "no paging" means.
167
247
  *
@@ -217,6 +297,14 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
217
297
  private pageState;
218
298
  private pageSizeState;
219
299
  private paginatedState;
300
+ private orderState;
301
+ private selectedState;
302
+ /** The order while a grip is being dragged, before anybody has been asked. */
303
+ private dragOrder;
304
+ /** The column a grip is dragging, and the pointer doing it. */
305
+ private dragging;
306
+ /** Said once, so a table that re-renders does not repeat it. */
307
+ private warnedAboutKey;
220
308
  private readonly virtualize;
221
309
  /**
222
310
  * The rows in the shadow tree, each with the row of the page it drew.
@@ -259,6 +347,16 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
259
347
  private windowOf;
260
348
  /** The columns declared as children, in the order they are written. */
261
349
  get tableColumns(): HelixHeadlessTableColumn[];
350
+ /**
351
+ * The columns in the order a reader arranged them.
352
+ *
353
+ * The ones with a field are put in `order`'s order, those it does not name
354
+ * after them in the order they were written, and a column with no field stays
355
+ * where it was written: it has nothing to be named by.
356
+ */
357
+ get orderedColumns(): HelixHeadlessTableColumn[];
358
+ /** The fields in the order on screen. */
359
+ private get fields();
262
360
  /** The columns reduced to what the ordering needs. */
263
361
  private get axes();
264
362
  /** The track list, from the columns if there are any. */
@@ -337,6 +435,47 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
337
435
  * consumer refuses the local reorder, fetches, and hands back a new set.
338
436
  */
339
437
  private requestSort;
438
+ /** Asks for the columns in a different order, as every field in the new one. */
439
+ private requestOrder;
440
+ /** Asks for a different set of selected rows. */
441
+ private requestSelected;
442
+ /** The keys selected now. */
443
+ private get chosen();
444
+ /**
445
+ * A row's identity: its `row-key` field, or its place in the whole result.
446
+ *
447
+ * The place is a fallback that is right only while nothing reorders, and a
448
+ * selection outlives an order, so `selectable` without a `row-key` says so once.
449
+ */
450
+ private keyOf;
451
+ /**
452
+ * Every key the box in the heading answers for: the whole result in `client`
453
+ * mode, across every page, and the page it was handed in `server` mode, which
454
+ * is all it holds.
455
+ */
456
+ private get everyKey();
457
+ /** Ticks or clears one row, which the checkbox asked for and was refused. */
458
+ private readonly toggleRow;
459
+ /** Selects every row, or clears them all where every one already was. */
460
+ private readonly toggleAll;
461
+ /** Moves a column by some places among the ones that move. */
462
+ private moveColumn;
463
+ /**
464
+ * The result as CSV: every row the filter lets through, in the order on
465
+ * screen, in the columns on screen, headed by their headings.
466
+ *
467
+ * **Values, not cells.** A cell may be a template, a badge or a link, so each
468
+ * column gives its field's value, which is what a spreadsheet wants anyway.
469
+ * A column with no field has no value and is left out. In `server` mode this
470
+ * is the page it holds, which is all it can know.
471
+ *
472
+ * **A text value that a spreadsheet would run is quoted as text.** A cell
473
+ * opening with `=`, `+`, `-` or `@` is a formula to a spreadsheet, and a
474
+ * result is often something a reader did not write: those values gain a
475
+ * leading apostrophe, the mark spreadsheets read as "this is text". Numbers are
476
+ * left alone, so a negative one stays a number.
477
+ */
478
+ csv(): string;
340
479
  /** Asks for a different query. */
341
480
  private requestFilter;
342
481
  /** Asks for a different page. */
@@ -402,6 +541,13 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
402
541
  /** Columns arrive from the light DOM, which changes no property here. */
403
542
  private readonly handleSlotChange;
404
543
  private renderCell;
544
+ /**
545
+ * A checkbox, named for what it chooses, whose change the table takes.
546
+ *
547
+ * Refused and re-asked rather than left to tick itself: the selection is the
548
+ * table's, so the box only ever shows what `selected` says.
549
+ */
550
+ private renderCheckbox;
405
551
  private renderRows;
406
552
  /**
407
553
  * A row's place in the whole result, counting the header as row 1.
@@ -439,6 +585,8 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
439
585
  private renderSpacer;
440
586
  /** The controller owns the geometry; this is only what tells it to look again. */
441
587
  private readonly handleScroll;
588
+ /** The heading of the checkbox column, which selects the whole result. */
589
+ private renderSelectAll;
442
590
  protected render(): TemplateResult;
443
591
  /**
444
592
  * Listens on the host, not on the slot the columns are assigned to.
@@ -457,10 +605,34 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
457
605
  */
458
606
  connectedCallback(): void;
459
607
  disconnectedCallback(): void;
460
- /** A click on a sortable heading. */
608
+ /**
609
+ * A click on a sortable heading, anywhere but its grip or its edge.
610
+ *
611
+ * Enter and Space arrive here too, as the click the heading's sort button
612
+ * makes of them, so a keyboard needs nothing of its own.
613
+ */
461
614
  private readonly handleHeadClick;
462
- /** The same on Enter and Space, since a heading that sorts is a control. */
463
- private readonly handleHeadKey;
615
+ /** Whether the event passed through a part of that name on its way here. */
616
+ private partIn;
617
+ /** The arrow keys on a grip move its column a place, along the writing direction. */
618
+ private readonly handleGripKey;
619
+ /**
620
+ * A grip taken hold of: the headings follow the pointer and the order is asked
621
+ * for once it lets go.
622
+ *
623
+ * Previewed without an event, because a drag passes through a dozen orders on
624
+ * its way to the one the reader meant, and a consumer should hear the one.
625
+ */
626
+ private readonly handleGripDown;
627
+ private readonly handleGripMove;
628
+ private readonly handleGripUp;
629
+ private readonly handleGripCancel;
630
+ private stopDrag;
631
+ /**
632
+ * A column asked for a width. The column keeps its own, so all this does is
633
+ * draw the grid again once it has, a microtask on, when the width has landed.
634
+ */
635
+ private readonly handleResize;
464
636
  private columnFrom;
465
637
  /**
466
638
  * The composed pagination's request, relayed as this element's own.