@fusion.dev/helix-headless 0.23.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 (102) hide show
  1. package/custom-elements.json +48376 -26638
  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 +31 -6
  54. package/dist/menu/item/menu-item.js +18 -5
  55. package/dist/menu/root/menu-root.js +7 -2
  56. package/dist/notifications/group/notification-group.d.ts +129 -0
  57. package/dist/notifications/group/notification-group.js +106 -0
  58. package/dist/notifications/index.d.ts +1 -0
  59. package/dist/notifications/index.js +1 -0
  60. package/dist/notifications/notification/notification.d.ts +39 -13
  61. package/dist/notifications/notification/notification.js +11 -8
  62. package/dist/notifications/root/notifications-root.d.ts +57 -1
  63. package/dist/notifications/root/notifications-root.js +35 -4
  64. package/dist/number-input/root/number-input-root.d.ts +21 -1
  65. package/dist/number-input/root/number-input-root.js +18 -5
  66. package/dist/object-inspector/index.d.ts +1 -0
  67. package/dist/object-inspector/index.js +1 -0
  68. package/dist/object-inspector/root/object-inspector-root.d.ts +183 -0
  69. package/dist/object-inspector/root/object-inspector-root.js +348 -0
  70. package/dist/outline/root/outline-root.d.ts +16 -1
  71. package/dist/outline/root/outline-root.js +27 -9
  72. package/dist/radio/group/radio-group.d.ts +1 -0
  73. package/dist/scatter-chart/index.d.ts +1 -0
  74. package/dist/scatter-chart/index.js +1 -0
  75. package/dist/scatter-chart/root/scatter-chart-root.d.ts +144 -0
  76. package/dist/scatter-chart/root/scatter-chart-root.js +205 -0
  77. package/dist/skip-link/index.d.ts +1 -0
  78. package/dist/skip-link/index.js +1 -0
  79. package/dist/skip-link/root/skip-link-root.d.ts +81 -0
  80. package/dist/skip-link/root/skip-link-root.js +58 -0
  81. package/dist/speed-dial/root/speed-dial-root.d.ts +1 -0
  82. package/dist/speed-dial/root/speed-dial-root.js +6 -2
  83. package/dist/steps/root/steps-root.d.ts +1 -0
  84. package/dist/table/column/table-column.d.ts +79 -0
  85. package/dist/table/column/table-column.js +125 -5
  86. package/dist/table/root/table-root.d.ts +2 -0
  87. package/dist/table/root/table-root.js +12 -3
  88. package/dist/time-picker/root/time-picker-root.d.ts +9 -0
  89. package/dist/time-picker/root/time-picker-root.js +5 -0
  90. package/dist/toast/root/toast-root.d.ts +2 -0
  91. package/dist/tooltip/root/tooltip-root.js +1 -1
  92. package/dist/tour/root/tour-root.d.ts +1 -0
  93. package/dist/tree/item/tree-item.d.ts +7 -4
  94. package/dist/tree/item/tree-item.js +4 -1
  95. package/dist/video/root/video-root.d.ts +23 -1
  96. package/dist/video/root/video-root.js +42 -22
  97. package/dist/virtual-list/root/virtual-list-root.d.ts +10 -6
  98. package/dist/virtual-list/root/virtual-list-root.js +10 -4
  99. package/package.json +5 -5
  100. package/types/jsx.d.ts +4535 -346
  101. package/types/svelte.d.ts +11 -0
  102. package/types/vue.d.ts +737 -39
@@ -5,25 +5,31 @@ 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
  var HelixHeadlessDataTable_1;
8
- import { customElement, HelixElement, HelixFilterEvent, HelixNavigateEvent, HelixSortEvent, hostAria, MixinCollection, requestChange, slotFilled, VirtualizeController, } from '@fusion.dev/helix-core';
8
+ import { customElement, HelixElement, HelixFilterEvent, HelixNavigateEvent, HelixReorderEvent, HelixSelectEvent, HelixSortEvent, hostAria, MixinCollection, requestChange, slotFilled, VirtualizeController, } from '@fusion.dev/helix-core';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { repeat } from 'lit/directives/repeat.js';
12
12
  import { html } from 'lit/static-html.js';
