@fusion.dev/helix-headless 0.160.0 → 0.161.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 (39) hide show
  1. package/custom-elements.json +1729 -50
  2. package/dist/canvas/root/canvas-root.d.ts +59 -2
  3. package/dist/canvas/root/canvas-root.js +123 -8
  4. package/dist/chat/message/chat-message.d.ts +8 -0
  5. package/dist/chat/message/chat-message.js +11 -0
  6. package/dist/chat/root/chat-root.d.ts +47 -1
  7. package/dist/chat/root/chat-root.js +130 -4
  8. package/dist/code-block/lines.d.ts +12 -0
  9. package/dist/code-block/lines.js +41 -0
  10. package/dist/code-block/root/code-block-root.d.ts +88 -5
  11. package/dist/code-block/root/code-block-root.js +154 -10
  12. package/dist/element-events.generated.js +4 -0
  13. package/dist/splitter/root/splitter-root.d.ts +15 -0
  14. package/dist/splitter/root/splitter-root.js +16 -2
  15. package/dist/table/grid-controller.d.ts +7 -1
  16. package/dist/table/grid-controller.js +9 -1
  17. package/dist/table/root/table-root.d.ts +122 -0
  18. package/dist/table/root/table-root.js +293 -17
  19. package/dist/table/row/table-row.d.ts +41 -0
  20. package/dist/table/row/table-row.js +56 -2
  21. package/dist/timeline/item/timeline-item.d.ts +47 -10
  22. package/dist/timeline/item/timeline-item.js +36 -4
  23. package/dist/video-playlist/item/video-playlist-item.d.ts +11 -0
  24. package/dist/video-playlist/item/video-playlist-item.js +5 -0
  25. package/dist/video-playlist/menu/video-playlist-menu.d.ts +6 -0
  26. package/dist/video-playlist/menu/video-playlist-menu.js +11 -0
  27. package/dist/video-playlist/root/video-playlist-root.d.ts +59 -5
  28. package/dist/video-playlist/root/video-playlist-root.js +82 -10
  29. package/dist/virtual-list/fill.d.ts +6 -1
  30. package/dist/virtual-list/fill.js +1 -1
  31. package/dist/virtual-list/keys.d.ts +8 -0
  32. package/dist/virtual-list/keys.js +20 -0
  33. package/dist/virtual-list/root/virtual-list-root.d.ts +98 -13
  34. package/dist/virtual-list/root/virtual-list-root.js +195 -20
  35. package/lint.json +1 -1
  36. package/package.json +5 -5
  37. package/types/jsx.d.ts +936 -68
  38. package/types/vue.d.ts +226 -12
  39. package/types/words.d.ts +79 -0
@@ -5,25 +5,58 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { customElement, drawStatus, formatWords, HelixElement, HelixEndEvent, inlineStep, MixinControllerActive, StatusController, words, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
8
+ import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessVideoPlaylistItem } from '../item/video-playlist-item.js';
11
11
  import { playOrder, stepTo } from '../order.js';
