@fusion.dev/helix-headless 0.159.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 +1252 -140
- 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 +2 -0
- 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/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/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/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/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +660 -156
- package/types/vue.d.ts +178 -37
- package/types/words.d.ts +72 -0
|
@@ -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);
|
|
@@ -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);
|
|
@@ -19,20 +19,25 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
|
|
|
19
19
|
* `seconds`, because that is what the form
|
|
20
20
|
* submits, what `min` and `max` compare against, and what sorts correctly as a
|
|
21
21
|
* string. While idle, the field shows the time in the user's locale format.
|
|
22
|
-
* Once the caret is in the field, it shows the value itself
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* Once the caret is in the field, it shows the value itself. You can type
|
|
23
|
+
* `14:30`, or a time as you write one, such as `2:30 PM`, `2.30pm`, or
|
|
24
|
+
* `14h30`, and the field reads it as you leave it, with AM and PM in the
|
|
25
|
+
* locale's words.
|
|
25
26
|
*
|
|
26
27
|
* Typing sends `input` on each keystroke and `change` when you leave the
|
|
27
28
|
* field. The trigger and ArrowDown open the list, and so does a press into the
|
|
28
|
-
* field; Tab into the field opens nothing.
|
|
29
|
+
* field; Tab into the field opens nothing. The text field is a `combobox`
|
|
30
|
+
* with `aria-haspopup="dialog"`, `aria-expanded`, and `aria-controls`, since
|
|
31
|
+
* Down opens the list from it. In the list, the arrow keys, typed digits and PageUp and PageDown
|
|
29
32
|
* move through the times, and Enter, Space or a click chooses one and closes
|
|
30
33
|
* the panel. With `variant="wheels"` each wheel moves the time as you turn it,
|
|
31
34
|
* the panel stays open, and Enter or Escape closes it.
|
|
32
35
|
*
|
|
33
36
|
* With `segmented`, the field is a row of segments in place of the text: an
|
|
34
37
|
* hour, a minute, and AM or PM where the clock has them, each set with digits
|
|
35
|
-
* or the arrow keys. Alt+Down opens the list from a segment.
|
|
38
|
+
* or the arrow keys. Alt+Down opens the list from a segment. A `range` is two
|
|
39
|
+
* rows with a dash between them, the start and the end, and `multiple` keeps
|
|
40
|
+
* the text.
|
|
36
41
|
*
|
|
37
42
|
* Text that is not a time gives the form an empty value, as a native time
|
|
38
43
|
* field does, and `value` keeps the text so it can be corrected.
|
|
@@ -62,6 +67,7 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
|
|
|
62
67
|
* @aliases TimeField, InputTime
|
|
63
68
|
* @documentation https://fusion.dev/helix/components/time-input
|
|
64
69
|
*
|
|
70
|
+
* @slot clear-icon - Replaces the cross on the clear button, under `clearable`.
|
|
65
71
|
* @slot end - Content after the time, before the trigger, such as a clear button.
|
|
66
72
|
* @slot feedback - The message that says what is wrong with the value.
|
|
67
73
|
* @slot hint - Help under the field.
|
|
@@ -75,9 +81,11 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
|
|
|
75
81
|
* @event {HelixBeforeChangeEvent} helix-before-change - Fired before the time changes. Cancelable: canceling it stops the change and leaves the decision to you.
|
|
76
82
|
* @event {HelixBeforeCloseEvent} helix-before-close - Fired before the list closes. Cancelable: canceling it keeps the list open and leaves the decision to you.
|
|
77
83
|
* @event {HelixBeforeOpenEvent} helix-before-open - Fired before the list opens. Cancelable: canceling it keeps the list closed and leaves the decision to you.
|
|
84
|
+
* @event {HelixClearEvent} helix-clear - Fired after the clear button has emptied the field, after `input` and `change`.
|
|
78
85
|
* @event {Event} input - Fired after the value changes.
|
|
79
86
|
* @event {ToggleEvent} toggle - Fired after the list opens or closes, with `newState` and `oldState`.
|
|
80
87
|
*
|
|
88
|
+
* @csspart clear - The button that empties the field, under `clearable`, while it holds a time.
|
|
81
89
|
* @csspart control - The text input.
|
|
82
90
|
* @csspart feedback - The message that says what is wrong with the value.
|
|
83
91
|
* @csspart field - The box that holds the text and the trigger.
|
|
@@ -123,6 +131,7 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
|
|
|
123
131
|
* @csspart picker-option-item-selected - The row of a chosen time: the one time, or either end of a range.
|
|
124
132
|
* @csspart picker-option-label - The option's label.
|
|
125
133
|
* @csspart picker-option-text - The label and the caption, as one column.
|
|
134
|
+
* @csspart range-separator - The dash between a range's two rows of segments, in `segmented`, beside `literal`.
|
|
126
135
|
* @csspart rest - The count of times held beyond the ones shown. A `<button>` with `expandable`, and a `<span>` without it.
|
|
127
136
|
* @csspart segment - One part of the time, an hour, a minute, a second or AM and PM, in `segmented`.
|
|
128
137
|
* @csspart segment-placeholder - A segment with nothing in it yet. Applied with `segment`.
|
|
@@ -139,6 +148,7 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
|
|
|
139
148
|
* @csspart trigger - The control that opens the list.
|
|
140
149
|
* @csspart trigger-icon - The icon on the button that opens the clock.
|
|
141
150
|
*
|
|
151
|
+
* @cssstate blank - The field holds no time.
|
|
142
152
|
* @cssstate invalid - The control has an error to show, from the browser's validation or from your `feedback`.
|
|
143
153
|
* @cssstate open - While the clock is open.
|
|
144
154
|
*/
|
|
@@ -156,6 +166,14 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
|
|
|
156
166
|
* lets autofill work at all.
|
|
157
167
|
*/
|
|
158
168
|
autocomplete: string;
|
|
169
|
+
/**
|
|
170
|
+
* Whether the field shows a button that empties it, as Input's `clearable`
|
|
171
|
+
* does. Shown only while the field holds something to clear and is neither
|
|
172
|
+
* disabled nor read-only.
|
|
173
|
+
*/
|
|
174
|
+
clearable: boolean;
|
|
175
|
+
/** The accessible name of the clear button, which shows only an icon. */
|
|
176
|
+
clearLabel: string;
|
|
159
177
|
/** Typed, so a keystroke is announced as `input` alone. See `announcesInput`. */
|
|
160
178
|
protected readonly announcesInput = true;
|
|
161
179
|
/**
|
|
@@ -256,13 +274,20 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
|
|
|
256
274
|
* The message shown when `disable-times` bars a time.
|
|
257
275
|
*/
|
|
258
276
|
unavailableMessage: string;
|
|
277
|
+
/**
|
|
278
|
+
* The message for text that is no time, where `bad-input-message` is not
|
|
279
|
+
* set. `{mode}` is `single`, `range`, or `multiple`, and `{shape}` is `HH:mm`, or `HH:mm:ss` with `seconds`.
|
|
280
|
+
*/
|
|
281
|
+
unreadableMessage: string;
|
|
259
282
|
/** Every time in the value, which may hold more than one. */
|
|
260
283
|
private get held();
|
|
261
284
|
/**
|
|
262
|
-
*
|
|
263
|
-
*
|
|
285
|
+
* A row of segments holds one time, and `range` takes two rows with a dash
|
|
286
|
+
* between them, as React Aria's range fields do. `multiple` keeps the line
|
|
287
|
+
* of text, whose tags are how it writes several.
|
|
264
288
|
*/
|
|
265
289
|
protected get segmentable(): boolean;
|
|
290
|
+
protected get segmentGroups(): number;
|
|
266
291
|
protected get segmentedKind(): 'time';
|
|
267
292
|
protected get segmentLocale(): string;
|
|
268
293
|
protected get segmentOptions(): {
|
|
@@ -287,6 +312,18 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
|
|
|
287
312
|
private get shownTimes();
|
|
288
313
|
/** How many are held beyond the ones shown. */
|
|
289
314
|
private get restCount();
|
|
315
|
+
/**
|
|
316
|
+
* A time as the reader wrote it, as the value writes one: `HH:mm` as it
|
|
317
|
+
* is, and `2:30 PM`, `2.30pm`, `14h30`, or `1430` read back, with the half
|
|
318
|
+
* of the day in the locale's words. Empty where it is no time.
|
|
319
|
+
*/
|
|
320
|
+
private read;
|
|
321
|
+
/**
|
|
322
|
+
* The text the reader leaves, read: a time written as a reader writes one
|
|
323
|
+
* becomes the value as the field commits. Not on each key, where `2:3` is
|
|
324
|
+
* a time a moment before it is `2:30`.
|
|
325
|
+
*/
|
|
326
|
+
private readonly handleTextCommit;
|
|
290
327
|
/** Adds what was typed to the set, or takes the last one back out. */
|
|
291
328
|
private readonly handleDraftKey;
|
|
292
329
|
/** Takes one time back out of the set. */
|
|
@@ -368,8 +405,19 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
|
|
|
368
405
|
* consumer gets an inert unknown element and no mark whatsoever.
|
|
369
406
|
*/
|
|
370
407
|
protected renderTrigger(): TemplateResult;
|
|
408
|
+
/** Whether there is a clear button on screen right now. */
|
|
409
|
+
private get showsClear();
|
|
410
|
+
/**
|
|
411
|
+
* Empties the field, through `requestValue` so a consumer who owns the
|
|
412
|
+
* value decides, then says `change` and `helix-clear` and puts focus back
|
|
413
|
+
* in the field, the button having gone.
|
|
414
|
+
*/
|
|
415
|
+
private readonly handleClear;
|
|
416
|
+
/** The cross on the clear button, behind a seam a tier can move. */
|
|
417
|
+
protected renderClearIcon(): TemplateResult;
|
|
371
418
|
/** The line of text the times are typed into, where the field shows no segments. */
|
|
372
419
|
private renderControl;
|
|
420
|
+
protected willUpdate(changed: PropertyValues): void;
|
|
373
421
|
protected render(): TemplateResult;
|
|
374
422
|
/**
|
|
375
423
|
* The value as a `Date` on January 1, 1970 UTC, or `null` while there is no
|