@fusion.dev/helix-headless 0.182.2 → 0.183.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 +143 -15
- 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/heatmap/root/heatmap-root.d.ts +11 -1
- package/dist/heatmap/root/heatmap-root.js +17 -9
- 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 +108 -20
- package/types/vue.d.ts +11 -3
|
@@ -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. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { AriaForwardController, anchorAt, commaList, customElement, elementById, HelixElement, HelixLoadMoreEvent, HelixRangeChangeEvent, inRowOrder, isRtl, pressedOwnControl, rangeAdd, requestValue, SCROLLER_RULE, searchableById, selectsAll, Typeahead, toggleValue, typesCharacter, VIRTUAL_RULE, VirtualizeController, words, } from '@fusion.dev/helix-core';
|
|
2
|
+
import { AriaForwardController, anchorAt, commaList, customElement, elementById, HelixElement, HelixLoadMoreEvent, HelixRangeChangeEvent, inRowOrder, isRtl, pressedOwnControl, rangeAdd, requestValue, SCROLLER_RULE, searchableById, selectsAll, Typeahead, toggleValue, typesCharacter, VIRTUAL_RULE, VirtualizeController, Waterfall, words, } from '@fusion.dev/helix-core';
|
|
3
3
|
import { html, nothing, render } from 'lit';
|
|
4
4
|
import { property, state } from 'lit/decorators.js';
|
|
5
5
|
import { holdControls, releaseControls } from '../controls.js';
|
|
@@ -13,6 +13,7 @@ const nextSectionId = () => {
|
|
|
13
13
|
return `virtual-list-${sectionCount}`;
|
|
14
14
|
};
|
|
15
15
|
const ROWS = ':scope > [data-index], :scope > [data-section] > [data-index]';
|
|
16
|
+
const LINE = 'helix-line-';
|
|
16
17
|
let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixElement {
|
|
17
18
|
constructor() {
|
|
18
19
|
super(...arguments);
|
|
@@ -24,6 +25,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
24
25
|
this.gap = 0;
|
|
25
26
|
this.keyField = '';
|
|
26
27
|
this.label = '';
|
|
28
|
+
this.layout = 'grid';
|
|
27
29
|
this.loading = false;
|
|
28
30
|
this.loadingLabel = 'Loading more';
|
|
29
31
|
this.measured = false;
|
|
@@ -54,7 +56,12 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
54
56
|
horizontal: () => this.horizontal,
|
|
55
57
|
measured: () => this.measured,
|
|
56
58
|
rows: () => this.lines(),
|
|
59
|
+
waterfall: () => {
|
|
60
|
+
this.lanes.lanes = this.columnCount;
|
|
61
|
+
return this.waterfall ? this.lanes : undefined;
|
|
62
|
+
},
|
|
57
63
|
});
|
|
64
|
+
this.lanes = new Waterfall();
|
|
58
65
|
this.boxWidth = 0;
|
|
59
66
|
this.measuredAcross = 1;
|
|
60
67
|
this.handleKeydown = (event) => {
|
|
@@ -97,7 +104,9 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
97
104
|
}
|
|
98
105
|
}
|
|
99
106
|
const extend = event.shiftKey && this.selectionMode === 'multiple';
|
|
100
|
-
|
|
107
|
+
const ends = event.key === 'Home' || event.key === 'End';
|
|
108
|
+
const lined = this.lined;
|
|
109
|
+
if (event.altKey || (toggle && !replacing && !(lined && ends)) || (event.shiftKey && !extend)) {
|
|
101
110
|
return;
|
|
102
111
|
}
|
|
103
112
|
const count = this.data.length;
|
|
@@ -112,13 +121,19 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
112
121
|
: event.key === 'ArrowDown' || event.key === 'ArrowUp'
|
|
113
122
|
? ''
|
|
114
123
|
: event.key;
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
const disabled = (one) => this.isDisabled(one);
|
|
125
|
+
const across = this.across;
|
|
126
|
+
const start = at - (at % across);
|
|
127
|
+
const stepped = lined && ends && !toggle
|
|
128
|
+
? key === 'Home'
|
|
129
|
+
? start
|
|
130
|
+
: Math.min(start + across, count) - 1
|
|
131
|
+
: this.waterfall && !ends
|
|
132
|
+
? this.inWaterfall(key, at, count, forward)
|
|
133
|
+
: stepIndex(key, at, count, last - first - 1, { across, forward });
|
|
119
134
|
const next = stepped === undefined
|
|
120
135
|
? undefined
|
|
121
|
-
: skipDisabled(key, at, stepped, count,
|
|
136
|
+
: skipDisabled(key, at, stepped, count, disabled, this.waterfall ? 1 : across);
|
|
122
137
|
if (next === undefined) {
|
|
123
138
|
if (stepped !== undefined) {
|
|
124
139
|
event.preventDefault();
|
|
@@ -172,6 +187,8 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
172
187
|
this.requestUpdate();
|
|
173
188
|
};
|
|
174
189
|
this.visited = false;
|
|
190
|
+
this.placedWaterfall = false;
|
|
191
|
+
this.drawnLines = [];
|
|
175
192
|
this.sectionId = nextSectionId();
|
|
176
193
|
}
|
|
177
194
|
static { this.markRules = [SCROLLER_RULE, VIRTUAL_RULE]; }
|
|
@@ -182,7 +199,20 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
182
199
|
get space() {
|
|
183
200
|
return Number.isFinite(this.gap) && this.gap > 0 ? this.gap : 0;
|
|
184
201
|
}
|
|
202
|
+
get waterfall() {
|
|
203
|
+
return this.layout === 'waterfall';
|
|
204
|
+
}
|
|
205
|
+
get lined() {
|
|
206
|
+
return (!this.selectionMode &&
|
|
207
|
+
!this.waterfall &&
|
|
208
|
+
!this.horizontal &&
|
|
209
|
+
this.sections === undefined &&
|
|
210
|
+
(this.across > 1 || this.minColumnWidth > 0));
|
|
211
|
+
}
|
|
185
212
|
get across() {
|
|
213
|
+
return this.waterfall ? 1 : this.columnCount;
|
|
214
|
+
}
|
|
215
|
+
get columnCount() {
|
|
186
216
|
if (this.minColumnWidth > 0 && this.boxWidth > 0) {
|
|
187
217
|
const space = this.space;
|
|
188
218
|
return Math.max(1, Math.floor((this.boxWidth + space) / (this.minColumnWidth + space)));
|
|
@@ -191,7 +221,9 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
191
221
|
}
|
|
192
222
|
watchWidth() {
|
|
193
223
|
const viewport = this.partNamed('viewport');
|
|
194
|
-
if (this.minColumnWidth <= 0
|
|
224
|
+
if ((this.minColumnWidth <= 0 && !this.waterfall) ||
|
|
225
|
+
!viewport ||
|
|
226
|
+
typeof ResizeObserver === 'undefined') {
|
|
195
227
|
this.widthWatch?.disconnect();
|
|
196
228
|
this.widthWatch = undefined;
|
|
197
229
|
return;
|
|
@@ -218,11 +250,11 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
218
250
|
return sections ? entryOf(sections, at) : Math.floor(at / this.across);
|
|
219
251
|
}
|
|
220
252
|
get horizontal() {
|
|
221
|
-
return this.orientation === 'horizontal' && this.across === 1;
|
|
253
|
+
return this.orientation === 'horizontal' && this.across === 1 && !this.waterfall;
|
|
222
254
|
}
|
|
223
255
|
get sections() {
|
|
224
256
|
const data = this.data;
|
|
225
|
-
if (this.sectionField === '' || data.length === 0 || this.
|
|
257
|
+
if (this.sectionField === '' || data.length === 0 || this.columnCount !== 1 || this.waterfall) {
|
|
226
258
|
return undefined;
|
|
227
259
|
}
|
|
228
260
|
const read = this.sectionsRead;
|
|
@@ -275,7 +307,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
275
307
|
this.readFor = this.for;
|
|
276
308
|
this.read();
|
|
277
309
|
}
|
|
278
|
-
const across = this.across;
|
|
310
|
+
const across = this.waterfall ? `${this.columnCount} ${this.boxWidth}` : this.across;
|
|
279
311
|
if (across !== this.measuredAcross) {
|
|
280
312
|
this.measuredAcross = across;
|
|
281
313
|
this.virtualize.forget();
|
|
@@ -450,6 +482,16 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
450
482
|
rowFor(index) {
|
|
451
483
|
return ([...this.querySelectorAll(ROWS)].find((row) => row.dataset.index === String(index)) ?? null);
|
|
452
484
|
}
|
|
485
|
+
inWaterfall(key, at, count, forward) {
|
|
486
|
+
const way = key === forward ? 'next' : key === 'ArrowLeft' || key === 'ArrowRight' ? 'previous' : key;
|
|
487
|
+
const box = this.partNamed('viewport');
|
|
488
|
+
let to = this.lanes.step(way, at, count, box?.clientHeight ?? 0);
|
|
489
|
+
const again = way === 'PageDown' ? 'ArrowDown' : way === 'PageUp' ? 'ArrowUp' : way;
|
|
490
|
+
while (to !== undefined && this.isDisabled(to)) {
|
|
491
|
+
to = this.lanes.step(again, to, count, 0);
|
|
492
|
+
}
|
|
493
|
+
return to;
|
|
494
|
+
}
|
|
453
495
|
keyAt(at) {
|
|
454
496
|
return keyOf(this.data[at], at, this.keyField);
|
|
455
497
|
}
|
|
@@ -530,7 +572,22 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
530
572
|
: undefined;
|
|
531
573
|
}
|
|
532
574
|
mark(element, at, chosen, off) {
|
|
533
|
-
|
|
575
|
+
const lined = this.lined;
|
|
576
|
+
element.setAttribute('role', this.selectionMode ? 'option' : lined ? 'gridcell' : 'listitem');
|
|
577
|
+
const across = this.across;
|
|
578
|
+
for (const [name, value] of [
|
|
579
|
+
['aria-colindex', lined ? (at % across) + 1 : undefined],
|
|
580
|
+
['aria-posinset', lined ? undefined : at + 1],
|
|
581
|
+
['aria-setsize', lined ? undefined : this.data.length],
|
|
582
|
+
['slot', lined ? `${LINE}${Math.floor(at / across)}` : undefined],
|
|
583
|
+
]) {
|
|
584
|
+
if (value === undefined) {
|
|
585
|
+
element.removeAttribute(name);
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
element.setAttribute(name, String(value));
|
|
589
|
+
}
|
|
590
|
+
}
|
|
534
591
|
const selected = Boolean(this.selectionMode) && chosen.has(this.keyAt(at));
|
|
535
592
|
if (this.selectionMode) {
|
|
536
593
|
element.setAttribute('aria-selected', String(selected));
|
|
@@ -549,8 +606,6 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
549
606
|
element.toggleAttribute('data-disabled', disabled);
|
|
550
607
|
}
|
|
551
608
|
stamp(element, at) {
|
|
552
|
-
element.setAttribute('aria-posinset', String(at + 1));
|
|
553
|
-
element.setAttribute('aria-setsize', String(this.data.length));
|
|
554
609
|
element.setAttribute('data-index', String(at));
|
|
555
610
|
}
|
|
556
611
|
fill(elements, at) {
|
|
@@ -595,12 +650,13 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
595
650
|
this.partNamed('after')?.style.setProperty(size, `${spacers.after}px`);
|
|
596
651
|
}
|
|
597
652
|
const sections = this.sections;
|
|
598
|
-
const regroup = sections !== this.placedSections;
|
|
653
|
+
const regroup = sections !== this.placedSections || this.waterfall !== this.placedWaterfall;
|
|
599
654
|
this.placedSections = sections;
|
|
655
|
+
this.placedWaterfall = this.waterfall;
|
|
600
656
|
const children = [
|
|
601
657
|
...[...this.children].filter((child) => child !== template &&
|
|
602
658
|
child instanceof HTMLElement &&
|
|
603
|
-
!child.hasAttribute('slot') &&
|
|
659
|
+
(child.dataset.index !== undefined || !child.hasAttribute('slot')) &&
|
|
604
660
|
!child.hasAttribute('data-section')),
|
|
605
661
|
...this.querySelectorAll(':scope > [data-section] > [data-index]'),
|
|
606
662
|
];
|
|
@@ -660,9 +716,23 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
660
716
|
});
|
|
661
717
|
const chosen = this.chosen;
|
|
662
718
|
const off = this.disabled;
|
|
719
|
+
const lines = this.lined ? [...new Set(plan.wanted.map((at) => this.lineOf(at)))] : [];
|
|
720
|
+
if (lines.join() !== this.drawnLines.join()) {
|
|
721
|
+
this.drawnLines = lines;
|
|
722
|
+
this.requestUpdate();
|
|
723
|
+
}
|
|
724
|
+
const gap = this.space;
|
|
725
|
+
const columns = this.columnCount;
|
|
663
726
|
for (const [at, group] of drawn) {
|
|
664
727
|
for (const element of group) {
|
|
665
728
|
this.mark(element, at, chosen, off);
|
|
729
|
+
if (this.waterfall) {
|
|
730
|
+
const style = element.style;
|
|
731
|
+
style.setProperty('position', 'absolute');
|
|
732
|
+
style.setProperty('inset-block-start', `${this.virtualize.offsetOf(at)}px`);
|
|
733
|
+
style.setProperty('inset-inline-start', `calc((100% + ${gap}px) * ${this.lanes.lane(at)} / ${columns})`);
|
|
734
|
+
style.setProperty('inline-size', `calc((100% - ${gap * (columns - 1)}px) / ${columns})`);
|
|
735
|
+
}
|
|
666
736
|
}
|
|
667
737
|
const stop = at === this.active;
|
|
668
738
|
group[0]?.setAttribute('tabindex', stop ? '0' : '-1');
|
|
@@ -745,6 +815,9 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
745
815
|
return groups;
|
|
746
816
|
}
|
|
747
817
|
spacerSizes() {
|
|
818
|
+
if (this.waterfall) {
|
|
819
|
+
return { after: 0, before: 0 };
|
|
820
|
+
}
|
|
748
821
|
const kept = this.hasUpdated ? this.keptIndex() : undefined;
|
|
749
822
|
const room = this.virtualize.spacers(kept === undefined ? undefined : this.lineOf(kept));
|
|
750
823
|
const sections = this.sections;
|
|
@@ -779,14 +852,20 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
779
852
|
const size = horizontal ? 'inline-size' : 'block-size';
|
|
780
853
|
const flex = horizontal ? '; flex: none' : '';
|
|
781
854
|
const gap = this.space;
|
|
855
|
+
const grid = gap === 0 ? '' : `; gap: ${gap}px; padding-block-end: ${gap}px`;
|
|
782
856
|
const empty = this.readFor !== undefined &&
|
|
783
857
|
!this.loading &&
|
|
784
858
|
this.data.length === 0 &&
|
|
785
859
|
(this.emptyLabel !== '' || this.querySelector(':scope > [slot="empty"]') !== null);
|
|
860
|
+
const lined = this.lined;
|
|
861
|
+
const own = (content) => lined ? html `<div role="gridcell">${content}</div>` : content;
|
|
862
|
+
const role = this.selectionMode ? 'option' : lined ? 'row' : 'listitem';
|
|
786
863
|
return html `<div
|
|
787
864
|
data-scroller
|
|
788
865
|
part="viewport"
|
|
789
|
-
role=${this.selectionMode ? 'listbox' : 'list'}
|
|
866
|
+
role=${this.selectionMode ? 'listbox' : lined ? 'grid' : 'list'}
|
|
867
|
+
aria-colcount=${lined ? across : nothing}
|
|
868
|
+
aria-rowcount=${lined ? Math.ceil(this.data.length / across) : nothing}
|
|
790
869
|
aria-multiselectable=${this.selectionMode === 'multiple' ? 'true' : nothing}
|
|
791
870
|
aria-busy=${this.loading ? 'true' : nothing}
|
|
792
871
|
aria-label=${this.label}
|
|
@@ -798,24 +877,37 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
798
877
|
}}
|
|
799
878
|
>
|
|
800
879
|
<div part="before" style="${size}: ${before}px${flex}"></div>
|
|
880
|
+
${lined
|
|
881
|
+
? html `<div style=${`display: grid; grid-template-columns: repeat(${across}, minmax(0, 1fr))${grid}`}>
|
|
882
|
+
${this.drawnLines.map((line) => html `<div
|
|
883
|
+
aria-rowindex=${line + 1}
|
|
884
|
+
role="row"
|
|
885
|
+
style="display: grid; grid-column: 1 / -1; grid-template-columns: subgrid"
|
|
886
|
+
>
|
|
887
|
+
<slot name=${`${LINE}${line}`}></slot>
|
|
888
|
+
</div>`)}
|
|
889
|
+
</div>`
|
|
890
|
+
: nothing}
|
|
801
891
|
<slot
|
|
802
|
-
style=${
|
|
803
|
-
? `display:
|
|
804
|
-
:
|
|
805
|
-
? gap === 0
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
892
|
+
style=${this.waterfall
|
|
893
|
+
? `display: block; position: relative; block-size: ${this.virtualize.offsetOf(this.data.length)}px`
|
|
894
|
+
: horizontal
|
|
895
|
+
? `display: flex; flex: none${gap === 0 ? '' : `; column-gap: ${gap}px; padding-inline-end: ${gap}px`}`
|
|
896
|
+
: across === 1 || lined
|
|
897
|
+
? gap === 0
|
|
898
|
+
? nothing
|
|
899
|
+
: `display: flex; flex-direction: column; row-gap: ${gap}px; padding-block-end: ${gap}px`
|
|
900
|
+
: `display: grid; grid-template-columns: repeat(${across}, minmax(0, 1fr))${grid}`}
|
|
809
901
|
></slot>
|
|
810
902
|
<div part="after" style="${size}: ${after}px${flex}"></div>
|
|
811
903
|
${this.loading
|
|
812
|
-
? html `<div part="loading" role=${
|
|
813
|
-
|
|
904
|
+
? html `<div part="loading" role=${role}>
|
|
905
|
+
${own(html `<slot name="loading">${this.loadingLabel}</slot>`)}
|
|
814
906
|
</div>`
|
|
815
907
|
: nothing}
|
|
816
908
|
${empty
|
|
817
|
-
? html `<div part="empty" role=${
|
|
818
|
-
|
|
909
|
+
? html `<div part="empty" role=${role}>
|
|
910
|
+
${own(html `<slot name="empty">${this.emptyLabel}</slot>`)}
|
|
819
911
|
</div>`
|
|
820
912
|
: nothing}
|
|
821
913
|
</div>`;
|
|
@@ -848,6 +940,9 @@ __decorate([
|
|
|
848
940
|
__decorate([
|
|
849
941
|
property()
|
|
850
942
|
], HelixHeadlessVirtualList.prototype, "label", void 0);
|
|
943
|
+
__decorate([
|
|
944
|
+
property()
|
|
945
|
+
], HelixHeadlessVirtualList.prototype, "layout", void 0);
|
|
851
946
|
__decorate([
|
|
852
947
|
property({ type: Boolean })
|
|
853
948
|
], HelixHeadlessVirtualList.prototype, "loading", void 0);
|