13
+ import { at } from '../../table/column/table-column.js';
14
+ import '../../checkbox/index.js';
13
15
  import '../../pagination/index.js';
14
16
  import { arranged, cycle, format, ordered, parse, window_, } from './order.js';
15
17
  let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollection(HelixElement) {
16
18
  constructor() {
17
19
  super(...arguments);
18
20
  this.defaultFilter = '';
21
+ this.defaultOrder = '';
19
22
  this.defaultPage = 1;
20
23
  this.defaultPageSize = 0;
24
+ this.defaultSelected = '';
21
25
  this.defaultSort = '';
22
26
  this.estimate = 32;
23
- this.key = '';
24
27
  this.label = '';
25
28
  this.mode = 'client';
29
+ this.reorderable = false;
30
+ this.rowKey = '';
26
31
  this.rows = [];
32
+ this.selectable = false;
27
33
  this.total = 0;
28
34
  this.windowed = false;
29
35
  this.sortState = undefined;
@@ -31,6 +37,10 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
31
37
  this.pageState = undefined;
32
38
  this.pageSizeState = undefined;
33
39
  this.paginatedState = undefined;
40
+ this.orderState = undefined;
41
+ this.selectedState = undefined;
42
+ this.dragOrder = undefined;
43
+ this.warnedAboutKey = false;
34
44
  this.virtualize = new VirtualizeController(this, {
35
45
  container: () => this.windowed ? this.renderRoot.querySelector('[part="table"]') : null,
36
46
  count: () => this.available,
@@ -39,6 +49,33 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
39
49
  rows: () => (this.windowed ? this.drawn() : []),
40
50
  });
41
51
  this.warnedAboutSize = false;
52
+ this.toggleRow = (key, event) => {
53
+ event.preventDefault();
54
+ const keys = this.chosen;
55
+ if (event.detail.checked) {
56
+ keys.add(key);
57
+ }
58
+ else {
59
+ keys.delete(key);
60
+ }
61
+ this.requestSelected(keys);
62
+ };
63
+ this.toggleAll = (event) => {
64
+ event.preventDefault();
65
+ const every = this.everyKey;
66
+ const keys = this.chosen;
67
+ if (every.length > 0 && every.every((key) => keys.has(key))) {
68
+ for (const key of every) {
69
+ keys.delete(key);
70
+ }
71
+ }
72
+ else {
73
+ for (const key of every) {
74
+ keys.add(key);
75
+ }
76
+ }
77
+ this.requestSelected(keys);
78
+ };
42
79
  this.handleSlotChange = () => {
43
80
  this.requestUpdate();
44
81
  };
@@ -49,21 +86,74 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
49
86
  }
50
87
  };
51
88
  this.handleHeadClick = (event) => {
89
+ if (this.partIn(event, 'grip') || this.partIn(event, 'resizer')) {
90
+ return;
91
+ }
52
92
  const column = this.columnFrom(event);
53
93
  if (column?.canSort) {
54
94
  this.toggleSort(column.field);
55
95
  }
56
96
  };