12
12
  let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinControllerActive(HelixElement) {
13
13
  constructor() {
14
14
  super(...arguments);
15
+ this.emptyLabel = 'No videos';
15
16
  this.label = 'Playlist';
17
+ this.noAdvance = false;
16
18
  this.playingLabel = 'Now playing {title}';
17
19
  this.status = new StatusController(this);
18
20
  this.shuffle = false;
19
21
  this.chased = false;
22
+ this.known = {
23
+ current: undefined,
24
+ values: [],
25
+ };
20
26
  this.handleSelect = (event) => {
21
27
  event.stopImmediatePropagation();
22
28
  if (event.defaultPrevented) {
23
29
  return;
24
30
  }
25
31
  this.chased = true;
26
- this.requestActive(event.detail.value);
32
+ this.requestActive(event.detail.value, 'press');
33
+ };
34
+ this.handleFocusIn = (event) => {
35
+ const entry = event.target;
36
+ if (entry instanceof HelixHeadlessVideoPlaylistItem && this.items.includes(entry)) {
37
+ this.stop = entry.value;
38
+ this.placeStop(this.items);
39
+ }
40
+ };
41
+ this.handleEntries = () => {
42
+ const { current, values } = this.known;
43
+ const items = this.items;
44
+ const present = new Set(items.filter((one) => !one.disabled).map((one) => one.value));
45
+ if (current !== undefined && !items.some((one) => one.value === current)) {
46
+ const at = values.indexOf(current);
47
+ const next = values.slice(at + 1).find((value) => present.has(value)) ??
48
+ values
49
+ .slice(0, at)
50
+ .reverse()
51
+ .find((value) => present.has(value));
52
+ if (next !== undefined) {
53
+ this.chased = this.player?.paused === false;
54
+ if (!this.requestActive(next, 'removed')) {
55
+ this.chased = false;
56
+ }
57
+ }
58
+ }
59
+ this.requestUpdate();
27
60
  };
28
61
  this.handleKeydown = (event) => {
29
62
  const from = event.target;
@@ -77,7 +110,9 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
77
110
  this.dispatchEvent(new HelixEndEvent());
78
111
  return;
79
112
  }
80
- this.step(1);
113
+ if (!this.noAdvance) {
114
+ this.step(1, 'ended');
115
+ }
81
116
  };
82
117
  }
83
118
  static { this.words = 'helix-video-playlist'; }
@@ -96,7 +131,7 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
96
131
  const order = this.dealt ?? values;
97
132
  return { current, next: stepTo(order, current, by, this.repeat === 'all') };
98
133
  }
99
- step(by) {
134
+ step(by, cause) {
100
135
  const { current, next } = this.following(by);
101
136
  if (next === undefined) {
102
137
  return;
@@ -106,7 +141,7 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
106
141
  return;
107
142
  }
108
143
  this.chased = true;
109
- if (!this.requestActive(next)) {
144
+ if (!this.requestActive(next, cause)) {
110
145
  this.chased = false;
111
146
  }
112
147
  }
@@ -122,10 +157,19 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
122
157
  return this.resolveActive(this.items);
123
158
  }
124
159
  next() {
125
- this.step(1);
160
+ this.step(1, 'next');
126
161
  }
127
162
  previous() {
128
- this.step(-1);
163
+ this.step(-1, 'previous');
164
+ }
165
+ placeStop(items) {
166
+ const enabled = items.filter((one) => !one.disabled);
167
+ const holder = enabled.find((one) => one.value === this.stop) ??
168
+ enabled.find((one) => one.current) ??
169
+ enabled[0];
170
+ for (const item of items) {
171
+ item.focusable = item === holder;
172
+ }
129
173
  }
130
174
  takePlayerKey(event) {
131
175
  const player = this.player;
@@ -144,20 +188,27 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
144
188
  return false;
145
189
  }
146
190
  event.preventDefault();
147
- this.step(event.key === 'N' ? 1 : -1);
191
+ this.step(event.key === 'N' ? 1 : -1, event.key === 'N' ? 'next' : 'previous');
148
192
  return true;
149
193
  }
150
194
  connectedCallback() {
151
195
  super.connectedCallback();
196
+ this.addEventListener('focusin', this.handleFocusIn);
152
197
  this.addEventListener('helix-select', this.handleSelect);
153
198
  this.addEventListener('keydown', this.handleKeydown);
154
199
  this.addEventListener('helix-next', this.handleNext);
155
200
  this.addEventListener('helix-previous', this.handlePrevious);
156
201
  this.addEventListener('ended', this.handleEnded, true);
157
202
  this.addEventListener('play', this.handlePlay, true);
203
+ if (typeof MutationObserver !== 'undefined') {
204
+ this.watcher ??= new MutationObserver(this.handleEntries);
205
+ this.watcher.observe(this, { childList: true, subtree: true });
206
+ }
158
207
  }
159
208
  disconnectedCallback() {
160
209
  super.disconnectedCallback();
210
+ this.watcher?.disconnect();
211
+ this.removeEventListener('focusin', this.handleFocusIn);
161
212
  this.removeEventListener('helix-select', this.handleSelect);
162
213
  this.removeEventListener('keydown', this.handleKeydown);
163
214
  this.removeEventListener('helix-next', this.handleNext);
@@ -165,6 +216,10 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
165
216
  this.removeEventListener('ended', this.handleEnded, true);
166
217
  this.removeEventListener('play', this.handlePlay, true);
167
218
  }
