@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, anchorElement, customElement, DrilldownController, deepActiveElement, deepContains, focusTarget, HelixElement, inlineStep, KeyboardNavigationController, linkDescription, MixinCollection, MixinControllerOpen, onUnlessFalse, PopoverController, scrollItemIntoView, sideOf, slottedWords, tellDisclosure, typeaheadBy, unlinkDescription, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, anchorElement, customElement, DrilldownController, deepActiveElement, deepContains, filterKeeps, focusTarget, HelixElement, HelixFilterEvent, inlineStep, KeyboardNavigationController, linkDescription, MixinCollection, MixinControllerOpen, markMatches, onUnlessFalse, PopoverController, scrollItemIntoView, sideOf, slottedWords, tellDisclosure, typeaheadBy, unlinkDescription, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretLeft } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
@@ -33,8 +33,12 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
33
33
|
this.backLabel = 'Back';
|
|
34
34
|
this.closeOnSelect = true;
|
|
35
35
|
this.context = false;
|
|
36
|
+
this.filter = '';
|
|
37
|
+
this.filterable = false;
|
|
38
|
+
this.filterLabel = 'Filter';
|
|
36
39
|
this.label = '';
|
|
37
40
|
this.longPressLabel = 'Long press or press Alt+Down to show the menu';
|
|
41
|
+
this.noResultsText = 'No matches';
|
|
38
42
|
this.offset = 4;
|
|
39
43
|
this.longPress = new LongPress(this, {
|
|
40
44
|
onLongPress: () => {
|
|
@@ -68,7 +72,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
68
72
|
});
|
|
69
73
|
this.aria = new AriaForwardController(this, {
|
|
70
74
|
label: true,
|
|
71
|
-
target: () => this.
|
|
75
|
+
target: () => this.menuBox,
|
|
72
76
|
});
|
|
73
77
|
this.panel = new PopoverController(this, {
|
|
74
78
|
anchor: () => this.context && this.point
|
|
@@ -89,6 +93,11 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
89
93
|
surface: () => this.partNamed('surface'),
|
|
90
94
|
});
|
|
91
95
|
this.settledOnce = false;
|
|
96
|
+
this.filterPage = null;
|
|
97
|
+
this.handleFilterInput = (event) => {
|
|
98
|
+
event.stopPropagation();
|
|
99
|
+
this.filterBy(event.target.value);
|
|
100
|
+
};
|
|
92
101
|
this.handleContextMenu = (event) => {
|
|
93
102
|
if (!this.context) {
|
|
94
103
|
return;
|
|
@@ -129,14 +138,9 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
129
138
|
};
|
|
130
139
|
this.handleFocusIn = (event) => {
|
|
131
140
|
const item = this.items.find((row) => row.contains(event.target));
|
|
132
|
-
if (
|
|
133
|
-
|
|
141
|
+
if (item) {
|
|
142
|
+
this.revealRow(item);
|
|
134
143
|
}
|
|
135
|
-
const holder = this.flies ? this.holderOf(item) : null;
|
|
136
|
-
const surface = holder
|
|
137
|
-
? holder.shadowRoot?.querySelector('[part="surface"]')
|
|
138
|
-
: this.partNamed('surface');
|
|
139
|
-
scrollItemIntoView(item, { padding: 4, ...(surface ? { scroller: surface } : {}) });
|
|
140
144
|
};
|
|
141
145
|
this.handleKeydown = (event) => {
|
|
142
146
|
if (!this.open) {
|
|
@@ -146,9 +150,23 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
146
150
|
const items = this.items;
|
|
147
151
|
const at = items.findIndex((item) => item.active);
|
|
148
152
|
const along = inlineStep(this, event.key);
|
|
153
|
+
const typing = this.filterable && event.composedPath()[0] === this.field;
|
|
154
|
+
const step = event.key === 'ArrowDown' ? 1 : event.key === 'ArrowUp' ? -1 : 0;
|
|
149
155
|
if (event.key === 'Escape') {
|
|
150
156
|
event.stopPropagation();
|
|
151
|
-
this.
|
|
157
|
+
if (!this.filterable || this.filter === '') {
|
|
158
|
+
this.leave();
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
this.filterBy('');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else if (typing && step !== 0) {
|
|
165
|
+
const count = items.length;
|
|
166
|
+
this.reach(items[at < 0 ? (step > 0 ? 0 : count - 1) : (at + step + count) % count]);
|
|
167
|
+
}
|
|
168
|
+
else if (typing && event.key === 'Enter') {
|
|
169
|
+
items[at]?.renderRoot.querySelector('[part="item"]')?.click();
|
|
152
170
|
}
|
|
153
171
|
else if (along === 1 && items[at]?.hasSubmenu) {
|
|
154
172
|
this.enter(items[at]);
|
|
@@ -188,6 +206,14 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
188
206
|
this.longPress.down(event);
|
|
189
207
|
}
|
|
190
208
|
};
|
|
209
|
+
this.handleMouseDown = (event) => {
|
|
210
|
+
if (this.filterable &&
|
|
211
|
+
this.open &&
|
|
212
|
+
event.composedPath()[0] !== this.field &&
|
|
213
|
+
!this.fromTrigger(event)) {
|
|
214
|
+
event.preventDefault();
|
|
215
|
+
}
|
|
216
|
+
};
|
|
191
217
|
this.handlePointerMove = (event) => {
|
|
192
218
|
this.longPress.move(event);
|
|
193
219
|
};
|
|
@@ -209,7 +235,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
209
235
|
}
|
|
210
236
|
};
|
|
211
237
|
this.handleHover = (event) => {
|
|
212
|
-
if (!this.flies || !this.open || event.pointerType !== 'mouse') {
|
|
238
|
+
if (!(this.flies || this.filterable) || !this.open || event.pointerType !== 'mouse') {
|
|
213
239
|
return;
|
|
214
240
|
}
|
|
215
241
|
const row = event
|
|
@@ -221,7 +247,10 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
221
247
|
this.hovered = row;
|
|
222
248
|
this.grace = undefined;
|
|
223
249
|
if (!row.disabled) {
|
|
224
|
-
|
|
250
|
+
this.reach(row);
|
|
251
|
+
}
|
|
252
|
+
if (!this.flies) {
|
|
253
|
+
return;
|
|
225
254
|
}
|
|
226
255
|
clearTimeout(this.hoverTimer);
|
|
227
256
|
this.hoverTimer = setTimeout(() => {
|
|
@@ -334,7 +363,53 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
334
363
|
}
|
|
335
364
|
get items() {
|
|
336
365
|
const rows = this.flies ? this.rowsOf(this.keyPage) : this.drilldown.rows;
|
|
337
|
-
return rows.filter((row) => !row.disabled);
|
|
366
|
+
return rows.filter((row) => !row.disabled && this.kept(row));
|
|
367
|
+
}
|
|
368
|
+
get menuBox() {
|
|
369
|
+
return this.filterable
|
|
370
|
+
? (this.renderRoot?.querySelector('#items') ?? null)
|
|
371
|
+
: this.partNamed('surface');
|
|
372
|
+
}
|
|
373
|
+
get field() {
|
|
374
|
+
return this.partNamed('search-field');
|
|
375
|
+
}
|
|
376
|
+
get filtering() {
|
|
377
|
+
return this.filterable && this.filter.trim() !== '' && this.filter !== this.refused;
|
|
378
|
+
}
|
|
379
|
+
kept(row) {
|
|
380
|
+
return (!this.filtering ||
|
|
381
|
+
this.holderOf(row) !== this.filterPage ||
|
|
382
|
+
filterKeeps(slottedWords(row, ['']), this.filter.trim(), this.filterMatch));
|
|
383
|
+
}
|
|
384
|
+
requestFilter(value) {
|
|
385
|
+
if (value === this.filter) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
this.filterPage = this.flies ? this.keyPage : this.drilldown.page;
|
|
389
|
+
this.refused = this.dispatchEvent(new HelixFilterEvent({ value })) ? undefined : value;
|
|
390
|
+
this.filter = value;
|
|
391
|
+
}
|
|
392
|
+
filterBy(value) {
|
|
393
|
+
this.requestFilter(value);
|
|
394
|
+
void this.updateComplete.then(() => this.reach(this.items[0]));
|
|
395
|
+
}
|
|
396
|
+
reach(row) {
|
|
397
|
+
if (!this.filterable) {
|
|
398
|
+
row?.focus();
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (this.highlighted && this.highlighted !== row) {
|
|
402
|
+
this.highlighted.highlighted = false;
|
|
403
|
+
}
|
|
404
|
+
this.highlighted = row;
|
|
405
|
+
const field = this.field;
|
|
406
|
+
if (field && 'ariaActiveDescendantElement' in field) {
|
|
407
|
+
field.ariaActiveDescendantElement = row ?? null;
|
|
408
|
+
}
|
|
409
|
+
if (row) {
|
|
410
|
+
row.highlighted = true;
|
|
411
|
+
this.revealRow(row);
|
|
412
|
+
}
|
|
338
413
|
}
|
|
339
414
|
show() {
|
|
340
415
|
this.openAt('first');
|
|
@@ -345,7 +420,8 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
345
420
|
}
|
|
346
421
|
this.updateComplete.then(() => {
|
|
347
422
|
const items = this.items;
|
|
348
|
-
|
|
423
|
+
this.field?.focus();
|
|
424
|
+
this.reach(end === 'first' ? items[0] : items[items.length - 1]);
|
|
349
425
|
});
|
|
350
426
|
}
|
|
351
427
|
hide() {
|
|
@@ -355,6 +431,8 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
355
431
|
this.panel.hide();
|
|
356
432
|
this.drilldown.reset();
|
|
357
433
|
this.stopHover();
|
|
434
|
+
this.requestFilter('');
|
|
435
|
+
this.reach(undefined);
|
|
358
436
|
if (deepContains(this, deepActiveElement())) {
|
|
359
437
|
;
|
|
360
438
|
(this.context ? (this.returnTo ?? this.trigger) : this.trigger).focus();
|
|
@@ -377,18 +455,20 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
377
455
|
if (!this.drilldown.enter(item)) {
|
|
378
456
|
return;
|
|
379
457
|
}
|
|
458
|
+
this.requestFilter('');
|
|
380
459
|
void this.updateComplete.then(async () => {
|
|
381
460
|
if (!this.flies) {
|
|
382
|
-
this.items[0]
|
|
461
|
+
this.reach(this.items[0]);
|
|
383
462
|
return;
|
|
384
463
|
}
|
|
385
464
|
await item.updateComplete;
|
|
386
465
|
const first = this.rowsOf(item).find((row) => !row.disabled);
|
|
387
466
|
await first?.updateComplete;
|
|
388
|
-
first
|
|
467
|
+
this.reach(first);
|
|
389
468
|
});
|
|
390
469
|
}
|
|
391
470
|
leave() {
|
|
471
|
+
this.requestFilter('');
|
|
392
472
|
if (this.flies) {
|
|
393
473
|
const holder = this.keyPage;
|
|
394
474
|
if (!holder) {
|
|
@@ -396,7 +476,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
396
476
|
return;
|
|
397
477
|
}
|
|
398
478
|
this.openPage(this.holderOf(holder));
|
|
399
|
-
this.updateComplete.then(() =>
|
|
479
|
+
this.updateComplete.then(() => this.reach(holder));
|
|
400
480
|
return;
|
|
401
481
|
}
|
|
402
482
|
if (this.drilldown.atRoot) {
|
|
@@ -405,7 +485,14 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
405
485
|
}
|
|
406
486
|
const leaving = this.drilldown.page;
|
|
407
487
|
this.drilldown.back();
|
|
408
|
-
this.updateComplete.then(() => leaving
|
|
488
|
+
this.updateComplete.then(() => this.reach(leaving ?? undefined));
|
|
489
|
+
}
|
|
490
|
+
revealRow(item) {
|
|
491
|
+
const holder = this.flies ? this.holderOf(item) : null;
|
|
492
|
+
const surface = holder
|
|
493
|
+
? holder.shadowRoot?.querySelector('[part="surface"]')
|
|
494
|
+
: this.partNamed('surface');
|
|
495
|
+
scrollItemIntoView(item, { padding: 4, ...(surface ? { scroller: surface } : {}) });
|
|
409
496
|
}
|
|
410
497
|
handleClosedKeydown(event) {
|
|
411
498
|
if (this.context && event.shiftKey && event.key === 'F10' && this.fromTrigger(event)) {
|
|
@@ -462,6 +549,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
462
549
|
this.addEventListener('focusin', this.handleFocusIn);
|
|
463
550
|
this.addEventListener('contextmenu', this.handleContextMenu);
|
|
464
551
|
this.addEventListener('pointerdown', this.handlePointerDown);
|
|
552
|
+
this.addEventListener('mousedown', this.handleMouseDown);
|
|
465
553
|
this.addEventListener('pointermove', this.handlePointerMove);
|
|
466
554
|
this.addEventListener('pointerup', this.handlePointerUp);
|
|
467
555
|
this.addEventListener('pointercancel', this.handlePointerCancel);
|
|
@@ -484,6 +572,10 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
484
572
|
this.removeEventListener('focusin', this.handleFocusIn);
|
|
485
573
|
this.removeEventListener('contextmenu', this.handleContextMenu);
|
|
486
574
|
this.removeEventListener('pointerdown', this.handlePointerDown);
|
|
575
|
+
this.removeEventListener('mousedown', this.handleMouseDown);
|
|
576
|
+
if (this.filterable) {
|
|
577
|
+
markMatches(this, [], '');
|
|
578
|
+
}
|
|
487
579
|
this.removeEventListener('pointermove', this.handlePointerMove);
|
|
488
580
|
this.removeEventListener('pointerup', this.handlePointerUp);
|
|
489
581
|
this.removeEventListener('pointercancel', this.handlePointerCancel);
|
|
@@ -505,7 +597,9 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
505
597
|
const top = this.flies || this.drilldown.atRoot;
|
|
506
598
|
for (const child of this.children) {
|
|
507
599
|
if (isSection(child)) {
|
|
508
|
-
child.onPage =
|
|
600
|
+
child.onPage =
|
|
601
|
+
top &&
|
|
602
|
+
(!this.filtering || [...child.children].some((row) => isItem(row) && this.kept(row)));
|
|
509
603
|
for (const row of child.children) {
|
|
510
604
|
if (row.isMenuItem === true) {
|
|
511
605
|
this.mark(row, top);
|
|
@@ -522,9 +616,18 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
522
616
|
this.toggleState('drilled', !this.flies && !this.drilldown.atRoot);
|
|
523
617
|
this.markColumns();
|
|
524
618
|
this.nameSurface();
|
|
619
|
+
if (this.filterable) {
|
|
620
|
+
markMatches(this, this.rowsOf(this.filterPage).filter((row) => this.kept(row)), this.filtering ? this.filter.trim() : '');
|
|
621
|
+
const items = this.items;
|
|
622
|
+
if (changed.has('filter') &&
|
|
623
|
+
this.open &&
|
|
624
|
+
!items.includes(this.highlighted)) {
|
|
625
|
+
this.reach(items[0]);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
525
628
|
}
|
|
526
629
|
nameSurface() {
|
|
527
|
-
const surface = this.
|
|
630
|
+
const surface = this.menuBox;
|
|
528
631
|
if (!surface || !('ariaLabelledByElements' in surface)) {
|
|
529
632
|
return;
|
|
530
633
|
}
|
|
@@ -544,7 +647,8 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
544
647
|
}
|
|
545
648
|
mark(item, onPage) {
|
|
546
649
|
const page = this.drilldown.page;
|
|
547
|
-
item.onPage = onPage;
|
|
650
|
+
item.onPage = onPage && this.kept(item);
|
|
651
|
+
item.virtual = this.filterable;
|
|
548
652
|
const holds = page !== null && item.contains(page);
|
|
549
653
|
item.flyout = this.flies;
|
|
550
654
|
item.expanded = this.flies && holds;
|
|
@@ -553,7 +657,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
553
657
|
for (const child of item.children) {
|
|
554
658
|
const nested = child;
|
|
555
659
|
if (nested.isMenuItem === true && child.slot === 'submenu') {
|
|
556
|
-
this.mark(nested, onPage && holds);
|
|
660
|
+
this.mark(nested, item.onPage && holds);
|
|
557
661
|
}
|
|
558
662
|
}
|
|
559
663
|
return;
|
|
@@ -568,6 +672,32 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
568
672
|
}
|
|
569
673
|
render() {
|
|
570
674
|
const point = this.context && this.point;
|
|
675
|
+
const filtering = this.filtering;
|
|
676
|
+
const none = filtering && !this.rowsOf(this.filterPage).some((row) => this.kept(row));
|
|
677
|
+
this.announced = none ? this.noResultsText : '';
|
|
678
|
+
const rows = html `${this.flies || this.drilldown.atRoot
|
|
679
|
+
? nothing
|
|
680
|
+
: html `<div part="header">
|
|
681
|
+
<button
|
|
682
|
+
part="back"
|
|
683
|
+
type="button"
|
|
684
|
+
role="menuitem"
|
|
685
|
+
tabindex="-1"
|
|
686
|
+
aria-label=${this.backLabel}
|
|
687
|
+
@click=${() => this.leave()}
|
|
688
|
+
>
|
|
689
|
+
<svg part="back-mark" data-mirror fill="currentColor" viewBox=${caretLeft.viewBox} aria-hidden="true">
|
|
690
|
+
${unsafeSVG(caretLeft.content)}
|
|
691
|
+
</svg>
|
|
692
|
+
</button>
|
|
693
|
+
<span part="title">${this.drilldown.label}</span>
|
|
694
|
+
</div>`}
|
|
695
|
+
<slot @slotchange=${(event) => this.countRows(event)}></slot>
|
|
696
|
+
${none
|
|
697
|
+
? html `<p part="empty"><slot name="no-results">${this.noResultsText}</slot></p>`
|
|
698
|
+
: this.renderStatus(this.hasRows)}`;
|
|
699
|
+
const busy = this.loading ? 'true' : nothing;
|
|
700
|
+
const label = this.label || nothing;
|
|
571
701
|
return html `<slot name="trigger" @click=${this.handleTriggerClick}></slot>${point
|
|
572
702
|
? html `<span
|
|
573
703
|
id="point"
|
|
@@ -578,31 +708,27 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
|
|
|
578
708
|
<div
|
|
579
709
|
part="surface"
|
|
580
710
|
popover="manual"
|
|
581
|
-
role
|
|
582
|
-
aria-busy=${this.
|
|
583
|
-
aria-label=${this.
|
|
711
|
+
role=${this.filterable ? nothing : 'menu'}
|
|
712
|
+
aria-busy=${this.filterable ? nothing : busy}
|
|
713
|
+
aria-label=${this.filterable ? nothing : label}
|
|
584
714
|
data-drilled=${this.flies || this.drilldown.atRoot ? nothing : ''}
|
|
715
|
+
data-filtered=${filtering ? '' : nothing}
|
|
585
716
|
?hidden=${!this.open}
|
|
586
717
|
>
|
|
587
|
-
${this.
|
|
588
|
-
?
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
aria-label=${this.
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
</button>
|
|
602
|
-
<span part="title">${this.drilldown.label}</span>
|
|
603
|
-
</div>`}
|
|
604
|
-
<slot @slotchange=${(event) => this.countRows(event)}></slot>
|
|
605
|
-
${this.renderStatus(this.hasRows)}
|
|
718
|
+
${this.filterable
|
|
719
|
+
? html `<input
|
|
720
|
+
part="search-field"
|
|
721
|
+
type="text"
|
|
722
|
+
autocomplete="off"
|
|
723
|
+
spellcheck="false"
|
|
724
|
+
.value=${this.filter}
|
|
725
|
+
placeholder=${this.filterLabel}
|
|
726
|
+
aria-label=${this.filterLabel}
|
|
727
|
+
aria-autocomplete="list"
|
|
728
|
+
aria-controls="items"
|
|
729
|
+
@input=${this.handleFilterInput}
|
|
730
|
+
/><div id="items" role="menu" aria-busy=${busy} aria-label=${label}>${rows}</div>`
|
|
731
|
+
: rows}
|
|
606
732
|
</div>${this.renderLive(this.hasRows)}`;
|
|
607
733
|
}
|
|
608
734
|
};
|
|
@@ -616,6 +742,19 @@ __decorate([
|
|
|
616
742
|
__decorate([
|
|
617
743
|
property({ reflect: true, type: Boolean })
|
|
618
744
|
], HelixHeadlessMenu.prototype, "context", void 0);
|
|
745
|
+
__decorate([
|
|
746
|
+
property()
|
|
747
|
+
], HelixHeadlessMenu.prototype, "filter", void 0);
|
|
748
|
+
__decorate([
|
|
749
|
+
property({ type: Boolean })
|
|
750
|
+
], HelixHeadlessMenu.prototype, "filterable", void 0);
|
|
751
|
+
__decorate([
|
|
752
|
+
words,
|
|
753
|
+
property({ attribute: 'filter-label' })
|
|
754
|
+
], HelixHeadlessMenu.prototype, "filterLabel", void 0);
|
|
755
|
+
__decorate([
|
|
756
|
+
property({ attribute: false })
|
|
757
|
+
], HelixHeadlessMenu.prototype, "filterMatch", void 0);
|
|
619
758
|
__decorate([
|
|
620
759
|
property()
|
|
621
760
|
], HelixHeadlessMenu.prototype, "label", void 0);
|
|
@@ -623,6 +762,10 @@ __decorate([
|
|
|
623
762
|
words,
|
|
624
763
|
property({ attribute: 'long-press-label' })
|
|
625
764
|
], HelixHeadlessMenu.prototype, "longPressLabel", void 0);
|
|
765
|
+
__decorate([
|
|
766
|
+
words,
|
|
767
|
+
property({ attribute: 'no-results-text' })
|
|
768
|
+
], HelixHeadlessMenu.prototype, "noResultsText", void 0);
|
|
626
769
|
__decorate([
|
|
627
770
|
property({ type: Number })
|
|
628
771
|
], HelixHeadlessMenu.prototype, "offset", void 0);
|
|
@@ -390,6 +390,11 @@ export declare class HelixHeadlessObjectInspector extends HelixElement {
|
|
|
390
390
|
private counted;
|
|
391
391
|
/** Says how many entries the filter matched, and nothing for an empty one. */
|
|
392
392
|
private tell;
|
|
393
|
+
/**
|
|
394
|
+
* Moves focus to the row the keys move from, or to the tree while that row
|
|
395
|
+
* is scrolled out of the window, as Tab reaches them.
|
|
396
|
+
*/
|
|
397
|
+
focus(options?: FocusOptions): void;
|
|
393
398
|
protected firstUpdated(changed: PropertyValues): void;
|
|
394
399
|
protected updated(changed: PropertyValues): void;
|
|
395
400
|
/**
|
|
@@ -268,6 +268,11 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
|
|
|
268
268
|
this.status.say(this.counted('result', this.found.size));
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
|
+
focus(options) {
|
|
272
|
+
this.renderRoot
|
|
273
|
+
?.querySelector('#tree[tabindex="0"], #tree [tabindex="0"]')
|
|
274
|
+
?.focus(options);
|
|
275
|
+
}
|
|
271
276
|
firstUpdated(changed) {
|
|
272
277
|
super.firstUpdated(changed);
|
|
273
278
|
this.finishHydration();
|
|
@@ -340,7 +340,7 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
340
340
|
}
|
|
341
341
|
renderIndicator() {
|
|
342
342
|
return html `<slot name="indicator">
|
|
343
|
-
<svg part="indicator" fill="currentColor" viewBox=${caretDown.viewBox} aria-hidden="true">
|
|
343
|
+
<svg part="indicator" fill="currentColor" width="1em" height="1em" viewBox=${caretDown.viewBox} aria-hidden="true">
|
|
344
344
|
${unsafeSVG(caretDown.content)}
|
|
345
345
|
</svg>
|
|
346
346
|
</slot>`;
|
|
@@ -158,6 +158,13 @@ export declare class HelixHeadlessSteps extends HelixHeadlessSteps_base {
|
|
|
158
158
|
* calls to `next()` in one task then move two stages.
|
|
159
159
|
*/
|
|
160
160
|
private get at();
|
|
161
|
+
/**
|
|
162
|
+
* Requests a new current stage, and returns whether it moved.
|
|
163
|
+
*
|
|
164
|
+
* Where only a step's `current` says where the reader is, `active` is first
|
|
165
|
+
* written with that step, so `helix-activate` names it as `previous`.
|
|
166
|
+
*/
|
|
167
|
+
requestActive(next: string, cause?: string): boolean;
|
|
161
168
|
/**
|
|
162
169
|
* Moves to the next stage that can be gone to, as Ark UI's `goToNextStep`
|
|
163
170
|
* does. Returns whether it moved: not past the last stage, not into a stage
|
|
@@ -65,6 +65,13 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
65
65
|
const item = this.resolveActive(steps);
|
|
66
66
|
return item ? steps.indexOf(item) : -1;
|
|
67
67
|
}
|
|
68
|
+
requestActive(next, cause) {
|
|
69
|
+
const here = this.active === '' ? this.steps[this.at]?.value : undefined;
|
|
70
|
+
if (here) {
|
|
71
|
+
this.active = here;
|
|
72
|
+
}
|
|
73
|
+
return super.requestActive(next, cause);
|
|
74
|
+
}
|
|
68
75
|
next() {
|
|
69
76
|
return this.step(1);
|
|
70
77
|
}
|
|
@@ -51,6 +51,16 @@ export interface TableGridOptions {
|
|
|
51
51
|
disclose?: (row: number, forward: boolean) => boolean | number;
|
|
52
52
|
/** Whether the table is a grid now. */
|
|
53
53
|
on: () => boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The rows drawn now, in a table that draws only some of them, which are
|
|
56
|
+
* the only rows whose cells are marked. Every row where it is left out.
|
|
57
|
+
*/
|
|
58
|
+
drawn?: () => ReadonlySet<Element> | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Brings drawn row `row` into view before a key focuses it, in a table that
|
|
61
|
+
* draws only the rows in view, so the row is drawn by then.
|
|
62
|
+
*/
|
|
63
|
+
reveal?: (row: number) => void;
|
|
54
64
|
}
|
|
55
65
|
/**
|
|
56
66
|
* The keyboard of a `grid` table: one tab stop among its cells, the arrow
|
|
@@ -247,6 +247,9 @@ export class TableGridController {
|
|
|
247
247
|
this.stopRow = place.row === -1 ? 'head' : this.options.rows()[place.row];
|
|
248
248
|
this.stopColumn = place.column;
|
|
249
249
|
this.focusWanted = focus;
|
|
250
|
+
if (focus && place.row >= 0) {
|
|
251
|
+
this.options.reveal?.(place.row);
|
|
252
|
+
}
|
|
250
253
|
this.host.requestUpdate();
|
|
251
254
|
}
|
|
252
255
|
mark() {
|
|
@@ -259,7 +262,11 @@ export class TableGridController {
|
|
|
259
262
|
column.gridCell = on;
|
|
260
263
|
column.tabStop = on && at.row === -1 && at.column === index + this.offset;
|
|
261
264
|
}
|
|
265
|
+
const drawn = this.options.drawn?.();
|
|
262
266
|
for (const [index, line] of this.options.rows().entries()) {
|
|
267
|
+
if (drawn && !drawn.has(line)) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
263
270
|
const row = line;
|
|
264
271
|
if (row.isTableRow) {
|
|
265
272
|
const here = on && at.row === index;
|
|
@@ -36,6 +36,7 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
36
36
|
* The `heading` slot names the table: slot a heading or a line of text into
|
|
37
37
|
* it, and the table is named by its words. An `aria-label` or `aria-labelledby` on the
|
|
38
38
|
* host wins over it.
|
|
39
|
+
* A table with no name is reported once in a development build.
|
|
39
40
|
*
|
|
40
41
|
* **Selecting.** `selectable` adds a column of checkboxes.
|
|
41
42
|
*
|
|
@@ -121,9 +122,17 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
121
122
|
* | A letter or a word | Moves to the next row whose first cell starts with what you typed. |
|
|
122
123
|
* | Tab | Leaves the table. |
|
|
123
124
|
*
|
|
125
|
+
* **Long tables.** `windowed` draws only the rows in view, as Data Table's
|
|
126
|
+
* does, and the box of the table scrolls under headings that stick. The
|
|
127
|
+
* table still says how many rows it has with `aria-rowcount`, and each row
|
|
128
|
+
* its place with `aria-rowindex`. The keys reach every row, drawn or not, and
|
|
129
|
+
* the row holding focus stays drawn when it scrolls away. Nested rows,
|
|
130
|
+
* sorting, and selecting work as they do in a table that draws every row.
|
|
131
|
+
*
|
|
124
132
|
* **Loading more.** `load-more` sends `helix-load-more` once the end of the
|
|
125
133
|
* table comes within a screen of view, in the box that scrolls it or the
|
|
126
|
-
* page,
|
|
134
|
+
* page, or in the table's own box when it is `windowed`, once for each
|
|
135
|
+
* number of rows: add rows and it can send it again. Set
|
|
127
136
|
* `loading` while you fetch, and the table asks for nothing until it clears.
|
|
128
137
|
*
|
|
129
138
|
* When the rows did not load, set `error-text`, such as "The orders did not
|
|
@@ -158,6 +167,8 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
158
167
|
* @event {HelixSortedEvent} helix-sorted - Fired after the sort order changes, with the new order.
|
|
159
168
|
* @event {HelixToggleEvent} helix-toggle - Fired from a row once the rows under it are shown or hidden, with whether it is open in `detail.checked`.
|
|
160
169
|
*
|
|
170
|
+
* @csspart after - The space that stands in for the rows below the window. Only while `windowed`.
|
|
171
|
+
* @csspart before - The space that stands in for the rows above the window. Only while `windowed`.
|
|
161
172
|
* @csspart checkbox - The checkbox in the heading that selects every row, in a `selectable` table.
|
|
162
173
|
* @csspart checkbox-box - The box drawn for the checkbox's input.
|
|
163
174
|
* @csspart checkbox-control - The checkbox's input.
|
|
@@ -399,11 +410,35 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
399
410
|
sortedLabel: string;
|
|
400
411
|
/** What screen readers hear when the table stops being sorted. */
|
|
401
412
|
unsortedLabel: string;
|
|
413
|
+
/**
|
|
414
|
+
* Draws only the rows in view, with empty space in place of the rest, as
|
|
415
|
+
* Data Table's `windowed` does. The table's box then scrolls, at the
|
|
416
|
+
* height `--table-block-size` gives it in the styled tier, and the
|
|
417
|
+
* headings stick to its top.
|
|
418
|
+
*
|
|
419
|
+
* Off by default. Windowing costs a scroll listener, a measurement per row
|
|
420
|
+
* drawn, and a height on the table, and a table of twenty rows should pay
|
|
421
|
+
* none of them.
|
|
422
|
+
*/
|
|
423
|
+
windowed: boolean;
|
|
402
424
|
private sortState;
|
|
403
425
|
private selectedState;
|
|
404
426
|
private expandedState;
|
|
405
427
|
/** Whether any row sits under another, which makes the table a tree. */
|
|
406
428
|
private get nests();
|
|
429
|
+
/**
|
|
430
|
+
* What the table read from its rows, kept until anything asks for an
|
|
431
|
+
* update or the turn ends.
|
|
432
|
+
*
|
|
433
|
+
* **Read once rather than on every question.** A render, the assignment
|
|
434
|
+
* and the keyboard each asked for the rows in order dozens of times, and
|
|
435
|
+
* each answer walked every row: in Chromium a windowed table of 3,000 rows
|
|
436
|
+
* took 89 milliseconds to move its window, 42 of them reading the keys.
|
|
437
|
+
*/
|
|
438
|
+
private memo;
|
|
439
|
+
private remember;
|
|
440
|
+
/** @internal Forgets what was read from the rows, then asks for an update as Lit does. */
|
|
441
|
+
requestUpdate(name?: PropertyKey, oldValue?: unknown, options?: Parameters<HelixElement['requestUpdate']>[2]): void;
|
|
407
442
|
/** The keys of the rows whose rows are shown. */
|
|
408
443
|
private get open();
|
|
409
444
|
/**
|
|
@@ -424,6 +459,37 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
424
459
|
protected readonly aria: AriaForwardController;
|
|
425
460
|
/** The keyboard of a `grid` table, in `grid-controller.ts`. */
|
|
426
461
|
private readonly gridKeys;
|
|
462
|
+
/**
|
|
463
|
+
* The slice of a long table that is drawn, under `windowed`. The rows are
|
|
464
|
+
* the page's elements, so the window is which of them the body slot is
|
|
465
|
+
* given: a row it is not given is not laid out and not in the
|
|
466
|
+
* accessibility tree.
|
|
467
|
+
*/
|
|
468
|
+
private readonly virtualize;
|
|
469
|
+
/** How many rows are drawn in order, read when the rows are assigned. */
|
|
470
|
+
private arrangedCount;
|
|
471
|
+
/** A `windowed` table has rows while it holds any, drawn or not. */
|
|
472
|
+
protected get hasRows(): boolean;
|
|
473
|
+
/** The rows the body slot was last given, under `windowed`. */
|
|
474
|
+
private drawnRows;
|
|
475
|
+
/**
|
|
476
|
+
* The rows drawn now, each by its first cell, with its place among the
|
|
477
|
+
* rows. A row is `display: contents` and has no box, and a cell is a grid
|
|
478
|
+
* item stretched to its row, as Data Table measures its rows.
|
|
479
|
+
*/
|
|
480
|
+
private measuredRows;
|
|
481
|
+
/** The row outside the window that stays drawn: the grid's stop, or the row holding focus. */
|
|
482
|
+
private keptRow;
|
|
483
|
+
/**
|
|
484
|
+
* The rows to draw under `windowed`: the window, and the row kept for
|
|
485
|
+
* focus beside it on its side.
|
|
486
|
+
*/
|
|
487
|
+
private windowOf;
|
|
488
|
+
/**
|
|
489
|
+
* Scrolls drawn row `row` into view below the headings, and moves the
|
|
490
|
+
* window to it, before a key focuses it.
|
|
491
|
+
*/
|
|
492
|
+
private revealRow;
|
|
427
493
|
/**
|
|
428
494
|
* Moving rows by their grips, in a `reorderable` table. The rows are the
|
|
429
495
|
* page's, so a drop asks the page to move them, and the grips do nothing
|
|
@@ -459,6 +525,8 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
459
525
|
* which is what a framework holding those rows needs.
|
|
460
526
|
*/
|
|
461
527
|
private get arrangedRows();
|
|
528
|
+
/** The rows in the order they are drawn, worked out again. */
|
|
529
|
+
private arrange;
|
|
462
530
|
/**
|
|
463
531
|
* The rows of a tree in the order they are drawn: each row followed by the
|
|
464
532
|
* rows under it while it is open, at every depth, keeping the order `rows`
|
|
@@ -714,6 +782,8 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
714
782
|
* are asked for before a reader reaches the last. List does the same.
|
|
715
783
|
*/
|
|
716
784
|
private watchEnd;
|
|
785
|
+
/** Asks for more once a reader of a `windowed` table is within a screen of its end. */
|
|
786
|
+
private readonly nearEnd;
|
|
717
787
|
/** Whether the end of the table was in reach when last measured. */
|
|
718
788
|
private endInView;
|
|
719
789
|
/** Sends `helix-load-more`, once for each number of rows, and never while loading. */
|