57
- this.handleHeadKey = (event) => {
58
- if (event.key !== 'Enter' && event.key !== ' ') {
97
+ this.handleGripKey = (event) => {
98
+ if (!this.partIn(event, 'grip')) {
59
99
  return;
60
100
  }
61
101
  const column = this.columnFrom(event);
62
- if (column?.canSort) {
102
+ const forward = getComputedStyle(this).direction === 'rtl' ? -1 : 1;
103
+ const by = event.key === 'ArrowRight' ? forward : event.key === 'ArrowLeft' ? -forward : 0;
104
+ if (column && by !== 0) {
63
105
  event.preventDefault();
64
- this.toggleSort(column.field);
106
+ this.moveColumn(column.field, by);
107
+ }
108
+ };
109
+ this.handleGripDown = (event) => {
110
+ if (event.button !== 0 || !this.partIn(event, 'grip')) {
111
+ return;
112
+ }
113
+ const column = this.columnFrom(event);
114
+ if (!column?.field) {
115
+ return;
116
+ }
117
+ this.dragging = { field: column.field, pointer: event.pointerId };
118
+ this.dragOrder = this.fields;
119
+ window.addEventListener('pointermove', this.handleGripMove);
120
+ window.addEventListener('pointerup', this.handleGripUp);
121
+ window.addEventListener('pointercancel', this.handleGripCancel);
122
+ };
123
+ this.handleGripMove = (event) => {
124
+ const dragging = this.dragging;
125
+ if (!dragging || event.pointerId !== dragging.pointer || !this.dragOrder) {
126
+ return;
127
+ }
128
+ const rtl = getComputedStyle(this).direction === 'rtl';
129
+ const others = this.orderedColumns.filter((column) => column.field !== '' && column.field !== dragging.field);
130
+ const place = others.filter((column) => {
131
+ const box = column.shadowRoot?.querySelector('[part="column"]')?.getBoundingClientRect();
132
+ if (!box) {
133
+ return false;
134
+ }
135
+ const middle = box.left + box.width / 2;
136
+ return rtl ? event.clientX < middle : event.clientX > middle;
137
+ }).length;
138
+ const next = others.map((column) => column.field);
139
+ next.splice(place, 0, dragging.field);
140
+ if (next.join(' ') !== this.dragOrder.join(' ')) {
141
+ this.dragOrder = next;
142
+ }
143
+ };
144
+ this.handleGripUp = () => {
145
+ const wanted = this.dragOrder;
146
+ this.stopDrag();
147
+ if (wanted && wanted.join(' ') !== this.fields.join(' ')) {
148
+ this.requestOrder(wanted);
65
149
  }
66
150
  };
151
+ this.handleGripCancel = () => {
152
+ this.stopDrag();
153
+ };
154
+ this.handleResize = () => {
155
+ queueMicrotask(() => this.requestUpdate());
156
+ };
67
157
  this.handleNavigate = (event) => {
68
158
  event.stopPropagation();
69
159
  const wanted = Number(event.detail.value);
@@ -102,6 +192,22 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
102
192
  this.pageState = next;
103
193
  this.requestUpdate('page', previous);
104
194
  }
195
+ get order() {
196
+ return this.orderState ?? this.defaultOrder;
197
+ }
198
+ set order(next) {
199
+ const previous = this.order;
200
+ this.orderState = next;
201
+ this.requestUpdate('order', previous);
202
+ }
203
+ get selected() {
204
+ return this.selectedState ?? this.defaultSelected;
205
+ }
206
+ set selected(next) {
207
+ const previous = this.selected;
208
+ this.selectedState = next;
209
+ this.requestUpdate('selected', previous);
210
+ }
105
211
  get pageSize() {
106
212
  return Math.max(0, Math.floor(this.pageSizeState ?? this.defaultPageSize) || 0);
107
213
  }
@@ -150,7 +256,24 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
150
256
  return rows.slice(first, last);
151
257
  }
152
258
  get tableColumns() {
153
- return [...(this.children ?? [])].filter((child) => 'isTableColumn' in child && child.isTableColumn === true);
259
+ return this.knownChildren.filter((child) => 'isTableColumn' in child && child.isTableColumn === true);
260
+ }
261
+ get orderedColumns() {
262
+ const columns = this.tableColumns;
263
+ const wanted = this.dragOrder ?? this.order.split(/[\s,]+/).filter(Boolean);
264
+ if (wanted.length === 0) {
265
+ return columns;
266
+ }
267
+ const rank = new Map(wanted.map((field, place) => [field, place]));
268
+ const moving = columns
269
+ .filter((column) => column.field !== '')
270
+ .sort((a, b) => (rank.get(a.field) ?? Number.POSITIVE_INFINITY) -
271
+ (rank.get(b.field) ?? Number.POSITIVE_INFINITY));
272
+ let next = 0;
273
+ return columns.map((column) => (column.field === '' ? column : (moving[next++] ?? column)));
274
+ }
275
+ get fields() {
276
+ return this.orderedColumns.filter((column) => column.field !== '').map((column) => column.field);
154
277
  }
155
278
  get axes() {
156
279
  return this.tableColumns
@@ -158,8 +281,12 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
158
281
  .map((column) => ({ field: column.field, order: (row) => column.order(row) }));
159
282
  }
160
283
  get template() {
161
- const declared = this.tableColumns;
162
- return declared.length > 0 ? declared.map((column) => column.track).join(' ') : '';
284
+ const declared = this.orderedColumns;
285
+ if (declared.length === 0) {
286
+ return '';
287
+ }
288
+ const tracks = declared.map((column) => column.track).join(' ');
289
+ return this.selectable ? `max-content ${tracks}` : tracks;
163
290
  }
164
291
  static { this.COMFORTABLE = 2_000; }
165
292
  warnIfCrowded(drawn) {
@@ -235,6 +362,78 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
235
362
  next,
236
363
  });
237
364
  }