219
+ firstUpdated(changed) {
220
+ super.firstUpdated(changed);
221
+ this.finishHydration();
222
+ }
168
223
  willUpdate(changed) {
169
224
  super.willUpdate(changed);
170
225
  if (changed.has('shuffle') && !this.shuffle) {
@@ -178,13 +233,21 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
178
233
  for (const item of items) {
179
234
  item.current = item === current;
180
235
  }
236
+ this.known = { current: current?.value, values: items.map((one) => one.value) };
237
+ this.placeStop(items);
181
238
  const player = this.player;
182
239
  if (!player || !current) {
183
240
  return;
184
241
  }
185
- if (player.src !== current.src) {
242
+ if (player.src !== current.src || current !== this.pointed) {
243
+ this.pointed = current;
186
244
  this.toggleState('ended', false);
187
- player.src = current.src;
245
+ if (player.src !== current.src) {
246
+ player.src = current.src;
247
+ }
248
+ else if (this.chased) {
249
+ player.currentTime = 0;
250
+ }
188
251
  player.poster = current.poster;
189
252
  player.mediaTitle = current.text;
190
253
  if (!player.hasAttribute('label')) {
@@ -198,17 +261,26 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
198
261
  this.chased = false;
199
262
  }
200
263
  render() {
264
+ const empty = this.childrenKnown && this.items.length === 0;
201
265
  return html `<div part="player"><slot name="player"></slot></div>
202
266
  <div part="items" role="list" aria-label=${this.label}>
203
267
  <slot></slot>
204
268
  </div>
269
+ ${empty ? html `<div part="empty"><slot name="empty">${this.emptyLabel}</slot></div>` : nothing}
205
270
  ${drawStatus(this.status)}`;
206
271
  }
207
272
  };
273
+ __decorate([
274
+ words,
275
+ property({ attribute: 'empty-label' })
276
+ ], HelixHeadlessVideoPlaylist.prototype, "emptyLabel", void 0);
208
277
  __decorate([
209
278
  words,
210
279
  property()
211
280
  ], HelixHeadlessVideoPlaylist.prototype, "label", void 0);
281
+ __decorate([
282
+ property({ attribute: 'no-advance', type: Boolean })
283
+ ], HelixHeadlessVideoPlaylist.prototype, "noAdvance", void 0);
212
284
  __decorate([
213
285
  words,
214
286
  property({ attribute: 'playing-label' })
@@ -18,7 +18,12 @@
18
18
  * does not read "undefined".
19
19
  */
20
20
  export declare const textOf: (value: unknown) => string;
21
- /** Fills one element's holes, its own and those below it, from an item. */
21
+ /**
22
+ * Fills one element's holes, its own and those below it, from an item.
23
+ *
24
+ * The element itself whatever hole it carries, so a row whose root is a link
25
+ * takes its `data-field-href` as readily as its text.
26
+ */
22
27
  export declare const fillHoles: (root: Element, item: unknown) => void;
23
28
  /**
24
29
  * The key of item `at` in a selecting list's `value`: its `keyField` where it
@@ -9,7 +9,7 @@ const writeAttribute = (element, name, value) => {
9
9
  element.setAttribute(name, value === true ? '' : textOf(value));
10
10
  };
11
11
  export const fillHoles = (root, item) => {
12
- const holes = [...(root.matches('[data-field]') ? [root] : []), ...root.querySelectorAll('*')];
12
+ const holes = [root, ...root.querySelectorAll('*')];
13
13
  for (const hole of holes) {
14
14
  for (const { name, value } of [...hole.attributes]) {
15
15
  if (name === 'data-field') {
@@ -22,3 +22,11 @@ export declare const stepIndex: (key: string, at: number, count: number, page: n
22
22
  across?: number;
23
23
  forward?: string;
24
24
  }) => number | undefined;
25
+ /**
26
+ * Where a key lands once disabled items are passed over: from `to`, which
27
+ * `stepIndex` gave, on in the way the key moves until an item that is not
28
+ * disabled. A line move steps a line at a time, in the same column. Home looks
29
+ * forward from the first item and End back from the last. `undefined` where
30
+ * every item that way is disabled, and the key moves nothing.
31
+ */
32
+ export declare const skipDisabled: (key: string, from: number, to: number, count: number, disabled: (index: number) => boolean, across?: number) => number | undefined;
@@ -33,3 +33,23 @@ export const stepIndex = (key, at, count, page, { across = 1, forward = 'ArrowRi
33
33
  return undefined;
34
34
  }
35
35
  };
36
+ export const skipDisabled = (key, from, to, count, disabled, across = 1) => {
37
+ if (!disabled(to)) {
38
+ return to;
39
+ }
40
+ const width = Math.max(1, Math.trunc(across));
41
+ const line = key === 'ArrowDown' || key === 'ArrowUp' || key === 'PageDown' || key === 'PageUp';
42
+ const way = key === 'Home' ? 1 : key === 'End' ? -1 : to >= from ? 1 : -1;
43
+ const step = (line ? width : 1) * way;
44
+ for (let at = to + step; at >= 0 && at < count; at += step) {
45
+ if (!disabled(at)) {
46
+ return at;
47
+ }
48
+ }
49
+ for (let at = to + way; at >= 0 && at < count; at += way) {
50
+ if (!disabled(at)) {
51
+ return at === from ? undefined : at;
52
+ }
53
+ }
54
+ return undefined;
55
+ };
@@ -4,9 +4,10 @@ import { type PropertyValues, type TemplateResult } from 'lit';
4
4
  * Draws one item into its row, for a list given `renderItem`.
5
5
  *
6
6
  * Returns anything Lit renders: a template from `html`, a string, or a DOM
7
- * node the function built.
7
+ * node the function built. `Item` types the item, so a page writes
8
+ * `const row: VirtualListRenderer<Person> = (person) => person.name`.
8
9
  */
9
- export type VirtualListRenderer = (item: unknown, index: number) => unknown;
10
+ export type VirtualListRenderer<Item = unknown> = (item: Item, index: number) => unknown;
10
11
  /** Where `scrollToIndex` puts an item, and how it gets there. */
11
12
  export interface VirtualListScrollOptions {
12
13
  /**
@@ -49,8 +50,10 @@ export interface VirtualListRange {
49
50
  * **A grid.** `columns` lays the items out that many to a line, as a grid of
50
51
  * equal columns, and windows the set a line at a time, as React Aria's grid
51
52
  * layout does. Each item draws one element, a cell of the grid, and
52
- * `estimate` is the height of a line. Space the items with padding inside
53
- * each one, which the list measures with the line.
53
+ * `estimate` is the height of a line. `min-column-width` lays out as many
54
+ * columns as fit the box instead, as React Aria's grid layout does with its
55
+ * smallest item size, and follows the box as it resizes. `gap` spaces the rows, and the columns
56
+ * of a grid, by a number of pixels the list counts in each row's place.
54
57
  *
55
58
  * **Keyboard.** The list is one tab stop.
56
59
  *
@@ -61,6 +64,7 @@ export interface VirtualListRange {
61
64
  * | Home, End | Moves to the first or the last item. |
62
65
  * | Page Down, Page Up | Moves by the number of items, or of lines in a grid, on screen. |
63
66
  * | A letter or a word | Moves to the next item whose text starts with what you typed. |
67
+ * | Escape | Lets go of every chosen item, in a list with `selection-mode`. |
64
68
  * | Tab | Moves into the focused row's own links and buttons, then out of the list. |
65
69
  *
66
70
  * An item that is not drawn is scrolled into view and focused, so a keyboard
@@ -77,12 +81,23 @@ export interface VirtualListRange {
77
81
  * from: `single` selects one item and `multiple` any number. Space or a press
78
82
  * selects an item, Shift with Space, an arrow, Home, End or a press takes in
79
83
  * the items from the last one chosen, and Control or Command with A selects
80
- * them all, in `multiple`. `value` holds the chosen items' keys, separated by
81
- * commas, in the order of the set: `key-field` names each item's key, and a
82
- * string or a number item is its own key. A change asks with
83
- * `helix-before-change` and tells with `change`. Each row is an `option`
84
- * marked `aria-selected`, so a row's own links and buttons are not reached
85
- * there: keep those to a list without `selection-mode`.
84
+ * them all, in `multiple`. Escape lets go of every chosen item, and keeps the
85
+ * key from the dialog or menu around the list while there was something to
86
+ * let go of. `value` holds the chosen items' keys, separated by commas, in
87
+ * the order of the set: `key-field` names each item's key, and a string or a
88
+ * number item is its own key. A change asks with `helix-before-change` and
89
+ * tells with `change`. Each row is an `option` marked `aria-selected`, so a
90
+ * row's own links and buttons are not reached there: keep those to a list
91
+ * without `selection-mode`.
92
+ *
93
+ * `selection-behavior="replace"` selects the item pressed alone, as a file
94
+ * list does, and the arrow keys carry the selection with them. Control or
95
+ * Command with a press or with Space adds or takes out one item, and with an
96
+ * arrow moves without selecting.
97
+ *
98
+ * **Disabled items.** `disabled-keys` names items by their keys, separated by
99
+ * commas. Their rows carry `aria-disabled`, the keys and typing pass over
100
+ * them, and a press, Space, or Control with A does not select them.
86
101
  *
87
102
  * **Loading more.** `helix-range-change` fires as the items on screen change,
88
103
  * and `range` holds them. `helix-load-more` fires once a reader is within one
@@ -152,6 +167,11 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
152
167
  * time, with Left and Right moving along a line.
153
168
  */
154
169
  columns: number;
170
+ /**
171
+ * The keys of the items a reader cannot choose or move to, separated by
172
+ * commas, as `value` writes keys. Their rows carry `aria-disabled`.
173
+ */
174
+ disabledKeys: string;
155
175
  /**
156
176
  * The text of the row shown when there are no items and none are loading,
157
177
  * where the `empty` slot is empty. Empty, the default, shows no row.
@@ -169,6 +189,13 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
169
189
  * where both are set.
170
190
  */
171
191
  for: string;
192
+ /**
193
+ * The space between two rows, and between two columns in a grid, in
194
+ * pixels. The list counts it in each row's place, so a gap does not walk
195
+ * the rows away from the scroll position the way a margin on each row
196
+ * would.
197
+ */
198
+ gap: number;
172
199
  /**
173
200
  * The items, as an array. Set it as a property.
174
201
  *
@@ -214,6 +241,12 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
214
241
  * one-line rows should leave this alone.
215
242
  */
216
243
  measured: boolean;
244
+ /**
245
+ * The narrowest a column of a grid may be, in pixels. Set, the list lays
246
+ * out as many columns as fit the box at that width, at least one, and
247
+ * follows the box as it resizes, which `columns` then does not decide.
248
+ */
249
+ minColumnWidth: number;
217
250
  /**
218
251
  * Draws one item into its row, given the item and its index. Set it as a
219
252
  * property.
@@ -222,7 +255,13 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
222
255
  * node. The list wraps each result in a `<div>` that carries the row's role
223
256
  * and place in the set. Where it is unset, the row `<template>` is used.
224
257
  */
225
- renderItem: VirtualListRenderer | undefined;
258
+ renderItem: VirtualListRenderer<any> | undefined;
259
+ /**
260
+ * What a press does to the items already chosen: `toggle` adds or takes out
261
+ * the item, and `replace` selects it alone and lets the arrow keys carry the
262
+ * selection, with Control or Command adding or taking out one item.
263
+ */
264
+ selectionBehavior: 'replace' | 'toggle';
226
265
  /**
227
266
  * Makes the list a list box a reader chooses from: `single` selects one
228
267
  * item, and `multiple` any number. Unset is a list a reader reads.
@@ -279,6 +318,19 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
279
318
  * script replaced under the same id.
280
319
  */
281
320
  private watcher;
321
+ /**
322
+ * Watches the drawn rows for a change of size after they were drawn: an
323
+ * image that loads, a row that opens, text a page edits in place.
324
+ *
325
+ * **Measured once, a row that grew later kept its old height**, so the
326
+ * window and the spacers placed every row after it by the old one. React
327
+ * Aria's virtualizer observes its items for the same reason. A frame later,
328
+ * so a size the next render changes is not asked of the browser's pass over
329
+ * the observers a second time.
330
+ */
331
+ private sizes;
332
+ /** The frame the next measure of resized rows waits for. */
333
+ private resized;
282
334
  /** Carries a host `aria-label` and `aria-labelledby` to the element with the `list` role. */
283
335
  protected readonly aria: AriaForwardController;
284
336
  private readonly virtualize;
@@ -289,8 +341,21 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
289
341
  * box to measure.
290
342
  */
291
343
  get range(): VirtualListRange;
292
- /** How many items a line holds: `columns`, at least one. */
344
+ /** The gap in pixels, none where it is not a positive number. */
345
+ private get space();
346
+ /**
347
+ * How many items a line holds: as many as fit at `min-column-width` where
348
+ * it is set and the box has a width, and `columns` otherwise, at least one.
349
+ */
293
350
  private get across();
351
+ /** How wide the box is, read for `min-column-width`. */
352
+ private boxWidth;
353
+ /** Watches the box's width, for `min-column-width`. */
354
+ private widthWatch;
355
+ /** The items to a line the measurements were taken at. */
356
+ private measuredAcross;
357
+ /** Watches the box for its width while `min-column-width` asks for it. */
358
+ private watchWidth;
294
359
  /** The items a span of lines holds. */
295
360
  private itemsOn;
296
361
  /**
@@ -335,8 +400,21 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
335
400
  * so by itself.
336
401
  */
337
402
  protected willUpdate(changed: PropertyValues): void;
403
+ /**
404
+ * Keeps the reader on the item they were reading when items arrive before
405
+ * it, as a feed loading newer posts above does.
406
+ *
407
+ * **Every index moves by what arrived**, so the drawn rows are renumbered,
408
+ * the tab stop moves with its item, and the controller is told, which moves
409
+ * the scroll offset by the room the new items take. Recognized where the
410
+ * new array ends with the old one: the same items, or the same keys under
411
+ * `key-field`.
412
+ */
413
+ private keepPlace;
338
414
  connectedCallback(): void;
339
415
  disconnectedCallback(): void;
416
+ /** Watches a row drawn on this pass for a change of size. */
417
+ private watchSize;
340
418
  /**
341
419
  * Reads the source again when its text changed or another element took its
342
420
  * id. Rows this list drew are its own writes and are not news.
@@ -359,6 +437,10 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
359
437
  * `data-field`, or a string or number item itself.
360
438
  */
361
439
  private texts;
440
+ /** The keys `disabled-keys` names. */
441
+ private get disabled();
442
+ /** Whether item `at` is one a reader cannot choose or move to. */
443
+ private isDisabled;
362
444
  /** The item a row element drew, for an element that is one of the rows. */
363
445
  private indexOf;
364
446
  /** The row that holds the tab stop for item `index`, where it is drawn. */
@@ -423,7 +505,8 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
423
505
  private keptIndex;
424
506
  /**
425
507
  * Writes a row's role, and whether it is chosen in a list that selects:
426
- * `option` with `aria-selected` there, and `listitem` otherwise. Every pass,
508
+ * `option` with `aria-selected` there, and `listitem` otherwise, and
509
+ * `aria-disabled` on an item `disabled-keys` names. Every pass,
427
510
  * since a choice changes no row's content.
428
511
  */
429
512
  private mark;
@@ -458,6 +541,8 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
458
541
  * buttons are in the tab order, and those of every other row wait.
459
542
  */
460
543
  private place;
544
+ /** The spacers' heights: the window's, with the line kept for focus taking its own room. */
545
+ private spacerSizes;
461
546
  protected render(): TemplateResult;
462
547
  }
463
548
  declare global {