@fusion.dev/helix-headless 0.142.1 → 0.143.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 +16710 -707
- package/dist/accordion/item/accordion-item.d.ts +38 -2
- package/dist/accordion/item/accordion-item.js +38 -6
- package/dist/accordion/root/accordion-root.d.ts +23 -1
- package/dist/accordion/root/accordion-root.js +39 -3
- package/dist/announcement/root/announcement-root.d.ts +11 -7
- package/dist/announcement/root/announcement-root.js +1 -1
- package/dist/app-header/root/app-header-root.d.ts +21 -0
- package/dist/app-header/root/app-header-root.js +31 -5
- package/dist/area-chart/root/area-chart-root.js +16 -5
- package/dist/avatar/group/avatar-group.d.ts +24 -3
- package/dist/avatar/group/avatar-group.js +34 -2
- package/dist/avatar/root/avatar-root.d.ts +3 -1
- package/dist/avatar/root/avatar-root.js +8 -1
- package/dist/badge/root/badge-root.d.ts +8 -0
- package/dist/bar-chart/root/bar-chart-root.d.ts +19 -3
- package/dist/bar-chart/root/bar-chart-root.js +43 -13
- package/dist/blockquote/root/blockquote-root.d.ts +12 -7
- package/dist/blockquote/root/blockquote-root.js +6 -4
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
- package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
- package/dist/button/root/button-root.d.ts +37 -0
- package/dist/button/root/button-root.js +44 -0
- package/dist/callout/root/callout-root.d.ts +14 -5
- package/dist/callout/root/callout-root.js +11 -3
- package/dist/canvas/root/canvas-root.d.ts +1 -0
- package/dist/canvas/root/canvas-root.js +10 -1
- package/dist/carousel/root/carousel-root.d.ts +47 -5
- package/dist/carousel/root/carousel-root.js +53 -21
- package/dist/chat/composer/chat-composer.d.ts +13 -11
- package/dist/chat/composer/chat-composer.js +6 -12
- package/dist/chat/message/chat-message.js +8 -1
- package/dist/chat/root/chat-root.d.ts +1 -0
- package/dist/chat/root/chat-root.js +2 -1
- package/dist/checkbox/root/checkbox-root.d.ts +7 -0
- package/dist/checkbox/root/checkbox-root.js +7 -3
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +65 -32
- package/dist/checkbox-group/root/checkbox-group-root.js +85 -37
- package/dist/code/root/code-root.d.ts +1 -1
- package/dist/code-block/root/code-block-root.d.ts +19 -0
- package/dist/code-block/root/code-block-root.js +21 -1
- package/dist/color-input/root/color-input-root.d.ts +26 -1
- package/dist/color-input/root/color-input-root.js +32 -3
- package/dist/color-picker/root/color-picker-root.d.ts +37 -7
- package/dist/color-picker/root/color-picker-root.js +83 -6
- package/dist/combobox/root/combobox-root.d.ts +90 -2
- package/dist/combobox/root/combobox-root.js +160 -19
- package/dist/command/root/command-root.d.ts +17 -6
- package/dist/command/root/command-root.js +12 -1
- package/dist/copy-button/root/copy-button-root.d.ts +21 -6
- package/dist/copy-button/root/copy-button-root.js +11 -1
- package/dist/data-table/root/data-table-root.d.ts +164 -10
- package/dist/data-table/root/data-table-root.js +488 -57
- package/dist/date-input/root/date-input-root.d.ts +53 -8
- package/dist/date-input/root/date-input-root.js +70 -18
- package/dist/date-picker/root/date-picker-root.d.ts +84 -17
- package/dist/date-picker/root/date-picker-root.js +125 -20
- package/dist/dialog/root/dialog-root.d.ts +38 -10
- package/dist/dialog/root/dialog-root.js +55 -2
- package/dist/donut-chart/root/donut-chart-root.d.ts +39 -15
- package/dist/donut-chart/root/donut-chart-root.js +30 -10
- package/dist/drawer/root/drawer-root.d.ts +32 -7
- package/dist/drawer/root/drawer-root.js +50 -5
- package/dist/drilldown/item/drilldown-item.d.ts +16 -13
- package/dist/drilldown/item/drilldown-item.js +6 -2
- package/dist/drilldown/root/drilldown-root.d.ts +21 -5
- package/dist/drilldown/root/drilldown-root.js +26 -10
- package/dist/element-events.generated.js +35 -9
- package/dist/file-input/root/file-input-root.d.ts +18 -1
- package/dist/file-input/root/file-input-root.js +34 -8
- package/dist/filter-input/menu/filter-menu.d.ts +9 -0
- package/dist/filter-input/menu/filter-menu.js +8 -0
- package/dist/filter-input/root/filter-input-root.d.ts +5 -2
- package/dist/filter-input/root/filter-input-root.js +22 -7
- package/dist/format-date/root/format-date-root.d.ts +11 -3
- package/dist/format-date/root/format-date-root.js +26 -5
- package/dist/graph/root/graph-root.d.ts +24 -3
- package/dist/graph/root/graph-root.js +66 -12
- package/dist/heatmap/root/buckets.d.ts +2 -1
- package/dist/heatmap/root/buckets.js +5 -1
- package/dist/heatmap/root/heatmap-root.d.ts +27 -6
- package/dist/heatmap/root/heatmap-root.js +23 -6
- package/dist/icon/root/icon-root.d.ts +24 -3
- package/dist/icon/root/icon-root.js +34 -1
- package/dist/icon-button/root/icon-button-root.d.ts +1 -1
- package/dist/image/root/image-root.d.ts +9 -2
- package/dist/image/root/image-root.js +8 -2
- package/dist/input/root/input-root.d.ts +51 -9
- package/dist/input/root/input-root.js +46 -5
- package/dist/kbd/root/kbd-root.d.ts +12 -4
- package/dist/kbd/root/kbd-root.js +10 -11
- package/dist/legend/root/legend-root.d.ts +7 -0
- package/dist/legend/root/legend-root.js +7 -2
- package/dist/lightbox/item/lightbox-item.d.ts +2 -1
- package/dist/lightbox/root/lightbox-root.d.ts +16 -5
- package/dist/lightbox/root/lightbox-root.js +24 -3
- package/dist/line-chart/root/line-chart-root.d.ts +19 -2
- package/dist/line-chart/root/line-chart-root.js +40 -13
- package/dist/list/item/list-item.d.ts +11 -5
- package/dist/list/item/list-item.js +5 -6
- package/dist/listbox/root/listbox-root.d.ts +111 -2
- package/dist/listbox/root/listbox-root.js +144 -16
- package/dist/map/root/map-root.d.ts +19 -4
- package/dist/map/root/map-root.js +36 -7
- package/dist/megamenu/root/megamenu-root.d.ts +10 -1
- package/dist/megamenu/root/megamenu-root.js +10 -1
- package/dist/menu/item/menu-item.d.ts +41 -12
- package/dist/menu/item/menu-item.js +46 -3
- package/dist/menu/root/menu-root.d.ts +32 -2
- package/dist/menu/root/menu-root.js +48 -8
- package/dist/meter/root/meter-root.d.ts +6 -0
- package/dist/meter/root/meter-root.js +18 -2
- package/dist/navigation/root/navigation-root.d.ts +10 -2
- package/dist/navigation/root/navigation-root.js +13 -2
- package/dist/notifications/group/notification-group.d.ts +1 -0
- package/dist/notifications/notification/notification.d.ts +12 -0
- package/dist/notifications/notification/notification.js +31 -2
- package/dist/notifications/root/notifications-root.d.ts +7 -0
- package/dist/notifications/root/notifications-root.js +17 -2
- package/dist/number-input/root/number-input-root.d.ts +62 -7
- package/dist/number-input/root/number-input-root.js +68 -16
- package/dist/object-inspector/root/object-inspector-root.d.ts +48 -5
- package/dist/object-inspector/root/object-inspector-root.js +91 -11
- package/dist/option/root/option-root.d.ts +4 -1
- package/dist/option/root/option-root.js +1 -0
- package/dist/outline/root/outline-root.d.ts +13 -2
- package/dist/outline/root/outline-root.js +4 -2
- package/dist/pagination/root/pagination-root.d.ts +19 -1
- package/dist/pagination/root/pagination-root.js +49 -9
- package/dist/pagination/size/pagination-size.d.ts +13 -3
- package/dist/pagination/size/pagination-size.js +7 -0
- package/dist/pin-input/root/pin-input-root.d.ts +16 -0
- package/dist/pin-input/root/pin-input-root.js +19 -5
- package/dist/popover/root/popover-root.d.ts +19 -2
- package/dist/popover/root/popover-root.js +22 -1
- package/dist/progress/root/progress-root.d.ts +16 -1
- package/dist/progress/root/progress-root.js +31 -7
- package/dist/qr-code/root/qr-code-root.d.ts +19 -0
- package/dist/qr-code/root/qr-code-root.js +29 -1
- package/dist/radio/group/radio-group.d.ts +41 -0
- package/dist/radio/group/radio-group.js +50 -14
- package/dist/radio/root/radio-root.d.ts +8 -5
- package/dist/radio/root/radio-root.js +1 -2
- package/dist/rating/root/rating-root.d.ts +24 -1
- package/dist/rating/root/rating-root.js +24 -7
- package/dist/scatter-chart/root/scatter-chart-root.js +15 -7
- package/dist/select/root/select-root.d.ts +69 -3
- package/dist/select/root/select-root.js +92 -8
- package/dist/skeleton/root/skeleton-root.d.ts +14 -3
- package/dist/skeleton/root/skeleton-root.js +17 -3
- package/dist/skip-link/root/skip-link-root.d.ts +10 -0
- package/dist/skip-link/root/skip-link-root.js +15 -1
- package/dist/slider/root/slider-root.d.ts +20 -2
- package/dist/slider/root/slider-root.js +41 -3
- package/dist/sparkline/root/sparkline-root.d.ts +9 -10
- package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
- package/dist/speed-dial/item/speed-dial-item.js +7 -2
- package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
- package/dist/speed-dial/root/speed-dial-root.js +19 -5
- package/dist/splitter/root/splitter-root.d.ts +28 -6
- package/dist/splitter/root/splitter-root.js +35 -1
- package/dist/stat/root/stat-root.d.ts +7 -1
- package/dist/stat/root/stat-root.js +14 -1
- package/dist/steps/root/steps-root.js +1 -1
- package/dist/switch/root/switch-root.d.ts +2 -0
- package/dist/switch/root/switch-root.js +1 -0
- package/dist/table/cell/table-cell.d.ts +24 -4
- package/dist/table/cell/table-cell.js +21 -1
- package/dist/table/column/table-column.d.ts +32 -6
- package/dist/table/column/table-column.js +36 -6
- package/dist/table/root/table-root.d.ts +94 -3
- package/dist/table/root/table-root.js +128 -5
- package/dist/table/row/table-row.d.ts +7 -0
- package/dist/table/row/table-row.js +9 -1
- package/dist/tabs/root/tabs-root.d.ts +6 -1
- package/dist/tabs/root/tabs-root.js +6 -2
- package/dist/tag/root/tag-root.d.ts +23 -1
- package/dist/tag/root/tag-root.js +16 -2
- package/dist/textarea/root/textarea-root.d.ts +4 -0
- package/dist/textarea/root/textarea-root.js +1 -1
- package/dist/time-input/root/time-input-root.d.ts +28 -2
- package/dist/time-input/root/time-input-root.js +46 -10
- package/dist/time-picker/root/time-picker-root.d.ts +101 -18
- package/dist/time-picker/root/time-picker-root.js +115 -16
- package/dist/toast/root/toast-root.d.ts +64 -1
- package/dist/toast/root/toast-root.js +147 -2
- package/dist/toggle/group/toggle-group.d.ts +38 -1
- package/dist/toggle/group/toggle-group.js +34 -8
- package/dist/toggle/root/toggle-root.d.ts +14 -10
- package/dist/toggle/root/toggle-root.js +3 -3
- package/dist/toolbar/root/toolbar-root.d.ts +55 -15
- package/dist/toolbar/root/toolbar-root.js +99 -16
- package/dist/tooltip/root/tooltip-root.d.ts +37 -4
- package/dist/tooltip/root/tooltip-root.js +48 -0
- package/dist/tour/root/tour-root.d.ts +34 -5
- package/dist/tour/root/tour-root.js +51 -14
- package/dist/tour/stop/tour-stop.d.ts +18 -1
- package/dist/tour/stop/tour-stop.js +25 -5
- package/dist/tree/item/tree-item.d.ts +21 -5
- package/dist/tree/item/tree-item.js +14 -4
- package/dist/tree/root/tree-root.d.ts +29 -5
- package/dist/tree/root/tree-root.js +64 -8
- package/dist/trend/root/trend-root.d.ts +23 -1
- package/dist/trend/root/trend-root.js +28 -7
- package/dist/video/root/keys.d.ts +5 -0
- package/dist/video/root/keys.js +13 -1
- package/dist/video/root/video-root.d.ts +19 -0
- package/dist/video/root/video-root.js +48 -4
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
- package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
- package/dist/video-playlist/root/video-playlist-root.js +6 -3
- package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
- package/dist/virtual-list/root/virtual-list-root.js +103 -21
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +4348 -965
- package/types/vue.d.ts +972 -232
package/dist/video/root/keys.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { SKIP_SECONDS } from '@fusion.dev/helix-core';
|
|
2
|
+
const PRESSED_BY_SPACE = new Set(['a', 'button', 'select', 'summary', 'textarea']);
|
|
3
|
+
const PRESSED_ROLES = /^(button|checkbox|link|menuitem\w*|option|radio|switch|tab)$/;
|
|
2
4
|
export const takeKey = (event, video, actions) => {
|
|
3
|
-
if (event.
|
|
5
|
+
if (event.defaultPrevented) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const origin = (event.composedPath?.()[0] ?? event.target);
|
|
9
|
+
if (origin?.tagName === 'INPUT' || origin?.isContentEditable) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (event.key === ' ' &&
|
|
13
|
+
origin &&
|
|
14
|
+
(PRESSED_BY_SPACE.has(origin.localName) ||
|
|
15
|
+
PRESSED_ROLES.test(origin.getAttribute?.('role') ?? ''))) {
|
|
4
16
|
return false;
|
|
5
17
|
}
|
|
6
18
|
const step = event.shiftKey ? SKIP_SECONDS : 5;
|
|
@@ -383,6 +383,8 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
383
383
|
private silent;
|
|
384
384
|
private full;
|
|
385
385
|
private captioned;
|
|
386
|
+
/** Which of the player's own menus is open, for its trigger to say so. */
|
|
387
|
+
private menuOpen;
|
|
386
388
|
/**
|
|
387
389
|
* Which caption track is showing, or `-1` for none.
|
|
388
390
|
*
|
|
@@ -582,6 +584,15 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
582
584
|
* presses it twice expects twenty seconds rather than an accelerating jump.
|
|
583
585
|
*/
|
|
584
586
|
private skipBy;
|
|
587
|
+
/**
|
|
588
|
+
* Keeps each choice row's tick where this element puts it.
|
|
589
|
+
*
|
|
590
|
+
* A choice row checks itself after `helix-before-toggle`, and this element
|
|
591
|
+
* already sets `checked` on every row from its own state. The request is
|
|
592
|
+
* refused, so a tick moves only when the choice is applied, and stopped, so
|
|
593
|
+
* it does not reach a page that never placed the row.
|
|
594
|
+
*/
|
|
595
|
+
private readonly keepTick;
|
|
585
596
|
/** Shows one track and disables every other, or shows none at `-1`. */
|
|
586
597
|
private showCaption;
|
|
587
598
|
/** Turns captions on or off, which is what the button means with one track. */
|
|
@@ -695,6 +706,14 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
695
706
|
* component's to paper over with "Track 2".
|
|
696
707
|
*/
|
|
697
708
|
protected renderCaptions(): TemplateResult | typeof nothing;
|
|
709
|
+
/**
|
|
710
|
+
* Notes which menu opened or closed, so its trigger says so.
|
|
711
|
+
*
|
|
712
|
+
* **The trigger is this element's own button**, so the state is written onto
|
|
713
|
+
* it here: a menu cannot write onto a plain button slotted into it, and the
|
|
714
|
+
* button said neither that it opens a menu nor whether that menu was open.
|
|
715
|
+
*/
|
|
716
|
+
private heardMenu;
|
|
698
717
|
protected renderOverflowItem(one: string): TemplateResult;
|
|
699
718
|
protected renderBar(): TemplateResult;
|
|
700
719
|
protected render(): TemplateResult;
|
|
@@ -10,6 +10,7 @@ import { nothing } from 'lit';
|
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { html } from 'lit/static-html.js';
|
|
12
12
|
import '../../menu/index.js';
|
|
13
|
+
import { HelixHeadlessMenu } from '../../menu/root/menu-root.js';
|
|
13
14
|
import { barControls, menuControls, ownFile } from './extra-controls.js';
|
|
14
15
|
import { takeKey } from './keys.js';
|
|
15
16
|
const clock = (seconds) => {
|
|
@@ -75,6 +76,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
75
76
|
this.silent = false;
|
|
76
77
|
this.full = false;
|
|
77
78
|
this.captioned = false;
|
|
79
|
+
this.menuOpen = undefined;
|
|
78
80
|
this.captionIndex = -1;
|
|
79
81
|
this.captionCount = 0;
|
|
80
82
|
this.rate = 1;
|
|
@@ -161,6 +163,12 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
161
163
|
const track = captionTracks(this.video)[this.captionIndex] ?? this.video?.textTracks[0];
|
|
162
164
|
this.cue = activeCue(track);
|
|
163
165
|
};
|
|
166
|
+
this.keepTick = (event) => {
|
|
167
|
+
if (event.target === event.currentTarget) {
|
|
168
|
+
event.preventDefault();
|
|
169
|
+
event.stopPropagation();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
164
172
|
this.handlePicture = () => {
|
|
165
173
|
if (this.controls) {
|
|
166
174
|
return;
|
|
@@ -179,6 +187,9 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
179
187
|
if (!video) {
|
|
180
188
|
return;
|
|
181
189
|
}
|
|
190
|
+
if (event.composedPath().some((node) => node instanceof HelixHeadlessMenu)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
182
193
|
const taken = takeKey(event, video, {
|
|
183
194
|
fullscreen: () => void this.fullscreen.toggle(),
|
|
184
195
|
seek: (seconds) => this.seek(seconds),
|
|
@@ -521,8 +532,16 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
521
532
|
return html `<${this.tag('menu')} part="captions-menu"
|
|
522
533
|
exportparts="back: captions-menu-back, back-mark: captions-menu-back-mark, empty: captions-menu-empty,
|
|
523
534
|
header: captions-menu-header, live: captions-menu-live, loading: captions-menu-loading, surface: captions-menu-surface,
|
|
524
|
-
title: captions-menu-title" label=${this.subtitlesLabel} placement="top-end"
|
|
525
|
-
|
|
535
|
+
title: captions-menu-title" label=${this.subtitlesLabel} placement="top-end"
|
|
536
|
+
@toggle=${(event) => this.heardMenu('captions', event)}>
|
|
537
|
+
<button
|
|
538
|
+
part="captions"
|
|
539
|
+
slot="trigger"
|
|
540
|
+
type="button"
|
|
541
|
+
aria-label=${this.subtitlesLabel}
|
|
542
|
+
aria-haspopup="menu"
|
|
543
|
+
aria-expanded=${this.menuOpen === 'captions' ? 'true' : 'false'}
|
|
544
|
+
>
|
|
526
545
|
${drawIcon(closedCaptioning)}
|
|
527
546
|
</button>
|
|
528
547
|
<${this.tag('menu-item')}
|
|
@@ -532,6 +551,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
532
551
|
checkable
|
|
533
552
|
exclusive
|
|
534
553
|
?checked=${this.captionIndex < 0}
|
|
554
|
+
@helix-before-toggle=${this.keepTick}
|
|
535
555
|
@helix-select=${() => this.showCaption(-1)}
|
|
536
556
|
>
|
|
537
557
|
${this.captionsOffLabel}
|
|
@@ -542,12 +562,25 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
542
562
|
checkable
|
|
543
563
|
exclusive
|
|
544
564
|
?checked=${this.captionIndex === at}
|
|
565
|
+
@helix-before-toggle=${this.keepTick}
|
|
545
566
|
@helix-select=${() => this.showCaption(at)}
|
|
546
567
|
>
|
|
547
568
|
${track.label || track.language}
|
|
548
569
|
</${this.tag('menu-item')}>`)}
|
|
549
570
|
</${this.tag('menu')}>`;
|
|
550
571
|
}
|
|
572
|
+
heardMenu(menu, event) {
|
|
573
|
+
if (event.target !== event.currentTarget) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const open = event.newState === 'open';
|
|
577
|
+
if (open) {
|
|
578
|
+
this.menuOpen = menu;
|
|
579
|
+
}
|
|
580
|
+
else if (this.menuOpen === menu) {
|
|
581
|
+
this.menuOpen = undefined;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
551
584
|
renderOverflowItem(one) {
|
|
552
585
|
if (one === 'remote') {
|
|
553
586
|
return html `<${this.tag('menu-item')} part="entry"
|
|
@@ -682,8 +715,16 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
682
715
|
: html `<${this.tag('menu')} part="overflow"
|
|
683
716
|
exportparts="back: overflow-back, back-mark: overflow-back-mark, empty: overflow-empty,
|
|
684
717
|
header: overflow-header, live: overflow-live, loading: overflow-loading, surface: overflow-surface,
|
|
685
|
-
title: overflow-title" label=${this.moreLabel} placement="top-end"
|
|
686
|
-
|
|
718
|
+
title: overflow-title" label=${this.moreLabel} placement="top-end"
|
|
719
|
+
@toggle=${(event) => this.heardMenu('overflow', event)}>
|
|
720
|
+
<button
|
|
721
|
+
part="more"
|
|
722
|
+
slot="trigger"
|
|
723
|
+
type="button"
|
|
724
|
+
aria-label=${this.moreLabel}
|
|
725
|
+
aria-haspopup="menu"
|
|
726
|
+
aria-expanded=${this.menuOpen === 'overflow' ? 'true' : 'false'}
|
|
727
|
+
>
|
|
687
728
|
${drawIcon(dotsThreeVertical)}
|
|
688
729
|
</button>
|
|
689
730
|
${this.overflow.map((one) => this.renderOverflowItem(one))}
|
|
@@ -941,6 +982,9 @@ __decorate([
|
|
|
941
982
|
__decorate([
|
|
942
983
|
state()
|
|
943
984
|
], HelixHeadlessVideo.prototype, "captioned", void 0);
|
|
985
|
+
__decorate([
|
|
986
|
+
state()
|
|
987
|
+
], HelixHeadlessVideo.prototype, "menuOpen", void 0);
|
|
944
988
|
__decorate([
|
|
945
989
|
state()
|
|
946
990
|
], HelixHeadlessVideo.prototype, "captionIndex", void 0);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { HelixElement, type Placement } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type TemplateResult } from 'lit';
|
|
3
3
|
import '../../menu/index.js';
|
|
4
4
|
/**
|
|
@@ -51,7 +51,16 @@ export declare class HelixHeadlessVideoPlaylistMenu extends HelixElement {
|
|
|
51
51
|
* `top-end` because a control bar sits at the foot of the picture, and a menu
|
|
52
52
|
* opening downward from there opens off the player.
|
|
53
53
|
*/
|
|
54
|
-
placement:
|
|
54
|
+
placement: Placement;
|
|
55
|
+
/** Whether the menu is open, for the built-in trigger to say so. */
|
|
56
|
+
private open;
|
|
57
|
+
/**
|
|
58
|
+
* Notes the menu opening and closing, for the built-in trigger.
|
|
59
|
+
*
|
|
60
|
+
* The trigger is this element's own button, so the state is written onto it
|
|
61
|
+
* here. The menu cannot write onto a plain button slotted into it.
|
|
62
|
+
*/
|
|
63
|
+
private readonly heardToggle;
|
|
55
64
|
private observer;
|
|
56
65
|
connectedCallback(): void;
|
|
57
66
|
disconnectedCallback(): void;
|
|
@@ -88,6 +97,15 @@ export declare class HelixHeadlessVideoPlaylistMenu extends HelixElement {
|
|
|
88
97
|
*/
|
|
89
98
|
private watch;
|
|
90
99
|
private readonly reread;
|
|
100
|
+
/**
|
|
101
|
+
* Keeps each choice row's tick where this element puts it.
|
|
102
|
+
*
|
|
103
|
+
* A choice row checks itself after `helix-before-toggle`, and this element
|
|
104
|
+
* already sets `checked` on every row from its own state. The request is
|
|
105
|
+
* refused, so a tick moves only when the choice is applied, and stopped, so
|
|
106
|
+
* it does not reach a page that never placed the row.
|
|
107
|
+
*/
|
|
108
|
+
private readonly keepTick;
|
|
91
109
|
private readonly choose;
|
|
92
110
|
protected render(): TemplateResult;
|
|
93
111
|
/** The menu itself, behind a method so a styled tier can render another one. */
|
|
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { closestInstance, customElement, drawIcon, HelixElement, HelixSelectEvent, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { list } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
|
-
import { property } from 'lit/decorators.js';
|
|
10
|
+
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { html } from 'lit/static-html.js';
|
|
12
12
|
import { HelixHeadlessVideoPlaylistItem } from '../item/video-playlist-item.js';
|
|
13
13
|
import { HelixHeadlessVideoPlaylist } from '../root/video-playlist-root.js';
|
|
@@ -17,9 +17,21 @@ let HelixHeadlessVideoPlaylistMenu = class HelixHeadlessVideoPlaylistMenu extend
|
|
|
17
17
|
super(...arguments);
|
|
18
18
|
this.label = 'Episodes';
|
|
19
19
|
this.placement = 'top-end';
|
|
20
|
+
this.open = false;
|
|
21
|
+
this.heardToggle = (event) => {
|
|
22
|
+
if (event.target === event.currentTarget) {
|
|
23
|
+
this.open = event.newState === 'open';
|
|
24
|
+
}
|
|
25
|
+
};
|
|
20
26
|
this.reread = () => {
|
|
21
27
|
this.requestUpdate();
|
|
22
28
|
};
|
|
29
|
+
this.keepTick = (event) => {
|
|
30
|
+
if (event.target === event.currentTarget) {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
event.stopPropagation();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
23
35
|
this.choose = (value) => {
|
|
24
36
|
this.dispatchEvent(new HelixSelectEvent({ value }));
|
|
25
37
|
};
|
|
@@ -58,21 +70,28 @@ let HelixHeadlessVideoPlaylistMenu = class HelixHeadlessVideoPlaylistMenu extend
|
|
|
58
70
|
return this.renderMenu();
|
|
59
71
|
}
|
|
60
72
|
renderMenu() {
|
|
61
|
-
const active = this.playlist?.active;
|
|
62
73
|
return html `<${this.tag('menu')} part="menu"
|
|
63
74
|
exportparts="back: menu-back, back-mark: menu-back-mark, empty: menu-empty, header: menu-header,
|
|
64
|
-
live: menu-live, loading: menu-loading, surface: menu-surface, title: menu-title" label=${this.label} placement=${this.placement}
|
|
75
|
+
live: menu-live, loading: menu-loading, surface: menu-surface, title: menu-title" label=${this.label} placement=${this.placement}
|
|
76
|
+
@toggle=${this.heardToggle}>
|
|
65
77
|
<slot name="trigger" slot="trigger">
|
|
66
|
-
<button
|
|
78
|
+
<button
|
|
79
|
+
part="trigger"
|
|
80
|
+
type="button"
|
|
81
|
+
aria-label=${this.label}
|
|
82
|
+
aria-haspopup="menu"
|
|
83
|
+
aria-expanded=${this.open ? 'true' : 'false'}
|
|
84
|
+
>${drawIcon(list)}</button>
|
|
67
85
|
</slot>
|
|
68
86
|
${this.entries.map((entry) => html `<${this.tag('menu-item')}
|
|
69
87
|
part="entry"
|
|
70
88
|
exportparts="item: entry-item, mark: entry-mark, start: entry-start"
|
|
71
89
|
value=${entry.value}
|
|
72
90
|
checkable
|
|
73
|
-
?checked=${entry.
|
|
91
|
+
?checked=${entry.current}
|
|
74
92
|
?disabled=${entry.disabled}
|
|
75
93
|
exclusive
|
|
94
|
+
@helix-before-toggle=${this.keepTick}
|
|
76
95
|
@helix-select=${(event) => {
|
|
77
96
|
event.stopPropagation();
|
|
78
97
|
this.choose(entry.value);
|
|
@@ -92,6 +111,9 @@ __decorate([
|
|
|
92
111
|
__decorate([
|
|
93
112
|
property()
|
|
94
113
|
], HelixHeadlessVideoPlaylistMenu.prototype, "placement", void 0);
|
|
114
|
+
__decorate([
|
|
115
|
+
state()
|
|
116
|
+
], HelixHeadlessVideoPlaylistMenu.prototype, "open", void 0);
|
|
95
117
|
HelixHeadlessVideoPlaylistMenu = __decorate([
|
|
96
118
|
customElement('helix-headless-video-playlist-menu')
|
|
97
119
|
], HelixHeadlessVideoPlaylistMenu);
|
|
@@ -29,7 +29,10 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
29
29
|
this.chased = true;
|
|
30
30
|
this.step(-1);
|
|
31
31
|
};
|
|
32
|
-
this.handleEnded = () => {
|
|
32
|
+
this.handleEnded = (event) => {
|
|
33
|
+
if (event.target !== this.player) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
33
36
|
this.chased = true;
|
|
34
37
|
this.step(1);
|
|
35
38
|
};
|
|
@@ -55,14 +58,14 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
55
58
|
this.addEventListener('helix-select', this.handleSelect);
|
|
56
59
|
this.addEventListener('helix-next', this.handleNext);
|
|
57
60
|
this.addEventListener('helix-previous', this.handlePrevious);
|
|
58
|
-
this.addEventListener('ended', this.handleEnded);
|
|
61
|
+
this.addEventListener('ended', this.handleEnded, true);
|
|
59
62
|
}
|
|
60
63
|
disconnectedCallback() {
|
|
61
64
|
super.disconnectedCallback();
|
|
62
65
|
this.removeEventListener('helix-select', this.handleSelect);
|
|
63
66
|
this.removeEventListener('helix-next', this.handleNext);
|
|
64
67
|
this.removeEventListener('helix-previous', this.handlePrevious);
|
|
65
|
-
this.removeEventListener('ended', this.handleEnded);
|
|
68
|
+
this.removeEventListener('ended', this.handleEnded, true);
|
|
66
69
|
}
|
|
67
70
|
updated(changed) {
|
|
68
71
|
super.updated?.(changed);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { AriaForwardController, HelixElement } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
/**
|
|
4
4
|
* @summary A list that renders only the rows of a long set that are on screen.
|
|
@@ -21,6 +21,11 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
21
21
|
* accessible `<table>`, and ten thousand rows in one is the cost this component
|
|
22
22
|
* avoids.
|
|
23
23
|
*
|
|
24
|
+
* The list follows the source: editing the script's text, or replacing the
|
|
25
|
+
* script under the same id, draws the new rows. A row that stays in view keeps
|
|
26
|
+
* its element as the list scrolls, and the row holding focus stays drawn when
|
|
27
|
+
* it scrolls out, so a link or a button in a row keeps focus.
|
|
28
|
+
*
|
|
24
29
|
* The viewport scrolls on this tier. It carries `data-scroller`, and `core`
|
|
25
30
|
* ships `[data-scroller] { overflow: auto }` into every shadow root, so
|
|
26
31
|
* `scrollTop` works with no stylesheet. You still need to set a block size.
|
|
@@ -30,6 +35,8 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
30
35
|
* @category layout
|
|
31
36
|
* @documentation https://fusion.dev/helix/components/virtual-list
|
|
32
37
|
*
|
|
38
|
+
* @slot - The `<template>` that defines one row. The list writes the rows it renders beside it, and this slot shows them between the two spacers.
|
|
39
|
+
*
|
|
33
40
|
* @csspart after - The space that stands in for the rows below the rendered ones.
|
|
34
41
|
* @csspart before - The space that stands in for the rows above the rendered ones.
|
|
35
42
|
* @csspart viewport - The scrolling box. On this tier, it needs only a block size.
|
|
@@ -49,6 +56,9 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
49
56
|
/**
|
|
50
57
|
* The list's name, read by screen readers, because only some of its rows
|
|
51
58
|
* are rendered at a time.
|
|
59
|
+
*
|
|
60
|
+
* An `aria-label` or `aria-labelledby` on the host names the list too, and
|
|
61
|
+
* wins over this.
|
|
52
62
|
*/
|
|
53
63
|
label: string;
|
|
54
64
|
/**
|
|
@@ -70,6 +80,21 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
70
80
|
private rows;
|
|
71
81
|
/** The `for` that `rows` came from, so it is read again only when that changes. */
|
|
72
82
|
private readFor;
|
|
83
|
+
/** The rows the drawn elements were filled from, so new data fills them again. */
|
|
84
|
+
private filledFrom;
|
|
85
|
+
/** The script the rows were read from, which the watcher follows. */
|
|
86
|
+
private source;
|
|
87
|
+
/**
|
|
88
|
+
* Watches the source and the tree around it.
|
|
89
|
+
*
|
|
90
|
+
* **The list read its source once per `for`**, so a page that wrote new JSON
|
|
91
|
+
* into the script, the way a framework updates a text child, kept the old
|
|
92
|
+
* rows. The script's own text is watched, and the tree it sits in, for a
|
|
93
|
+
* script replaced under the same id.
|
|
94
|
+
*/
|
|
95
|
+
private watcher;
|
|
96
|
+
/** Carries a host `aria-label` and `aria-labelledby` to the element with the `list` role. */
|
|
97
|
+
protected readonly aria: AriaForwardController;
|
|
73
98
|
private readonly virtualize;
|
|
74
99
|
/**
|
|
75
100
|
* The rows in the tree, each with the item it drew.
|
|
@@ -98,15 +123,38 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
98
123
|
* so by itself.
|
|
99
124
|
*/
|
|
100
125
|
protected willUpdate(changed: PropertyValues): void;
|
|
126
|
+
connectedCallback(): void;
|
|
127
|
+
disconnectedCallback(): void;
|
|
128
|
+
/**
|
|
129
|
+
* Reads the source again when its text changed or another element took its
|
|
130
|
+
* id. Rows this list drew are its own writes and are not news.
|
|
131
|
+
*/
|
|
132
|
+
private handleMutations;
|
|
101
133
|
protected firstUpdated(changed: PropertyValues): void;
|
|
102
134
|
protected updated(changed: PropertyValues): void;
|
|
103
135
|
private read;
|
|
136
|
+
/**
|
|
137
|
+
* The item whose row holds focus, where one does.
|
|
138
|
+
*
|
|
139
|
+
* Asked of the root this list is in, which retargets focus inside a row's
|
|
140
|
+
* own shadow root to the row's element.
|
|
141
|
+
*/
|
|
142
|
+
private focusedIndex;
|
|
143
|
+
/** The item drawn outside the window to keep focus, if there is one. */
|
|
144
|
+
private keptIndex;
|
|
145
|
+
/** Fills a row's holes and its place in the set from an item. */
|
|
146
|
+
private fill;
|
|
104
147
|
/**
|
|
105
148
|
* Puts the windowed rows into the light DOM, and takes the rest out.
|
|
106
149
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
150
|
+
* **A row that stays in the window keeps its element.** Clearing and
|
|
151
|
+
* cloning every row on every move replaced the node a reader had focused,
|
|
152
|
+
* so a link in a row lost focus to the body on any scroll, even with its row
|
|
153
|
+
* still on screen. Rows are keyed by item: the ones leaving go, the ones
|
|
154
|
+
* arriving are cloned in their place, and the ones staying are not moved.
|
|
155
|
+
*
|
|
156
|
+
* **The row holding focus is drawn outside the window too**, beside it on the
|
|
157
|
+
* side it belongs, and the spacer on that side gives up its room.
|
|
110
158
|
*/
|
|
111
159
|
private place;
|
|
112
160
|
protected render(): TemplateResult;
|
|
@@ -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 { customElement, elementById, HelixElement, searchableById, VirtualizeController, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, customElement, elementById, HelixElement, searchableById, VirtualizeController, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixElement {
|
|
@@ -15,6 +15,11 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
15
15
|
this.label = '';
|
|
16
16
|
this.measured = false;
|
|
17
17
|
this.rows = [];
|
|
18
|
+
this.source = null;
|
|
19
|
+
this.aria = new AriaForwardController(this, {
|
|
20
|
+
label: true,
|
|
21
|
+
target: () => this.partNamed('viewport'),
|
|
22
|
+
});
|
|
18
23
|
this.virtualize = new VirtualizeController(this, {
|
|
19
24
|
container: () => this.partNamed('viewport'),
|
|
20
25
|
count: () => this.rows.length,
|
|
@@ -36,6 +41,33 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
36
41
|
this.read();
|
|
37
42
|
}
|
|
38
43
|
}
|
|
44
|
+
connectedCallback() {
|
|
45
|
+
super.connectedCallback();
|
|
46
|
+
if (typeof MutationObserver === 'undefined') {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
this.watcher = new MutationObserver((records) => this.handleMutations(records));
|
|
50
|
+
this.watcher.observe(this.getRootNode(), {
|
|
51
|
+
characterData: true,
|
|
52
|
+
childList: true,
|
|
53
|
+
subtree: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
disconnectedCallback() {
|
|
57
|
+
super.disconnectedCallback();
|
|
58
|
+
this.watcher?.disconnect();
|
|
59
|
+
this.watcher = undefined;
|
|
60
|
+
}
|
|
61
|
+
handleMutations(records) {
|
|
62
|
+
if (this.for === '' || this.hydrating || this.readFor === undefined) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const found = elementById(this, this.for);
|
|
66
|
+
const edited = records.some((record) => found !== null && (record.target === found || found.contains(record.target)));
|
|
67
|
+
if (found !== this.source || edited) {
|
|
68
|
+
this.read();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
39
71
|
firstUpdated(changed) {
|
|
40
72
|
super.firstUpdated?.(changed);
|
|
41
73
|
this.finishHydration();
|
|
@@ -47,6 +79,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
47
79
|
}
|
|
48
80
|
read() {
|
|
49
81
|
const source = this.for === '' ? null : elementById(this, this.for);
|
|
82
|
+
this.source = source;
|
|
50
83
|
const text = source?.textContent ?? '';
|
|
51
84
|
if (text.trim() === '') {
|
|
52
85
|
this.rows = [];
|
|
@@ -60,40 +93,89 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
60
93
|
this.rows = [];
|
|
61
94
|
}
|
|
62
95
|
}
|
|
96
|
+
focusedIndex() {
|
|
97
|
+
const root = this.getRootNode();
|
|
98
|
+
let node = root.activeElement;
|
|
99
|
+
while (node && node.parentElement !== this) {
|
|
100
|
+
node = node.parentElement;
|
|
101
|
+
}
|
|
102
|
+
const index = node instanceof HTMLElement ? node.dataset.index : undefined;
|
|
103
|
+
return index === undefined ? undefined : Number(index);
|
|
104
|
+
}
|
|
105
|
+
keptIndex() {
|
|
106
|
+
const index = this.focusedIndex();
|
|
107
|
+
const { first, last } = this.virtualize.window;
|
|
108
|
+
return index !== undefined && index < this.rows.length && (index < first || index >= last)
|
|
109
|
+
? index
|
|
110
|
+
: undefined;
|
|
111
|
+
}
|
|
112
|
+
fill(elements, row, at) {
|
|
113
|
+
const total = String(this.rows.length);
|
|
114
|
+
for (const element of elements) {
|
|
115
|
+
for (const hole of [
|
|
116
|
+
...(element.matches('[data-field]') ? [element] : []),
|
|
117
|
+
...element.querySelectorAll('[data-field]'),
|
|
118
|
+
]) {
|
|
119
|
+
hole.textContent = row[hole.dataset.field ?? ''] ?? '';
|
|
120
|
+
}
|
|
121
|
+
element.setAttribute('role', 'listitem');
|
|
122
|
+
element.setAttribute('aria-posinset', String(at + 1));
|
|
123
|
+
element.setAttribute('aria-setsize', total);
|
|
124
|
+
element.setAttribute('data-index', String(at));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
63
127
|
place() {
|
|
64
128
|
const template = this.querySelector('template');
|
|
65
129
|
if (!template) {
|
|
66
130
|
return;
|
|
67
131
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
132
|
+
const { first, last } = this.virtualize.window;
|
|
133
|
+
const kept = this.keptIndex();
|
|
134
|
+
const wanted = [];
|
|
135
|
+
for (let at = first; at < Math.min(last, this.rows.length); at += 1) {
|
|
136
|
+
wanted.push(at);
|
|
137
|
+
}
|
|
138
|
+
if (kept !== undefined) {
|
|
139
|
+
wanted.push(kept);
|
|
140
|
+
wanted.sort((a, b) => a - b);
|
|
141
|
+
}
|
|
142
|
+
const keep = new Set(wanted);
|
|
143
|
+
const drawn = new Map();
|
|
144
|
+
for (const child of [...this.children]) {
|
|
145
|
+
if (child === template) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const index = Number(child.dataset.index);
|
|
149
|
+
if (!Number.isInteger(index) || !keep.has(index)) {
|
|
150
|
+
child.remove();
|
|
151
|
+
continue;
|
|
71
152
|
}
|
|
153
|
+
drawn.set(index, [...(drawn.get(index) ?? []), child]);
|
|
72
154
|
}
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
for (
|
|
155
|
+
const refill = this.filledFrom !== this.rows;
|
|
156
|
+
this.filledFrom = this.rows;
|
|
157
|
+
let anchor = drawn.values().next().value?.[0] ?? null;
|
|
158
|
+
for (const at of wanted) {
|
|
77
159
|
const row = this.rows[at];
|
|
78
|
-
|
|
160
|
+
const group = drawn.get(at);
|
|
161
|
+
if (group) {
|
|
162
|
+
if (refill) {
|
|
163
|
+
this.fill(group, row, at);
|
|
164
|
+
}
|
|
165
|
+
anchor = group.at(-1).nextElementSibling;
|
|
79
166
|
continue;
|
|
80
167
|
}
|
|
81
168
|
const clone = template.content.cloneNode(true);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
for (const element of clone.children) {
|
|
86
|
-
element.setAttribute('role', 'listitem');
|
|
87
|
-
element.setAttribute('aria-posinset', String(at + 1));
|
|
88
|
-
element.setAttribute('aria-setsize', String(total));
|
|
89
|
-
element.setAttribute('data-index', String(at));
|
|
90
|
-
}
|
|
91
|
-
fragment.append(clone);
|
|
169
|
+
this.fill([...clone.children], row, at);
|
|
170
|
+
this.insertBefore(clone, anchor);
|
|
92
171
|
}
|
|
93
|
-
this.append(fragment);
|
|
94
172
|
}
|
|
95
173
|
render() {
|
|
96
|
-
const
|
|
174
|
+
const window = this.virtualize.window;
|
|
175
|
+
const kept = this.hasUpdated ? this.keptIndex() : undefined;
|
|
176
|
+
const room = kept === undefined ? 0 : this.virtualize.sizeOf(kept);
|
|
177
|
+
const before = window.before - (kept !== undefined && kept < window.first ? room : 0);
|
|
178
|
+
const after = window.after - (kept !== undefined && kept >= window.last ? room : 0);
|
|
97
179
|
return html `<div
|
|
98
180
|
data-scroller
|
|
99
181
|
part="viewport"
|