365
+ requestOrder(fields) {
366
+ return requestChange({
367
+ apply: (value) => {
368
+ const previous = this.order;
369
+ this.orderState = value;
370
+ this.requestUpdate('order', previous);
371
+ },
372
+ event: (value) => new HelixReorderEvent({ value }),
373
+ host: this,
374
+ next: fields.join(' '),
375
+ });
376
+ }
377
+ requestSelected(keys) {
378
+ return requestChange({
379
+ apply: (value) => {
380
+ const previous = this.selected;
381
+ this.selectedState = value;
382
+ this.requestUpdate('selected', previous);
383
+ },
384
+ event: (value) => new HelixSelectEvent({ value }),
385
+ host: this,
386
+ next: [...keys].join(','),
387
+ });
388
+ }
389
+ get chosen() {
390
+ return new Set(this.selected
391
+ .split(',')
392
+ .map((key) => key.trim())
393
+ .filter(Boolean));
394
+ }
395
+ keyOf(row, place) {
396
+ if (this.rowKey) {
397
+ const value = at(row, this.rowKey);
398
+ return value === undefined || value === null ? String(place) : String(value);
399
+ }
400
+ if (this.selectable && !this.warnedAboutKey && typeof window !== 'undefined') {
401
+ this.warnedAboutKey = true;
402
+ console.warn(`<${this.localName} selectable> has no \`row-key\`, so it knows each row by its place in the result, and a sort, a filter or new data selects a different row than the one that was chosen. Set \`row-key\` to the field that identifies a row.`);
403
+ }
404
+ return String(place);
405
+ }
406
+ get everyKey() {
407
+ const rows = this.mode === 'server' ? this.rows : this.arrange().rows;
408
+ return rows.map((row, place) => this.keyOf(row, place));
409
+ }
410
+ moveColumn(field, by) {
411
+ const fields = this.fields;
412
+ const from = fields.indexOf(field);
413
+ const to = from + by;
414
+ if (from < 0 || to < 0 || to >= fields.length) {
415
+ return false;
416
+ }
417
+ fields.splice(to, 0, ...fields.splice(from, 1));
418
+ return this.requestOrder(fields);
419
+ }
420
+ csv() {
421
+ const columns = this.orderedColumns.filter((column) => column.field !== '');
422
+ const rows = this.mode === 'server' ? this.rows : this.arrange().rows;
423
+ const quote = (value) => {
424
+ if (value === undefined || value === null) {
425
+ return '';
426
+ }
427
+ let text = String(value);
428
+ if (typeof value === 'string' && /^[=+\-@\t\r]/.test(text)) {
429
+ text = `'${text}`;
430
+ }
431
+ return /[",\r\n]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text;
432
+ };
433
+ const head = columns.map((column) => quote(column.textContent?.trim() || column.field));
434
+ const body = rows.map((row) => columns.map((column) => quote(at(row, column.field))));
435
+ return [head, ...body].map((line) => line.join(',')).join('\r\n');
436
+ }
238
437
  requestFilter(next) {
239
438
  return requestChange({
240
439
  apply: (value) => {
@@ -273,11 +472,18 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
273
472
  if (!root?.querySelector) {
274
473
  return;
275
474
  }
475
+ if (this.assignsByName) {
476
+ const single = root.querySelector('slot[name="columns"]') !== null;
477
+ this.orderedColumns.forEach((column, index) => {
478
+ this.route(column, single ? 'columns' : `column-${index + 1}`);
479
+ });
480
+ return;
481
+ }
276
482
  const head = root.querySelector('slot[name="columns"]');
277
483
  if (!head?.assign) {
278
484
  return;
279
485
  }
280
- const columns = this.tableColumns;
486
+ const columns = this.orderedColumns;
281
487
  head.assign(...columns);
282
488
  const rest = [...this.children].filter((child) => !columns.includes(child));
283
489
  for (const [name, wanted] of [
@@ -293,6 +499,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
293
499
  willUpdate(changed) {
294
500
  super.willUpdate(changed);
295
501
  hostAria(this.internals, { label: this.label || 'Table', role: 'grid' });
502
+ this.internals.ariaMultiSelectable = this.selectable ? 'true' : null;
296
503
  const placement = this.placement;
297
504
  if (changed.has('rows') || placement !== this.measuredAgainst) {
298
505
  this.measuredAgainst = placement;
@@ -321,6 +528,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
321
528
  announceSort() {
322
529
  const current = parse(this.sort);
323
530
  for (const column of this.tableColumns) {
531
+ column.movable = this.reorderable && column.field !== '';
324
532
  column.sorted =
325
533
  current && column.canSort && column.field === current.field
326
534
  ? current.direction === 'asc'
@@ -337,18 +545,39 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
337
545
  >${column.read(row)}</span
338
546
  >`;
339
547
  }
548
+ renderCheckbox(name, checked, indeterminate, toggle) {
549
+ const checkbox = this.tag('checkbox');
550
+ return html `<${checkbox}
551
+ part="checkbox"
552
+ exportparts="box: checkbox-box, control: checkbox-control, feedback: checkbox-feedback,
553
+ field: checkbox-field, hint: checkbox-hint, label: checkbox-label, mark: checkbox-mark"
554
+ aria-label=${name}
555
+ .checked=${checked}
556
+ .indeterminate=${indeterminate}
557
+ @helix-before-toggle=${toggle}
558
+ ></${checkbox}>`;
559
+ }
340
560
  renderRows(rows) {
341
- const columns = this.tableColumns;
342
- const field = this.key;
561
+ const columns = this.orderedColumns;
562
+ const field = this.rowKey;
343
563
  const start = this.windowStart;
344
- return html `${repeat(rows, (row, index) => (field ? String(row[field] ?? start + index) : start + index), (row, index) => html `<div
345
- part="row"
346
- role="row"
347
- aria-rowindex=${this.rowIndex(index)}
348
- data-index=${this.windowed ? start + index : nothing}
349
- >
350
- ${columns.map((column) => this.renderCell(column, row))}
351
- </div>`)}`;
564
+ const chosen = this.selectable ? this.chosen : undefined;
565
+ const named = columns.find((column) => column.field !== '');
566
+ return html `${repeat(rows, (row, index) => (field ? String(row[field] ?? start + index) : start + index), (row, index) => {
567
+ const key = chosen ? this.keyOf(row, this.rowIndex(index) - 2) : '';
568
+ const picked = chosen?.has(key) ?? false;
569
+ return html `<div
570
+ part="row"
571
+ role="row"
572
+ aria-rowindex=${this.rowIndex(index)}
573
+ aria-selected=${chosen ? String(picked) : nothing}
574
+ data-index=${this.windowed ? start + index : nothing}
575
+ >
576
+ ${chosen
577
+ ? html `<span part="select" role="gridcell">${this.renderCheckbox(`Select ${named ? String(named.order(row) ?? key) : key}`, picked, false, (event) => this.toggleRow(key, event))}</span>`
578
+ : nothing}${columns.map((column) => this.renderCell(column, row))}
579
+ </div>`;
580
+ })}`;
352
581
  }
353
582
  rowIndex(within) {
354
583
  const size = this.pageSize;
@@ -370,6 +599,12 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
370
599
  ? html `<div part="before" role="presentation" style="block-size: ${before}px"></div>`
371
600
  : html `<div part="after" role="presentation" style="block-size: ${after}px"></div>`;
372
601
  }
602
+ renderSelectAll() {
603
+ const every = this.everyKey;
604
+ const keys = this.chosen;
605
+ const count = every.filter((key) => keys.has(key)).length;
606
+ return html `<span part="select-all" role="columnheader">${this.renderCheckbox('Select every row', every.length > 0 && count === every.length, count > 0 && count < every.length, this.toggleAll)}</span>`;
607
+ }
373
608
  render() {
374
609
  const { available, matched, rows } = this.view;
375
610
  const template = this.template;
@@ -381,12 +616,14 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
381
616
  role="rowgroup"
382
617
  aria-busy=${this.loading ? 'true' : nothing}
383
618
  aria-rowcount=${this.mode === 'server' ? Math.max(this.total, available) + 1 : matched + 1}
384
- aria-colcount=${columns.length || nothing}
619
+ aria-colcount=${columns.length ? columns.length + (this.selectable ? 1 : 0) : nothing}
385
620
  style=${template ? `--data-table-template: ${template}` : nothing}
386
621
  @scroll=${this.handleScroll}
387
622
  >
388
623
  <div part="head" role="row">
389
- <slot name="columns" @slotchange=${this.handleSlotChange}></slot>
624
+ ${this.selectable ? this.renderSelectAll() : nothing}${this.assignsByName && !this.hydrating
625
+ ? this.orderedColumns.map((_, index) => html `<slot name=${`column-${index + 1}`} @slotchange=${this.handleSlotChange}></slot>`)
626
+ : html `<slot name="columns" @slotchange=${this.handleSlotChange}></slot>`}
390
627
  </div>
391
628
  ${this.renderSpacer('before')}${this.renderRows(rows)}${this.renderSpacer('after')}
392
629
  ${this.renderStatus(available > 0)}
@@ -407,12 +644,29 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
407
644
  connectedCallback() {
408
645
  super.connectedCallback();
409
646
  this.addEventListener('click', this.handleHeadClick);
410
- this.addEventListener('keydown', this.handleHeadKey);
647
+ this.addEventListener('keydown', this.handleGripKey);
648
+ this.addEventListener('pointerdown', this.handleGripDown);
649
+ this.addEventListener('helix-before-resize', this.handleResize);
411
650
  }
412
651
  disconnectedCallback() {
413
652
  super.disconnectedCallback();
414
653
  this.removeEventListener('click', this.handleHeadClick);
415
- this.removeEventListener('keydown', this.handleHeadKey);
654
+ this.removeEventListener('keydown', this.handleGripKey);
655
+ this.removeEventListener('pointerdown', this.handleGripDown);
656
+ this.removeEventListener('helix-before-resize', this.handleResize);
657
+ this.stopDrag();
658
+ }
659
+ partIn(event, name) {
660
+ return event
661
+ .composedPath()
662
+ .some((node) => node instanceof Element && node.getAttribute('part') === name);
663
+ }
664
+ stopDrag() {
665
+ this.dragging = undefined;
666
+ this.dragOrder = undefined;
667
+ window.removeEventListener('pointermove', this.handleGripMove);
668
+ window.removeEventListener('pointerup', this.handleGripUp);
669
+ window.removeEventListener('pointercancel', this.handleGripCancel);
416
670
  }
417
671
  columnFrom(event) {
418
672
  const columns = this.tableColumns;
@@ -427,12 +681,18 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
427
681
  __decorate([
428
682
  property({ attribute: 'default-filter' })
429
683
  ], HelixHeadlessDataTable.prototype, "defaultFilter", void 0);
684
+ __decorate([
685
+ property({ attribute: 'default-order' })
686
+ ], HelixHeadlessDataTable.prototype, "defaultOrder", void 0);
430
687
  __decorate([
431
688
  property({ attribute: 'default-page', type: Number })
432
689
  ], HelixHeadlessDataTable.prototype, "defaultPage", void 0);
433
690
  __decorate([
434
691
  property({ attribute: 'default-page-size', type: Number })
435
692
  ], HelixHeadlessDataTable.prototype, "defaultPageSize", void 0);
693
+ __decorate([
694
+ property({ attribute: 'default-selected' })
695
+ ], HelixHeadlessDataTable.prototype, "defaultSelected", void 0);
436
696
  __decorate([
437
697
  property({ attribute: 'default-sort' })
438
698
  ], HelixHeadlessDataTable.prototype, "defaultSort", void 0);
@@ -444,22 +704,34 @@ __decorate([
444
704
  ], HelixHeadlessDataTable.prototype, "sort", null);
445
705
  __decorate([
446
706
  property()
447
- ], HelixHeadlessDataTable.prototype, "key", void 0);
707
+ ], HelixHeadlessDataTable.prototype, "label", void 0);
448
708
  __decorate([
449
709
  property()
450
710
  ], HelixHeadlessDataTable.prototype, "filter", null);
451
711
  __decorate([
452
712
  property()
453
- ], HelixHeadlessDataTable.prototype, "label", void 0);
713
+ ], HelixHeadlessDataTable.prototype, "mode", void 0);
454
714
  __decorate([
455
715
  property({ type: Number })
456
716
  ], HelixHeadlessDataTable.prototype, "page", null);
717
+ __decorate([
718
+ property({ type: Boolean })
719
+ ], HelixHeadlessDataTable.prototype, "reorderable", void 0);
457
720
  __decorate([
458
721
  property()
459
- ], HelixHeadlessDataTable.prototype, "mode", void 0);
722
+ ], HelixHeadlessDataTable.prototype, "order", null);
723
+ __decorate([
724
+ property({ attribute: 'row-key' })
725
+ ], HelixHeadlessDataTable.prototype, "rowKey", void 0);
460
726
  __decorate([
461
727
  property({ attribute: false })
462
728
  ], HelixHeadlessDataTable.prototype, "rows", void 0);
729
+ __decorate([
730
+ property({ type: Boolean })
731
+ ], HelixHeadlessDataTable.prototype, "selectable", void 0);
732
+ __decorate([
733
+ property()
734
+ ], HelixHeadlessDataTable.prototype, "selected", null);
463
735
  __decorate([
464
736
  property({ attribute: 'page-size', type: Number })
465
737
  ], HelixHeadlessDataTable.prototype, "pageSize", null);
@@ -487,6 +759,15 @@ __decorate([
487
759
  __decorate([
488
760
  state()
489
761
  ], HelixHeadlessDataTable.prototype, "paginatedState", void 0);
762
+ __decorate([
763
+ state()
764
+ ], HelixHeadlessDataTable.prototype, "orderState", void 0);
765
+ __decorate([
766
+ state()
767
+ ], HelixHeadlessDataTable.prototype, "selectedState", void 0);
768
+ __decorate([
769
+ state()
770
+ ], HelixHeadlessDataTable.prototype, "dragOrder", void 0);
490
771
  HelixHeadlessDataTable = HelixHeadlessDataTable_1 = __decorate([
491
772
  customElement('helix-headless-data-table')
492
773
  ], HelixHeadlessDataTable);
@@ -0,0 +1,2 @@
1
+ export * from './item/description-list-item.js';
2
+ export * from './root/description-list-root.js';
@@ -0,0 +1,2 @@
1
+ export * from './item/description-list-item.js';
2
+ export * from './root/description-list-root.js';
@@ -0,0 +1,39 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { type TemplateResult } from 'lit';
3
+ /**
4
+ * @summary One label and its value.
5
+ *
6
+ * @description
7
+ * An element rather than a pair of slots on the list, because a pair is the
8
+ * unit a reader hears: the label is the `<dt>` and the value its `<dd>`, and the
9
+ * two stay together however the list lays them out.
10
+ *
11
+ * The value is the default slot, since it is what varies and is often more
12
+ * than text: a tag, an avatar, a link. The label is text almost every time, so
13
+ * it is also a property.
14
+ *
15
+ * @since 0.1.0
16
+ * @status experimental
17
+ * @documentation https://helix.fusion.dev/components/description-list
18
+ *
19
+ * @slot - The value.
20
+ * @slot label - The label, when it is more than text. Falls back to `label`.
21
+ *
22
+ * @csspart label - The `<dt>`, holding the label.
23
+ * @csspart value - The `<dd>`, holding the value.
24
+ */
25
+ export declare class HelixHeadlessDescriptionListItem extends HelixElement {
26
+ /**
27
+ * What the value is, for the common case of plain text.
28
+ *
29
+ * The fallback of the `label` slot, so slotted content replaces it rather than
30
+ * appearing beside it.
31
+ */
32
+ label: string;
33
+ protected render(): TemplateResult;
34
+ }
35
+ declare global {
36
+ interface HTMLElementTagNameMap {
37
+ 'helix-headless-description-list-item': HelixHeadlessDescriptionListItem;
38
+ }
39
+ }
@@ -0,0 +1,26 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { customElement, HelixElement } from '@fusion.dev/helix-core';
8
+ import { html } from 'lit';
9
+ import { property } from 'lit/decorators.js';
10
+ let HelixHeadlessDescriptionListItem = class HelixHeadlessDescriptionListItem extends HelixElement {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.label = '';
14
+ }
15
+ render() {
16
+ return html `<dt part="label"><slot name="label">${this.label}</slot></dt>
17
+ <dd part="value"><slot></slot></dd>`;
18
+ }
19
+ };
20
+ __decorate([
21
+ property()
22
+ ], HelixHeadlessDescriptionListItem.prototype, "label", void 0);
23
+ HelixHeadlessDescriptionListItem = __decorate([
24
+ customElement('helix-headless-description-list-item')
25
+ ], HelixHeadlessDescriptionListItem);
26
+ export { HelixHeadlessDescriptionListItem };
@@ -0,0 +1,56 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { type TemplateResult } from 'lit';
3
+ /**
4
+ * @summary Labels and their values, read as pairs: the details panel every
5
+ * product has.
6
+ *
7
+ * @description
8
+ * "Owner: Ada, Region: us-east, Created: 3 days ago" is the commonest panel in
9
+ * any console, and built from layout primitives it is a run of adjacent text:
10
+ * nothing tells a screen reader that a label and a value belong together, or
11
+ * how many pairs there are. This renders the element the platform has for it,
12
+ * a `<dl>`, with each item's label a `<dt>` and its value a `<dd>`.
13
+ *
14
+ * **The items render their own term and description**, and the list takes them
15
+ * through its slot. The accessibility tree is built from the flattened tree, so
16
+ * the pairs reach it inside the list rather than beside it. Measured in
17
+ * Chromium's tree rather than assumed, because a list whose semantics stopped
18
+ * at a shadow boundary would look identical on screen: one description list
19
+ * holding term and definition pairs, node for node what a plain `<dl>` with the
20
+ * same content produces, with a slotted label and with an item drawn as
21
+ * `display: contents` alike.
22
+ *
23
+ * @since 0.1.0
24
+ * @status experimental
25
+ * @category data
26
+ * @aliases Data list, Key-value list, Details, Properties
27
+ * @documentation https://helix.fusion.dev/components/description-list
28
+ *
29
+ * @slot - The items.
30
+ *
31
+ * @csspart list - The `<dl>` holding the items.
32
+ */
33
+ export declare class HelixHeadlessDescriptionList extends HelixElement {
34
+ /**
35
+ * Whether each label sits over its value or beside it.
36
+ *
37
+ * Absent is a column, the label over the value, which fits any width and is
38
+ * how a narrow panel reads. `row` puts every label in one column and every
39
+ * value in the next, which is how a wide panel is scanned: down the labels to
40
+ * the one wanted, then across.
41
+ *
42
+ * On the list rather than on each item, because beside only reads when the
43
+ * labels line up, and lining up is a fact about all of them.
44
+ *
45
+ * **Optional rather than defaulted to `column`**, for the reason `Stat` gives:
46
+ * a reflecting default writes an attribute onto markup the consumer rendered,
47
+ * and React reports that as a hydration mismatch.
48
+ */
49
+ direction?: 'row';
50
+ protected render(): TemplateResult;
51
+ }
52
+ declare global {
53
+ interface HTMLElementTagNameMap {
54
+ 'helix-headless-description-list': HelixHeadlessDescriptionList;
55
+ }
56
+ }
@@ -0,0 +1,21 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { customElement, HelixElement } from '@fusion.dev/helix-core';
8
+ import { html } from 'lit';
9
+ import { property } from 'lit/decorators.js';
10
+ let HelixHeadlessDescriptionList = class HelixHeadlessDescriptionList extends HelixElement {
11
+ render() {
12
+ return html `<dl part="list"><slot></slot></dl>`;
13
+ }
14
+ };
15
+ __decorate([
16
+ property({ reflect: true })
17
+ ], HelixHeadlessDescriptionList.prototype, "direction", void 0);
18
+ HelixHeadlessDescriptionList = __decorate([
19
+ customElement('helix-headless-description-list')
20
+ ], HelixHeadlessDescriptionList);
21
+ export { HelixHeadlessDescriptionList };