@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
@@ -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 { AriaForwardController, anchorAt, assignSlots, commaList, customElement, cycle, everyState, followLink, format, formatWords, HelixElement, HelixSelectEvent, HelixSelectedEvent, HelixSortEvent, HelixSortedEvent, inRowOrder, MixinCollection, onAndroid, parse, ReorderController, rangeAdd, reflectElements, requestChange, sort, splitValues, toggleEvery, whenHydrated, words, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, anchorAt, assignSlots, commaList, customElement, cycle, everyState, followLink, format, formatWords, HelixBeforeToggleEvent, HelixElement, HelixLoadMoreEvent, HelixSelectEvent, HelixSelectedEvent, HelixSortEvent, HelixSortedEvent, HelixToggleEvent, inRowOrder, MixinCollection, onAndroid, parse, ReorderController, rangeAdd, reflectElements, requestChange, scrollingAncestor, sort, splitValues, toggleEvery, whenHydrated, words, } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
@@ -53,14 +53,18 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
53
53
  this.ascendingLabel = 'ascending';
54
54
  this.canceledLabel = 'Canceled';
55
55
  this.columns = '';
56
+ this.defaultExpanded = '';
56
57
  this.defaultSelected = '';
57
58
  this.defaultSort = '';
58
59
  this.descendingLabel = 'descending';
59
60
  this.droppedLabel = 'Dropped';
60
61
  this.errorText = '';
62
+ this.expandColumnLabel = 'Expand';
63
+ this.expandRowLabel = 'Expand {row}';
61
64
  this.fallbackLabel = 'Table';
62
65
  this.grid = false;
63
66
  this.label = '';
67
+ this.loadMore = false;
64
68
  this.movedLabel = '{name} moved to position {position} of {count}';
65
69
  this.moveRowLabel = 'Move {row}';
66
70
  this.pickedUpLabel = 'Picked up {name}';
@@ -68,12 +72,15 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
68
72
  this.rowGripsLabel = 'Move rows';
69
73
  this.selectable = false;
70
74
  this.selectAllLabel = 'Select every row';
75
+ this.selectColumnLabel = 'Select';
71
76
  this.selectedLabel = '{count} selected';
77
+ this.selectionBehavior = 'toggle';
72
78
  this.selectRowLabel = 'Select {row}';
73
79
  this.sortedLabel = '{direction, select, ascending {Sorted by {column}, ascending} descending {Sorted by {column}, descending} other {Sorted by {column}}}';
74
80
  this.unsortedLabel = 'Not sorted';
75
81
  this.sortState = undefined;
76
82
  this.selectedState = undefined;
83
+ this.expandedState = undefined;
77
84
  this.headed = false;
78
85
  this.aria = new AriaForwardController(this, {
79
86
  label: true,
@@ -81,13 +88,20 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
81
88
  });
82
89
  this.gridKeys = new TableGridController(this, {
83
90
  activate: (place, event) => this.activate(place, event),
91
+ arrive: (to, control) => {
92
+ if (this.replaces && !control) {
93
+ this.chooseOnly(to);
94
+ }
95
+ },
84
96
  columns: () => this.tableColumns.filter((column) => column.shown),
85
97
  lead: () => this.lead,
86
98
  on: () => this.grid,
87
99
  part: (name) => this.partNamed(name),
88
100
  rows: () => (this.childrenKnown ? this.arrangedRows : []),
89
101
  rowWords: () => this.arrangedRows.map((row) => textOf(row.children[0])),
90
- selectAll: () => this.selectable && this.requestSelected(new Set([...this.chosen, ...this.enabledKeys])),
102
+ selectAll: () => this.selectable &&
103
+ !this.single &&
104
+ this.requestSelected(new Set([...this.chosen, ...this.enabledKeys])),
91
105
  selectRange: (to, from) => this.selectRange(to, from),
92
106
  });
