@fusion.dev/helix-headless 0.182.2 → 0.184.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 +625 -90
- package/dist/bar-chart/root/bar-chart-root.d.ts +1 -0
- package/dist/bar-chart/root/bar-chart-root.js +3 -2
- package/dist/code-block/root/code-block-root.d.ts +42 -1
- package/dist/code-block/root/code-block-root.js +25 -4
- package/dist/heatmap/root/heatmap-root.d.ts +11 -1
- package/dist/heatmap/root/heatmap-root.js +17 -9
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/menu/filter/index.d.ts +1 -0
- package/dist/menu/filter/index.js +4 -0
- package/dist/menu/filter/menu-filter.d.ts +114 -0
- package/dist/menu/filter/menu-filter.js +172 -0
- package/dist/menu/root/menu-root.d.ts +28 -29
- package/dist/menu/root/menu-root.js +32 -107
- package/dist/time-input/root/time-input-root.d.ts +11 -0
- package/dist/time-input/root/time-input-root.js +11 -0
- package/dist/virtual-list/reorder/virtual-list-reorder.js +5 -1
- package/dist/virtual-list/root/virtual-list-root.d.ts +95 -17
- package/dist/virtual-list/root/virtual-list-root.js +123 -28
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +232 -30
- package/types/vue.d.ts +38 -7
- package/types/words.d.ts +7 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { AriaForwardController, anchorElement, customElement, DrilldownController, deepActiveElement, deepContains,
|
|
2
|
+
import { AriaForwardController, anchorElement, auditEntry, customElement, DrilldownController, deepActiveElement, deepContains, focusTarget, HelixElement, inlineStep, KeyboardNavigationController, linkDescription, MIRROR_RULE, MixinCollection, MixinControllerOpen, OpenPhaseController, onUnlessFalse, PopoverController, scrollItemIntoView, sideOf, slottedWords, tellDisclosure, typeaheadBy, unlinkDescription, words, } from '@fusion.dev/helix-core';
|
|
3
3
|
import { caretLeft } from '@fusion.dev/helix-icons';
|
|
4
4
|
import { html, nothing } from 'lit';
|
|
5
5
|
import { property } from 'lit/decorators.js';
|
|
@@ -98,18 +98,13 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
98
98
|
surface: () => this.partNamed('surface'),
|
|
99
99
|
});
|
|
100
100
|
this.settledOnce = false;
|
|
101
|
-
this.filterPage = null;
|
|
102
|
-
this.handleFilterInput = (event) => {
|
|
103
|
-
event.stopPropagation();
|
|
104
|
-
this.filterBy(event.target.value);
|
|
105
|
-
};
|
|
106
101
|
this.followRemoved = (records) => {
|
|
107
102
|
if (!this.open) {
|
|
108
103
|
return;
|
|
109
104
|
}
|
|
110
105
|
const active = deepActiveElement();
|
|
111
106
|
const lost = active === null || active === document.body || !active.isConnected;
|
|
112
|
-
const record = records.find((one) => [...one.removedNodes].some((node) => isItem(node) && (this.
|
|
107
|
+
const record = records.find((one) => [...one.removedNodes].some((node) => isItem(node) && (this.filters ? node === this.filters.highlighted : lost)));
|
|
113
108
|
if (!record) {
|
|
114
109
|
return;
|
|
115
110
|
}
|
|
@@ -125,7 +120,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
125
120
|
const next = after(record.nextSibling, 'nextSibling') ??
|
|
126
121
|
after(record.previousSibling, 'previousSibling') ??
|
|
127
122
|
items.at(-1);
|
|
128
|
-
if (this.
|
|
123
|
+
if (this.filters || next) {
|
|
129
124
|
this.reach(next);
|
|
130
125
|
return;
|
|
131
126
|
}
|
|
@@ -188,23 +183,13 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
188
183
|
const items = this.items;
|
|
189
184
|
const at = items.findIndex((item) => item.active);
|
|
190
185
|
const along = inlineStep(this, event.key);
|
|
191
|
-
|
|
192
|
-
|
|
186
|
+
if (this.filters?.keydown(event, items, at)) {
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
193
190
|
if (event.key === 'Escape') {
|
|
194
191
|
event.stopPropagation();
|
|
195
|
-
|
|
196
|
-
this.leave();
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
this.filterBy('');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
else if (typing && step !== 0) {
|
|
203
|
-
const count = items.length;
|
|
204
|
-
this.reach(items[at < 0 ? (step > 0 ? 0 : count - 1) : (at + step + count) % count]);
|
|
205
|
-
}
|
|
206
|
-
else if (typing && event.key === 'Enter') {
|
|
207
|
-
items[at]?.renderRoot.querySelector('[part="item"]')?.click();
|
|
192
|
+
this.leave();
|
|
208
193
|
}
|
|
209
194
|
else if (along === 1 && items[at]?.hasSubmenu) {
|
|
210
195
|
this.enter(items[at]);
|
|
@@ -244,14 +229,6 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
244
229
|
this.longPress.down(event);
|
|
245
230
|
}
|
|
246
231
|
};
|
|
247
|
-
this.handleMouseDown = (event) => {
|
|
248
|
-
if (this.filterable &&
|
|
249
|
-
this.open &&
|
|
250
|
-
event.composedPath()[0] !== this.field &&
|
|
251
|
-
!this.fromTrigger(event)) {
|
|
252
|
-
event.preventDefault();
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
232
|
this.handlePointerMove = (event) => {
|
|
256
233
|
this.longPress.move(event);
|
|
257
234
|
};
|
|
@@ -406,48 +383,27 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
406
383
|
return rows.filter((row) => !row.disabled && this.kept(row));
|
|
407
384
|
}
|
|
408
385
|
get menuBox() {
|
|
409
|
-
return this.
|
|
386
|
+
return this.filters
|
|
410
387
|
? (this.renderRoot?.querySelector('#items') ?? null)
|
|
411
388
|
: this.partNamed('surface');
|
|
412
389
|
}
|
|
413
|
-
get
|
|
414
|
-
return this.
|
|
415
|
-
}
|
|
416
|
-
get filtering() {
|
|
417
|
-
return this.filterable && this.filter.trim() !== '' && this.filter !== this.refused;
|
|
390
|
+
get filters() {
|
|
391
|
+
return this.filterable ? this.filterer : undefined;
|
|
418
392
|
}
|
|
419
393
|
kept(row) {
|
|
420
|
-
return
|
|
421
|
-
this.holderOf(row) !== this.filterPage ||
|
|
422
|
-
filterKeeps(slottedWords(row, ['']), this.filter.trim(), this.filterMatch));
|
|
394
|
+
return this.filters?.kept(row) ?? true;
|
|
423
395
|
}
|
|
424
396
|
requestFilter(value) {
|
|
425
|
-
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
this.filterPage = this.flies ? this.keyPage : this.drilldown.page;
|
|
429
|
-
this.refused = this.dispatchEvent(new HelixFilterEvent({ value })) ? undefined : value;
|
|
430
|
-
this.filter = value;
|
|
431
|
-
}
|
|
432
|
-
filterBy(value) {
|
|
433
|
-
this.requestFilter(value);
|
|
434
|
-
void this.updateComplete.then(() => this.reach(this.items[0]));
|
|
397
|
+
this.filterer?.request(value);
|
|
435
398
|
}
|
|
436
399
|
reach(row) {
|
|
437
|
-
|
|
400
|
+
const filters = this.filters;
|
|
401
|
+
if (!filters) {
|
|
438
402
|
row?.focus();
|
|
439
403
|
return;
|
|
440
404
|
}
|
|
441
|
-
|
|
442
|
-
this.highlighted.highlighted = false;
|
|
443
|
-
}
|
|
444
|
-
this.highlighted = row;
|
|
445
|
-
const field = this.field;
|
|
446
|
-
if (field && 'ariaActiveDescendantElement' in field) {
|
|
447
|
-
field.ariaActiveDescendantElement = row ?? null;
|
|
448
|
-
}
|
|
405
|
+
filters.highlight(row);
|
|
449
406
|
if (row) {
|
|
450
|
-
row.highlighted = true;
|
|
451
407
|
this.revealRow(row);
|
|
452
408
|
}
|
|
453
409
|
}
|
|
@@ -460,7 +416,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
460
416
|
}
|
|
461
417
|
this.updateComplete.then(() => {
|
|
462
418
|
const items = this.items;
|
|
463
|
-
this.field?.focus();
|
|
419
|
+
this.filters?.field?.focus();
|
|
464
420
|
this.reach(end === 'first' ? items[0] : items[items.length - 1]);
|
|
465
421
|
});
|
|
466
422
|
}
|
|
@@ -610,7 +566,6 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
610
566
|
this.addEventListener('focusin', this.handleFocusIn);
|
|
611
567
|
this.addEventListener('contextmenu', this.handleContextMenu);
|
|
612
568
|
this.addEventListener('pointerdown', this.handlePointerDown);
|
|
613
|
-
this.addEventListener('mousedown', this.handleMouseDown);
|
|
614
569
|
this.addEventListener('pointermove', this.handlePointerMove);
|
|
615
570
|
this.addEventListener('pointerup', this.handlePointerUp);
|
|
616
571
|
this.addEventListener('pointercancel', this.handlePointerCancel);
|
|
@@ -635,10 +590,6 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
635
590
|
this.removeEventListener('focusin', this.handleFocusIn);
|
|
636
591
|
this.removeEventListener('contextmenu', this.handleContextMenu);
|
|
637
592
|
this.removeEventListener('pointerdown', this.handlePointerDown);
|
|
638
|
-
this.removeEventListener('mousedown', this.handleMouseDown);
|
|
639
|
-
if (this.filterable) {
|
|
640
|
-
markMatches(this, [], '');
|
|
641
|
-
}
|
|
642
593
|
this.removeEventListener('pointermove', this.handlePointerMove);
|
|
643
594
|
this.removeEventListener('pointerup', this.handlePointerUp);
|
|
644
595
|
this.removeEventListener('pointercancel', this.handlePointerCancel);
|
|
@@ -667,7 +618,8 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
667
618
|
if (isSection(child)) {
|
|
668
619
|
child.onPage =
|
|
669
620
|
top &&
|
|
670
|
-
(!this.filtering ||
|
|
621
|
+
(!this.filters?.filtering ||
|
|
622
|
+
[...child.children].some((row) => isItem(row) && this.kept(row)));
|
|
671
623
|
for (const row of child.children) {
|
|
672
624
|
if (row.isMenuItem === true) {
|
|
673
625
|
this.mark(row, top);
|
|
@@ -684,14 +636,9 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
684
636
|
this.toggleState('drilled', !this.flies && !this.drilldown.atRoot);
|
|
685
637
|
this.markColumns();
|
|
686
638
|
this.nameSurface();
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
if (changed.has('filter') &&
|
|
691
|
-
this.open &&
|
|
692
|
-
!items.includes(this.highlighted)) {
|
|
693
|
-
this.reach(items[0]);
|
|
694
|
-
}
|
|
639
|
+
this.filterer?.updated();
|
|
640
|
+
if (this.filterable && !this.filterer) {
|
|
641
|
+
auditEntry(this, 'filterable', 'menu/filter', 'it shows no filter field', () => this.filterable && !this.filterer);
|
|
695
642
|
}
|
|
696
643
|
}
|
|
697
644
|
nameSurface() {
|
|
@@ -729,7 +676,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
729
676
|
mark(item, onPage) {
|
|
730
677
|
const page = this.drilldown.page;
|
|
731
678
|
item.onPage = onPage && this.kept(item);
|
|
732
|
-
item.virtual = this.
|
|
679
|
+
item.virtual = this.filters !== undefined;
|
|
733
680
|
const holds = page !== null && item.contains(page);
|
|
734
681
|
item.flyout = this.flies;
|
|
735
682
|
item.expanded = this.flies && holds;
|
|
@@ -753,16 +700,9 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
753
700
|
}
|
|
754
701
|
render() {
|
|
755
702
|
const point = this.context && this.point;
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
: 0;
|
|
760
|
-
const none = filtering && count === 0;
|
|
761
|
-
this.announced = none
|
|
762
|
-
? this.noResultsText
|
|
763
|
-
: filtering
|
|
764
|
-
? formatWords(this, 'resultsLabel', { count })
|
|
765
|
-
: '';
|
|
703
|
+
const filters = this.filters;
|
|
704
|
+
this.announced = '';
|
|
705
|
+
const none = filters?.status();
|
|
766
706
|
const rows = html `${this.flies || this.drilldown.atRoot
|
|
767
707
|
? nothing
|
|
768
708
|
: html `<div part="header">
|
|
@@ -781,9 +721,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
781
721
|
<span part="title" id="title">${this.drilldown.label}</span>
|
|
782
722
|
</div>`}
|
|
783
723
|
<slot @slotchange=${(event) => this.countRows(event)}></slot>
|
|
784
|
-
${none
|
|
785
|
-
? html `<p part="empty"><slot name="no-results">${this.noResultsText}</slot></p>`
|
|
786
|
-
: this.renderStatus(this.hasRows)}`;
|
|
724
|
+
${none ?? this.renderStatus(this.hasRows)}`;
|
|
787
725
|
const busy = this.loading ? 'true' : nothing;
|
|
788
726
|
const label = this.label || nothing;
|
|
789
727
|
return html `<slot name="trigger" @click=${this.handleTriggerClick}></slot>${point
|
|
@@ -796,27 +734,14 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
796
734
|
<div
|
|
797
735
|
part="surface"
|
|
798
736
|
popover="manual"
|
|
799
|
-
role=${
|
|
800
|
-
aria-busy=${
|
|
801
|
-
aria-label=${
|
|
737
|
+
role=${filters ? nothing : 'menu'}
|
|
738
|
+
aria-busy=${filters ? nothing : busy}
|
|
739
|
+
aria-label=${filters ? nothing : label}
|
|
802
740
|
data-drilled=${this.flies || this.drilldown.atRoot ? nothing : ''}
|
|
803
|
-
data-filtered=${filtering ? '' : nothing}
|
|
741
|
+
data-filtered=${filters?.filtering ? '' : nothing}
|
|
804
742
|
?hidden=${!this.open}
|
|
805
743
|
>
|
|
806
|
-
${
|
|
807
|
-
? html `<input
|
|
808
|
-
part="search-field"
|
|
809
|
-
type="text"
|
|
810
|
-
autocomplete="off"
|
|
811
|
-
spellcheck="false"
|
|
812
|
-
.value=${this.filter}
|
|
813
|
-
placeholder=${this.filterLabel}
|
|
814
|
-
aria-label=${this.filterLabel}
|
|
815
|
-
aria-autocomplete="list"
|
|
816
|
-
aria-controls="items"
|
|
817
|
-
@input=${this.handleFilterInput}
|
|
818
|
-
/><div id="items" role="menu" aria-busy=${busy} aria-label=${label}>${rows}</div>`
|
|
819
|
-
: rows}
|
|
744
|
+
${filters ? filters.drawFilter(rows, busy) : rows}
|
|
820
745
|
</div>${this.renderLive(this.hasRows)}`;
|
|
821
746
|
}
|
|
822
747
|
};
|
|
@@ -217,6 +217,11 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
|
|
|
217
217
|
* on why a wheel cannot do either.
|
|
218
218
|
*/
|
|
219
219
|
mode?: 'multiple' | 'range';
|
|
220
|
+
/**
|
|
221
|
+
* The words on the list's button that chooses the current time, drawn with
|
|
222
|
+
* `show-now`.
|
|
223
|
+
*/
|
|
224
|
+
nowLabel: string;
|
|
220
225
|
/**
|
|
221
226
|
* Placeholder text shown before a time is entered.
|
|
222
227
|
*/
|
|
@@ -252,6 +257,12 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
|
|
|
252
257
|
* See Time Picker's `seconds`.
|
|
253
258
|
*/
|
|
254
259
|
seconds: boolean;
|
|
260
|
+
/**
|
|
261
|
+
* Draws a button under the times in the list that chooses the current time,
|
|
262
|
+
* moved to the nearest time on offer. See Time Picker's `show-now`. Its words
|
|
263
|
+
* are `now-label`.
|
|
264
|
+
*/
|
|
265
|
+
showNow: boolean;
|
|
255
266
|
/**
|
|
256
267
|
* How far apart the offered times are, in minutes, or in seconds with
|
|
257
268
|
* `seconds`.
|
|
@@ -21,12 +21,14 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
21
21
|
this.locale = '';
|
|
22
22
|
this.max = '23:59';
|
|
23
23
|
this.min = '00:00';
|
|
24
|
+
this.nowLabel = 'Now';
|
|
24
25
|
this.placeholder = 'HH:mm';
|
|
25
26
|
this.rangeLengthMessage = 'A range is two times.';
|
|
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.removeLabel = 'Remove {name}';
|
|
29
30
|
this.seconds = false;
|
|
31
|
+
this.showNow = false;
|
|
30
32
|
this.step = 15;
|
|
31
33
|
this.timeStyle = 'local';
|
|
32
34
|
this.triggerLabel = 'Choose a time';
|
|
@@ -246,7 +248,9 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
246
248
|
locale=${handedLocale(this) || nothing}
|
|
247
249
|
max=${this.max}
|
|
248
250
|
min=${this.min}
|
|
251
|
+
now-label=${this.nowLabel}
|
|
249
252
|
?seconds=${this.seconds}
|
|
253
|
+
?show-now=${this.showNow}
|
|
250
254
|
step=${this.step}
|
|
251
255
|
.value=${this.picked}
|
|
252
256
|
@helix-before-change=${this.handlePicked}
|
|
@@ -400,6 +404,10 @@ __decorate([
|
|
|
400
404
|
__decorate([
|
|
401
405
|
property({ reflect: true })
|
|
402
406
|
], HelixHeadlessTimeInput.prototype, "mode", void 0);
|
|
407
|
+
__decorate([
|
|
408
|
+
words,
|
|
409
|
+
property({ attribute: 'now-label' })
|
|
410
|
+
], HelixHeadlessTimeInput.prototype, "nowLabel", void 0);
|
|
403
411
|
__decorate([
|
|
404
412
|
words,
|
|
405
413
|
property()
|
|
@@ -423,6 +431,9 @@ __decorate([
|
|
|
423
431
|
__decorate([
|
|
424
432
|
property({ type: Boolean })
|
|
425
433
|
], HelixHeadlessTimeInput.prototype, "seconds", void 0);
|
|
434
|
+
__decorate([
|
|
435
|
+
property({ attribute: 'show-now', type: Boolean })
|
|
436
|
+
], HelixHeadlessTimeInput.prototype, "showNow", void 0);
|
|
426
437
|
__decorate([
|
|
427
438
|
property({ type: Number })
|
|
428
439
|
], HelixHeadlessTimeInput.prototype, "step", void 0);
|
|
@@ -57,7 +57,11 @@ export class VirtualListReorderController {
|
|
|
57
57
|
}
|
|
58
58
|
get enabled() {
|
|
59
59
|
const host = this.host;
|
|
60
|
-
return (host.reorderable &&
|
|
60
|
+
return (host.reorderable &&
|
|
61
|
+
host.columns <= 1 &&
|
|
62
|
+
host.minColumnWidth <= 0 &&
|
|
63
|
+
host.sectionField === '' &&
|
|
64
|
+
host.layout !== 'waterfall');
|
|
61
65
|
}
|
|
62
66
|
hostUpdated() {
|
|
63
67
|
queueMicrotask(() => this.mark());
|
|
@@ -61,6 +61,32 @@ export interface VirtualListRange {
|
|
|
61
61
|
* smallest item size, and follows the box as it resizes. `gap` spaces the rows, and the columns
|
|
62
62
|
* of a grid, by a number of pixels the list counts in each row's place.
|
|
63
63
|
*
|
|
64
|
+
* A grid without `selection-mode` is a grid to a reader, as the APG's grid
|
|
65
|
+
* and React Aria's grid list are: the box is a `grid` with `aria-rowcount`
|
|
66
|
+
* and `aria-colcount`, each line drawn is a `row` with `aria-rowindex`, and
|
|
67
|
+
* each item a `gridcell` with `aria-colindex`. Each line is a slot of the
|
|
68
|
+
* list's own, so your rows stay its children: give an item one element. With
|
|
69
|
+
* `selection-mode` it stays a list box of options, as React Aria's list box
|
|
70
|
+
* in a grid layout does.
|
|
71
|
+
*
|
|
72
|
+
* **A waterfall.** `layout="waterfall"` puts each item, in the order of the
|
|
73
|
+
* set, under the column that ends highest so far, the leftmost of a tie, as a
|
|
74
|
+
* wall of photos of different heights is, and as React Aria's waterfall
|
|
75
|
+
* layout and TanStack Virtual's lanes do. `columns`, or `min-column-width`,
|
|
76
|
+
* says how many columns. Every item is measured, at the width of its column,
|
|
77
|
+
* and placed at its column and its height, so items of any height leave no
|
|
78
|
+
* holes. The list draws the items that reach into the box, and the item
|
|
79
|
+
* holding focus. A reader hears a list, or a list box, in the order of the
|
|
80
|
+
* set, which is also the order Tab and a screen reader's own keys take.
|
|
81
|
+
*
|
|
82
|
+
* In a waterfall, Down and Up move along the column to the next or the
|
|
83
|
+
* previous item in it, Right and Left move to the next or the previous
|
|
84
|
+
* column, to the item there that sits beside this one the most, or the
|
|
85
|
+
* nearest above where none does, and Page Down and Page Up move a box down
|
|
86
|
+
* or up the column. Home and End go to the first and the last item of the
|
|
87
|
+
* set, and Shift with a key takes in the items between in the order of the
|
|
88
|
+
* set.
|
|
89
|
+
*
|
|
64
90
|
* **Across.** `orientation="horizontal"` lays the items out along a line and
|
|
65
91
|
* scrolls it sideways, as a strip of cards does, windowed the same way:
|
|
66
92
|
* `estimate` is then an item's width, and the box needs an inline size.
|
|
@@ -79,10 +105,11 @@ export interface VirtualListRange {
|
|
|
79
105
|
*
|
|
80
106
|
* | Key | What it does |
|
|
81
107
|
* |---|---|
|
|
82
|
-
* | Down, Up | Moves to the next or the previous item, or in a grid to the item in the same column on the next or the previous line. |
|
|
83
|
-
* | Right, Left | In a grid, moves to the next or the previous item along the line, the other way round on a right-to-left page. In a list that runs across, moves to the next or the previous item the same way, and Down and Up are left to the row. |
|
|
84
|
-
* | Home, End | Moves to the first or the last item. |
|
|
85
|
-
* |
|
|
108
|
+
* | Down, Up | Moves to the next or the previous item, or in a grid to the item in the same column on the next or the previous line, or in a waterfall to the next or the previous item down the same column. |
|
|
109
|
+
* | Right, Left | In a grid, moves to the next or the previous item along the line, the other way round on a right-to-left page. In a waterfall, moves to the item beside in the next or the previous column. In a list that runs across, moves to the next or the previous item the same way, and Down and Up are left to the row. |
|
|
110
|
+
* | Home, End | Moves to the first or the last item. In a grid without `selection-mode`, to the first or the last item of the line. |
|
|
111
|
+
* | Control or Command with Home or End | In a grid without `selection-mode`, moves to the first or the last item. |
|
|
112
|
+
* | Page Down, Page Up | Moves by the number of items, or of lines in a grid, on screen, or in a waterfall a box down or up the column. |
|
|
86
113
|
* | A letter or a word | Moves to the next item whose text starts with what you typed. |
|
|
87
114
|
* | Escape | Lets go of every chosen item, in a list with `selection-mode`. |
|
|
88
115
|
* | Alt with Up or Down | In a `reorderable` list, moves the item a place. Alt with Home or End moves it to either end. |
|
|
@@ -97,8 +124,9 @@ export interface VirtualListRange {
|
|
|
97
124
|
* or the item itself when it is a string or a number.
|
|
98
125
|
*
|
|
99
126
|
* Each row says its place in the whole set with `aria-posinset` and
|
|
100
|
-
* `aria-setsize`, so a screen reader says "4,000 of 10,000"
|
|
101
|
-
* focus stays drawn when it
|
|
127
|
+
* `aria-setsize`, so a screen reader says "4,000 of 10,000", and each cell of
|
|
128
|
+
* a grid its line and its column. The row holding focus stays drawn when it
|
|
129
|
+
* scrolls out.
|
|
102
130
|
*
|
|
103
131
|
* **Selecting.** `selection-mode` makes the list a list box a reader chooses
|
|
104
132
|
* from: `single` selects one item and `multiple` any number. Space or a press
|
|
@@ -267,6 +295,15 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
267
295
|
* wins over this.
|
|
268
296
|
*/
|
|
269
297
|
label: string;
|
|
298
|
+
/**
|
|
299
|
+
* How items of more than one column are laid out. `grid`, the default, puts
|
|
300
|
+
* them in lines of equal columns. `waterfall` puts each item, in the order
|
|
301
|
+
* of the set, under the column that ends highest, as a wall of photos of
|
|
302
|
+
* different heights is: `columns`, or `min-column-width`, says how many
|
|
303
|
+
* columns. Rows of a waterfall are measured, and Up and Down move along a
|
|
304
|
+
* column.
|
|
305
|
+
*/
|
|
306
|
+
layout: 'grid' | 'waterfall';
|
|
270
307
|
/**
|
|
271
308
|
* Marks the list as busy fetching more items, and draws a row after the last
|
|
272
309
|
* item that says so.
|
|
@@ -300,7 +337,7 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
300
337
|
* Which way the list runs. `horizontal` lays the items out along a line
|
|
301
338
|
* and scrolls it sideways: Left and Right move between items, the other
|
|
302
339
|
* way round on a right-to-left page, and `estimate` is an item's width. A
|
|
303
|
-
* grid always
|
|
340
|
+
* grid and a waterfall always run down.
|
|
304
341
|
*/
|
|
305
342
|
orientation: 'horizontal' | 'vertical';
|
|
306
343
|
/**
|
|
@@ -324,7 +361,8 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
324
361
|
* with an arrow moves the row the keys are on, and a pointer drags a row.
|
|
325
362
|
* The list moves no item itself: you move your `items` on `helix-reorder`.
|
|
326
363
|
* Without that import it does nothing, so a list that never moves items
|
|
327
|
-
* carries none of the code. A list of one column, without sections
|
|
364
|
+
* carries none of the code. A list of one column, without sections or a
|
|
365
|
+
* waterfall.
|
|
328
366
|
*/
|
|
329
367
|
reorderable: boolean;
|
|
330
368
|
/**
|
|
@@ -422,24 +460,42 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
422
460
|
get range(): VirtualListRange;
|
|
423
461
|
/** The gap in pixels, none where it is not a positive number. */
|
|
424
462
|
private get space();
|
|
463
|
+
/** Where each item of a waterfall stands, which the controller places. */
|
|
464
|
+
private readonly lanes;
|
|
465
|
+
/** Whether the items are laid out as a waterfall, each down a column. */
|
|
466
|
+
private get waterfall();
|
|
425
467
|
/**
|
|
426
|
-
*
|
|
427
|
-
*
|
|
468
|
+
* Whether the list is a grid a reader reads, and not a list box: lines of
|
|
469
|
+
* columns with no `selection-mode`, which are `grid`, `row` and `gridcell`
|
|
470
|
+
* to a reader, as the APG's grid and React Aria's grid list are.
|
|
471
|
+
*/
|
|
472
|
+
private get lined();
|
|
473
|
+
/**
|
|
474
|
+
* How many items a line holds, which a waterfall draws one at a time: its
|
|
475
|
+
* columns are not lines.
|
|
428
476
|
*/
|
|
429
477
|
private get across();
|
|
478
|
+
/**
|
|
479
|
+
* How many columns there are: as many as fit at `min-column-width` where
|
|
480
|
+
* it is set and the box has a width, and `columns` otherwise, at least one.
|
|
481
|
+
*/
|
|
482
|
+
private get columnCount();
|
|
430
483
|
/** How wide the box is, read for `min-column-width`. */
|
|
431
484
|
private boxWidth;
|
|
432
485
|
/** Watches the box's width, for `min-column-width`. */
|
|
433
486
|
private widthWatch;
|
|
434
|
-
/** The items to a line the measurements were taken at. */
|
|
487
|
+
/** The items to a line the measurements were taken at, or a waterfall's columns and width. */
|
|
435
488
|
private measuredAcross;
|
|
436
|
-
/**
|
|
489
|
+
/**
|
|
490
|
+
* Watches the box for its width while `min-column-width` asks for it, or a
|
|
491
|
+
* waterfall, whose items are as tall as their width makes them.
|
|
492
|
+
*/
|
|
437
493
|
private watchWidth;
|
|
438
494
|
/** The items a span of lines holds. */
|
|
439
495
|
private itemsOn;
|
|
440
496
|
/** The line item `at` is on: its entry in a list with sections. */
|
|
441
497
|
private lineOf;
|
|
442
|
-
/** Whether the list runs across, which a grid never
|
|
498
|
+
/** Whether the list runs across, which a grid and a waterfall never do. */
|
|
443
499
|
private get horizontal();
|
|
444
500
|
/** The sections and the items they were read from, kept until either changes. */
|
|
445
501
|
private sectionsRead;
|
|
@@ -541,6 +597,14 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
541
597
|
* is that control's, so a field in a row keeps its arrows.
|
|
542
598
|
*/
|
|
543
599
|
private readonly handleKeydown;
|
|
600
|
+
/**
|
|
601
|
+
* Where a key goes in a waterfall, through the columns `Waterfall.step`
|
|
602
|
+
* reads: along the column for Down, Up, Page Down and Page Up, and to the
|
|
603
|
+
* item beside in the next column or the one before for the keys along the
|
|
604
|
+
* line, the other way round on a right-to-left page. Past a disabled item,
|
|
605
|
+
* on the same way.
|
|
606
|
+
*/
|
|
607
|
+
private inWaterfall;
|
|
544
608
|
/** The key of item `at` in `value`. See `keyField`. */
|
|
545
609
|
private keyAt;
|
|
546
610
|
/** @internal Every item's key, in the order of the set, which the reorder entry moves. */
|
|
@@ -600,15 +664,25 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
600
664
|
private keptIndex;
|
|
601
665
|
/**
|
|
602
666
|
* Writes a row's role, and whether it is chosen in a list that selects:
|
|
603
|
-
* `option` with `aria-selected` there,
|
|
604
|
-
* `aria-disabled` on an item `disabled-keys` names. Every
|
|
605
|
-
* since a choice changes no row's content.
|
|
667
|
+
* `option` with `aria-selected` there, `gridcell` in a grid, and `listitem`
|
|
668
|
+
* otherwise, and `aria-disabled` on an item `disabled-keys` names. Every
|
|
669
|
+
* pass, since a choice changes no row's content.
|
|
670
|
+
*
|
|
671
|
+
* Its place too: `aria-posinset` and `aria-setsize` in a list, and in a
|
|
672
|
+
* grid its column, with the line's `row` given by the slot it is drawn in.
|
|
606
673
|
*
|
|
607
674
|
* `data-selected` and `data-disabled` say the same to a stylesheet, as
|
|
608
675
|
* React Aria's rows do, so one rule reads the same in either kind of list.
|
|
609
676
|
*/
|
|
610
677
|
private mark;
|
|
611
|
-
/**
|
|
678
|
+
/**
|
|
679
|
+
* Writes which item a row drew, for the controller to measure it against:
|
|
680
|
+
* the real index, the same one `aria-posinset` carries one higher.
|
|
681
|
+
*
|
|
682
|
+
* `mark` writes the place a reader hears from it, the real index and the
|
|
683
|
+
* real total, not the position in the window. A reader arrowing through
|
|
684
|
+
* this should hear where they are in the set.
|
|
685
|
+
*/
|
|
612
686
|
private stamp;
|
|
613
687
|
/** Fills a drawn row from its item, by `renderItem` or by the template's holes. */
|
|
614
688
|
private fill;
|
|
@@ -642,6 +716,10 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
642
716
|
private place;
|
|
643
717
|
/** The sections the rows were last placed in. */
|
|
644
718
|
private placedSections;
|
|
719
|
+
/** Whether the rows were last placed in a waterfall. */
|
|
720
|
+
private placedWaterfall;
|
|
721
|
+
/** The lines of a grid drawn on the last pass, each a `row` the render draws a slot in. */
|
|
722
|
+
private drawnLines;
|
|
645
723
|
/** The function the drawn headers were drawn by, so a new one draws them again. */
|
|
646
724
|
private headedBy;
|
|
647
725
|
/** Ties each header's id to this list. */
|