@fusion.dev/helix-headless 0.23.0 → 0.24.1

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 (124) hide show
  1. package/custom-elements.json +50579 -26954
  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/avatar/group/avatar-group.d.ts +1 -0
  8. package/dist/avatar/group/avatar-group.js +5 -1
  9. package/dist/avatar/root/avatar-root.d.ts +1 -0
  10. package/dist/avatar/root/avatar-root.js +11 -0
  11. package/dist/carousel/root/carousel-root.d.ts +3 -0
  12. package/dist/carousel/root/carousel-root.js +23 -4
  13. package/dist/chat/index.d.ts +1 -1
  14. package/dist/chat/message/chat-message.d.ts +7 -0
  15. package/dist/chat/message/chat-message.js +2 -1
  16. package/dist/chat/root/chat-root.d.ts +195 -4
  17. package/dist/chat/root/chat-root.js +316 -23
  18. package/dist/checkbox-group/root/checkbox-group-root.d.ts +2 -0
  19. package/dist/checkbox-group/root/checkbox-group-root.js +12 -2
  20. package/dist/code-block/index.d.ts +1 -0
  21. package/dist/code-block/index.js +1 -0
  22. package/dist/code-block/patch.d.ts +62 -0
  23. package/dist/code-block/patch.js +93 -0
  24. package/dist/code-block/root/code-block-root.d.ts +82 -17
  25. package/dist/code-block/root/code-block-root.js +83 -6
  26. package/dist/color-picker/root/color-picker-root.d.ts +4 -3
  27. package/dist/color-picker/root/color-picker-root.js +4 -1
  28. package/dist/combobox/root/combobox-root.d.ts +1 -0
  29. package/dist/combobox/root/combobox-root.js +6 -1
  30. package/dist/command/root/command-root.d.ts +7 -0
  31. package/dist/command/root/command-root.js +10 -0
  32. package/dist/data-table/root/data-table-root.d.ts +195 -23
  33. package/dist/data-table/root/data-table-root.js +308 -27
  34. package/dist/description-list/index.d.ts +2 -0
  35. package/dist/description-list/index.js +2 -0
  36. package/dist/description-list/item/description-list-item.d.ts +39 -0
  37. package/dist/description-list/item/description-list-item.js +26 -0
  38. package/dist/description-list/root/description-list-root.d.ts +56 -0
  39. package/dist/description-list/root/description-list-root.js +21 -0
  40. package/dist/element-events.generated.js +27 -6
  41. package/dist/filter-input/chip/filter-chip.d.ts +114 -0
  42. package/dist/filter-input/chip/filter-chip.js +61 -0
  43. package/dist/filter-input/field/filter-field.d.ts +128 -0
  44. package/dist/filter-input/field/filter-field.js +70 -0
  45. package/dist/filter-input/grammar.d.ts +96 -0
  46. package/dist/filter-input/grammar.js +133 -0
  47. package/dist/filter-input/index.d.ts +5 -0
  48. package/dist/filter-input/index.js +5 -0
  49. package/dist/filter-input/menu/filter-menu.d.ts +153 -0
  50. package/dist/filter-input/menu/filter-menu.js +164 -0
  51. package/dist/filter-input/operator/filter-operator.d.ts +105 -0
  52. package/dist/filter-input/operator/filter-operator.js +39 -0
  53. package/dist/filter-input/root/filter-input-root.d.ts +118 -9
  54. package/dist/filter-input/root/filter-input-root.js +96 -2
  55. package/dist/format-date/root/format-date-root.js +5 -2
  56. package/dist/image/root/image-root.d.ts +9 -0
  57. package/dist/image/root/image-root.js +16 -3
  58. package/dist/index.d.ts +5 -0
  59. package/dist/index.js +5 -0
  60. package/dist/kbd/root/kbd-root.d.ts +9 -2
  61. package/dist/kbd/root/kbd-root.js +7 -2
  62. package/dist/line-chart/root/line-chart-root.d.ts +9 -2
  63. package/dist/listbox/root/listbox-root.d.ts +8 -7
  64. package/dist/listbox/root/listbox-root.js +7 -2
  65. package/dist/menu/item/menu-item.d.ts +31 -6
  66. package/dist/menu/item/menu-item.js +18 -5
  67. package/dist/menu/root/menu-root.js +7 -2
  68. package/dist/notifications/group/notification-group.d.ts +129 -0
  69. package/dist/notifications/group/notification-group.js +106 -0
  70. package/dist/notifications/index.d.ts +1 -0
  71. package/dist/notifications/index.js +1 -0
  72. package/dist/notifications/notification/notification.d.ts +39 -13
  73. package/dist/notifications/notification/notification.js +11 -8
  74. package/dist/notifications/root/notifications-root.d.ts +57 -1
  75. package/dist/notifications/root/notifications-root.js +35 -4
  76. package/dist/number-input/root/number-input-root.d.ts +21 -1
  77. package/dist/number-input/root/number-input-root.js +18 -5
  78. package/dist/object-inspector/index.d.ts +1 -0
  79. package/dist/object-inspector/index.js +1 -0
  80. package/dist/object-inspector/root/object-inspector-root.d.ts +183 -0
  81. package/dist/object-inspector/root/object-inspector-root.js +348 -0
  82. package/dist/option/root/option-root.d.ts +17 -0
  83. package/dist/option/root/option-root.js +4 -0
  84. package/dist/outline/root/outline-root.d.ts +16 -1
  85. package/dist/outline/root/outline-root.js +27 -9
  86. package/dist/qr-code/root/qr-code-root.d.ts +3 -1
  87. package/dist/qr-code/root/qr-code-root.js +11 -1
  88. package/dist/radio/group/radio-group.d.ts +1 -0
  89. package/dist/rating/root/rating-root.d.ts +3 -0
  90. package/dist/rating/root/rating-root.js +9 -0
  91. package/dist/scatter-chart/index.d.ts +1 -0
  92. package/dist/scatter-chart/index.js +1 -0
  93. package/dist/scatter-chart/root/scatter-chart-root.d.ts +144 -0
  94. package/dist/scatter-chart/root/scatter-chart-root.js +205 -0
  95. package/dist/select/root/select-root.d.ts +8 -0
  96. package/dist/select/root/select-root.js +8 -6
  97. package/dist/skip-link/index.d.ts +1 -0
  98. package/dist/skip-link/index.js +1 -0
  99. package/dist/skip-link/root/skip-link-root.d.ts +81 -0
  100. package/dist/skip-link/root/skip-link-root.js +58 -0
  101. package/dist/speed-dial/root/speed-dial-root.d.ts +1 -0
  102. package/dist/speed-dial/root/speed-dial-root.js +6 -2
  103. package/dist/steps/root/steps-root.d.ts +1 -0
  104. package/dist/steps/root/steps-root.js +5 -2
  105. package/dist/table/column/table-column.d.ts +79 -0
  106. package/dist/table/column/table-column.js +125 -5
  107. package/dist/table/root/table-root.d.ts +2 -0
  108. package/dist/table/root/table-root.js +12 -3
  109. package/dist/time-picker/root/time-picker-root.d.ts +9 -0
  110. package/dist/time-picker/root/time-picker-root.js +5 -0
  111. package/dist/toast/root/toast-root.d.ts +2 -0
  112. package/dist/tooltip/root/tooltip-root.js +1 -1
  113. package/dist/tour/root/tour-root.d.ts +14 -0
  114. package/dist/tour/root/tour-root.js +43 -18
  115. package/dist/tree/item/tree-item.d.ts +7 -4
  116. package/dist/tree/item/tree-item.js +4 -1
  117. package/dist/video/root/video-root.d.ts +23 -1
  118. package/dist/video/root/video-root.js +46 -23
  119. package/dist/virtual-list/root/virtual-list-root.d.ts +10 -6
  120. package/dist/virtual-list/root/virtual-list-root.js +10 -4
  121. package/package.json +5 -5
  122. package/types/jsx.d.ts +4536 -346
  123. package/types/svelte.d.ts +11 -0
  124. package/types/vue.d.ts +738 -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">