93
107
  this.reorder = new ReorderController(this, {
@@ -131,6 +145,33 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
131
145
  event.preventDefault();
132
146
  this.chooseRow(found.key, event.detail.checked);
133
147
  };
148
+ this.handleRowClick = (event) => {
149
+ if (!this.replaces || event.defaultPrevented) {
150
+ return;
151
+ }
152
+ const path = event.composedPath();
153
+ const at = this.arrangedRows.findIndex((row) => path.includes(row));
154
+ const row = this.arrangedRows[at];
155
+ if (!row) {
156
+ return;
157
+ }
158
+ const control = path.find((node) => node instanceof Element &&
159
+ (node.matches('a[href], button, input, select, textarea, label, [contenteditable]') ||
160
+ (node.getRootNode() === row.shadowRoot && node.localName !== 'slot')));
161
+ if (control) {
162
+ return;
163
+ }
164
+ if (event.shiftKey && !this.single) {
165
+ this.selectRange(at, at);
166
+ return;
167
+ }
168
+ const found = this.keyed.find((one) => one.row === row);
169
+ if (found && (event.ctrlKey || event.metaKey)) {
170
+ this.chooseRow(found.key, !this.chosen.has(found.key));
171
+ return;
172
+ }
173
+ this.chooseOnly(at);
174
+ };
134
175
  this.anchorKey = undefined;
135
176
  this.handleHeadClick = (event) => {
136
177
  const path = event.composedPath();
@@ -149,8 +190,17 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
149
190
  this.watcher = undefined;
150
191
  this.alignment = undefined;
151
192
  this.aligned = '';
193
+ this.endInView = false;
152
194
  }
153
195
  static { this.words = 'helix-table'; }
196
+ get expanded() {
197
+ return this.expandedState ?? this.defaultExpanded;
198
+ }
199
+ set expanded(next) {
200
+ const previous = this.expanded;
201
+ this.expandedState = next;
202
+ this.requestUpdate('expanded', previous);
203
+ }
154
204
  get selected() {
155
205
  return this.selectedState ?? this.defaultSelected;
156
206
  }
@@ -167,10 +217,44 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
167
217
  this.sortState = next;
168
218
  this.requestUpdate('sort', previous);
169
219
  }
220
+ get nests() {
221
+ return this.childrenKnown && this.keyed.some(({ row }) => (row.parent ?? '') !== '');
222
+ }
223
+ get open() {
224
+ return new Set(commaList(this.expanded));
225
+ }
226
+ toggleRow(key) {
227
+ const row = this.rowFor(key);
228
+ if (!row) {
229
+ return;
230
+ }
231
+ const next = !this.open.has(key);
232
+ const applied = requestChange({
233
+ apply: (checked) => {
234
+ const keys = this.open;
235
+ if (checked) {
236
+ keys.add(key);
237
+ }
238
+ else {
239
+ keys.delete(key);
240
+ }
241
+ const previous = this.expanded;
242
+ this.expandedState = inRowOrder(keys, this.everyKey).join(',');
243
+ this.requestUpdate('expanded', previous);
244
+ },
245
+ event: (checked) => new HelixBeforeToggleEvent({ checked }),
246
+ host: row,
247
+ next,
248
+ });
249
+ if (applied) {
250
+ row.dispatchEvent(new HelixToggleEvent({ checked: next }));
251
+ }
252
+ }
170
253
  get lead() {
171
254
  return [
172
255
  ...(this.reorderable ? ['grip'] : []),
173
256
  ...(this.selectable ? ['select'] : []),
257
+ ...(this.nests ? ['expand'] : []),
174
258
  ];
175
259
  }
