@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
|
@@ -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);
|
|
@@ -37,7 +37,7 @@ declare const HelixHeadlessToggleGroup_base: (new (...args: any[]) => import("@f
|
|
|
37
37
|
* | Right, Down | Moves to the next item, from the last back to the first. In a single group, chooses it. |
|
|
38
38
|
* | Left, Up | Moves to the item before, from the first round to the last. In a single group, chooses it. |
|
|
39
39
|
* | Home, End | Moves to the first or the last item. In a single group, chooses it. |
|
|
40
|
-
* | Space, Enter | Chooses the focused item, or presses or releases it in a multiple group. |
|
|
40
|
+
* | Space, Enter | Chooses the focused item, or presses or releases it in a multiple group. With `allow-empty-selection`, takes back the chosen item. |
|
|
41
41
|
*
|
|
42
42
|
* Left and Right swap on a right-to-left page, and a disabled item is passed
|
|
43
43
|
* over. A single group is a `radiogroup` of items with `role="radio"` and
|
|
@@ -117,6 +117,15 @@ export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_b
|
|
|
117
117
|
* `MixinCollection` records for the same reason.
|
|
118
118
|
*/
|
|
119
119
|
private adopt;
|
|
120
|
+
/**
|
|
121
|
+
* The items whose `selected` this group has written, which `adopt` passes
|
|
122
|
+
* over: the mark is this group's answer, and the markup declared nothing.
|
|
123
|
+
*
|
|
124
|
+
* Measured before this: in a group with no default, choosing List and then
|
|
125
|
+
* resetting the form left List chosen and submitted, because `adopt` read
|
|
126
|
+
* the mark `sync` had put on it as a declaration and made it the default.
|
|
127
|
+
*/
|
|
128
|
+
private readonly marked;
|
|
120
129
|
/** Marks the chosen item, separately from placing the indicator. */
|
|
121
130
|
private sync;
|
|
122
131
|
/**
|
|
@@ -139,6 +148,14 @@ export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_b
|
|
|
139
148
|
private get chosenItem();
|
|
140
149
|
/** Whether several items can be pressed at once. */
|
|
141
150
|
private get multiple();
|
|
151
|
+
/**
|
|
152
|
+
* Whether a press on the chosen item, or Space or Enter on it, takes it
|
|
153
|
+
* back so nothing is chosen, as React Aria's `ToggleButtonGroup` and
|
|
154
|
+
* Radix's `ToggleGroup` do in a single group. Absent, a single group always
|
|
155
|
+
* keeps its choice once it has one, as a radio group does. A multiple group
|
|
156
|
+
* releases a pressed item either way.
|
|
157
|
+
*/
|
|
158
|
+
allowEmptySelection: boolean;
|
|
142
159
|
/**
|
|
143
160
|
* Whether the group holds a tab stop of its own.
|
|
144
161
|
*
|
|
@@ -11,6 +11,7 @@ import { HelixHeadlessToggle } from '../root/toggle-root.js';
|
|
|
11
11
|
let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinControllerValue(MixinField(HelixFormElement)) {
|
|
12
12
|
constructor() {
|
|
13
13
|
super(...arguments);
|
|
14
|
+
this.marked = new WeakSet();
|
|
14
15
|
this.lastFocused = undefined;
|
|
15
16
|
this.noteFocus = (event) => {
|
|
16
17
|
const item = event.target;
|
|
@@ -19,6 +20,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
19
20
|
this.sync();
|
|
20
21
|
}
|
|
21
22
|
};
|
|
23
|
+
this.allowEmptySelection = false;
|
|
22
24
|
this.focusable = true;
|
|
23
25
|
this.indicator = new IndicatorController(this, {
|
|
24
26
|
active: () => this.chosenItem,
|
|
@@ -40,7 +42,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
40
42
|
}
|
|
41
43
|
const value = event.detail.value;
|
|
42
44
|
if (!this.multiple) {
|
|
43
|
-
this.requestValue(value);
|
|
45
|
+
this.requestValue(this.allowEmptySelection && value === this.value ? '' : value);
|
|
44
46
|
return;
|
|
45
47
|
}
|
|
46
48
|
const next = toggleValue(this.values, value, this.items.map((item) => item.value));
|
|
@@ -63,7 +65,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
63
65
|
if (this.value !== '' || this.children === undefined) {
|
|
64
66
|
return;
|
|
65
67
|
}
|
|
66
|
-
const declared = this.items.find((item) => item.selected);
|
|
68
|
+
const declared = this.items.find((item) => item.defaultSelected || (item.selected && !this.marked.has(item)));
|
|
67
69
|
if (declared) {
|
|
68
70
|
this.defaultValue = declared.value;
|
|
69
71
|
}
|
|
@@ -73,6 +75,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
73
75
|
const pressed = new Set(this.values);
|
|
74
76
|
const entry = this.focusable ? this.entry() : undefined;
|
|
75
77
|
for (const item of this.items) {
|
|
78
|
+
this.marked.add(item);
|
|
76
79
|
item.selected = item === active;
|
|
77
80
|
item.pressed = this.multiple ? pressed.has(item.value) : undefined;
|
|
78
81
|
item.focusable = item === entry;
|
|
@@ -192,6 +195,9 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
192
195
|
return html `${inside ? nothing : label}<div part="row"><slot name="start"></slot><div part="track">${inside ? label : nothing}<div part="indicator" aria-hidden="true"></div><slot @slotchange=${this.handleItems}></slot></div><slot name="end"></slot></div>${this.renderHint()}${this.renderFeedback()}`;
|
|
193
196
|
}
|
|
194
197
|
};
|
|
198
|
+
__decorate([
|
|
199
|
+
property({ attribute: 'allow-empty-selection', type: Boolean })
|
|
200
|
+
], HelixHeadlessToggleGroup.prototype, "allowEmptySelection", void 0);
|
|
195
201
|
__decorate([
|
|
196
202
|
property({ attribute: false })
|
|
197
203
|
], HelixHeadlessToggleGroup.prototype, "focusable", void 0);
|
|
@@ -31,9 +31,9 @@ declare const HelixHeadlessToggle_base: (new (...args: any[]) => import("@fusion
|
|
|
31
31
|
* @event {Event} change - Fired after a toggle button on its own is pressed or released.
|
|
32
32
|
* @event {HelixSelectEvent} helix-select - Fired when the item is chosen. Cancelable: call `preventDefault()` to keep the current choice.
|
|
33
33
|
*
|
|
34
|
-
* @csspart item - The radio button.
|
|
34
|
+
* @csspart item - The button: a radio in a group where one is chosen, and a button with `aria-pressed` on its own or in a group where several can be.
|
|
35
35
|
*
|
|
36
|
-
* @cssstate pressed - The item is pressed, in a group where several can be.
|
|
36
|
+
* @cssstate pressed - The item is pressed, on its own or in a group where several can be.
|
|
37
37
|
* @cssstate selected - The item is chosen.
|
|
38
38
|
*/
|
|
39
39
|
export declare class HelixHeadlessToggle extends HelixHeadlessToggle_base {
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { customElement, HelixElement, HostClickController, MixinSelectableItem, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { auditName, customElement, HelixElement, HostClickController, MixinSelectableItem, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(HelixElement) {
|
|
@@ -39,6 +39,7 @@ let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(
|
|
|
39
39
|
firstUpdated(changed) {
|
|
40
40
|
super.firstUpdated(changed);
|
|
41
41
|
this.finishHydration();
|
|
42
|
+
auditName(this, () => this.partNamed('item'), 'Give it words in its default slot, an `aria-label`, or a label on the icon it holds.');
|
|
42
43
|
}
|
|
43
44
|
render() {
|
|
44
45
|
const alone = this.alone;
|