@fusion.dev/helix-headless 0.159.0 → 0.161.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 +2980 -189
- package/dist/canvas/root/canvas-root.d.ts +59 -2
- package/dist/canvas/root/canvas-root.js +123 -8
- package/dist/chat/message/chat-message.d.ts +8 -0
- package/dist/chat/message/chat-message.js +11 -0
- package/dist/chat/root/chat-root.d.ts +47 -1
- package/dist/chat/root/chat-root.js +130 -4
- package/dist/code-block/lines.d.ts +12 -0
- package/dist/code-block/lines.js +41 -0
- package/dist/code-block/root/code-block-root.d.ts +88 -5
- package/dist/code-block/root/code-block-root.js +154 -10
- 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 +6 -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/splitter/root/splitter-root.d.ts +15 -0
- package/dist/splitter/root/splitter-root.js +16 -2
- package/dist/table/grid-controller.d.ts +7 -1
- package/dist/table/grid-controller.js +9 -1
- package/dist/table/root/table-root.d.ts +122 -0
- package/dist/table/root/table-root.js +293 -17
- package/dist/table/row/table-row.d.ts +41 -0
- package/dist/table/row/table-row.js +56 -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/timeline/item/timeline-item.d.ts +47 -10
- package/dist/timeline/item/timeline-item.js +36 -4
- 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/video-playlist/item/video-playlist-item.d.ts +11 -0
- package/dist/video-playlist/item/video-playlist-item.js +5 -0
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +6 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +11 -0
- package/dist/video-playlist/root/video-playlist-root.d.ts +59 -5
- package/dist/video-playlist/root/video-playlist-root.js +82 -10
- package/dist/virtual-list/fill.d.ts +6 -1
- package/dist/virtual-list/fill.js +1 -1
- package/dist/virtual-list/keys.d.ts +8 -0
- package/dist/virtual-list/keys.js +20 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +98 -13
- package/dist/virtual-list/root/virtual-list-root.js +195 -20
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +1596 -224
- package/types/vue.d.ts +404 -49
- package/types/words.d.ts +151 -0
|
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
import { customElement, followLink, HelixElement, hostAria, pressedOwnControl, } from '@fusion.dev/helix-core';
|
|
8
|
-
import { dotsSixVertical } from '@fusion.dev/helix-icons';
|
|
8
|
+
import { caretRight, dotsSixVertical } 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';
|
|
@@ -17,6 +17,7 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
|
|
|
17
17
|
this.isTableRow = true;
|
|
18
18
|
this.disabled = false;
|
|
19
19
|
this.href = '';
|
|
20
|
+
this.parent = '';
|
|
20
21
|
this.rel = '';
|
|
21
22
|
this.target = '';
|
|
22
23
|
this.value = '';
|
|
@@ -29,6 +30,12 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
|
|
|
29
30
|
this.move = undefined;
|
|
30
31
|
this.gridCell = false;
|
|
31
32
|
this.selectStop = false;
|
|
33
|
+
this.level = 0;
|
|
34
|
+
this.branch = false;
|
|
35
|
+
this.open = false;
|
|
36
|
+
this.expandLabel = '';
|
|
37
|
+
this.expandStop = false;
|
|
38
|
+
this.expandWith = undefined;
|
|
32
39
|
this.columnRules = undefined;
|
|
33
40
|
this.handleClick = (event) => {
|
|
34
41
|
if (this.disabled || this.href === '' || event.button !== 0 || event.defaultPrevented) {
|
|
@@ -70,6 +77,10 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
|
|
|
70
77
|
hostAria(this.internals, { role: 'row' });
|
|
71
78
|
this.internals.ariaSelected = this.gridCell && this.selectable ? String(this.selected) : null;
|
|
72
79
|
this.internals.ariaDisabled = this.disabled ? 'true' : null;
|
|
80
|
+
const tree = this.gridCell && this.level > 0;
|
|
81
|
+
this.internals.ariaLevel = tree ? String(this.level) : null;
|
|
82
|
+
this.internals.ariaExpanded = tree && this.branch ? String(this.open) : null;
|
|
83
|
+
this.toggleState('expanded', this.branch && this.open);
|
|
73
84
|
this.toggleState('selected', this.selectable && this.selected);
|
|
74
85
|
this.toggleState('link', this.href !== '' && !this.disabled);
|
|
75
86
|
this.toggleState('dragging', this.move === 'dragging');
|
|
@@ -125,8 +136,30 @@ let HelixHeadlessTableRow = class HelixHeadlessTableRow extends HelixElement {
|
|
|
125
136
|
><svg aria-hidden="true" fill="currentColor" part="grip-mark" viewBox=${dotsSixVertical.viewBox}>${unsafeSVG(dotsSixVertical.content)}</svg></button
|
|
126
137
|
></span>`;
|
|
127
138
|
}
|
|
139
|
+
renderExpand() {
|
|
140
|
+
if (this.level === 0) {
|
|
141
|
+
return nothing;
|
|
142
|
+
}
|
|
143
|
+
const grid = this.gridCell;
|
|
144
|
+
return html `<span
|
|
145
|
+
part="expand"
|
|
146
|
+
role=${grid ? 'gridcell' : 'cell'}
|
|
147
|
+
style="--table-row-level: ${this.level}"
|
|
148
|
+
tabindex=${grid ? (this.expandStop ? '0' : '-1') : nothing}
|
|
149
|
+
>${this.branch
|
|
150
|
+
? html `<button
|
|
151
|
+
part="expand-button"
|
|
152
|
+
type="button"
|
|
153
|
+
tabindex=${grid ? '-1' : nothing}
|
|
154
|
+
aria-label=${this.expandLabel}
|
|
155
|
+
aria-expanded=${this.open ? 'true' : 'false'}
|
|
156
|
+
@click=${() => this.expandWith?.()}
|
|
157
|
+
><svg aria-hidden="true" data-mirror fill="currentColor" part="expand-mark" viewBox=${caretRight.viewBox}>${unsafeSVG(caretRight.content)}</svg></button>`
|
|
158
|
+
: nothing}</span
|
|
159
|
+
>`;
|
|
160
|
+
}
|
|
128
161
|
render() {
|
|
129
|
-
return html `${this.renderGrip()}${this.renderSelect()}<slot @slotchange=${this.handleSlotChange}></slot>`;
|
|
162
|
+
return html `${this.renderGrip()}${this.renderSelect()}${this.renderExpand()}<slot @slotchange=${this.handleSlotChange}></slot>`;
|
|
130
163
|
}
|
|
131
164
|
};
|
|
132
165
|
__decorate([
|
|
@@ -135,6 +168,9 @@ __decorate([
|
|
|
135
168
|
__decorate([
|
|
136
169
|
property()
|
|
137
170
|
], HelixHeadlessTableRow.prototype, "href", void 0);
|
|
171
|
+
__decorate([
|
|
172
|
+
property()
|
|
173
|
+
], HelixHeadlessTableRow.prototype, "parent", void 0);
|
|
138
174
|
__decorate([
|
|
139
175
|
property()
|
|
140
176
|
], HelixHeadlessTableRow.prototype, "rel", void 0);
|
|
@@ -171,6 +207,24 @@ __decorate([
|
|
|
171
207
|
__decorate([
|
|
172
208
|
state()
|
|
173
209
|
], HelixHeadlessTableRow.prototype, "selectStop", void 0);
|
|
210
|
+
__decorate([
|
|
211
|
+
state()
|
|
212
|
+
], HelixHeadlessTableRow.prototype, "level", void 0);
|
|
213
|
+
__decorate([
|
|
214
|
+
state()
|
|
215
|
+
], HelixHeadlessTableRow.prototype, "branch", void 0);
|
|
216
|
+
__decorate([
|
|
217
|
+
state()
|
|
218
|
+
], HelixHeadlessTableRow.prototype, "open", void 0);
|
|
219
|
+
__decorate([
|
|
220
|
+
state()
|
|
221
|
+
], HelixHeadlessTableRow.prototype, "expandLabel", void 0);
|
|
222
|
+
__decorate([
|
|
223
|
+
state()
|
|
224
|
+
], HelixHeadlessTableRow.prototype, "expandStop", void 0);
|
|
225
|
+
__decorate([
|
|
226
|
+
state()
|
|
227
|
+
], HelixHeadlessTableRow.prototype, "expandWith", void 0);
|
|
174
228
|
HelixHeadlessTableRow = __decorate([
|
|
175
229
|
customElement('helix-headless-table-row')
|
|
176
230
|
], HelixHeadlessTableRow);
|
|
@@ -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
|
|
@@ -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);
|