176
260
  rowFor(key) {
@@ -203,16 +287,69 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
203
287
  const index = spec
204
288
  ? declared.findIndex((column) => column.canSort && column.field === spec.field)
205
289
  : -1;
206
- if (!spec || index < 0) {
207
- return rows;
290
+ let ordered = rows;
291
+ if (spec && index >= 0) {
292
+ const records = rows.map((element) => ({ element, value: textOf(element.children[index]) }));
293
+ const numeric = records.every(({ value }) => value === '' || Number.isFinite(numberOf(value)));
294
+ const order = numeric
295
+ ? (row) => (row.value === '' ? '' : numberOf(row.value))
296
+ : (row) => row.value;
297
+ const sorted = sort(records, spec, [
298
+ { field: spec.field, order },
299
+ ]);
300
+ ordered = sorted.map((record) => record.element);
208
301
  }
209
- const records = rows.map((element) => ({ element, value: textOf(element.children[index]) }));
210
- const numeric = records.every(({ value }) => value === '' || Number.isFinite(numberOf(value)));
211
- const order = numeric
212
- ? (row) => (row.value === '' ? '' : numberOf(row.value))
213
- : (row) => row.value;
214
- const sorted = sort(records, spec, [{ field: spec.field, order }]);
215
- return sorted.map((record) => record.element);
302
+ return this.nests ? this.nested(ordered) : ordered;
303
+ }
304
+ nested(rows) {
305
+ const keys = new Map(this.keyed.map(({ key, row }) => [row, key]));
306
+ const present = new Set(keys.values());
307
+ const under = new Map();
308
+ for (const row of rows) {
309
+ const parent = row.parent ?? '';
310
+ const at = parent !== '' && present.has(parent) ? parent : '';
311
+ under.set(at, [...(under.get(at) ?? []), row]);
312
+ }
313
+ const open = this.open;
314
+ const drawn = [];
315
+ const seen = new Set();
316
+ const walk = (parent) => {
317
+ for (const row of under.get(parent) ?? []) {
318
+ if (seen.has(row)) {
319
+ continue;
320
+ }
321
+ seen.add(row);
322
+ drawn.push(row);
323
+ const key = keys.get(row) ?? '';
324
+ if (open.has(key)) {
325
+ walk(key);
326
+ }
327
+ }
328
+ };
329
+ walk('');
330
+ return drawn;
331
+ }
332
+ shape() {
333
+ const parents = new Map(this.keyed.map(({ key, row }) => [key, row.parent ?? '']));
334
+ const shapes = new Map();
335
+ for (const key of parents.keys()) {
336
+ let level = 1;
337
+ let at = parents.get(key) ?? '';
338
+ const passed = new Set([key]);
339
+ while (at !== '' && parents.has(at) && !passed.has(at)) {
340
+ passed.add(at);
341
+ level += 1;
342
+ at = parents.get(at) ?? '';
343
+ }
344
+ shapes.set(key, { branch: false, level });
345
+ }
346
+ for (const parent of parents.values()) {
347
+ const shape = shapes.get(parent);
348
+ if (shape) {
349
+ shape.branch = true;
350
+ }
351
+ }
352
+ return shapes;
216
353
  }
217
354
  news() {
218
355
  const current = this.sort;
@@ -274,12 +411,38 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
274
411
  return this.keyed.find((one) => one.key === key)?.row.disabled === true;
275
412
  }
276
413
  chooseAll() {
414
+ if (this.single) {
415
+ return;
416
+ }
277
417
  this.requestSelected(new Set(toggleEvery(this.chosen, this.enabledKeys)));
278
418
  }
419
+ get single() {
420
+ return this.selectionMode === 'single';
421
+ }
422
+ get replaces() {
423
+ return this.selectable && this.selectionBehavior === 'replace';
424
+ }
425
+ chooseOnly(at) {
426
+ const row = this.arrangedRows[at];
427
+ const found = this.keyed.find((one) => one.row === row);
428
+ if (!found || found.row.disabled) {
429
+ return;
430
+ }
431
+ this.anchorKey = found.key;
432
+ const now = this.chosen;
433
+ if (now.size === 1 && now.has(found.key)) {
434
+ return;
435
+ }
436
+ this.requestSelected(new Set([found.key]));
437
+ }
279
438
  selectRange(to, from) {
280
439
  if (!this.selectable) {
281
440
  return false;
282
441
  }
442
+ if (this.single) {
443
+ this.chooseOnly(to);
444
+ return true;
445
+ }
283
446
  const keyOf = new Map(this.keyed.map(({ key, row }) => [row, key]));
284
447
  const keys = this.arrangedRows.map((row) => keyOf.get(row) ?? '');
285
448
  const { anchor, anchorKey } = anchorAt(keys, this.anchorKey, from);
@@ -296,8 +459,11 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
296
459
  return;
297
460
  }
298
461
  this.anchorKey = key;
299
- const keys = this.chosen;
462
+ const keys = this.single ? new Set(this.chosen.has(key) ? [key] : []) : this.chosen;
300
463
  if (on) {
464
+ if (this.single) {
465
+ keys.clear();
466
+ }
301
467
  keys.add(key);
302
468
  }
303
469
  else {
@@ -311,6 +477,15 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
311
477
  if (event.key === 'F2') {
312
478
  return false;
313
479
  }
480
+ if (place.row >= 0 && leads[place.column] === 'expand') {
481
+ const row = this.arrangedRows[place.row];
482
+ const found = this.keyed.find((one) => one.row === row);
483
+ if (found && row?.branch) {
484
+ this.toggleRow(found.key);
485
+ return true;
486
+ }
487
+ return false;
488
+ }
314
489
  if (place.row === -1) {
315
490
  if (lead) {
316
491
  this.chooseAll();
@@ -327,7 +502,12 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
327
502
  const row = this.arrangedRows[place.row];
328
503
  const found = this.keyed.find((one) => one.row === row);
329
504
  if (this.selectable && found && (event.key === ' ' || lead)) {
330
- this.chooseRow(found.key, !this.chosen.has(found.key));
505
+ if (this.replaces && !lead && !(event.ctrlKey || event.metaKey)) {
506
+ this.chooseOnly(place.row);
507
+ }
508
+ else {
509
+ this.chooseRow(found.key, !this.chosen.has(found.key));
510
+ }
331
511
  return true;
332
512
  }
333
513
  if (event.key === 'Enter' && row?.href && !row.disabled) {
@@ -339,6 +519,9 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
339
519
  markSelected() {
340
520
  const keys = this.chosen;
341
521
  const still = Boolean(parse(this.sort));
522
+ const tree = this.nests;
523
+ const shapes = tree ? this.shape() : undefined;
524
+ const open = this.open;
342
525
  for (const { key, row } of this.keyed) {
343
526
  if (!row.isTableRow) {
344
527
  continue;
@@ -347,6 +530,8 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
347
530
  const label = formatWords(this, 'selectRowLabel', { row: name });
348
531
  const grip = this.reorderable ? formatWords(this, 'moveRowLabel', { row: name }) : '';
349
532
  const move = this.reorder.moveOf(key);
533
+ const shape = shapes?.get(key);
534
+ const expand = tree ? formatWords(this, 'expandRowLabel', { row: name }) : '';
350
535
  whenHydrated(row, () => {
351
536
  row.selectable = this.selectable;
352
537
  row.selected = keys.has(key);
@@ -354,6 +539,11 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
354
539
  row.gripLabel = grip;
355
540
  row.gripStill = still;
356
541
  row.move = move;
542
+ row.level = shape?.level ?? 0;
543
+ row.branch = shape?.branch ?? false;
544
+ row.open = open.has(key);
545
+ row.expandLabel = expand;
546
+ row.expandWith = tree ? () => this.toggleRow(key) : undefined;
357
547
  });
358
548
  }
359
549
  }
@@ -434,14 +624,22 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
434
624
  super.connectedCallback();
435
625
  this.watcher = new MutationObserver(() => this.requestUpdate());
436
626
  this.watcher.observe(this, { characterData: true, childList: true, subtree: true });
627
+ if (this.hasUpdated) {
628
+ this.watchEnd();
629
+ }
437
630
  this.addEventListener('click', this.handleHeadClick);
631
+ this.addEventListener('click', this.handleRowClick);
438
632
  this.addEventListener('helix-before-toggle', this.handleToggle);
439
633
  }
440
634
  disconnectedCallback() {
441
635
  super.disconnectedCallback();
636
+ this.endWatcher?.disconnect();
637
+ this.endWatcher = undefined;
638
+ this.watchedEnd = undefined;
442
639
  this.watcher?.disconnect();
443
640
  this.watcher = undefined;
444
641
  this.removeEventListener('click', this.handleHeadClick);
642
+ this.removeEventListener('click', this.handleRowClick);
445
643
  this.removeEventListener('helix-before-toggle', this.handleToggle);
446
644
  }
447
645
  static { this.shadowRootOptions = {
@@ -508,6 +706,38 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
508
706
  this.gridKeys.mark();
509
707
  this.gridKeys.place();
510
708
  }
709
+ this.watchEnd();
710
+ }
711
+ watchEnd() {
712
+ const end = this.loadMore ? this.renderRoot.querySelector('[data-end]') : null;
713
+ if (end === this.watchedEnd) {
714
+ if (this.endInView) {
715
+ this.askForMore();
716
+ }
717
+ return;
718
+ }
719
+ this.endWatcher?.disconnect();
720
+ this.endWatcher = undefined;
721
+ this.endInView = false;
722
+ this.watchedEnd = end ?? undefined;
723
+ if (!end || typeof IntersectionObserver === 'undefined') {
724
+ return;
725
+ }
726
+ this.endWatcher = new IntersectionObserver((entries) => {
727
+ this.endInView = entries.some((entry) => entry.isIntersecting);
728
+ if (this.endInView) {
729
+ this.askForMore();
730
+ }
731
+ }, { root: scrollingAncestor(this, { axis: 'y' }), rootMargin: '0px 0px 100% 0px' });
732
+ this.endWatcher.observe(end);
733
+ }
734
+ askForMore() {
735
+ const count = this.slotted.length;
736
+ if (!this.loadMore || this.loading || this.askedAt === count) {
737
+ return;
738
+ }
739
+ this.askedAt = count;
740
+ this.dispatchEvent(new HelixLoadMoreEvent());
511
741
  }
512
742
  renderGripHead() {
513
743
  if (!this.reorderable) {
@@ -520,10 +750,26 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
520
750
  tabindex=${this.grid ? (this.gridKeys.isStop({ column: 0, row: -1 }) ? '0' : '-1') : nothing}
521
751
  ></span>`;
522
752
  }
753
+ renderExpandHead() {
754
+ if (!this.nests) {
755
+ return nothing;
756
+ }
757
+ return html `<span
758
+ part="expand-head"
759
+ role="columnheader"
760
+ aria-label=${this.expandColumnLabel}
761
+ tabindex=${this.grid ? (this.gridKeys.isStop({ column: this.lead.indexOf('expand'), row: -1 }) ? '0' : '-1') : nothing}
762
+ ></span>`;
763
+ }
523
764
  renderSelectAll() {
524
765
  if (!this.selectable) {
525
766
  return nothing;
526
767
  }
768
+ if (this.single) {
769
+ return html `<span part="select-all" role="columnheader" aria-label=${this.selectColumnLabel}
770
+ tabindex=${this.grid ? (this.gridKeys.isStop({ column: this.lead.indexOf('select'), row: -1 }) ? '0' : '-1') : nothing}
771
+ ></span>`;
772
+ }
527
773
  const every = this.childrenKnown ? this.enabledKeys : [];
528
774
  const { checked, indeterminate } = everyState(this.chosen, every);
529
775
  const checkbox = this.tag('checkbox');
@@ -551,13 +797,13 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
551
797
  const end = grid ? html `<span part="grid-end" tabindex="-1"></span>` : nothing;
552
798
  return html `<slot name="heading"></slot>${start}<div
553
799
  part="table"
554
- role=${grid ? 'grid' : 'table'}
555
- aria-multiselectable=${grid && this.selectable ? 'true' : nothing}
800
+ role=${grid ? (this.nests ? 'treegrid' : 'grid') : 'table'}
801
+ aria-multiselectable=${grid && this.selectable ? (this.single ? 'false' : 'true') : nothing}
556
802
  aria-busy=${this.loading ? 'true' : nothing}
557
803
  aria-label=${this.label || this.fallbackLabel}
558
804
  style=${template ? `--table-template: ${template}` : ''}
559
805
  >
560
- <div part="head" role="row">${this.renderGripHead()}${this.renderSelectAll()}<slot name="columns"></slot></div>
806
+ <div part="head" role="row">${this.renderGripHead()}${this.renderSelectAll()}${this.renderExpandHead()}<slot name="columns"></slot></div>
561
807
  <slot
562
808
  @slotchange=${(event) => {
563
809
  this.countRows(event);
@@ -567,7 +813,7 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
567
813
  ${this.errorText === ''
568
814
  ? this.renderStatus(this.hasRows, grid ? 'gridcell' : 'cell')
569
815
  : html `<p part="error" role="row"><span role=${grid ? 'gridcell' : 'cell'}><slot name="error">${this.errorText}</slot></span></p>`}
570
- </div>${end}${this.errorText === ''
816
+ </div>${end}${this.loadMore ? html `<span data-end aria-hidden="true"></span>` : nothing}${this.errorText === ''
571
817
  ? this.renderLive(this.hasRows)
572
818
  : html `<span part="live" role="status">${this.errorText}</span>`}${this.reorder.drawDragPreview()}`;
573
819
  }
@@ -583,6 +829,9 @@ __decorate([
583
829
  __decorate([
584
830
  property()
585
831
  ], HelixHeadlessTable.prototype, "columns", void 0);
832
+ __decorate([
833
+ property({ attribute: 'default-expanded' })
834
+ ], HelixHeadlessTable.prototype, "defaultExpanded", void 0);
586
835
  __decorate([
587
836
  property({ attribute: 'default-selected' })
588
837
  ], HelixHeadlessTable.prototype, "defaultSelected", void 0);
@@ -600,6 +849,17 @@ __decorate([
600
849
  __decorate([
601
850
  property({ attribute: 'error-text' })
602
851
  ], HelixHeadlessTable.prototype, "errorText", void 0);
852
+ __decorate([
853
+ words,
854
+ property({ attribute: 'expand-column-label' })
855
+ ], HelixHeadlessTable.prototype, "expandColumnLabel", void 0);
856
+ __decorate([
857
+ property()
858
+ ], HelixHeadlessTable.prototype, "expanded", null);
859
+ __decorate([
860
+ words,
861
+ property({ attribute: 'expand-row-label' })
862
+ ], HelixHeadlessTable.prototype, "expandRowLabel", void 0);
603
863
  __decorate([
604
864
  words,
605
865
  property({ attribute: 'fallback-label' })
@@ -610,6 +870,9 @@ __decorate([
610
870
  __decorate([
611
871
  property()
612
872
  ], HelixHeadlessTable.prototype, "label", void 0);
873
+ __decorate([
874
+ property({ attribute: 'load-more', type: Boolean })
875
+ ], HelixHeadlessTable.prototype, "loadMore", void 0);
613
876
  __decorate([
614
877
  words,
615
878
  property({ attribute: 'moved-label' })
@@ -636,6 +899,10 @@ __decorate([
636
899
  words,
637
900
  property({ attribute: 'select-all-label' })
638
901
  ], HelixHeadlessTable.prototype, "selectAllLabel", void 0);
902
+ __decorate([
903
+ words,
904
+ property({ attribute: 'select-column-label' })
905
+ ], HelixHeadlessTable.prototype, "selectColumnLabel", void 0);
639
906
  __decorate([
640
907
  property()
641
908
  ], HelixHeadlessTable.prototype, "selected", null);
@@ -643,6 +910,12 @@ __decorate([
643
910
  words,
644
911
  property({ attribute: 'selected-label' })
645
912
  ], HelixHeadlessTable.prototype, "selectedLabel", void 0);
913
+ __decorate([
914
+ property({ attribute: 'selection-behavior' })
915
+ ], HelixHeadlessTable.prototype, "selectionBehavior", void 0);
916
+ __decorate([
917
+ property({ attribute: 'selection-mode' })
918
+ ], HelixHeadlessTable.prototype, "selectionMode", void 0);
646
919
  __decorate([
647
920
  words,
648
921
  property({ attribute: 'select-row-label' })
@@ -664,6 +937,9 @@ __decorate([
664
937
  __decorate([
665
938
  state()
666
939
  ], HelixHeadlessTable.prototype, "selectedState", void 0);
940
+ __decorate([
941
+ state()
942
+ ], HelixHeadlessTable.prototype, "expandedState", void 0);
667
943
  HelixHeadlessTable = __decorate([
668
944
  customElement('helix-headless-table')
669
945
  ], HelixHeadlessTable);
@@ -22,6 +22,12 @@ export type TableRowMove = 'dragging' | 'drop-after' | 'drop-before';
22
22
  * the drop indicator is, with the `dragging`, `drop-before`, and `drop-after`
23
23
  * states.
24
24
  *
25
+ * **`parent` nests the row under another**, by that row's key. The table
26
+ * then draws a column of disclosure buttons: a row with rows under it has one,
27
+ * named by the table's `expand-row-label` and saying whether it is open with
28
+ * `aria-expanded`, and its rows are drawn only while it is open. The cell is
29
+ * indented by the row's depth through `--table-row-level`.
30
+ *
25
31
  * **`href` makes the whole row a link**, as a row action does in React Aria's
26
32
  * table: a press anywhere on the row follows it, except on a link or a control
27
33
  * of the row's own, and Control, Command or Shift open it the way they open a
@@ -48,15 +54,21 @@ export type TableRowMove = 'dragging' | 'drop-after' | 'drop-before';
48
54
  * @csspart checkbox-mark-checked - Added to `checkbox-mark` while its box is checked.
49
55
  * @csspart checkbox-mark-dash - Added to `checkbox-mark` while it draws the dash, which it keeps until its box is checked.
50
56
  * @csspart checkbox-mark-indeterminate - Added to `checkbox-mark` while only some rows are selected.
57
+ * @csspart expand - The cell that holds the row's disclosure button, in a table whose rows nest. It carries `--table-row-level`, the row's depth from 1.
58
+ * @csspart expand-button - The button that shows and hides the rows under this one.
59
+ * @csspart expand-mark - The caret on the disclosure button, which points along the line, turned down by the styled tier while the row is open.
51
60
  * @csspart grip - The cell that holds the row's grip, in a `reorderable` table.
52
61
  * @csspart grip-button - The button that moves the row.
53
62
  * @csspart grip-mark - The icon on the grip.
54
63
  * @csspart select - The cell that holds the row's checkbox, in a `selectable` table.
55
64
  * @csspart select-selected - Added to `select` while the row is selected.
56
65
  *
66
+ * @cssproperty --table-row-level - Written by the row on its `expand` cell: how deep the row sits, from 1. Do not set it.
67
+ *
57
68
  * @cssstate dragging - The row is being moved, in a `reorderable` table.
58
69
  * @cssstate drop-after - The row being moved goes after this one when it is dropped.
59
70
  * @cssstate drop-before - The row being moved goes before this one when it is dropped.
71
+ * @cssstate expanded - While the rows under this one are shown.
60
72
  * @cssstate link - While the row has `href` and is not `disabled`, so a press on it follows the link.
61
73
  * @cssstate selected - While the row is selected, in a `selectable` table.
62
74
  */
@@ -80,6 +92,12 @@ export declare class HelixHeadlessTableRow extends HelixElement {
80
92
  * Empty, the default, leaves presses to the row's cells.
81
93
  */
82
94
  href: string;
95
+ /**
96
+ * The key of the row this one sits under, in a table whose rows nest: that
97
+ * row's `value`, or its place among the rows where it has none. Empty, the
98
+ * default, is a row at the top.
99
+ */
100
+ parent: string;
83
101
  /** The `rel` of the row's link. You set it yourself, as on an anchor. */
84
102
  rel: string;
85
103
  /** Where the row's link opens, such as `_blank`. */
@@ -133,6 +151,22 @@ export declare class HelixHeadlessTableRow extends HelixElement {
133
151
  * by the table that sets `gridCell`. Not a consumer's to set.
134
152
  */
135
153
  selectStop: boolean;
154
+ /**
155
+ * @internal How deep the row sits, from 1, in a table whose rows nest, and
156
+ * 0 in one whose rows do not, which draws no disclosure cell. Written by the
157
+ * table. Not a consumer's to set.
158
+ */
159
+ level: number;
160
+ /** @internal Whether rows sit under this one. Written by the table. */
161
+ branch: boolean;
162
+ /** @internal Whether the rows under this one are shown. Written by the table. */
163
+ open: boolean;
164
+ /** @internal The name of the disclosure button. Written by the table. */
165
+ expandLabel: string;
166
+ /** @internal Whether the disclosure cell holds the grid's one tab stop. Written by the table. */
167
+ expandStop: boolean;
168
+ /** @internal What a press on the disclosure button asks of the table. Written by the table. */
169
+ expandWith: (() => void) | undefined;
136
170
  /** The table's rules this row holds, so the next ones replace them rather than pile up. */
137
171
  private columnRules;
138
172
  /**
@@ -173,6 +207,13 @@ export declare class HelixHeadlessTableRow extends HelixElement {
173
207
  * leaves the tab order. `aria-disabled` while a sort decides the order.
174
208
  */
175
209
  protected renderGrip(): TemplateResult | typeof nothing;
210
+ /**
211
+ * The disclosure cell, in a table whose rows nest: a button where rows sit
212
+ * under this one, and an empty cell where none do, so the columns stay in
213
+ * line. In a grid the cell takes focus and Space or Enter on it presses the
214
+ * button, which leaves the tab order.
215
+ */
216
+ protected renderExpand(): TemplateResult | typeof nothing;
176
217
  protected render(): TemplateResult;
177
218
  }
178
219
  declare global {