@fusion.dev/helix-headless 0.158.0 → 0.160.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 +1925 -174
- package/dist/app-header/root/app-header-root.d.ts +59 -12
- package/dist/app-header/root/app-header-root.js +68 -29
- package/dist/button/root/button-root.d.ts +8 -0
- package/dist/button/root/button-root.js +4 -0
- package/dist/button-group/root/button-group-root.d.ts +20 -7
- package/dist/button-group/root/button-group-root.js +30 -5
- package/dist/date-input/root/date-input-root.d.ts +108 -19
- package/dist/date-input/root/date-input-root.js +112 -13
- package/dist/date-picker/root/date-picker-root.d.ts +81 -16
- package/dist/date-picker/root/date-picker-root.js +140 -36
- package/dist/element-events.generated.js +9 -1
- package/dist/format-date/fields.js +1 -0
- package/dist/format-date/options.d.ts +3 -1
- package/dist/format-date/options.js +4 -1
- package/dist/format-date/root/format-date-root.d.ts +27 -11
- package/dist/format-date/root/format-date-root.js +6 -0
- package/dist/lightbox/item/lightbox-item.d.ts +7 -1
- package/dist/lightbox/item/lightbox-item.js +15 -2
- package/dist/lightbox/root/lightbox-root.d.ts +8 -4
- package/dist/lightbox/root/lightbox-root.js +9 -1
- package/dist/lightbox/zoom-controller.d.ts +13 -4
- package/dist/lightbox/zoom-controller.js +17 -0
- package/dist/listbox/root/listbox-root.js +5 -0
- package/dist/notifications/notification/notification.d.ts +1 -0
- package/dist/notifications/notification/notification.js +2 -1
- package/dist/slider/root/slider-root.d.ts +54 -3
- package/dist/slider/root/slider-root.js +124 -2
- package/dist/steps/root/steps-root.js +8 -6
- package/dist/steps/step/step.d.ts +9 -0
- package/dist/steps/step/step.js +19 -2
- package/dist/tabs/panel/tab-panel.d.ts +22 -1
- package/dist/tabs/panel/tab-panel.js +22 -1
- package/dist/tabs/root/tabs-root.d.ts +21 -5
- package/dist/tabs/root/tabs-root.js +24 -13
- package/dist/tabs/tab/tab.d.ts +4 -2
- package/dist/tabs/tab/tab.js +3 -1
- package/dist/time-input/root/time-input-root.d.ts +55 -7
- package/dist/time-input/root/time-input-root.js +70 -10
- package/dist/time-picker/root/time-picker-root.d.ts +27 -5
- package/dist/time-picker/root/time-picker-root.js +52 -13
- package/dist/toast/item/toast-item.d.ts +27 -8
- package/dist/toast/item/toast-item.js +47 -11
- package/dist/toast/root/toast-root.d.ts +24 -1
- package/dist/toast/root/toast-root.js +54 -0
- package/dist/toggle/group/toggle-group.d.ts +18 -1
- package/dist/toggle/group/toggle-group.js +8 -2
- package/dist/toggle/root/toggle-root.d.ts +2 -2
- package/dist/toggle/root/toggle-root.js +2 -1
- package/dist/tour/root/tour-root.d.ts +34 -0
- package/dist/tour/root/tour-root.js +92 -3
- package/dist/tour/stop/tour-stop.d.ts +33 -1
- package/dist/tour/stop/tour-stop.js +12 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +1119 -218
- package/types/vue.d.ts +278 -51
- package/types/words.d.ts +72 -0
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
export interface Asked {
|
|
6
6
|
dateStyle?: 'full' | 'long' | 'medium' | 'short' | undefined;
|
|
7
7
|
day?: 'numeric' | '2-digit' | undefined;
|
|
8
|
+
dayPeriod?: 'long' | 'narrow' | 'short' | undefined;
|
|
8
9
|
era?: 'long' | 'narrow' | 'short' | undefined;
|
|
10
|
+
fractionalSecondDigits?: 1 | 2 | 3 | undefined;
|
|
9
11
|
hour?: 'numeric' | '2-digit' | undefined;
|
|
10
12
|
hourCycle?: 'h11' | 'h12' | 'h23' | 'h24' | undefined;
|
|
11
13
|
minute?: 'numeric' | '2-digit' | undefined;
|
|
@@ -13,7 +15,7 @@ export interface Asked {
|
|
|
13
15
|
second?: 'numeric' | '2-digit' | undefined;
|
|
14
16
|
timeStyle?: 'full' | 'long' | 'medium' | 'short' | undefined;
|
|
15
17
|
timeZone?: string | undefined;
|
|
16
|
-
timeZoneName?: 'long' | 'short' | undefined;
|
|
18
|
+
timeZoneName?: 'long' | 'longGeneric' | 'longOffset' | 'short' | 'shortGeneric' | 'shortOffset' | undefined;
|
|
17
19
|
weekday?: 'long' | 'short' | 'narrow' | undefined;
|
|
18
20
|
year?: 'numeric' | '2-digit' | undefined;
|
|
19
21
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { FIELD } from './fields.js';
|
|
2
|
-
const FIELDS =
|
|
2
|
+
const FIELDS = [
|
|
3
|
+
...Object.keys(FIELD).filter((one) => one !== 'fractionalSecond'),
|
|
4
|
+
'fractionalSecondDigits',
|
|
5
|
+
];
|
|
3
6
|
export const formatOptions = (asked, dateOnly = false) => {
|
|
4
7
|
const options = {
|
|
5
8
|
hourCycle: asked.hourCycle,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DateCalendar, HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { type DateCalendar, HelixElement, type MonthCalendar } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
/**
|
|
4
4
|
* @summary A timestamp, formatted for the user's locale.
|
|
@@ -45,8 +45,8 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
|
45
45
|
*
|
|
46
46
|
* **A date is written in a calendar Date Picker draws**: the locale's own
|
|
47
47
|
* where its months are the Gregorian months, as `th-TH` writes 2569 for 2026,
|
|
48
|
-
* and `gregory` where they are not, as for `fa-IR`. `calendar` picks
|
|
49
|
-
*
|
|
48
|
+
* and `gregory` where they are not, as for `fa-IR`. `calendar` picks any of
|
|
49
|
+
* them, `persian` and `hebrew` included.
|
|
50
50
|
*
|
|
51
51
|
* @since 0.1.0
|
|
52
52
|
* @status stable
|
|
@@ -57,6 +57,7 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
|
57
57
|
* @csspart day - The day of the month, with `fields`.
|
|
58
58
|
* @csspart day-period - AM or PM, or the locale's word for the part of the day, with `fields`.
|
|
59
59
|
* @csspart era - The era, such as AD, with `fields` and `era`.
|
|
60
|
+
* @csspart fractional-second - The fraction of a second, with `fields` and `fractional-second-digits`.
|
|
60
61
|
* @csspart hour - The hour, with `fields`.
|
|
61
62
|
* @csspart minute - The minute, with `fields`.
|
|
62
63
|
* @csspart month - The month, with `fields`.
|
|
@@ -72,14 +73,16 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
|
72
73
|
export declare class HelixHeadlessFormatDate extends HelixElement {
|
|
73
74
|
/**
|
|
74
75
|
* The calendar a date is written in: `gregory`, `buddhist`, `japanese`,
|
|
75
|
-
* `roc`, or `iso8601`,
|
|
76
|
+
* `roc`, or `iso8601`, or a calendar with months of its own, such as
|
|
77
|
+
* `persian`, `hebrew`, `islamic-umalqura`, `ethiopic`, or `chinese`: every
|
|
78
|
+
* calendar Date Picker and Date Input draw.
|
|
76
79
|
*
|
|
77
|
-
* Absent is the locale's own where
|
|
78
|
-
* `buddhist` for `th-TH`, and `gregory` for every other, such as
|
|
79
|
-
* for `fa-IR`, so a date written here reads as the same date a
|
|
80
|
-
* beside it shows.
|
|
80
|
+
* Absent is the locale's own where its months are the Gregorian months,
|
|
81
|
+
* such as `buddhist` for `th-TH`, and `gregory` for every other, such as
|
|
82
|
+
* `persian` for `fa-IR`, so a date written here reads as the same date a
|
|
83
|
+
* Date Picker beside it shows. Write `calendar="persian"` for Mehr 14, 1405.
|
|
81
84
|
*/
|
|
82
|
-
calendar?: DateCalendar;
|
|
85
|
+
calendar?: DateCalendar | MonthCalendar;
|
|
83
86
|
/**
|
|
84
87
|
* The timestamp, in any format `Date` can parse.
|
|
85
88
|
*
|
|
@@ -107,6 +110,12 @@ export declare class HelixHeadlessFormatDate extends HelixElement {
|
|
|
107
110
|
* How the day of the month is written, where a date is shown.
|
|
108
111
|
*/
|
|
109
112
|
day?: 'numeric' | '2-digit';
|
|
113
|
+
/**
|
|
114
|
+
* Writes the part of the day in words, such as "in the morning" or "at
|
|
115
|
+
* night", in its `long`, `short`, or `narrow` form, where `hour` is on a
|
|
116
|
+
* twelve-hour clock.
|
|
117
|
+
*/
|
|
118
|
+
dayPeriod?: 'long' | 'narrow' | 'short';
|
|
110
119
|
/**
|
|
111
120
|
* Shows how long ago the timestamp was, in a compact form such as "3h", in
|
|
112
121
|
* place of the date.
|
|
@@ -130,6 +139,11 @@ export declare class HelixHeadlessFormatDate extends HelixElement {
|
|
|
130
139
|
* way.
|
|
131
140
|
*/
|
|
132
141
|
fields?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* How many digits of a fraction of a second to write after `second`: 1, 2,
|
|
144
|
+
* or 3, as in 09:41:07.250.
|
|
145
|
+
*/
|
|
146
|
+
fractionalSecondDigits?: 1 | 2 | 3;
|
|
133
147
|
/**
|
|
134
148
|
* Draws an `elapsed` or `relative` reading once and leaves it, where it
|
|
135
149
|
* would otherwise move on as it goes stale.
|
|
@@ -206,9 +220,11 @@ export declare class HelixHeadlessFormatDate extends HelixElement {
|
|
|
206
220
|
*/
|
|
207
221
|
timeZone?: string;
|
|
208
222
|
/**
|
|
209
|
-
* Shows the name of the time zone
|
|
223
|
+
* Shows the name of the time zone: `long` or `short`, as Pacific Daylight
|
|
224
|
+
* Time or PDT, `longOffset` or `shortOffset`, as GMT-07:00 or GMT-7, or
|
|
225
|
+
* `longGeneric` or `shortGeneric`, as Pacific Time or PT.
|
|
210
226
|
*/
|
|
211
|
-
timeZoneName?: 'long' | 'short';
|
|
227
|
+
timeZoneName?: 'long' | 'longGeneric' | 'longOffset' | 'short' | 'shortGeneric' | 'shortOffset';
|
|
212
228
|
/**
|
|
213
229
|
* How the weekday is written, where one is shown.
|
|
214
230
|
*/
|
|
@@ -132,6 +132,9 @@ __decorate([
|
|
|
132
132
|
__decorate([
|
|
133
133
|
property()
|
|
134
134
|
], HelixHeadlessFormatDate.prototype, "day", void 0);
|
|
135
|
+
__decorate([
|
|
136
|
+
property({ attribute: 'day-period' })
|
|
137
|
+
], HelixHeadlessFormatDate.prototype, "dayPeriod", void 0);
|
|
135
138
|
__decorate([
|
|
136
139
|
property({ reflect: true, type: Boolean })
|
|
137
140
|
], HelixHeadlessFormatDate.prototype, "elapsed", void 0);
|
|
@@ -141,6 +144,9 @@ __decorate([
|
|
|
141
144
|
__decorate([
|
|
142
145
|
property({ type: Boolean })
|
|
143
146
|
], HelixHeadlessFormatDate.prototype, "fields", void 0);
|
|
147
|
+
__decorate([
|
|
148
|
+
property({ attribute: 'fractional-second-digits', type: Number })
|
|
149
|
+
], HelixHeadlessFormatDate.prototype, "fractionalSecondDigits", void 0);
|
|
144
150
|
__decorate([
|
|
145
151
|
property({ type: Boolean })
|
|
146
152
|
], HelixHeadlessFormatDate.prototype, "frozen", void 0);
|
|
@@ -11,7 +11,8 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
11
11
|
* Each item is a `group` with the role description `slide`, named by its
|
|
12
12
|
* `label` or, without one, by its place in the set, such as "2 of 5", as the
|
|
13
13
|
* APG carousel pattern names a slide. The lightbox supplies both words, so they
|
|
14
|
-
* are translated on the lightbox.
|
|
14
|
+
* are translated on the lightbox. A `description`, while there is one,
|
|
15
|
+
* describes the group, so a screen reader says it with the name.
|
|
15
16
|
*
|
|
16
17
|
* An item that is not current is hidden and stays in the DOM, as a `TabPanel`
|
|
17
18
|
* does. Crawlers can read it, it is in the server-rendered markup, and an
|
|
@@ -56,6 +57,11 @@ export declare class HelixHeadlessLightboxItem extends HelixElement {
|
|
|
56
57
|
* The item's identifier. The root compares its `active` value against it.
|
|
57
58
|
*/
|
|
58
59
|
value: string;
|
|
60
|
+
/** Whether anything is slotted into `description`, which then describes the group. */
|
|
61
|
+
private described;
|
|
62
|
+
/** Reads whether the `description` slot holds anything. */
|
|
63
|
+
private readonly readDescription;
|
|
64
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
59
65
|
/**
|
|
60
66
|
* Mirrors the shown state where a stylesheet can select on it.
|
|
61
67
|
*
|
|
@@ -6,7 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
import { customElement, HelixElement } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
|
-
import { property } from 'lit/decorators.js';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessLightboxItem = class HelixHeadlessLightboxItem extends HelixElement {
|
|
11
11
|
constructor() {
|
|
12
12
|
super(...arguments);
|
|
@@ -15,6 +15,15 @@ let HelixHeadlessLightboxItem = class HelixHeadlessLightboxItem extends HelixEle
|
|
|
15
15
|
this.position = '';
|
|
16
16
|
this.roleLabel = '';
|
|
17
17
|
this.value = '';
|
|
18
|
+
this.described = false;
|
|
19
|
+
this.readDescription = () => {
|
|
20
|
+
const slot = this.renderRoot.querySelector('slot[name="description"]');
|
|
21
|
+
this.described = (slot?.assignedNodes() ?? []).some((node) => node.nodeType === Node.ELEMENT_NODE || (node.textContent ?? '').trim() !== '');
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
firstUpdated(changed) {
|
|
25
|
+
super.firstUpdated(changed);
|
|
26
|
+
this.readDescription();
|
|
18
27
|
}
|
|
19
28
|
willUpdate(changed) {
|
|
20
29
|
super.willUpdate(changed);
|
|
@@ -26,10 +35,11 @@ let HelixHeadlessLightboxItem = class HelixHeadlessLightboxItem extends HelixEle
|
|
|
26
35
|
role="group"
|
|
27
36
|
aria-roledescription=${this.roleLabel || nothing}
|
|
28
37
|
aria-label=${this.label || this.position || nothing}
|
|
38
|
+
aria-describedby=${this.described ? 'description' : nothing}
|
|
29
39
|
?hidden=${!this.current}
|
|
30
40
|
>
|
|
31
41
|
<slot></slot>
|
|
32
|
-
<slot name="description"></slot>
|
|
42
|
+
<slot id="description" name="description" @slotchange=${this.readDescription}></slot>
|
|
33
43
|
</div>`;
|
|
34
44
|
}
|
|
35
45
|
};
|
|
@@ -48,6 +58,9 @@ __decorate([
|
|
|
48
58
|
__decorate([
|
|
49
59
|
property()
|
|
50
60
|
], HelixHeadlessLightboxItem.prototype, "value", void 0);
|
|
61
|
+
__decorate([
|
|
62
|
+
state()
|
|
63
|
+
], HelixHeadlessLightboxItem.prototype, "described", void 0);
|
|
51
64
|
HelixHeadlessLightboxItem = __decorate([
|
|
52
65
|
customElement('helix-headless-lightbox-item')
|
|
53
66
|
], HelixHeadlessLightboxItem);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommandController, FocusScopeController, HelixElement, SwipeController } from '@fusion.dev/helix-core';
|
|
1
|
+
import { CommandController, FocusScopeController, HelixElement, OpenPhaseController, SwipeController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
declare const HelixHeadlessLightbox_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerActiveInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & typeof HelixElement;
|
|
4
4
|
/**
|
|
@@ -25,9 +25,10 @@ declare const HelixHeadlessLightbox_base: (new (...args: any[]) => import("@fusi
|
|
|
25
25
|
* buttons that send `--previous`, `--next` and `--close`.
|
|
26
26
|
*
|
|
27
27
|
* **Set `max-zoom` to let a reader zoom into the shown item**, such as `3`.
|
|
28
|
-
* Two fingers pinch it,
|
|
29
|
-
* about the point
|
|
30
|
-
*
|
|
28
|
+
* Two fingers pinch it, and so does a trackpad pinch or the wheel with Ctrl
|
|
29
|
+
* held, about the point under the pointer. A double click or a double tap
|
|
30
|
+
* zooms all the way in about the point pressed and back out, and once zoomed
|
|
31
|
+
* one pointer drags it about and the wheel moves it. `+` and `-` zoom in and out from the keyboard, and `0` puts it back.
|
|
31
32
|
* The scale and the offset are on the `viewport` part as `--lightbox-zoom`,
|
|
32
33
|
* `--lightbox-pan-x` and `--lightbox-pan-y`, written only while zoomed, and a
|
|
33
34
|
* swipe moves to the next item only while the item is at its own size. With
|
|
@@ -109,6 +110,8 @@ declare const HelixHeadlessLightbox_base: (new (...args: any[]) => import("@fusi
|
|
|
109
110
|
* @cssproperty --lightbox-pan-y - How far the zoomed item is moved down from the middle of the viewport, in pixels. Written only while zoomed.
|
|
110
111
|
* @cssproperty --lightbox-zoom - The scale the shown item is zoomed to, such as `2`. Written only while zoomed.
|
|
111
112
|
*
|
|
113
|
+
* @cssstate entering - From the moment the viewer opens until its entrance has finished.
|
|
114
|
+
* @cssstate exiting - From the moment the viewer closes until its exit has finished.
|
|
112
115
|
* @cssstate fullscreen - While the viewer fills the screen.
|
|
113
116
|
* @cssstate idle - The user has been still long enough for the controls to hide.
|
|
114
117
|
* @cssstate open - While the viewer is open.
|
|
@@ -225,6 +228,7 @@ export declare class HelixHeadlessLightbox extends HelixHeadlessLightbox_base {
|
|
|
225
228
|
private quietZoom;
|
|
226
229
|
protected readonly swipes: SwipeController;
|
|
227
230
|
protected readonly scope: FocusScopeController;
|
|
231
|
+
protected readonly phases: OpenPhaseController;
|
|
228
232
|
private readonly zoomer;
|
|
229
233
|
/** Writes the zoom on the viewport, and nothing at the item's own size. */
|
|
230
234
|
private drawZoom;
|
|
@@ -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 { auditName, CommandController, customElement, DelayController, deepActiveElement, deepContains, drawDismiss, drawDismissGlyph, FocusScopeController, FullscreenController, formatWords, HelixElement, hostAria, IdleController, inlineStep, isRtl, keyClaimed, lockScroll, MixinControllerActive, MixinControllerOpen, resolveActiveItem, SwipeController, swipeStep, unlockScroll, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { auditName, CommandController, customElement, DelayController, deepActiveElement, deepContains, drawDismiss, drawDismissGlyph, FocusScopeController, FullscreenController, formatWords, HelixElement, hostAria, IdleController, inlineStep, isRtl, keyClaimed, lockScroll, MixinControllerActive, MixinControllerOpen, OpenPhaseController, resolveActiveItem, SwipeController, swipeStep, unlockScroll, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretLeft, caretRight, minus, plus } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -63,6 +63,14 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
|
|
|
63
63
|
],
|
|
64
64
|
surface: () => this.dialog,
|
|
65
65
|
});
|
|
66
|
+
this.phases = new OpenPhaseController(this, {
|
|
67
|
+
mark: {
|
|
68
|
+
entering: (on) => this.toggleState('entering', on),
|
|
69
|
+
exiting: (on) => this.toggleState('exiting', on),
|
|
70
|
+
},
|
|
71
|
+
open: () => this.open,
|
|
72
|
+
surface: () => this.dialog,
|
|
73
|
+
});
|
|
66
74
|
this.zoomer = new ZoomController(this, {
|
|
67
75
|
max: () => (this.open ? this.maxZoom : 1),
|
|
68
76
|
onChange: (scale, pan) => this.drawZoom(scale, pan),
|
|
@@ -12,10 +12,12 @@ export interface ZoomOptions {
|
|
|
12
12
|
* Pinch, double press and drag to zoom into the item a viewer shows, and to
|
|
13
13
|
* move about it once zoomed.
|
|
14
14
|
*
|
|
15
|
-
* Two fingers scale the item about the point between them. A
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* Two fingers scale the item about the point between them. A trackpad pinch,
|
|
16
|
+
* which the browser sends as a wheel with Ctrl held, and the wheel with Ctrl
|
|
17
|
+
* held scale it about the pointer. A double press, a double click or a double
|
|
18
|
+
* tap, zooms all the way in about the point pressed, and back out again. One
|
|
19
|
+
* pointer dragged across a zoomed item moves it, never so far that an edge
|
|
20
|
+
* comes inside the box, and so does the wheel without Ctrl. What it reports is a scale and an
|
|
19
21
|
* offset from the middle of the box; drawing them is the caller's.
|
|
20
22
|
*/
|
|
21
23
|
export declare class ZoomController implements ReactiveController {
|
|
@@ -58,6 +60,13 @@ export declare class ZoomController implements ReactiveController {
|
|
|
58
60
|
private readonly handleDown;
|
|
59
61
|
private readonly handleMove;
|
|
60
62
|
private readonly handleUp;
|
|
63
|
+
/**
|
|
64
|
+
* A wheel with Ctrl held, which is also how the browser sends a trackpad
|
|
65
|
+
* pinch, scales the item about the pointer. Without Ctrl, a wheel over a
|
|
66
|
+
* zoomed item moves it. Over an item at its own size, a plain wheel is left
|
|
67
|
+
* to the page.
|
|
68
|
+
*/
|
|
69
|
+
private readonly handleWheel;
|
|
61
70
|
/** A double press zooms all the way in about the point, or back out. */
|
|
62
71
|
private readonly handleDouble;
|
|
63
72
|
private release;
|
|
@@ -48,6 +48,21 @@ export class ZoomController {
|
|
|
48
48
|
}
|
|
49
49
|
this.begin();
|
|
50
50
|
};
|
|
51
|
+
this.handleWheel = (event) => {
|
|
52
|
+
if (this.options.max() <= 1 || !this.inside(event)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const unit = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? 400 : 1;
|
|
56
|
+
if (event.ctrlKey) {
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
this.zoomTo(this.scale * Math.exp((-event.deltaY * unit) / 250), this.offset(event));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (this.scale > 1) {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
this.panBy({ x: -event.deltaX * unit, y: -event.deltaY * unit });
|
|
64
|
+
}
|
|
65
|
+
};
|
|
51
66
|
this.handleDouble = (event) => {
|
|
52
67
|
if (this.options.max() <= 1 || !this.inside(event)) {
|
|
53
68
|
return;
|
|
@@ -69,10 +84,12 @@ export class ZoomController {
|
|
|
69
84
|
hostConnected() {
|
|
70
85
|
this.host.addEventListener('pointerdown', this.handleDown);
|
|
71
86
|
this.host.addEventListener('dblclick', this.handleDouble);
|
|
87
|
+
this.host.addEventListener('wheel', this.handleWheel, { passive: false });
|
|
72
88
|
}
|
|
73
89
|
hostDisconnected() {
|
|
74
90
|
this.host.removeEventListener('pointerdown', this.handleDown);
|
|
75
91
|
this.host.removeEventListener('dblclick', this.handleDouble);
|
|
92
|
+
this.host.removeEventListener('wheel', this.handleWheel);
|
|
76
93
|
this.release();
|
|
77
94
|
}
|
|
78
95
|
zoomTo(scale, at = { x: 0, y: 0 }) {
|
|
@@ -98,6 +98,11 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
|
|
|
98
98
|
}
|
|
99
99
|
else if (event.key === ' ') {
|
|
100
100
|
event.preventDefault();
|
|
101
|
+
const at = this.currentValue || shown.find((option) => !option.disabled)?.value;
|
|
102
|
+
if (at) {
|
|
103
|
+
this.activeValue = at;
|
|
104
|
+
this.choose(at);
|
|
105
|
+
}
|
|
101
106
|
}
|
|
102
107
|
return;
|
|
103
108
|
}
|
|
@@ -56,6 +56,7 @@ declare const HelixHeadlessNotification_base: (new (...args: any[]) => import("@
|
|
|
56
56
|
* @csspart time-day - Format Date's `day`, forwarded for a time drawn as fields. An elapsed time draws none.
|
|
57
57
|
* @csspart time-day-period - Format Date's `day-period`, forwarded for a time drawn as fields. An elapsed time draws none.
|
|
58
58
|
* @csspart time-era - Format Date's `era`, forwarded for a time drawn as fields. An elapsed time draws none.
|
|
59
|
+
* @csspart time-fractional-second - Format Date's `fractional-second`, forwarded for a time drawn as fields. An elapsed time draws none.
|
|
59
60
|
* @csspart time-hour - Format Date's `hour`, forwarded for a time drawn as fields. An elapsed time draws none.
|
|
60
61
|
* @csspart time-minute - Format Date's `minute`, forwarded for a time drawn as fields. An elapsed time draws none.
|
|
61
62
|
* @csspart time-month - Format Date's `month`, forwarded for a time drawn as fields. An elapsed time draws none.
|
|
@@ -104,7 +104,8 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
104
104
|
renderTime() {
|
|
105
105
|
const stamp = this.tag('format-date');
|
|
106
106
|
return html `<${stamp}
|
|
107
|
-
exportparts="time, day: time-day, day-period: time-day-period, era: time-era,
|
|
107
|
+
exportparts="time, day: time-day, day-period: time-day-period, era: time-era,
|
|
108
|
+
fractional-second: time-fractional-second, hour: time-hour,
|
|
108
109
|
minute: time-minute, month: time-month, second: time-second,
|
|
109
110
|
time-zone-name: time-time-zone-name, weekday: time-weekday, year: time-year"
|
|
110
111
|
elapsed
|
|
@@ -28,6 +28,10 @@ declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion
|
|
|
28
28
|
* `min-steps-between-thumbs` keeps them that many steps apart, and the readout
|
|
29
29
|
* joins the two as `range-text` writes them.
|
|
30
30
|
*
|
|
31
|
+
* A press on a range's track takes the nearest thumb to the point pressed,
|
|
32
|
+
* and moving the pointer drags it, as React Aria's and Web Awesome's sliders
|
|
33
|
+
* do. The thumb takes focus, and `change` is sent as the press ends.
|
|
34
|
+
*
|
|
31
35
|
* `origin` sets where a single thumb's fill starts: at `min`, at the middle of
|
|
32
36
|
* the track, or at `max`. A balance or an offset from zero fills from the
|
|
33
37
|
* middle, as Zag's slider `origin` does. The fill's ends are published as
|
|
@@ -52,7 +56,7 @@ declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion
|
|
|
52
56
|
* |---|---|
|
|
53
57
|
* | Right, Up | Moves the thumb up by `step`. |
|
|
54
58
|
* | Left, Down | Moves the thumb down by `step`. |
|
|
55
|
-
* | Page Up, Page Down | Moves the thumb by
|
|
59
|
+
* | Page Up, Page Down | Moves the thumb by `page-step`, or by the platform's larger step where it is not set. |
|
|
56
60
|
* | Home, End | Moves the thumb to its lowest or highest value. |
|
|
57
61
|
*
|
|
58
62
|
* Under `readonly` none of these move the thumb, and Tab still reaches it.
|
|
@@ -97,6 +101,8 @@ declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion
|
|
|
97
101
|
* @cssproperty --slider-marker-at - Where one marker sits, as a percentage of the track.
|
|
98
102
|
* @cssproperty --slider-to - Where the filled span ends, as a fraction of the track, 0 to 1: a range's upper thumb, or the farther of a single thumb and its `origin`.
|
|
99
103
|
*
|
|
104
|
+
* @cssstate dragging - A thumb is being dragged, by its thumb or from a press on the track.
|
|
105
|
+
* @cssstate focus-visible - A thumb has focus that the keyboard brought, where the browser draws a focus ring.
|
|
100
106
|
* @cssstate invalid - The slider has an error to show, from the browser's validation or from your `feedback`.
|
|
101
107
|
*/
|
|
102
108
|
export declare class HelixHeadlessSlider extends HelixHeadlessSlider_base {
|
|
@@ -193,6 +199,12 @@ export declare class HelixHeadlessSlider extends HelixHeadlessSlider_base {
|
|
|
193
199
|
* between its thumbs and ignores it.
|
|
194
200
|
*/
|
|
195
201
|
origin?: 'center' | 'end' | 'start';
|
|
202
|
+
/**
|
|
203
|
+
* How far Page Up and Page Down move a thumb, in the slider's own units, as
|
|
204
|
+
* React Aria's `pageSize` and Zag's `largeStep` do. Absent leaves the keys
|
|
205
|
+
* to the platform, which moves a tenth of the track.
|
|
206
|
+
*/
|
|
207
|
+
pageStep?: number;
|
|
196
208
|
/**
|
|
197
209
|
* Adds a second thumb, so the value is a pair.
|
|
198
210
|
*
|
|
@@ -298,13 +310,52 @@ export declare class HelixHeadlessSlider extends HelixHeadlessSlider_base {
|
|
|
298
310
|
/** Where one value sits along the track, as a fraction of it. */
|
|
299
311
|
private along;
|
|
300
312
|
private readonly handleInput;
|
|
313
|
+
/** Whether a pointer is dragging a thumb, which `:state(dragging)` says. */
|
|
314
|
+
private dragging;
|
|
315
|
+
/** Ends a drag when its pointer lifts, wherever it lifts. */
|
|
316
|
+
private readonly endDrag;
|
|
317
|
+
private startDrag;
|
|
318
|
+
/**
|
|
319
|
+
* Half a thumb, which is where the thumb's center stops short of each end
|
|
320
|
+
* of its input. Measured off the thumb's length as the stylesheet sets it,
|
|
321
|
+
* or a platform thumb's 16 pixels where none does.
|
|
322
|
+
*/
|
|
323
|
+
private thumbInset;
|
|
324
|
+
/**
|
|
325
|
+
* The value under a pointer, along a thumb's input, less `inset` at each
|
|
326
|
+
* end. Upward on a vertical track, and from the right on a right-to-left
|
|
327
|
+
* page.
|
|
328
|
+
*/
|
|
329
|
+
private valueAt;
|
|
330
|
+
/** Moves one of a range's thumbs, kept between its neighbors, and says whether it moved. */
|
|
331
|
+
private moveThumb;
|
|
332
|
+
/**
|
|
333
|
+
* A press on the field: on a thumb, the start of the platform's own drag,
|
|
334
|
+
* and on a range's track, the nearest thumb taken to the point and dragged
|
|
335
|
+
* from there.
|
|
336
|
+
*
|
|
337
|
+
* **A range's inputs take presses on their thumbs only**, being stacked on
|
|
338
|
+
* one track, so a press beside a thumb reached no input at all and did
|
|
339
|
+
* nothing. Measured before this: a press at 90 on a range holding 20 and 60
|
|
340
|
+
* left it at 20 and 60. React Aria's and Web Awesome's sliders move the
|
|
341
|
+
* nearest thumb there.
|
|
342
|
+
*/
|
|
343
|
+
private readonly handlePress;
|
|
344
|
+
disconnectedCallback(): void;
|
|
301
345
|
/** Relayed, because `change` does not cross a shadow boundary on its own. */
|
|
302
346
|
private readonly handleChange;
|
|
303
347
|
/**
|
|
304
|
-
* Keeps a read-only thumb still under the keys that move it
|
|
305
|
-
*
|
|
348
|
+
* Keeps a read-only thumb still under the keys that move it, and moves a
|
|
349
|
+
* thumb by `page-step` under Page Up and Page Down. Tab and every other key
|
|
350
|
+
* pass, so focus moves on.
|
|
306
351
|
*/
|
|
307
352
|
private readonly handleKeydown;
|
|
353
|
+
/**
|
|
354
|
+
* Says whether the keyboard brought focus to a thumb, as
|
|
355
|
+
* `:state(focus-visible)` on the host, where a stylesheet outside can reach
|
|
356
|
+
* the whole control: a ring around the track, or the readout in bold.
|
|
357
|
+
*/
|
|
358
|
+
private readonly handleFocusChange;
|
|
308
359
|
/**
|
|
309
360
|
* Reports the value: one entry, or for a named range one entry per thumb.
|
|
310
361
|
* The joined pair is the state the browser restores.
|
|
@@ -4,9 +4,9 @@ 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, customElement, formatNumber, formatWords, HelixFormElement, MixinControllerValue, MixinField, reportValues, restoreValue, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, customElement, formatNumber, formatWords, HelixFormElement, isRtl, MixinControllerValue, MixinField, reportValues, restoreValue, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
|
-
import { property } from 'lit/decorators.js';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
const SLIDER_KEYS = new Set([
|
|
11
11
|
'ArrowDown',
|
|
12
12
|
'ArrowLeft',
|
|
@@ -63,6 +63,54 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
63
63
|
this.requestValue(next);
|
|
64
64
|
restoreValue(control, this.value);
|
|
65
65
|
};
|
|
66
|
+
this.dragging = false;
|
|
67
|
+
this.endDrag = () => {
|
|
68
|
+
this.dragging = false;
|
|
69
|
+
window.removeEventListener('pointerup', this.endDrag);
|
|
70
|
+
window.removeEventListener('pointercancel', this.endDrag);
|
|
71
|
+
};
|
|
72
|
+
this.handlePress = (event) => {
|
|
73
|
+
if (this.isDisabled || this.readOnly || event.button !== 0) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const inputs = [...this.renderRoot.querySelectorAll('input[part="control"]')];
|
|
77
|
+
if (inputs.includes(event.composedPath()[0])) {
|
|
78
|
+
this.startDrag();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const first = inputs[0];
|
|
82
|
+
const field = event.currentTarget;
|
|
83
|
+
if (!this.range || !first) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const inset = this.thumbInset(field);
|
|
87
|
+
const wanted = this.valueAt(event, first, inset);
|
|
88
|
+
let at = 0;
|
|
89
|
+
this.points.forEach((one, index) => {
|
|
90
|
+
const distance = Math.abs(one - wanted);
|
|
91
|
+
const best = Math.abs((this.points[at] ?? 0) - wanted);
|
|
92
|
+
if (distance < best || (distance === best && wanted > one)) {
|
|
93
|
+
at = index;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
this.moveThumb(at, wanted);
|
|
98
|
+
inputs[at]?.focus({ focusVisible: false, preventScroll: true });
|
|
99
|
+
this.startDrag();
|
|
100
|
+
field.setPointerCapture?.(event.pointerId);
|
|
101
|
+
const move = (next) => {
|
|
102
|
+
this.moveThumb(at, this.valueAt(next, first, inset));
|
|
103
|
+
};
|
|
104
|
+
const end = () => {
|
|
105
|
+
field.removeEventListener('pointermove', move);
|
|
106
|
+
field.removeEventListener('pointerup', end);
|
|
107
|
+
field.removeEventListener('pointercancel', end);
|
|
108
|
+
this.announceChange();
|
|
109
|
+
};
|
|
110
|
+
field.addEventListener('pointermove', move);
|
|
111
|
+
field.addEventListener('pointerup', end);
|
|
112
|
+
field.addEventListener('pointercancel', end);
|
|
113
|
+
};
|
|
66
114
|
this.handleChange = (event) => {
|
|
67
115
|
event.stopPropagation();
|
|
68
116
|
if (!this.readOnly) {
|
|
@@ -72,7 +120,31 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
72
120
|
this.handleKeydown = (event) => {
|
|
73
121
|
if (this.readOnly && SLIDER_KEYS.has(event.key)) {
|
|
74
122
|
event.preventDefault();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const by = this.pageStep;
|
|
126
|
+
if ((event.key !== 'PageUp' && event.key !== 'PageDown') || !by || this.isDisabled) {
|
|
127
|
+
return;
|
|
75
128
|
}
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
const sign = event.key === 'PageUp' ? 1 : -1;
|
|
131
|
+
if (this.range) {
|
|
132
|
+
const at = Number(event.target.dataset.index ?? 0);
|
|
133
|
+
if (!this.moveThumb(at, this.settled((this.points[at] ?? this.min) + sign * by))) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const next = String(this.settled(Number(this.value) + sign * by));
|
|
139
|
+
if (next === this.value || !this.requestValue(next)) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
this.announceChange();
|
|
144
|
+
};
|
|
145
|
+
this.handleFocusChange = (event) => {
|
|
146
|
+
const input = event.target;
|
|
147
|
+
this.toggleState('focus-visible', event.type === 'focusin' && input.matches?.(':focus-visible') === true);
|
|
76
148
|
};
|
|
77
149
|
this.valueText = '';
|
|
78
150
|
this.handleThumbSlot = () => {
|
|
@@ -132,6 +204,46 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
132
204
|
}
|
|
133
205
|
return Math.min(1, Math.max(0, (at - this.min) / span));
|
|
134
206
|
}
|
|
207
|
+
startDrag() {
|
|
208
|
+
this.dragging = true;
|
|
209
|
+
window.addEventListener('pointerup', this.endDrag);
|
|
210
|
+
window.addEventListener('pointercancel', this.endDrag);
|
|
211
|
+
}
|
|
212
|
+
thumbInset(field) {
|
|
213
|
+
const probe = document.createElement('span');
|
|
214
|
+
probe.style.cssText =
|
|
215
|
+
'position:absolute;visibility:hidden;inline-size:var(--_slider-thumb-length,16px)';
|
|
216
|
+
field.append(probe);
|
|
217
|
+
const length = probe.getBoundingClientRect().width;
|
|
218
|
+
probe.remove();
|
|
219
|
+
return length / 2;
|
|
220
|
+
}
|
|
221
|
+
valueAt(event, input, inset) {
|
|
222
|
+
const box = input.getBoundingClientRect();
|
|
223
|
+
const vertical = this.orientation === 'vertical';
|
|
224
|
+
const length = vertical ? box.height : box.width;
|
|
225
|
+
const from = vertical
|
|
226
|
+
? box.bottom - event.clientY
|
|
227
|
+
: isRtl(this)
|
|
228
|
+
? box.right - event.clientX
|
|
229
|
+
: event.clientX - box.left;
|
|
230
|
+
const fraction = Math.min(1, Math.max(0, (from - inset) / Math.max(1, length - inset * 2)));
|
|
231
|
+
return this.settled(this.min + fraction * (this.max - this.min));
|
|
232
|
+
}
|
|
233
|
+
moveThumb(at, wanted) {
|
|
234
|
+
const points = this.points;
|
|
235
|
+
const gap = this.gap;
|
|
236
|
+
const low = at > 0 ? (points[at - 1] ?? this.min) + gap : Number.NEGATIVE_INFINITY;
|
|
237
|
+
const high = at < points.length - 1 ? (points[at + 1] ?? this.max) - gap : Number.POSITIVE_INFINITY;
|
|
238
|
+
const next = points
|
|
239
|
+
.map((one, index) => (index === at ? Math.min(high, Math.max(low, wanted)) : one))
|
|
240
|
+
.join(',');
|
|
241
|
+
return next !== points.join(',') && this.requestValue(next);
|
|
242
|
+
}
|
|
243
|
+
disconnectedCallback() {
|
|
244
|
+
super.disconnectedCallback();
|
|
245
|
+
this.endDrag();
|
|
246
|
+
}
|
|
135
247
|
sync() {
|
|
136
248
|
if (this.range && this.name) {
|
|
137
249
|
reportValues(this.internals, this.name, this.points.map(String));
|
|
@@ -168,6 +280,7 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
168
280
|
}
|
|
169
281
|
willUpdate(changed) {
|
|
170
282
|
super.willUpdate(changed);
|
|
283
|
+
this.toggleState('dragging', this.dragging);
|
|
171
284
|
if (!this.hasUpdated && this.value === '') {
|
|
172
285
|
this.defaultValue = this.range
|
|
173
286
|
? `${this.min},${this.max}`
|
|
@@ -306,6 +419,9 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
306
419
|
: nothing}
|
|
307
420
|
<div
|
|
308
421
|
part="field"
|
|
422
|
+
@focusin=${this.handleFocusChange}
|
|
423
|
+
@focusout=${this.handleFocusChange}
|
|
424
|
+
@pointerdown=${this.handlePress}
|
|
309
425
|
data-markers=${this.markerValues.length > 0 ? '' : nothing}
|
|
310
426
|
data-range=${this.range ? '' : nothing}
|
|
311
427
|
data-thumbs=${this.thumbContent ? '' : nothing}
|
|
@@ -405,6 +521,9 @@ __decorate([
|
|
|
405
521
|
__decorate([
|
|
406
522
|
property({ reflect: true })
|
|
407
523
|
], HelixHeadlessSlider.prototype, "origin", void 0);
|
|
524
|
+
__decorate([
|
|
525
|
+
property({ attribute: 'page-step', type: Number })
|
|
526
|
+
], HelixHeadlessSlider.prototype, "pageStep", void 0);
|
|
408
527
|
__decorate([
|
|
409
528
|
property({ reflect: true, type: Boolean })
|
|
410
529
|
], HelixHeadlessSlider.prototype, "range", void 0);
|
|
@@ -428,6 +547,9 @@ __decorate([
|
|
|
428
547
|
__decorate([
|
|
429
548
|
property({ type: Boolean })
|
|
430
549
|
], HelixHeadlessSlider.prototype, "tooltip", void 0);
|
|
550
|
+
__decorate([
|
|
551
|
+
state()
|
|
552
|
+
], HelixHeadlessSlider.prototype, "dragging", void 0);
|
|
431
553
|
__decorate([
|
|
432
554
|
property({ attribute: 'value-text' })
|
|
433
555
|
], HelixHeadlessSlider.prototype, "valueText", void 0);
|