@fusion.dev/helix-headless 0.165.0 → 0.167.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.
- package/custom-elements.json +3235 -534
- package/dist/callout/root/callout-root.d.ts +8 -2
- package/dist/callout/root/callout-root.js +14 -1
- package/dist/canvas/arrange.d.ts +32 -0
- package/dist/canvas/arrange.js +144 -0
- package/dist/canvas/paste.d.ts +25 -0
- package/dist/canvas/paste.js +58 -0
- package/dist/canvas/root/canvas-root.d.ts +60 -3
- package/dist/canvas/root/canvas-root.js +166 -2
- package/dist/chat/composer/chat-composer.js +5 -1
- package/dist/code-block/root/code-block-root.d.ts +63 -0
- package/dist/code-block/root/code-block-root.js +117 -6
- package/dist/combobox/root/combobox-root.d.ts +6 -1
- package/dist/combobox/root/combobox-root.js +2 -2
- package/dist/date-input/root/date-input-root.d.ts +1 -1
- package/dist/element-events.generated.js +17 -2
- package/dist/file-input/root/file-input-root.d.ts +1 -1
- package/dist/file-input/root/file-input-root.js +1 -1
- package/dist/filter-input/menu/filter-menu.d.ts +1 -0
- package/dist/filter-input/menu/filter-menu.js +1 -1
- package/dist/format-date/root/format-date-root.d.ts +10 -3
- package/dist/format-date/root/format-date-root.js +6 -1
- package/dist/listbox/root/listbox-root.d.ts +12 -1
- package/dist/listbox/root/listbox-root.js +15 -6
- package/dist/menu/item/menu-item.d.ts +20 -0
- package/dist/menu/item/menu-item.js +53 -20
- package/dist/menu/root/menu-root.d.ts +84 -1
- package/dist/menu/root/menu-root.js +187 -44
- package/dist/object-inspector/root/object-inspector-root.d.ts +5 -0
- package/dist/object-inspector/root/object-inspector-root.js +5 -0
- package/dist/select/root/select-root.js +1 -1
- package/dist/steps/root/steps-root.d.ts +7 -0
- package/dist/steps/root/steps-root.js +7 -0
- package/dist/table/grid-controller.d.ts +10 -0
- package/dist/table/grid-controller.js +7 -0
- package/dist/table/root/table-root.d.ts +71 -1
- package/dist/table/root/table-root.js +122 -7
- package/dist/table/row/table-row.d.ts +17 -0
- package/dist/table/row/table-row.js +23 -0
- package/dist/time-input/root/time-input-root.d.ts +2 -1
- package/dist/time-input/root/time-input-root.js +1 -1
- package/dist/time-picker/root/time-picker-root.d.ts +48 -1
- package/dist/time-picker/root/time-picker-root.js +79 -21
- package/dist/video/root/video-root.d.ts +2 -0
- package/dist/video/root/video-root.js +4 -2
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +1 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +1 -1
- package/dist/video-playlist/root/video-playlist-root.js +15 -1
- package/dist/virtual-list/root/virtual-list-root.d.ts +70 -2
- package/dist/virtual-list/root/virtual-list-root.js +216 -32
- package/dist/virtual-list/sections.d.ts +27 -0
- package/dist/virtual-list/sections.js +43 -0
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +716 -96
- package/types/vue.d.ts +148 -12
- package/types/words.d.ts +45 -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, HelixBeforeToggleEvent, HelixElement, HelixLoadMoreEvent, HelixSelectEvent, HelixSelectedEvent, HelixSortEvent, HelixSortedEvent, HelixToggleEvent, inRowOrder, MixinCollection, onAndroid, parse, ReorderController, rangeAdd, reflectElements, requestChange, scrollingAncestor, sort, splitValues, toggleEvery, treeArrow, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, anchorAt, assignSlots, auditName, 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, treeArrow, VirtualizeController, 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';
|
|
@@ -46,6 +46,7 @@ const lengthOf = (element) => textOf(element).length;
|
|
|
46
46
|
const numberOf = (value) => Number(value.replaceAll(',', ''));
|
|
47
47
|
const STATUS = new Set(['empty', 'error', 'loading']);
|
|
48
48
|
const NAMED = new Set([...STATUS, 'heading']);
|
|
49
|
+
const ESTIMATE = 40;
|
|
49
50
|
let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixElement) {
|
|
50
51
|
constructor() {
|
|
51
52
|
super(...arguments);
|
|
@@ -78,9 +79,11 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
78
79
|
this.selectRowLabel = 'Select {row}';
|
|
79
80
|
this.sortedLabel = '{direction, select, ascending {Sorted by {column}, ascending} descending {Sorted by {column}, descending} other {Sorted by {column}}}';
|
|
80
81
|
this.unsortedLabel = 'Not sorted';
|
|
82
|
+
this.windowed = false;
|
|
81
83
|
this.sortState = undefined;
|
|
82
84
|
this.selectedState = undefined;
|
|
83
85
|
this.expandedState = undefined;
|
|
86
|
+
this.memo = {};
|
|
84
87
|
this.headed = false;
|
|
85
88
|
this.aria = new AriaForwardController(this, {
|
|
86
89
|
label: true,
|
|
@@ -95,9 +98,11 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
95
98
|
},
|
|
96
99
|
columns: () => this.tableColumns.filter((column) => column.shown),
|
|
97
100
|
disclose: (at, forward) => this.disclose(at, forward),
|
|
101
|
+
drawn: () => (this.windowed ? new Set(this.drawnRows) : undefined),
|
|
98
102
|
lead: () => this.lead,
|
|
99
103
|
on: () => this.grid,
|
|
100
104
|
part: (name) => this.partNamed(name),
|
|
105
|
+
reveal: (row) => this.revealRow(row),
|
|
101
106
|
rows: () => (this.childrenKnown ? this.arrangedRows : []),
|
|
102
107
|
rowWords: () => this.arrangedRows.map((row) => textOf(row.children[0])),
|
|
103
108
|
selectAll: () => this.selectable &&
|
|
@@ -105,6 +110,15 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
105
110
|
this.requestSelected(new Set([...this.chosen, ...this.enabledKeys])),
|
|
106
111
|
selectRange: (to, from) => this.selectRange(to, from),
|
|
107
112
|
});
|
|
113
|
+
this.virtualize = new VirtualizeController(this, {
|
|
114
|
+
container: () => (this.windowed ? this.partNamed('table') : null),
|
|
115
|
+
count: () => this.arrangedCount,
|
|
116
|
+
estimate: () => ESTIMATE,
|
|
117
|
+
measured: () => this.windowed,
|
|
118
|
+
rows: () => (this.windowed ? this.measuredRows() : []),
|
|
119
|
+
});
|
|
120
|
+
this.arrangedCount = 0;
|
|
121
|
+
this.drawnRows = [];
|
|
108
122
|
this.reorder = new ReorderController(this, {
|
|
109
123
|
axis: 'block',
|
|
110
124
|
box: (key) => this.gripOf(this.rowFor(key))?.getBoundingClientRect(),
|
|
@@ -191,6 +205,11 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
191
205
|
this.watcher = undefined;
|
|
192
206
|
this.alignment = undefined;
|
|
193
207
|
this.aligned = '';
|
|
208
|
+
this.nearEnd = () => {
|
|
209
|
+
if (this.windowed && this.loadMore && this.virtualize.endInSight(this.arrangedCount)) {
|
|
210
|
+
this.askForMore();
|
|
211
|
+
}
|
|
212
|
+
};
|
|
194
213
|
this.endInView = false;
|
|
195
214
|
}
|
|
196
215
|
static { this.words = 'helix-table'; }
|
|
@@ -219,7 +238,23 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
219
238
|
this.requestUpdate('sort', previous);
|
|
220
239
|
}
|
|
221
240
|
get nests() {
|
|
222
|
-
return this.childrenKnown && this.keyed.some(({ row }) => (row.parent ?? '') !== '');
|
|
241
|
+
return this.remember('nests', () => this.childrenKnown && this.keyed.some(({ row }) => (row.parent ?? '') !== ''));
|
|
242
|
+
}
|
|
243
|
+
remember(key, compute) {
|
|
244
|
+
const memo = this.memo;
|
|
245
|
+
if (Object.keys(memo).length === 0) {
|
|
246
|
+
queueMicrotask(() => {
|
|
247
|
+
if (this.memo === memo) {
|
|
248
|
+
this.memo = {};
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
memo[key] ??= compute();
|
|
253
|
+
return memo[key];
|
|
254
|
+
}
|
|
255
|
+
requestUpdate(name, oldValue, options) {
|
|
256
|
+
this.memo = {};
|
|
257
|
+
super.requestUpdate(name, oldValue, options);
|
|
223
258
|
}
|
|
224
259
|
get open() {
|
|
225
260
|
return new Set(commaList(this.expanded));
|
|
@@ -251,6 +286,44 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
251
286
|
row.dispatchEvent(new HelixToggleEvent({ checked: next }));
|
|
252
287
|
}
|
|
253
288
|
}
|
|
289
|
+
get hasRows() {
|
|
290
|
+
return (this.windowed && this.arrangedCount > 0) || super.hasRows;
|
|
291
|
+
}
|
|
292
|
+
measuredRows() {
|
|
293
|
+
const rows = this.arrangedRows;
|
|
294
|
+
const at = new Map(rows.map((row, index) => [row, index]));
|
|
295
|
+
return this.drawnRows.flatMap((row) => {
|
|
296
|
+
const cell = [...row.children].find((child) => child.getAttribute('hidden') === null && child.getAttribute('slot') === null);
|
|
297
|
+
const index = at.get(row);
|
|
298
|
+
return cell instanceof HTMLElement && index !== undefined ? [{ element: cell, index }] : [];
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
keptRow(rows) {
|
|
302
|
+
const { first, last } = this.virtualize.window;
|
|
303
|
+
const active = this.getRootNode().activeElement;
|
|
304
|
+
const focused = this.grid
|
|
305
|
+
? this.gridKeys.current().row
|
|
306
|
+
: rows.findIndex((row) => active !== null && row.contains(active));
|
|
307
|
+
return focused >= 0 && (focused < first || focused >= last) ? focused : undefined;
|
|
308
|
+
}
|
|
309
|
+
windowOf(rows) {
|
|
310
|
+
const { first, last } = this.virtualize.window;
|
|
311
|
+
const drawn = rows.slice(first, last);
|
|
312
|
+
const kept = this.keptRow(rows);
|
|
313
|
+
const row = kept === undefined ? undefined : rows[kept];
|
|
314
|
+
if (kept !== undefined && row) {
|
|
315
|
+
return kept < first ? [row, ...drawn] : [...drawn, row];
|
|
316
|
+
}
|
|
317
|
+
return drawn;
|
|
318
|
+
}
|
|
319
|
+
revealRow(row) {
|
|
320
|
+
if (!this.windowed) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const heading = this.gridKeys.cellAt({ column: 0, row: -1 });
|
|
324
|
+
this.virtualize.reveal(row, heading?.getBoundingClientRect().height ?? 0);
|
|
325
|
+
this.virtualize.measure();
|
|
326
|
+
}
|
|
254
327
|
get lead() {
|
|
255
328
|
return [
|
|
256
329
|
...(this.reorderable ? ['grip'] : []),
|
|
@@ -282,6 +355,9 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
282
355
|
return this.requestSort(format(cycle(parse(this.sort), field)));
|
|
283
356
|
}
|
|
284
357
|
get arrangedRows() {
|
|
358
|
+
return this.remember('arranged', () => this.arrange());
|
|
359
|
+
}
|
|
360
|
+
arrange() {
|
|
285
361
|
const rows = this.slotted;
|
|
286
362
|
const spec = parse(this.sort);
|
|
287
363
|
const declared = this.tableColumns;
|
|
@@ -381,10 +457,10 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
381
457
|
});
|
|
382
458
|
}
|
|
383
459
|
get keyed() {
|
|
384
|
-
return this.slotted.map((row, index) => ({
|
|
460
|
+
return this.remember('keyed', () => this.slotted.map((row, index) => ({
|
|
385
461
|
key: row.value || String(index),
|
|
386
462
|
row: row,
|
|
387
|
-
}));
|
|
463
|
+
})));
|
|
388
464
|
}
|
|
389
465
|
get chosen() {
|
|
390
466
|
return new Set(commaList(this.selected));
|
|
@@ -538,10 +614,26 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
538
614
|
const tree = this.nests;
|
|
539
615
|
const shapes = tree ? this.shape() : undefined;
|
|
540
616
|
const open = this.open;
|
|
617
|
+
const drawn = this.windowed ? new Set(this.drawnRows) : undefined;
|
|
618
|
+
const places = drawn
|
|
619
|
+
? new Map(this.arrangedRows.map((row, index) => [row, index + 2]))
|
|
620
|
+
: undefined;
|
|
541
621
|
for (const { key, row } of this.keyed) {
|
|
542
622
|
if (!row.isTableRow) {
|
|
543
623
|
continue;
|
|
544
624
|
}
|
|
625
|
+
if (drawn && !drawn.has(row)) {
|
|
626
|
+
const shape = shapes?.get(key);
|
|
627
|
+
whenHydrated(row, () => {
|
|
628
|
+
row.selectable = this.selectable;
|
|
629
|
+
row.gripLabel = this.reorderable ? row.gripLabel || ' ' : '';
|
|
630
|
+
row.level = shape?.level ?? 0;
|
|
631
|
+
row.branch = shape?.branch ?? false;
|
|
632
|
+
row.open = open.has(key);
|
|
633
|
+
});
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
const place = places?.get(row) ?? 0;
|
|
545
637
|
const name = textOf(row.children[0]) || key;
|
|
546
638
|
const label = formatWords(this, 'selectRowLabel', { row: name });
|
|
547
639
|
const grip = this.reorderable ? formatWords(this, 'moveRowLabel', { row: name }) : '';
|
|
@@ -560,6 +652,7 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
560
652
|
row.open = open.has(key);
|
|
561
653
|
row.expandLabel = expand;
|
|
562
654
|
row.expandWith = tree ? () => this.toggleRow(key) : undefined;
|
|
655
|
+
row.rowIndex = place;
|
|
563
656
|
});
|
|
564
657
|
}
|
|
565
658
|
}
|
|
@@ -676,7 +769,11 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
676
769
|
const columns = new Set(this.tableColumns);
|
|
677
770
|
const headings = new Set(this.headings);
|
|
678
771
|
const status = [...(this.children ?? [])].filter((child) => STATUS.has(child.getAttribute('slot') ?? ''));
|
|
679
|
-
|
|
772
|
+
const arranged = this.arrangedRows;
|
|
773
|
+
this.arrangedCount = arranged.length;
|
|
774
|
+
const rows = this.windowed ? this.windowOf(arranged) : arranged;
|
|
775
|
+
this.drawnRows = this.windowed ? rows : [];
|
|
776
|
+
assignSlots(this.renderRoot, [...columns, ...headings, ...status, ...rows], (child) => columns.has(child)
|
|
680
777
|
? 'columns'
|
|
681
778
|
: headings.has(child)
|
|
682
779
|
? 'heading'
|
|
@@ -719,14 +816,22 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
719
816
|
this.markSorted();
|
|
720
817
|
this.markSelected();
|
|
721
818
|
this.nameByHeading();
|
|
819
|
+
auditName(this, () => null, 'Give it a `label`, or put a heading in its `heading` slot, that says what the rows are.', () => this.label !== '' || this.headings.length > 0);
|
|
722
820
|
if (this.childrenKnown) {
|
|
723
821
|
this.gridKeys.mark();
|
|
724
822
|
this.gridKeys.place();
|
|
725
823
|
}
|
|
824
|
+
if (this.windowed) {
|
|
825
|
+
this.virtualize.measure();
|
|
826
|
+
this.virtualize.settle();
|
|
827
|
+
}
|
|
726
828
|
this.watchEnd();
|
|
727
829
|
}
|
|
728
830
|
watchEnd() {
|
|
729
|
-
|
|
831
|
+
if (this.windowed) {
|
|
832
|
+
this.nearEnd();
|
|
833
|
+
}
|
|
834
|
+
const end = this.loadMore && !this.windowed ? this.renderRoot.querySelector('[data-end]') : null;
|
|
730
835
|
if (end === this.watchedEnd) {
|
|
731
836
|
if (this.endInView) {
|
|
732
837
|
this.askForMore();
|
|
@@ -810,6 +915,9 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
810
915
|
render() {
|
|
811
916
|
const template = this.template;
|
|
812
917
|
const grid = this.grid;
|
|
918
|
+
const windowed = this.windowed && this.childrenKnown;
|
|
919
|
+
const count = windowed ? this.arrangedRows.length : 0;
|
|
920
|
+
const room = windowed ? this.virtualize.spacers(this.keptRow(this.arrangedRows)) : undefined;
|
|
813
921
|
const start = grid ? html `<span part="grid-start" tabindex="-1"></span>` : nothing;
|
|
814
922
|
const end = grid ? html `<span part="grid-end" tabindex="-1"></span>` : nothing;
|
|
815
923
|
return html `<slot name="heading"></slot>${start}<div
|
|
@@ -818,15 +926,19 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
818
926
|
aria-multiselectable=${grid && this.selectable ? (this.single ? 'false' : 'true') : nothing}
|
|
819
927
|
aria-busy=${this.loading ? 'true' : nothing}
|
|
820
928
|
aria-label=${this.label || this.fallbackLabel}
|
|
929
|
+
aria-rowcount=${windowed ? count + 1 : nothing}
|
|
821
930
|
style=${template ? `--table-template: ${template}` : ''}
|
|
931
|
+
@scroll=${this.nearEnd}
|
|
822
932
|
>
|
|
823
|
-
<div part="head" role="row">${this.renderGripHead()}${this.renderSelectAll()}${this.renderExpandHead()}<slot name="columns"></slot></div>
|
|
933
|
+
<div part="head" role="row" aria-rowindex=${windowed ? 1 : nothing}>${this.renderGripHead()}${this.renderSelectAll()}${this.renderExpandHead()}<slot name="columns"></slot></div>
|
|
934
|
+
${room ? html `<div part="before" role="presentation" style="block-size: ${room.before}px"></div>` : nothing}
|
|
824
935
|
<slot
|
|
825
936
|
@slotchange=${(event) => {
|
|
826
937
|
this.countRows(event);
|
|
827
938
|
this.handleSlotChange();
|
|
828
939
|
}}
|
|
829
940
|
></slot>
|
|
941
|
+
${room ? html `<div part="after" role="presentation" style="block-size: ${room.after}px"></div>` : nothing}
|
|
830
942
|
${this.errorText === ''
|
|
831
943
|
? this.renderStatus(this.hasRows, grid ? 'gridcell' : 'cell')
|
|
832
944
|
: html `<p part="error" role="row"><span role=${grid ? 'gridcell' : 'cell'}><slot name="error">${this.errorText}</slot></span></p>`}
|
|
@@ -948,6 +1060,9 @@ __decorate([
|
|
|
948
1060
|
words,
|
|
949
1061
|
property({ attribute: 'unsorted-label' })
|
|
950
1062
|
], HelixHeadlessTable.prototype, "unsortedLabel", void 0);
|
|
1063
|
+
__decorate([
|
|
1064
|
+
property({ reflect: true, type: Boolean })
|
|
1065
|
+
], HelixHeadlessTable.prototype, "windowed", void 0);
|
|
951
1066
|
__decorate([
|
|
952
1067
|
state()
|
|
953
1068
|
], HelixHeadlessTable.prototype, "sortState", void 0);
|
|
@@ -157,6 +157,12 @@ export declare class HelixHeadlessTableRow extends HelixElement {
|
|
|
157
157
|
* table. Not a consumer's to set.
|
|
158
158
|
*/
|
|
159
159
|
level: number;
|
|
160
|
+
/**
|
|
161
|
+
* @internal The row's place among the table's rows, the headings being 1,
|
|
162
|
+
* in a `windowed` table, which draws only some of them, and 0 otherwise.
|
|
163
|
+
* Written by the table. Not a consumer's to set.
|
|
164
|
+
*/
|
|
165
|
+
rowIndex: number;
|
|
160
166
|
/** @internal Whether rows sit under this one. Written by the table. */
|
|
161
167
|
branch: boolean;
|
|
162
168
|
/** @internal Whether the rows under this one are shown. Written by the table. */
|
|
@@ -177,6 +183,17 @@ export declare class HelixHeadlessTableRow extends HelixElement {
|
|
|
177
183
|
*/
|
|
178
184
|
alignCells(rules: CSSStyleSheet): void;
|
|
179
185
|
protected willUpdate(changed: PropertyValues): void;
|
|
186
|
+
/**
|
|
187
|
+
* Names the row by its header cell, the cell with `header` among cells of
|
|
188
|
+
* values, or by its first cell where it has none, as React Aria's rows are
|
|
189
|
+
* named.
|
|
190
|
+
*
|
|
191
|
+
* **Named from its contents, a row read out its checkbox's name too**, "Select
|
|
192
|
+
* 412 412 main Passed" in a `selectable` table, since the checkbox is a cell of
|
|
193
|
+
* the row's own. Not on a server, which has no cells to point at, and not
|
|
194
|
+
* where a page named the row itself.
|
|
195
|
+
*/
|
|
196
|
+
private nameByHeader;
|
|
180
197
|
connectedCallback(): void;
|
|
181
198
|
disconnectedCallback(): void;
|
|
182
199
|
/**
|
|
@@ -31,6 +31,7 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
|
|
|
31
31
|
this.gridCell = false;
|
|
32
32
|
this.selectStop = false;
|
|
33
33
|
this.level = 0;
|
|
34
|
+
this.rowIndex = 0;
|
|
34
35
|
this.branch = false;
|
|
35
36
|
this.open = false;
|
|
36
37
|
this.expandLabel = '';
|
|
@@ -53,6 +54,7 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
|
|
|
53
54
|
followLink(this.ownerDocument, { href: this.href, rel: this.rel, target: this.target }, event);
|
|
54
55
|
};
|
|
55
56
|
this.handleSlotChange = () => {
|
|
57
|
+
this.requestUpdate();
|
|
56
58
|
for (const cell of this.children) {
|
|
57
59
|
if ('isTableCell' in cell) {
|
|
58
60
|
;
|
|
@@ -75,8 +77,10 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
|
|
|
75
77
|
willUpdate(changed) {
|
|
76
78
|
super.willUpdate(changed);
|
|
77
79
|
hostAria(this.internals, { role: 'row' });
|
|
80
|
+
this.nameByHeader();
|
|
78
81
|
this.internals.ariaSelected = this.gridCell && this.selectable ? String(this.selected) : null;
|
|
79
82
|
this.internals.ariaDisabled = this.disabled ? 'true' : null;
|
|
83
|
+
this.internals.ariaRowIndex = this.rowIndex > 0 ? String(this.rowIndex) : null;
|
|
80
84
|
const tree = this.gridCell && this.level > 0;
|
|
81
85
|
this.internals.ariaLevel = tree ? String(this.level) : null;
|
|
82
86
|
this.internals.ariaExpanded = tree && this.branch ? String(this.open) : null;
|
|
@@ -87,6 +91,22 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
|
|
|
87
91
|
this.toggleState('drop-after', this.move === 'drop-after');
|
|
88
92
|
this.toggleState('drop-before', this.move === 'drop-before');
|
|
89
93
|
}
|
|
94
|
+
nameByHeader() {
|
|
95
|
+
const cells = this.childrenKnown
|
|
96
|
+
? this.knownChildren.filter((child) => 'isTableCell' in child && !child.hasAttribute('hidden'))
|
|
97
|
+
: [];
|
|
98
|
+
const values = cells.some((cell) => !cell.hasAttribute('header'));
|
|
99
|
+
const header = (values ? cells.find((cell) => cell.hasAttribute('header')) : undefined) ?? cells[0];
|
|
100
|
+
const own = this.hasAttribute('aria-label') || this.hasAttribute('aria-labelledby');
|
|
101
|
+
if ('ariaLabelledByElements' in this.internals) {
|
|
102
|
+
hostAria(this.internals, { labelledBy: header && !own ? [header] : [] });
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
hostAria(this.internals, {
|
|
106
|
+
label: header && !own ? (header.textContent ?? '').replace(/\s+/g, ' ').trim() : '',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
90
110
|
connectedCallback() {
|
|
91
111
|
super.connectedCallback();
|
|
92
112
|
this.addEventListener('click', this.handleClick);
|
|
@@ -210,6 +230,9 @@ __decorate([
|
|
|
210
230
|
__decorate([
|
|
211
231
|
state()
|
|
212
232
|
], HelixHeadlessTableRow.prototype, "level", void 0);
|
|
233
|
+
__decorate([
|
|
234
|
+
state()
|
|
235
|
+
], HelixHeadlessTableRow.prototype, "rowIndex", void 0);
|
|
213
236
|
__decorate([
|
|
214
237
|
state()
|
|
215
238
|
], HelixHeadlessTableRow.prototype, "branch", void 0);
|
|
@@ -56,7 +56,7 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
|
|
|
56
56
|
* | Home, End on a segment | Goes to the segment's first or last value. |
|
|
57
57
|
* | Digits on a segment | Fill it, and move to the next once it can take no more. |
|
|
58
58
|
* | A letter on the half of the day | Picks the half whose name starts with it, such as A for AM. |
|
|
59
|
-
* | Backspace, Delete on a segment |
|
|
59
|
+
* | Backspace, Delete on a segment | Take off its last digit, or move to the segment before where it is empty. |
|
|
60
60
|
* | Left, Right on a segment | Move to the previous or the next segment, along the line of text. |
|
|
61
61
|
*
|
|
62
62
|
* In the list, the keys are `TimePicker`'s.
|
|
@@ -123,6 +123,7 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
|
|
|
123
123
|
* @csspart picker-list-loading - The text shown while the options load.
|
|
124
124
|
* @csspart picker-list-search - The region that holds the filter field.
|
|
125
125
|
* @csspart picker-list-search-field - The filter field.
|
|
126
|
+
* @csspart picker-now - The picker's button that chooses the current time, which Time Input does not draw.
|
|
126
127
|
* @csspart picker-option - One time in the picker. Carries `data-between` inside a range.
|
|
127
128
|
* @csspart picker-option-between - A time inside a chosen range, between its two ends. Set together with `picker-option`.
|
|
128
129
|
* @csspart picker-option-description - The caption under the label, when there is one.
|
|
@@ -233,7 +233,7 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
233
233
|
list-filter-icon: picker-list-filter-icon, list-filter-mark: picker-list-filter-mark,
|
|
234
234
|
list-hint: picker-list-hint, list-items: picker-list-items, list-label: picker-list-label,
|
|
235
235
|
list-live: picker-list-live, list-loading: picker-list-loading, list-search: picker-list-search,
|
|
236
|
-
list-search-field: picker-list-search-field, option: picker-option,
|
|
236
|
+
list-search-field: picker-list-search-field, now: picker-now, option: picker-option,
|
|
237
237
|
option-between: picker-option-between, option-item-active: picker-option-item-active,
|
|
238
238
|
option-item-selected: picker-option-item-selected,
|
|
239
239
|
option-description: picker-option-description, option-item: picker-option-item,
|
|
@@ -14,7 +14,9 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
|
|
|
14
14
|
* between the wheels, and Tab does too.
|
|
15
15
|
*
|
|
16
16
|
* `hour-cycle` writes every time on a twelve or a twenty-four hour clock
|
|
17
|
-
* whatever the locale, and `seconds` holds times to the second.
|
|
17
|
+
* whatever the locale, and `seconds` holds times to the second. `show-now`
|
|
18
|
+
* draws a button that chooses the current time, on the step and inside the
|
|
19
|
+
* bounds.
|
|
18
20
|
*
|
|
19
21
|
* The step keeps the list usable. At the default of 15 minutes a day is 96
|
|
20
22
|
* rows, which people can scroll or type into. At 1 minute it is 1,440. Use the
|
|
@@ -103,6 +105,7 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
|
|
|
103
105
|
* @csspart list-loading - The text shown while the options load.
|
|
104
106
|
* @csspart list-search - The region that holds the filter field.
|
|
105
107
|
* @csspart list-search-field - The filter field.
|
|
108
|
+
* @csspart now - The button that chooses the current time, drawn with `show-now`.
|
|
106
109
|
* @csspart option - One time in the list. It carries `data-between` inside a range, and inside the run to the time under the pointer while a range waits for its second end.
|
|
107
110
|
* @csspart option-between - A time inside a chosen range, excluding its ends, beside `option`.
|
|
108
111
|
* @csspart option-description - The caption under the time's label, when there is one.
|
|
@@ -242,6 +245,11 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
|
|
|
242
245
|
* whenever a mode is set.
|
|
243
246
|
*/
|
|
244
247
|
mode?: 'multiple' | 'range';
|
|
248
|
+
/**
|
|
249
|
+
* The words on the button that chooses the current time, drawn with
|
|
250
|
+
* `show-now`.
|
|
251
|
+
*/
|
|
252
|
+
nowLabel: string;
|
|
245
253
|
/**
|
|
246
254
|
* The validation message for a time after `max`. `{max}` is replaced with
|
|
247
255
|
* `max`, written in `locale`.
|
|
@@ -322,6 +330,35 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
|
|
|
322
330
|
* sequence.
|
|
323
331
|
*/
|
|
324
332
|
private readonly handleChange;
|
|
333
|
+
/**
|
|
334
|
+
* Requests the times a pick announced, kept to what the mode allows, and
|
|
335
|
+
* returns whether the value changed.
|
|
336
|
+
*/
|
|
337
|
+
private choose;
|
|
338
|
+
/**
|
|
339
|
+
* Chooses the current time, as a press on the `show-now` button does, and
|
|
340
|
+
* returns whether the value changed.
|
|
341
|
+
*
|
|
342
|
+
* The time is read off the reader's clock and moved to the nearest time on
|
|
343
|
+
* offer that is not barred, so it falls on the `step` and inside `min` and
|
|
344
|
+
* `max`. It is requested as a pick is, so `helix-before-change` can cancel
|
|
345
|
+
* it. With a `mode` the time joins the times held, and a range that holds
|
|
346
|
+
* two starts over from it, as a third pick does.
|
|
347
|
+
*/
|
|
348
|
+
selectNow(): boolean;
|
|
349
|
+
/**
|
|
350
|
+
* Puts the list's highlight on the offered time nearest one held between two
|
|
351
|
+
* steps, so the arrows move from there.
|
|
352
|
+
*
|
|
353
|
+
* No row holds `14:07` at a step of 15, so the list had nothing to move
|
|
354
|
+
* from: Down went to the top of the list, and in a list that chooses as it
|
|
355
|
+
* moves, the value became 00:00. Down now goes to 14:15.
|
|
356
|
+
*/
|
|
357
|
+
private highlightNearest;
|
|
358
|
+
private readonly handleListFocus;
|
|
359
|
+
private readonly handleNow;
|
|
360
|
+
/** The button that chooses the current time, drawn with `show-now`. */
|
|
361
|
+
private renderNow;
|
|
325
362
|
/**
|
|
326
363
|
* The time under the pointer while a range holds one end, which the list
|
|
327
364
|
* bands the range to before it is chosen, as Date Picker does.
|
|
@@ -409,6 +446,16 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
|
|
|
409
446
|
* the wheels.
|
|
410
447
|
*/
|
|
411
448
|
seconds: boolean;
|
|
449
|
+
/**
|
|
450
|
+
* Draws a button under the times that chooses the current time, as
|
|
451
|
+
* `selectNow()` does.
|
|
452
|
+
*
|
|
453
|
+
* For a form about something happening now, such as a shift starting or a
|
|
454
|
+
* dose taken, where the reader would otherwise scroll to the time on the
|
|
455
|
+
* clock. The button is a tab stop after the times, and its words are
|
|
456
|
+
* `now-label`.
|
|
457
|
+
*/
|
|
458
|
+
showNow: boolean;
|
|
412
459
|
/**
|
|
413
460
|
* How far apart the offered times are, in minutes, or in seconds with
|
|
414
461
|
* `seconds`.
|
|
@@ -23,6 +23,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
23
23
|
this.manual = false;
|
|
24
24
|
this.max = '23:59';
|
|
25
25
|
this.min = '00:00';
|
|
26
|
+
this.nowLabel = 'Now';
|
|
26
27
|
this.rangeOverflowMessage = 'Choose a time at or before {max}.';
|
|
27
28
|
this.rangeUnderflowMessage = 'Choose a time at or after {min}.';
|
|
28
29
|
this.handleListInput = (event) => {
|
|
@@ -33,25 +34,13 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
33
34
|
if (this.isDisabled) {
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
const next = announced
|
|
45
|
-
.split(',')
|
|
46
|
-
.map((one) => one.trim())
|
|
47
|
-
.filter(Boolean);
|
|
48
|
-
if (next.length <= 2) {
|
|
49
|
-
this.requestValue(next.sort().join(','));
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const before = new Set(this.held);
|
|
53
|
-
const added = next.find((one) => !before.has(one));
|
|
54
|
-
this.requestValue(added ?? '');
|
|
37
|
+
this.choose(event.detail.value);
|
|
38
|
+
};
|
|
39
|
+
this.handleListFocus = () => {
|
|
40
|
+
this.highlightNearest();
|
|
41
|
+
};
|
|
42
|
+
this.handleNow = () => {
|
|
43
|
+
this.selectNow();
|
|
55
44
|
};
|
|
56
45
|
this.previewed = '';
|
|
57
46
|
this.handleListHover = (event) => {
|
|
@@ -93,6 +82,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
93
82
|
this.readOnly = false;
|
|
94
83
|
this.required = false;
|
|
95
84
|
this.seconds = false;
|
|
85
|
+
this.showNow = false;
|
|
96
86
|
this.step = 15;
|
|
97
87
|
this.stepMismatchMessage = 'Choose one of the times on offer.';
|
|
98
88
|
this.unavailableMessage = 'Choose a time that is available.';
|
|
@@ -155,6 +145,65 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
155
145
|
get held() {
|
|
156
146
|
return commaList(this.value);
|
|
157
147
|
}
|
|
148
|
+
choose(announced) {
|
|
149
|
+
if (announced.split(',').some((one) => this.barred(one.trim()))) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
if (this.mode !== 'range') {
|
|
153
|
+
return this.requestValue(announced);
|
|
154
|
+
}
|
|
155
|
+
const next = announced
|
|
156
|
+
.split(',')
|
|
157
|
+
.map((one) => one.trim())
|
|
158
|
+
.filter(Boolean);
|
|
159
|
+
if (next.length <= 2) {
|
|
160
|
+
return this.requestValue(next.sort().join(','));
|
|
161
|
+
}
|
|
162
|
+
const before = new Set(this.held);
|
|
163
|
+
const added = next.find((one) => !before.has(one));
|
|
164
|
+
return this.requestValue(added ?? '');
|
|
165
|
+
}
|
|
166
|
+
selectNow() {
|
|
167
|
+
if (this.isDisabled) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
const clock = new Date();
|
|
171
|
+
const at = clock.getHours() * 3600 + clock.getMinutes() * 60 + clock.getSeconds();
|
|
172
|
+
const open = this.times.filter((time) => !this.barredAt(time));
|
|
173
|
+
if (open.length === 0) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const now = this.valueFor(nearestTime(open, at));
|
|
177
|
+
if (this.mode === undefined) {
|
|
178
|
+
return this.requestValue(now);
|
|
179
|
+
}
|
|
180
|
+
if (this.held.includes(now)) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
return this.choose([...this.held, now].join(','));
|
|
184
|
+
}
|
|
185
|
+
highlightNearest() {
|
|
186
|
+
const at = this.mode === undefined ? valueAsSeconds(this.value) : null;
|
|
187
|
+
if (at === null || this.times.includes(at)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const open = this.times.filter((time) => !this.barredAt(time));
|
|
191
|
+
if (open.length === 0) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
this.partNamed('list')?.highlightValue?.(this.valueFor(nearestTime(open, at)));
|
|
195
|
+
}
|
|
196
|
+
renderNow() {
|
|
197
|
+
if (!this.showNow) {
|
|
198
|
+
return nothing;
|
|
199
|
+
}
|
|
200
|
+
return html `<button
|
|
201
|
+
part="now"
|
|
202
|
+
type="button"
|
|
203
|
+
?disabled=${this.isDisabled || this.readOnly}
|
|
204
|
+
@click=${this.handleNow}
|
|
205
|
+
>${this.nowLabel}</button>`;
|
|
206
|
+
}
|
|
158
207
|
between(at) {
|
|
159
208
|
if (this.mode !== 'range') {
|
|
160
209
|
return false;
|
|
@@ -191,6 +240,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
191
240
|
return;
|
|
192
241
|
}
|
|
193
242
|
if (this.variant !== 'wheels') {
|
|
243
|
+
this.highlightNearest();
|
|
194
244
|
this.partNamed('list')?.reveal?.();
|
|
195
245
|
return;
|
|
196
246
|
}
|
|
@@ -421,6 +471,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
421
471
|
?manual=${this.manual}
|
|
422
472
|
?multiple=${this.mode !== undefined}
|
|
423
473
|
.value=${this.value}
|
|
474
|
+
@focusin=${this.handleListFocus}
|
|
424
475
|
@helix-before-change=${this.handleChange}
|
|
425
476
|
@input=${this.handleListInput}
|
|
426
477
|
@pointerleave=${this.handleListLeave}
|
|
@@ -446,9 +497,9 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
446
497
|
}
|
|
447
498
|
render() {
|
|
448
499
|
if (this.variant === 'wheels' && this.mode === undefined) {
|
|
449
|
-
return html `${this.renderSlottedLabel()}${this.renderSpinner()}${this.renderHint()}${this.renderPickerFeedback()}`;
|
|
500
|
+
return html `${this.renderSlottedLabel()}${this.renderSpinner()}${this.renderNow()}${this.renderHint()}${this.renderPickerFeedback()}`;
|
|
450
501
|
}
|
|
451
|
-
return html `${this.renderSlottedLabel()}${this.renderList()}${this.renderHint()}${this.renderPickerFeedback()}`;
|
|
502
|
+
return html `${this.renderSlottedLabel()}${this.renderList()}${this.renderNow()}${this.renderHint()}${this.renderPickerFeedback()}`;
|
|
452
503
|
}
|
|
453
504
|
get valueAsDate() {
|
|
454
505
|
const at = valueAsSeconds(this.value);
|
|
@@ -512,6 +563,10 @@ __decorate([
|
|
|
512
563
|
__decorate([
|
|
513
564
|
property({ reflect: true })
|
|
514
565
|
], HelixHeadlessTimePicker.prototype, "mode", void 0);
|
|
566
|
+
__decorate([
|
|
567
|
+
words,
|
|
568
|
+
property({ attribute: 'now-label' })
|
|
569
|
+
], HelixHeadlessTimePicker.prototype, "nowLabel", void 0);
|
|
515
570
|
__decorate([
|
|
516
571
|
words,
|
|
517
572
|
property({ attribute: 'range-overflow-message' })
|
|
@@ -535,6 +590,9 @@ __decorate([
|
|
|
535
590
|
__decorate([
|
|
536
591
|
property({ type: Boolean })
|
|
537
592
|
], HelixHeadlessTimePicker.prototype, "seconds", void 0);
|
|
593
|
+
__decorate([
|
|
594
|
+
property({ attribute: 'show-now', type: Boolean })
|
|
595
|
+
], HelixHeadlessTimePicker.prototype, "showNow", void 0);
|
|
538
596
|
__decorate([
|
|
539
597
|
property({ type: Number })
|
|
540
598
|
], HelixHeadlessTimePicker.prototype, "step", void 0);
|
|
@@ -115,6 +115,7 @@ import '../../menu/index.js';
|
|
|
115
115
|
* @csspart captions-menu-header - The bar above a submenu, holding the way back and its name.
|
|
116
116
|
* @csspart captions-menu-live - The inner menu's `live` part, a live region that repeats the empty and loading text for screen readers.
|
|
117
117
|
* @csspart captions-menu-loading - The text shown while the items are loading.
|
|
118
|
+
* @csspart captions-menu-search-field - The inner menu's filter field, drawn with `filterable`.
|
|
118
119
|
* @csspart captions-menu-surface - The floating panel the menu is drawn on.
|
|
119
120
|
* @csspart captions-menu-title - The name of the submenu that is showing.
|
|
120
121
|
* @csspart controls - The control bar along the bottom.
|
|
@@ -148,6 +149,7 @@ import '../../menu/index.js';
|
|
|
148
149
|
* @csspart overflow-header - The bar above a submenu, holding the way back and its name.
|
|
149
150
|
* @csspart overflow-live - The inner menu's `live` part, a live region that repeats the empty and loading text for screen readers.
|
|
150
151
|
* @csspart overflow-loading - The text shown while the items are loading.
|
|
152
|
+
* @csspart overflow-search-field - The inner menu's filter field, drawn with `filterable`.
|
|
151
153
|
* @csspart overflow-surface - The floating panel the menu is drawn on.
|
|
152
154
|
* @csspart overflow-title - The name of the submenu that is showing.
|
|
153
155
|
* @csspart pip - The picture-in-picture button, when `extra-controls` includes it and the browser supports it.
|