@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
|
@@ -4,8 +4,8 @@ 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, valueAsTime as asTime, clockLocale, customElement, formatWords, timeAsValue as fromTime, HelixFormElement, handedLocale, isClockValue, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerEditing, MixinPickerField, MixinSegmentedField, MixinTokens, timeFault, words, wordsLocale, writeClock, } from '@fusion.dev/helix-core';
|
|
8
|
-
import { clock } from '@fusion.dev/helix-icons';
|
|
7
|
+
import { AriaForwardController, valueAsTime as asTime, clockLocale, customElement, drawIcon, formatWords, timeAsValue as fromTime, HelixClearEvent, HelixFormElement, handedLocale, isClockValue, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerEditing, MixinPickerField, MixinSegmentedField, MixinTokens, readWrittenTime, timeFault, words, wordsLocale, writeClock, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { clock, x } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
@@ -19,6 +19,8 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
19
19
|
target: () => this.partNamed('control') ?? this.segmentGroup,
|
|
20
20
|
});
|
|
21
21
|
this.autocomplete = '';
|
|
22
|
+
this.clearable = false;
|
|
23
|
+
this.clearLabel = 'Clear';
|
|
22
24
|
this.announcesInput = true;
|
|
23
25
|
this.disableTimes = '';
|
|
24
26
|
this.locale = '';
|
|
@@ -34,16 +36,28 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
34
36
|
this.timeStyle = 'local';
|
|
35
37
|
this.triggerLabel = 'Choose a time';
|
|
36
38
|
this.unavailableMessage = 'Choose a time that is available.';
|
|
39
|
+
this.unreadableMessage = '{mode, select, single {Enter a time as {shape}.} other {Enter times as {shape}, separated by commas.}}';
|
|
37
40
|
this.draft = '';
|
|
41
|
+
this.handleTextCommit = (event) => {
|
|
42
|
+
if (this.mode !== 'multiple') {
|
|
43
|
+
const held = this.held;
|
|
44
|
+
const read = held.map((one) => this.read(one));
|
|
45
|
+
if (held.length > 0 && !held.every((one) => this.isTime(one)) && read.every(Boolean)) {
|
|
46
|
+
this.requestValue(read.join(','));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
this.handleCommit(event);
|
|
50
|
+
};
|
|
38
51
|
this.handleDraftKey = (event) => {
|
|
39
52
|
if (this.mode !== 'multiple') {
|
|
40
53
|
return;
|
|
41
54
|
}
|
|
42
55
|
const held = this.held;
|
|
43
|
-
|
|
56
|
+
const draft = this.read(this.draft.trim());
|
|
57
|
+
if (event.key === 'Enter' && draft !== '') {
|
|
44
58
|
event.preventDefault();
|
|
45
|
-
if (!held.includes(
|
|
46
|
-
this.requestPicked([...held,
|
|
59
|
+
if (!held.includes(draft)) {
|
|
60
|
+
this.requestPicked([...held, draft].sort().join(','));
|
|
47
61
|
}
|
|
48
62
|
this.draft = '';
|
|
49
63
|
}
|
|
@@ -91,6 +105,13 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
91
105
|
this.returnFocus();
|
|
92
106
|
}
|
|
93
107
|
};
|
|
108
|
+
this.handleClear = () => {
|
|
109
|
+
if (this.value !== '' && this.requestValue('')) {
|
|
110
|
+
this.announceChange();
|
|
111
|
+
this.dispatchEvent(new HelixClearEvent());
|
|
112
|
+
}
|
|
113
|
+
void this.updateComplete.then(() => this.returnFocus());
|
|
114
|
+
};
|
|
94
115
|
}
|
|
95
116
|
static { this.words = 'helix-time-input'; }
|
|
96
117
|
get language() {
|
|
@@ -104,7 +125,10 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
104
125
|
.filter(Boolean);
|
|
105
126
|
}
|
|
106
127
|
get segmentable() {
|
|
107
|
-
return this.mode
|
|
128
|
+
return this.mode !== 'multiple';
|
|
129
|
+
}
|
|
130
|
+
get segmentGroups() {
|
|
131
|
+
return this.mode === 'range' ? 2 : 1;
|
|
108
132
|
}
|
|
109
133
|
get segmentedKind() {
|
|
110
134
|
return 'time';
|
|
@@ -141,6 +165,9 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
141
165
|
get restCount() {
|
|
142
166
|
return Math.max(0, this.held.length - this.shownTimes.length);
|
|
143
167
|
}
|
|
168
|
+
read(text) {
|
|
169
|
+
return this.isTime(text) ? text : readWrittenTime(text, this.language, this.seconds);
|
|
170
|
+
}
|
|
144
171
|
get picked() {
|
|
145
172
|
const held = this.held;
|
|
146
173
|
return held.length > 0 && held.every((one) => this.isTime(one)) ? held.join(',') : '';
|
|
@@ -157,9 +184,7 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
157
184
|
const shape = this.seconds ? 'HH:mm:ss' : 'HH:mm';
|
|
158
185
|
if (held.length > 0 && !held.every((one) => this.isTime(one))) {
|
|
159
186
|
return (this.badInputMessage ||
|
|
160
|
-
(this.mode
|
|
161
|
-
? `Enter a time as ${shape}.`
|
|
162
|
-
: `Enter times as ${shape}, separated by commas.`));
|
|
187
|
+
formatWords(this, 'unreadableMessage', { mode: this.mode ?? 'single', shape }));
|
|
163
188
|
}
|
|
164
189
|
const rules = { disableTimes: this.disableTimes, max: this.max, min: this.min };
|
|
165
190
|
const faults = held.map((one) => timeFault(one, rules));
|
|
@@ -249,6 +274,12 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
249
274
|
${unsafeSVG(clock.content)}
|
|
250
275
|
</svg></slot>`;
|
|
251
276
|
}
|
|
277
|
+
get showsClear() {
|
|
278
|
+
return this.clearable && this.value !== '' && !this.isDisabled && !this.readOnly;
|
|
279
|
+
}
|
|
280
|
+
renderClearIcon() {
|
|
281
|
+
return html `<slot name="clear-icon">${drawIcon(x)}</slot>`;
|
|
282
|
+
}
|
|
252
283
|
renderControl() {
|
|
253
284
|
return html `<input
|
|
254
285
|
part="control"
|
|
@@ -257,6 +288,10 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
257
288
|
.value=${this.display}
|
|
258
289
|
name=${this.name || nothing}
|
|
259
290
|
placeholder=${this.mode === 'multiple' && this.held.length > 0 ? nothing : this.placeholder || nothing}
|
|
291
|
+
role="combobox"
|
|
292
|
+
aria-controls="surface"
|
|
293
|
+
aria-expanded=${this.open ? 'true' : 'false'}
|
|
294
|
+
aria-haspopup="dialog"
|
|
260
295
|
autocomplete=${this.autocomplete || nothing}
|
|
261
296
|
aria-describedby=${this.describedBy ?? nothing}
|
|
262
297
|
aria-invalid=${this.invalidity ?? nothing}
|
|
@@ -264,13 +299,17 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
264
299
|
?readonly=${this.readOnly}
|
|
265
300
|
?required=${this.required}
|
|
266
301
|
@blur=${this.handleBlur}
|
|
267
|
-
@change=${this.
|
|
302
|
+
@change=${this.handleTextCommit}
|
|
268
303
|
@focus=${this.handleFocus}
|
|
269
304
|
@input=${this.handleInput}
|
|
270
305
|
@keydown=${this.handleDraftKey}
|
|
271
306
|
@keypress=${this.handleKeypress}
|
|
272
307
|
/>`;
|
|
273
308
|
}
|
|
309
|
+
willUpdate(changed) {
|
|
310
|
+
super.willUpdate(changed);
|
|
311
|
+
this.toggleState('blank', this.value === '');
|
|
312
|
+
}
|
|
274
313
|
render() {
|
|
275
314
|
return html `${this.renderLabel()}
|
|
276
315
|
<div part="field" @keydown=${this.handleKeydown}>
|
|
@@ -280,6 +319,16 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
280
319
|
: nothing}
|
|
281
320
|
${this.segmentsShown ? this.renderSegments() : this.renderControl()}
|
|
282
321
|
<slot name="end"></slot>
|
|
322
|
+
${this.showsClear
|
|
323
|
+
? html `<button
|
|
324
|
+
part="clear"
|
|
325
|
+
type="button"
|
|
326
|
+
aria-label=${this.clearLabel}
|
|
327
|
+
@click=${this.handleClear}
|
|
328
|
+
>
|
|
329
|
+
${this.renderClearIcon()}
|
|
330
|
+
</button>`
|
|
331
|
+
: nothing}
|
|
283
332
|
<button
|
|
284
333
|
part="trigger"
|
|
285
334
|
type="button"
|
|
@@ -327,6 +376,13 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
|
|
|
327
376
|
__decorate([
|
|
328
377
|
property()
|
|
329
378
|
], HelixHeadlessTimeInput.prototype, "autocomplete", void 0);
|
|
379
|
+
__decorate([
|
|
380
|
+
property({ type: Boolean })
|
|
381
|
+
], HelixHeadlessTimeInput.prototype, "clearable", void 0);
|
|
382
|
+
__decorate([
|
|
383
|
+
words,
|
|
384
|
+
property({ attribute: 'clear-label' })
|
|
385
|
+
], HelixHeadlessTimeInput.prototype, "clearLabel", void 0);
|
|
330
386
|
__decorate([
|
|
331
387
|
property({ attribute: 'disable-times' })
|
|
332
388
|
], HelixHeadlessTimeInput.prototype, "disableTimes", void 0);
|
|
@@ -382,6 +438,10 @@ __decorate([
|
|
|
382
438
|
words,
|
|
383
439
|
property({ attribute: 'unavailable-message' })
|
|
384
440
|
], HelixHeadlessTimeInput.prototype, "unavailableMessage", void 0);
|
|
441
|
+
__decorate([
|
|
442
|
+
words,
|
|
443
|
+
property({ attribute: 'unreadable-message' })
|
|
444
|
+
], HelixHeadlessTimeInput.prototype, "unreadableMessage", void 0);
|
|
385
445
|
__decorate([
|
|
386
446
|
state()
|
|
387
447
|
], HelixHeadlessTimeInput.prototype, "draft", void 0);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixFormElement, type HourCycle } from '@fusion.dev/helix-core';
|
|
1
|
+
import { HelixFormElement, type HourCycle, type PickerFailure } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import '../../listbox/index.js';
|
|
4
4
|
import '../../option/index.js';
|
|
@@ -51,7 +51,10 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
|
|
|
51
51
|
* host's `aria-label` or its `aria-labelledby`. A development build warns
|
|
52
52
|
* where all four are empty. The list is described by the hint, or by the
|
|
53
53
|
* message in `feedback` in its place, and carries `aria-multiselectable` with
|
|
54
|
-
* a `mode
|
|
54
|
+
* a `mode`. It carries `aria-invalid` while `feedback` says what is wrong,
|
|
55
|
+
* while it holds a time outside `min` and `max` or one the picker does not
|
|
56
|
+
* offer, and, for a `required` picker with no time, once the reader has
|
|
57
|
+
* chosen one or a form has refused the picker. The wheels
|
|
55
58
|
* are a `group` with that name, holding one `listbox` per wheel, named "Hour",
|
|
56
59
|
* "Minute", "Second" and "AM/PM" in the page's language, and each described
|
|
57
60
|
* the same way.
|
|
@@ -100,7 +103,7 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
|
|
|
100
103
|
* @csspart list-loading - The text shown while the options load.
|
|
101
104
|
* @csspart list-search - The region that holds the filter field.
|
|
102
105
|
* @csspart list-search-field - The filter field.
|
|
103
|
-
* @csspart option - One time in the list. It carries `data-between` inside a range.
|
|
106
|
+
* @csspart option - One time in the list. It carries `data-between` inside a range, and inside the run to the time under the pointer while a range waits for its second end.
|
|
104
107
|
* @csspart option-between - A time inside a chosen range, excluding its ends, beside `option`.
|
|
105
108
|
* @csspart option-description - The caption under the time's label, when there is one.
|
|
106
109
|
* @csspart option-item - The row.
|
|
@@ -110,7 +113,8 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
|
|
|
110
113
|
* @csspart option-text - The label and the caption, as one column.
|
|
111
114
|
*
|
|
112
115
|
* @cssstate blank - While no time is chosen.
|
|
113
|
-
* @cssstate invalid - While `invalid` is set
|
|
116
|
+
* @cssstate invalid - While `invalid` is set, `feedback` says what is wrong, or the list carries `aria-invalid`.
|
|
117
|
+
* @cssstate range-pending - While a `range` holds its first time and waits for the second.
|
|
114
118
|
*/
|
|
115
119
|
export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_base {
|
|
116
120
|
/**
|
|
@@ -318,7 +322,18 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
|
|
|
318
322
|
* sequence.
|
|
319
323
|
*/
|
|
320
324
|
private readonly handleChange;
|
|
321
|
-
/**
|
|
325
|
+
/**
|
|
326
|
+
* The time under the pointer while a range holds one end, which the list
|
|
327
|
+
* bands the range to before it is chosen, as Date Picker does.
|
|
328
|
+
*/
|
|
329
|
+
private previewed;
|
|
330
|
+
private readonly handleListHover;
|
|
331
|
+
private readonly handleListLeave;
|
|
332
|
+
/**
|
|
333
|
+
* Whether a time falls inside a range, which is what gets banded: the
|
|
334
|
+
* chosen range, or while one end waits, the run to the time under the
|
|
335
|
+
* pointer.
|
|
336
|
+
*/
|
|
322
337
|
private between;
|
|
323
338
|
/** The readings the wheels show, from the value or from `min`. */
|
|
324
339
|
private get parts();
|
|
@@ -463,6 +478,13 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
|
|
|
463
478
|
* browser points its bubble at.
|
|
464
479
|
*/
|
|
465
480
|
private reportRequired;
|
|
481
|
+
/**
|
|
482
|
+
* What is wrong with the answer, as validity flags and a message, or nothing.
|
|
483
|
+
* `reportRequired` hands it to the form, and `failure` to the mark.
|
|
484
|
+
*/
|
|
485
|
+
private get problem();
|
|
486
|
+
/** A missing time waits for the reader; a refused one is marked at once. */
|
|
487
|
+
protected get failure(): PickerFailure | undefined;
|
|
466
488
|
/**
|
|
467
489
|
* Reports the times to the form, one entry each.
|
|
468
490
|
*
|
|
@@ -53,6 +53,19 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
53
53
|
const added = next.find((one) => !before.has(one));
|
|
54
54
|
this.requestValue(added ?? '');
|
|
55
55
|
};
|
|
56
|
+
this.previewed = '';
|
|
57
|
+
this.handleListHover = (event) => {
|
|
58
|
+
const option = event
|
|
59
|
+
.composedPath()
|
|
60
|
+
.find((one) => one instanceof HTMLElement && one.hasAttribute('value'));
|
|
61
|
+
const at = option?.getAttribute('value') ?? '';
|
|
62
|
+
if (this.mode === 'range' && this.held.length === 1 && at !== this.previewed) {
|
|
63
|
+
this.previewed = at;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
this.handleListLeave = () => {
|
|
67
|
+
this.previewed = '';
|
|
68
|
+
};
|
|
56
69
|
this.moveColumn = (part, to) => {
|
|
57
70
|
if (this.isDisabled) {
|
|
58
71
|
return;
|
|
@@ -146,8 +159,11 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
146
159
|
if (this.mode !== 'range') {
|
|
147
160
|
return false;
|
|
148
161
|
}
|
|
149
|
-
const [from, to] = this.held;
|
|
150
|
-
|
|
162
|
+
const [from, to = this.previewed] = this.held;
|
|
163
|
+
if (from === undefined || to === '' || this.held.length > 2) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
return at > (from < to ? from : to) && at < (from < to ? to : from);
|
|
151
167
|
}
|
|
152
168
|
get parts() {
|
|
153
169
|
const at = valueAsSeconds(this.value) ?? valueAsSeconds(this.min) ?? 0;
|
|
@@ -218,27 +234,41 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
218
234
|
}
|
|
219
235
|
}
|
|
220
236
|
reportRequired() {
|
|
221
|
-
const
|
|
222
|
-
if (
|
|
223
|
-
this.internals.setValidity(
|
|
237
|
+
const problem = this.problem;
|
|
238
|
+
if (problem) {
|
|
239
|
+
this.internals.setValidity(problem[0], problem[1], this.partNamed('list') ?? undefined);
|
|
224
240
|
return;
|
|
225
241
|
}
|
|
242
|
+
this.internals.setValidity({});
|
|
243
|
+
}
|
|
244
|
+
get problem() {
|
|
245
|
+
if (this.required && this.value === '') {
|
|
246
|
+
return [{ valueMissing: true }, this.valueMissingMessage];
|
|
247
|
+
}
|
|
226
248
|
const held = this.held;
|
|
227
249
|
if (held.length > 0 && held.some((one) => valueAsSeconds(one) === null)) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
250
|
+
return [
|
|
251
|
+
{ badInput: true },
|
|
252
|
+
formatWords(this, 'badInputMessage', {
|
|
253
|
+
mode: this.mode ?? 'single',
|
|
254
|
+
shape: this.seconds ? 'HH:mm:ss' : 'HH:mm',
|
|
255
|
+
}),
|
|
256
|
+
];
|
|
233
257
|
}
|
|
234
258
|
for (const one of held) {
|
|
235
259
|
const fault = this.fault(one);
|
|
236
260
|
if (fault) {
|
|
237
|
-
|
|
238
|
-
return;
|
|
261
|
+
return [{ [fault[0]]: true }, fault[1]];
|
|
239
262
|
}
|
|
240
263
|
}
|
|
241
|
-
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
get failure() {
|
|
267
|
+
const problem = this.problem;
|
|
268
|
+
if (!problem) {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
return problem[0].valueMissing ? 'missing' : 'refused';
|
|
242
272
|
}
|
|
243
273
|
report() {
|
|
244
274
|
if (this.isDisabled) {
|
|
@@ -290,7 +320,11 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
290
320
|
willUpdate(changed) {
|
|
291
321
|
super.willUpdate(changed);
|
|
292
322
|
this.report();
|
|
323
|
+
if (changed.has('value')) {
|
|
324
|
+
this.previewed = '';
|
|
325
|
+
}
|
|
293
326
|
this.toggleState('blank', this.value === '');
|
|
327
|
+
this.toggleState('range-pending', this.mode === 'range' && this.held.length === 1);
|
|
294
328
|
}
|
|
295
329
|
firstUpdated(changed) {
|
|
296
330
|
super.firstUpdated(changed);
|
|
@@ -386,6 +420,8 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
386
420
|
.value=${this.value}
|
|
387
421
|
@helix-before-change=${this.handleChange}
|
|
388
422
|
@input=${this.handleListInput}
|
|
423
|
+
@pointerleave=${this.handleListLeave}
|
|
424
|
+
@pointerover=${this.handleListHover}
|
|
389
425
|
>
|
|
390
426
|
${this.times.map((seconds) => {
|
|
391
427
|
const at = this.valueFor(seconds);
|
|
@@ -481,6 +517,9 @@ __decorate([
|
|
|
481
517
|
words,
|
|
482
518
|
property({ attribute: 'range-underflow-message' })
|
|
483
519
|
], HelixHeadlessTimePicker.prototype, "rangeUnderflowMessage", void 0);
|
|
520
|
+
__decorate([
|
|
521
|
+
state()
|
|
522
|
+
], HelixHeadlessTimePicker.prototype, "previewed", void 0);
|
|
484
523
|
__decorate([
|
|
485
524
|
property({ attribute: 'readonly', reflect: true, type: Boolean })
|
|
486
525
|
], HelixHeadlessTimePicker.prototype, "readOnly", void 0);
|
|
@@ -10,8 +10,9 @@ declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fus
|
|
|
10
10
|
*
|
|
11
11
|
* **Each item runs its own timer.** A timer held by the region would restart
|
|
12
12
|
* with each new message, and the last of three messages would stay for nine
|
|
13
|
-
* seconds. It pauses while the pointer or focus is on the message
|
|
14
|
-
* the page is hidden, so a message is not
|
|
13
|
+
* seconds. It pauses while the pointer or focus is on the message or anywhere
|
|
14
|
+
* in the region's stack, and while the page is hidden, so a message is not
|
|
15
|
+
* spent in a background tab.
|
|
15
16
|
*
|
|
16
17
|
* **Each message is an `alertdialog` named by its words**, which is how a reader
|
|
17
18
|
* who moves into the stack hears which message they are on. Give it a
|
|
@@ -20,10 +21,11 @@ declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fus
|
|
|
20
21
|
* says each message once as it arrives, a `danger` one assertively, so a
|
|
21
22
|
* message is never a live region of its own.
|
|
22
23
|
*
|
|
23
|
-
* A touch or a pen swipes a message toward the end of the line,
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `--toast-swipe-x
|
|
24
|
+
* A touch or a pen swipes a message toward the end of the line, or toward the
|
|
25
|
+
* edge the region's `swipe-direction` names, and past halfway, or quickly,
|
|
26
|
+
* dismisses it through the same `helix-dismiss` as its control. While it
|
|
27
|
+
* moves, the distance is on the `item` part as `--toast-swipe-x` or
|
|
28
|
+
* `--toast-swipe-y`.
|
|
27
29
|
*
|
|
28
30
|
* **Escape dismisses the message holding focus**, through the same
|
|
29
31
|
* `helix-dismiss`, and focus moves to the next message.
|
|
@@ -60,9 +62,10 @@ declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fus
|
|
|
60
62
|
*
|
|
61
63
|
* @cssproperty --toast-duration - How long this message stays, in milliseconds, for a progress hint.
|
|
62
64
|
* @cssproperty --toast-swipe-x - How far a swipe has moved the message across, in pixels, toward the right.
|
|
65
|
+
* @cssproperty --toast-swipe-y - How far a swipe has moved the message down, in pixels, under a `swipe-direction` of `top` or `bottom`.
|
|
63
66
|
*
|
|
64
67
|
* @cssstate closed - The message has been dismissed and is hidden, or leaving.
|
|
65
|
-
* @cssstate paused - The dismiss timer is paused, while a pointer or focus is on the message, or while the page is hidden.
|
|
68
|
+
* @cssstate paused - The dismiss timer is paused, while a pointer or focus is on the message or in the stack, or while the page is hidden.
|
|
66
69
|
* @cssstate waiting - The region holds the message back, past its `max-visible`, until a message before it goes.
|
|
67
70
|
*/
|
|
68
71
|
export declare class HelixHeadlessToastItem extends HelixHeadlessToastItem_base {
|
|
@@ -96,8 +99,24 @@ export declare class HelixHeadlessToastItem extends HelixHeadlessToastItem_base
|
|
|
96
99
|
*/
|
|
97
100
|
get open(): boolean;
|
|
98
101
|
set open(next: boolean);
|
|
102
|
+
/**
|
|
103
|
+
* @internal Set by the region: whether a pointer or focus in the stack holds
|
|
104
|
+
* this message's timer.
|
|
105
|
+
*/
|
|
106
|
+
regionHeld: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* @internal Set by the region: the edge a swipe dismisses toward, from its
|
|
109
|
+
* `swipe-direction`.
|
|
110
|
+
*/
|
|
111
|
+
swipeToward: 'bottom' | 'end' | 'start' | 'top';
|
|
112
|
+
/** Whether the timer is held for `regionHeld`, so the hold is let go once. */
|
|
113
|
+
private regionHolding;
|
|
114
|
+
/** Holds the timer while the region asks, and lets it go once. */
|
|
115
|
+
private holdForRegion;
|
|
116
|
+
/** The physical edge a swipe dismisses toward. */
|
|
117
|
+
private get swipeTo();
|
|
99
118
|
protected readonly swipes: SwipeController;
|
|
100
|
-
/** Writes how far a swipe has moved the message toward
|
|
119
|
+
/** Writes how far a swipe has moved the message toward its edge. */
|
|
101
120
|
private slide;
|
|
102
121
|
/**
|
|
103
122
|
* The dismiss timer, which pauses while a reader is on the message, and
|
|
@@ -4,30 +4,34 @@ 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 { CountdownController, customElement, drawDismiss, drawDismissGlyph, HelixElement, hostAria, isRtl, MixinDismissible, requestChange, SwipeController, settleSwipe, swipeEdge, toneMark, towardEdge, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { CountdownController, customElement, drawDismiss, drawDismissGlyph, edgeOffset, HelixElement, hostAria, isRtl, MixinDismissible, requestChange, SwipeController, settleSwipe, swipeAxis, swipeEdge, toneMark, towardEdge, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
11
11
|
let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissible(HelixElement) {
|
|
12
12
|
constructor() {
|
|
13
13
|
super(...arguments);
|
|
14
|
+
this.regionHeld = false;
|
|
15
|
+
this.swipeToward = 'end';
|
|
16
|
+
this.regionHolding = false;
|
|
14
17
|
this.swipes = new SwipeController(this, {
|
|
15
|
-
axis: () => (this.open ?
|
|
16
|
-
end: (dx,
|
|
17
|
-
const edge =
|
|
18
|
-
const
|
|
18
|
+
axis: () => (this.open ? swipeAxis(this.swipeTo) : undefined),
|
|
19
|
+
end: (dx, dy, vx, vy) => {
|
|
20
|
+
const edge = this.swipeTo;
|
|
21
|
+
const box = this.partNamed('item')?.getBoundingClientRect();
|
|
22
|
+
const across = swipeAxis(edge) === 'x';
|
|
19
23
|
const shown = settleSwipe({
|
|
20
24
|
points: [1],
|
|
21
|
-
size: width,
|
|
22
|
-
travel: Math.max(0, towardEdge(edge, dx,
|
|
23
|
-
velocity: towardEdge(edge, vx,
|
|
25
|
+
size: (across ? box?.width : box?.height) ?? 0,
|
|
26
|
+
travel: Math.max(0, towardEdge(edge, dx, dy)),
|
|
27
|
+
velocity: towardEdge(edge, vx, vy),
|
|
24
28
|
});
|
|
25
29
|
if (shown === 0 && this.dismiss()) {
|
|
26
30
|
return;
|
|
27
31
|
}
|
|
28
32
|
this.slide(0);
|
|
29
33
|
},
|
|
30
|
-
move: (dx) => this.slide(Math.max(0, towardEdge(
|
|
34
|
+
move: (dx, dy) => this.slide(Math.max(0, towardEdge(this.swipeTo, dx, dy))),
|
|
31
35
|
start: (_target, event) => event.pointerType !== 'mouse',
|
|
32
36
|
surface: () => this.partNamed('item'),
|
|
33
37
|
});
|
|
@@ -81,9 +85,27 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
|
|
|
81
85
|
this.openState = next;
|
|
82
86
|
this.requestUpdate('open', previous);
|
|
83
87
|
}
|
|
88
|
+
holdForRegion() {
|
|
89
|
+
const want = this.regionHeld && this.isConnected;
|
|
90
|
+
if (want === this.regionHolding) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.regionHolding = want;
|
|
94
|
+
if (want) {
|
|
95
|
+
this.countdown.hold();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.countdown.release();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
get swipeTo() {
|
|
102
|
+
return swipeEdge(this.swipeToward, isRtl(this));
|
|
103
|
+
}
|
|
84
104
|
slide(travel) {
|
|
85
|
-
const
|
|
86
|
-
|
|
105
|
+
const item = this.partNamed('item');
|
|
106
|
+
const { x, y } = edgeOffset(this.swipeTo, travel);
|
|
107
|
+
item?.style.setProperty('--toast-swipe-x', x === 0 ? '' : `${Math.round(x)}px`);
|
|
108
|
+
item?.style.setProperty('--toast-swipe-y', y === 0 ? '' : `${Math.round(y)}px`);
|
|
87
109
|
}
|
|
88
110
|
wait(waiting) {
|
|
89
111
|
if (waiting) {
|
|
@@ -133,12 +155,14 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
|
|
|
133
155
|
else {
|
|
134
156
|
this.resume();
|
|
135
157
|
}
|
|
158
|
+
this.holdForRegion();
|
|
136
159
|
}
|
|
137
160
|
this.addEventListener('keydown', this.handleKeydown);
|
|
138
161
|
}
|
|
139
162
|
disconnectedCallback() {
|
|
140
163
|
super.disconnectedCallback();
|
|
141
164
|
this.holding = false;
|
|
165
|
+
this.regionHolding = false;
|
|
142
166
|
this.removeEventListener('keydown', this.handleKeydown);
|
|
143
167
|
}
|
|
144
168
|
willUpdate(changed) {
|
|
@@ -156,6 +180,12 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
|
|
|
156
180
|
if (changed.has('waiting')) {
|
|
157
181
|
this.wait(this.waiting);
|
|
158
182
|
}
|
|
183
|
+
if (changed.has('regionHeld')) {
|
|
184
|
+
this.holdForRegion();
|
|
185
|
+
}
|
|
186
|
+
if (changed.has('swipeToward')) {
|
|
187
|
+
this.partNamed('item')?.style.setProperty('touch-action', swipeAxis(this.swipeTo) === 'y' ? 'pan-x' : '');
|
|
188
|
+
}
|
|
159
189
|
}
|
|
160
190
|
assigned(name) {
|
|
161
191
|
return (this.renderRoot
|
|
@@ -229,6 +259,12 @@ __decorate([
|
|
|
229
259
|
__decorate([
|
|
230
260
|
property({ type: Boolean })
|
|
231
261
|
], HelixHeadlessToastItem.prototype, "open", null);
|
|
262
|
+
__decorate([
|
|
263
|
+
property({ attribute: false, type: Boolean })
|
|
264
|
+
], HelixHeadlessToastItem.prototype, "regionHeld", void 0);
|
|
265
|
+
__decorate([
|
|
266
|
+
property({ attribute: false })
|
|
267
|
+
], HelixHeadlessToastItem.prototype, "swipeToward", void 0);
|
|
232
268
|
__decorate([
|
|
233
269
|
property({ reflect: true })
|
|
234
270
|
], HelixHeadlessToastItem.prototype, "tone", void 0);
|
|
@@ -34,7 +34,12 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
34
34
|
* to render them from: it appends a message of the region's own tier and
|
|
35
35
|
* removes it once it has been dismissed and has finished leaving. Messages
|
|
36
36
|
* you render yourself stay yours, and the region never removes them. A touch
|
|
37
|
-
* or a pen swipes a message away toward the end of the line
|
|
37
|
+
* or a pen swipes a message away toward the end of the line, or toward the
|
|
38
|
+
* edge `swipe-direction` names.
|
|
39
|
+
*
|
|
40
|
+
* **Every message's timer waits while a pointer or focus is in the stack**, as
|
|
41
|
+
* React Aria's and Radix's regions do, so a reader moving from one message to
|
|
42
|
+
* the next does not lose the one behind them.
|
|
38
43
|
*
|
|
39
44
|
* **The stack is in the top layer.** The `region` part is a `popover="manual"`
|
|
40
45
|
* shown while a message is showing, and shown again above a modal dialog that
|
|
@@ -127,6 +132,11 @@ export declare class HelixHeadlessToast extends HelixElement {
|
|
|
127
132
|
* departures, which is what `orientation` does.
|
|
128
133
|
*/
|
|
129
134
|
placement?: 'bottom-center' | 'bottom-end' | 'bottom-start' | 'top-center' | 'top-end' | 'top-start';
|
|
135
|
+
/**
|
|
136
|
+
* The edge a touch or a pen swipes a message toward to dismiss it: `end`,
|
|
137
|
+
* the end of the line, or `start`, `top` or `bottom`. Unset is `end`.
|
|
138
|
+
*/
|
|
139
|
+
swipeDirection?: 'bottom' | 'end' | 'start' | 'top';
|
|
130
140
|
protected willUpdate(changed: PropertyValues): void;
|
|
131
141
|
/** What the polite region is saying. */
|
|
132
142
|
private said;
|
|
@@ -190,6 +200,19 @@ export declare class HelixHeadlessToast extends HelixElement {
|
|
|
190
200
|
private watcher;
|
|
191
201
|
/** The messages that are showing, in order. */
|
|
192
202
|
private get showing();
|
|
203
|
+
/** Whether a pointer is over the stack. */
|
|
204
|
+
private pointerIn;
|
|
205
|
+
/** Whether focus is in the stack. */
|
|
206
|
+
private focusIn;
|
|
207
|
+
/**
|
|
208
|
+
* Tells every message whether the stack holds its timer and which way it
|
|
209
|
+
* swipes, which only the region knows.
|
|
210
|
+
*/
|
|
211
|
+
private inform;
|
|
212
|
+
private readonly handlePointerEnter;
|
|
213
|
+
private readonly handlePointerLeave;
|
|
214
|
+
/** Focus leaving the stack, and not moving between its messages. */
|
|
215
|
+
private readonly handleFocusOut;
|
|
193
216
|
/** The messages `max-visible` holds back. */
|
|
194
217
|
private readonly held;
|
|
195
218
|
/**
|