@@ -298,6 +298,7 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
298
298
  * component: a form that received "jav" got the filter, not an option.
299
299
  */
300
300
  private sync;
301
+ protected firstUpdated(changed: PropertyValues): void;
301
302
  protected willUpdate(changed: PropertyValues): void;
302
303
  protected updated(changed: PropertyValues): void;
303
304
  /**
@@ -10,6 +10,7 @@ import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
12
  import { html } from 'lit/static-html.js';
13
+ import { optionsAmong } from '../../option/root/option-root.js';
13
14
  import '../../listbox/index.js';
14
15
  import '../../tag/index.js';
15
16
  const isInsertion = (event) => !event.inputType?.startsWith('delete');
@@ -138,7 +139,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
138
139
  return this.value === '' ? [] : this.value.split(',');
139
140
  }
140
141
  get chosen() {
141
- const options = this.listbox?.options ?? [];
142
+ const options = optionsAmong(this.knownChildren);
142
143
  const known = options.length > 0;
143
144
  const words = new Map(options.map((option) => [option.value, option.text]));
144
145
  return this.values
@@ -214,6 +215,10 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
214
215
  this.internals.setFormValue(this.value === '' ? null : this.value);
215
216
  this.reportRequired(this.value === '', 'Choose one to continue.', 'control');
216
217
  }
218
+ firstUpdated(changed) {
219
+ super.firstUpdated(changed);
220
+ this.finishHydration();
221
+ }
217
222
  willUpdate(changed) {
218
223
  super.willUpdate(changed);
219
224
  this.internals.ariaExpanded = this.open ? 'true' : 'false';
@@ -86,6 +86,11 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
86
86
  * child element, which is the slot, so `:not(:has(*))` on it is never true
87
87
  * and an empty footer would draw its rule and its padding over nothing. Steps
88
88
  * was caught by the same thing.
89
+ *
90
+ * Read from the children before each render, and heard from the slot when
91
+ * they change. The slot alone left the first render without its footer and a
92
+ * second to follow, and a hydrated palette without one for good: a
93
+ * declarative shadow root reports its slots before anything listens.
89
94
  */
90
95
  private footed;
91
96
  private get listbox();
@@ -144,6 +149,8 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
144
149
  private readonly swallowOptionSelect;
145
150
  connectedCallback(): void;
146
151
  disconnectedCallback(): void;
152
+ protected willUpdate(changed: PropertyValues): void;
153
+ protected firstUpdated(changed: PropertyValues): void;
147
154
  protected updated(changed: PropertyValues): void;
148
155
  /**
149
156
  * The surface the palette opens in, behind a seam the styled tier can move.
@@ -95,6 +95,16 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
95
95
  this.removeEventListener('click', this.handleAlternate, { capture: true });
96
96
  document.removeEventListener('keydown', this.handleHotkey);
97
97
  }
98
+ willUpdate(changed) {
99
+ super.willUpdate(changed);
100
+ if (this.childrenKnown) {
101
+ this.footed = this.knownChildren.some((child) => child.slot === 'footer');
102
+ }
103
+ }
104
+ firstUpdated(changed) {
105
+ super.firstUpdated(changed);
106
+ this.finishHydration();
107
+ }
98
108
  updated(changed) {
99
109
  super.updated?.(changed);
100
110
  if (!changed.has('open')) {