@fusion.dev/helix-headless 0.143.0 → 0.144.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 +8551 -375
- package/dist/area-chart/root/area-chart-root.d.ts +12 -4
- package/dist/area-chart/root/area-chart-root.js +24 -7
- package/dist/avatar/root/avatar-root.d.ts +14 -0
- package/dist/avatar/root/avatar-root.js +23 -9
- package/dist/badge/root/badge-root.d.ts +11 -7
- package/dist/badge/root/badge-root.js +1 -1
- package/dist/bar-chart/root/bar-chart-root.d.ts +12 -3
- package/dist/bar-chart/root/bar-chart-root.js +17 -10
- package/dist/button/root/button-root.d.ts +47 -1
- package/dist/button/root/button-root.js +38 -4
- package/dist/callout/root/callout-root.d.ts +18 -1
- package/dist/callout/root/callout-root.js +15 -4
- package/dist/card/root/card-root.d.ts +30 -1
- package/dist/card/root/card-root.js +38 -5
- package/dist/checkbox/root/checkbox-root.js +2 -0
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +53 -3
- package/dist/checkbox-group/root/checkbox-group-root.js +71 -22
- package/dist/code-block/lines.d.ts +13 -0
- package/dist/code-block/lines.js +2 -0
- package/dist/code-block/root/code-block-root.d.ts +30 -0
- package/dist/code-block/root/code-block-root.js +23 -2
- package/dist/color-input/root/color-input-root.d.ts +34 -1
- package/dist/color-input/root/color-input-root.js +23 -4
- package/dist/color-picker/channels.d.ts +26 -0
- package/dist/color-picker/channels.js +84 -0
- package/dist/color-picker/root/color-picker-root.d.ts +81 -5
- package/dist/color-picker/root/color-picker-root.js +103 -10
- package/dist/combobox/root/combobox-root.d.ts +38 -3
- package/dist/combobox/root/combobox-root.js +64 -2
- package/dist/command/root/command-root.d.ts +30 -4
- package/dist/command/root/command-root.js +33 -3
- package/dist/copy-button/from.d.ts +22 -0
- package/dist/copy-button/from.js +28 -0
- package/dist/copy-button/root/copy-button-root.d.ts +27 -1
- package/dist/copy-button/root/copy-button-root.js +43 -7
- package/dist/date-input/root/date-input-root.d.ts +73 -4
- package/dist/date-input/root/date-input-root.js +93 -43
- package/dist/date-picker/root/date-picker-root.d.ts +145 -1
- package/dist/date-picker/root/date-picker-root.js +239 -48
- package/dist/dialog/description.d.ts +9 -0
- package/dist/dialog/description.js +14 -0
- package/dist/dialog/root/dialog-root.d.ts +64 -1
- package/dist/dialog/root/dialog-root.js +49 -23
- package/dist/donut-chart/root/donut-chart-root.d.ts +25 -4
- package/dist/donut-chart/root/donut-chart-root.js +21 -6
- package/dist/drawer/root/drawer-root.d.ts +63 -2
- package/dist/drawer/root/drawer-root.js +95 -19
- package/dist/drawer/swipe-start.d.ts +9 -0
- package/dist/drawer/swipe-start.js +12 -0
- package/dist/element-events.generated.js +8 -5
- package/dist/empty/root/empty-root.d.ts +1 -1
- package/dist/file-input/root/file-input-root.d.ts +47 -0
- package/dist/file-input/root/file-input-root.js +60 -22
- package/dist/file-input/screen.d.ts +35 -0
- package/dist/file-input/screen.js +49 -0
- package/dist/filter-input/root/filter-input-root.d.ts +38 -4
- package/dist/filter-input/root/filter-input-root.js +67 -3
- package/dist/filter-input/suggest.d.ts +17 -0
- package/dist/filter-input/suggest.js +23 -0
- package/dist/format-date/options.d.ts +33 -0
- package/dist/format-date/options.js +42 -0
- package/dist/format-date/root/format-date-root.d.ts +51 -0
- package/dist/format-date/root/format-date-root.js +58 -38
- package/dist/heatmap/root/heatmap-root.d.ts +23 -6
- package/dist/heatmap/root/heatmap-root.js +24 -13
- package/dist/icon/index.d.ts +2 -0
- package/dist/icon/index.js +2 -0
- package/dist/icon/registry.d.ts +37 -0
- package/dist/icon/registry.js +83 -0
- package/dist/icon/root/icon-root.d.ts +30 -4
- package/dist/icon/root/icon-root.js +41 -6
- package/dist/icon/root/index.d.ts +2 -0
- package/dist/icon/root/index.js +2 -0
- package/dist/icon/set.d.ts +1 -0
- package/dist/icon/set.js +3 -0
- package/dist/image/root/image-root.d.ts +14 -0
- package/dist/image/root/image-root.js +15 -2
- package/dist/input/root/input-root.d.ts +11 -0
- package/dist/input/root/input-root.js +15 -0
- package/dist/kbd/root/kbd-root.d.ts +20 -13
- package/dist/kbd/root/kbd-root.js +35 -37
- package/dist/kbd/shortcut.d.ts +18 -0
- package/dist/kbd/shortcut.js +35 -0
- package/dist/legend/hidden-series.d.ts +8 -0
- package/dist/legend/hidden-series.js +13 -0
- package/dist/legend/root/legend-root.d.ts +56 -0
- package/dist/legend/root/legend-root.js +54 -5
- package/dist/lightbox/item/lightbox-item.d.ts +15 -0
- package/dist/lightbox/item/lightbox-item.js +10 -1
- package/dist/lightbox/root/lightbox-root.d.ts +61 -1
- package/dist/lightbox/root/lightbox-root.js +94 -4
- package/dist/line-chart/root/line-chart-root.d.ts +11 -3
- package/dist/line-chart/root/line-chart-root.js +19 -5
- package/dist/listbox/range.d.ts +17 -0
- package/dist/listbox/range.js +5 -0
- package/dist/listbox/root/listbox-root.d.ts +74 -3
- package/dist/listbox/root/listbox-root.js +87 -9
- package/dist/loader/busy.d.ts +17 -0
- package/dist/loader/busy.js +24 -0
- package/dist/loader/root/loader-root.d.ts +37 -0
- package/dist/loader/root/loader-root.js +52 -3
- package/dist/meter/root/meter-root.d.ts +7 -0
- package/dist/meter/root/meter-root.js +6 -3
- package/dist/notifications/feed-keys.d.ts +10 -0
- package/dist/notifications/feed-keys.js +14 -0
- package/dist/notifications/notification/notification.d.ts +17 -0
- package/dist/notifications/notification/notification.js +21 -2
- package/dist/notifications/root/notifications-root.d.ts +42 -0
- package/dist/notifications/root/notifications-root.js +60 -1
- package/dist/number-input/root/number-input-root.d.ts +47 -0
- package/dist/number-input/root/number-input-root.js +58 -22
- package/dist/object-inspector/root/object-inspector-root.d.ts +1 -0
- package/dist/object-inspector/root/object-inspector-root.js +1 -1
- package/dist/option/root/option-root.d.ts +54 -5
- package/dist/option/root/option-root.js +34 -5
- package/dist/pin-input/format.d.ts +7 -0
- package/dist/pin-input/format.js +16 -0
- package/dist/pin-input/root/pin-input-root.d.ts +21 -0
- package/dist/pin-input/root/pin-input-root.js +29 -2
- package/dist/popover/root/popover-root.d.ts +62 -4
- package/dist/popover/root/popover-root.js +53 -5
- package/dist/progress/root/progress-root.d.ts +7 -0
- package/dist/progress/root/progress-root.js +9 -3
- package/dist/qr-code/export.d.ts +31 -0
- package/dist/qr-code/export.js +10 -0
- package/dist/qr-code/root/qr-code-root.d.ts +26 -0
- package/dist/qr-code/root/qr-code-root.js +49 -2
- package/dist/radio/group/radio-group.d.ts +23 -0
- package/dist/radio/group/radio-group.js +18 -0
- package/dist/rating/root/rating-root.d.ts +25 -0
- package/dist/rating/root/rating-root.js +45 -3
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
- package/dist/scatter-chart/root/scatter-chart-root.js +25 -7
- package/dist/select/root/select-root.d.ts +24 -0
- package/dist/select/root/select-root.js +20 -6
- package/dist/skeleton/root/skeleton-root.d.ts +25 -0
- package/dist/skeleton/root/skeleton-root.js +17 -2
- package/dist/slider/root/slider-root.d.ts +39 -0
- package/dist/slider/root/slider-root.js +45 -9
- package/dist/stat/root/stat-root.d.ts +2 -0
- package/dist/stat/root/stat-root.js +1 -1
- package/dist/steps/step/step.d.ts +1 -0
- package/dist/steps/step/step.js +1 -1
- package/dist/switch/root/switch-root.d.ts +12 -2
- package/dist/switch/root/switch-root.js +6 -1
- package/dist/tag/group/tag-group.d.ts +107 -0
- package/dist/tag/group/tag-group.js +157 -0
- package/dist/tag/index.d.ts +1 -0
- package/dist/tag/index.js +1 -0
- package/dist/tag/root/tag-root.d.ts +60 -0
- package/dist/tag/root/tag-root.js +74 -18
- package/dist/tag/selection.d.ts +10 -0
- package/dist/tag/selection.js +12 -0
- package/dist/textarea/grow.d.ts +22 -0
- package/dist/textarea/grow.js +20 -0
- package/dist/textarea/root/textarea-root.d.ts +9 -0
- package/dist/textarea/root/textarea-root.js +9 -1
- package/dist/time-input/root/time-input-root.d.ts +44 -5
- package/dist/time-input/root/time-input-root.js +69 -39
- package/dist/time-picker/root/time-picker-root.d.ts +54 -8
- package/dist/time-picker/root/time-picker-root.js +121 -91
- package/dist/toast/hotkey.d.ts +22 -0
- package/dist/toast/hotkey.js +23 -0
- package/dist/toast/item/toast-item.d.ts +16 -1
- package/dist/toast/item/toast-item.js +41 -13
- package/dist/toast/root/toast-root.d.ts +42 -8
- package/dist/toast/root/toast-root.js +64 -4
- package/dist/toggle/group/toggle-group.d.ts +57 -15
- package/dist/toggle/group/toggle-group.js +69 -9
- package/dist/toggle/root/toggle-root.d.ts +12 -1
- package/dist/toggle/root/toggle-root.js +11 -3
- package/dist/tooltip/focus-target.d.ts +6 -0
- package/dist/tooltip/focus-target.js +9 -0
- package/dist/tooltip/root/tooltip-root.d.ts +42 -17
- package/dist/tooltip/root/tooltip-root.js +50 -9
- package/dist/tour/naming.d.ts +30 -0
- package/dist/tour/naming.js +10 -0
- package/dist/tour/root/tour-root.d.ts +35 -0
- package/dist/tour/root/tour-root.js +56 -5
- package/dist/tour/stop/tour-stop.d.ts +53 -8
- package/dist/tour/stop/tour-stop.js +45 -15
- package/dist/tour/wait.d.ts +11 -0
- package/dist/tour/wait.js +19 -0
- package/dist/trend/root/trend-root.d.ts +13 -0
- package/dist/trend/root/trend-root.js +14 -9
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +4862 -254
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +1149 -67
|
@@ -4,17 +4,12 @@ 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 {
|
|
7
|
+
import { clockLocale, commaList, customElement, dayPeriod, elementById, fillTemplate, HelixFormElement, inlineStep, isTwelveHour, MixinControllerValue, nearestTime, offeredTimes, reportValues, secondsAsValue, timeBarred, timeFault, valueAsSeconds, wheelParts, wholeHour, writeClock, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { html } from 'lit/static-html.js';
|
|
11
11
|
import '../../listbox/index.js';
|
|
12
12
|
import '../../option/index.js';
|
|
13
|
-
const isTwelveHour = (locale) => new Intl.DateTimeFormat(locale || undefined, { timeStyle: 'short' }).resolvedOptions().hour12 ===
|
|
14
|
-
true;
|
|
15
|
-
const dayPeriod = (half, locale) => new Intl.DateTimeFormat(locale || undefined, { hour: 'numeric', hour12: true, timeZone: 'UTC' })
|
|
16
|
-
.formatToParts(new Date(Date.UTC(1970, 0, 1, half === 'AM' ? 9 : 21)))
|
|
17
|
-
.find((part) => part.type === 'dayPeriod')?.value ?? half;
|
|
18
13
|
let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControllerValue(HelixFormElement) {
|
|
19
14
|
constructor() {
|
|
20
15
|
super(...arguments);
|
|
@@ -60,69 +55,78 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
|
|
|
60
55
|
if (this.isDisabled) {
|
|
61
56
|
return;
|
|
62
57
|
}
|
|
63
|
-
const { hour, meridiem, minute } = this.parts;
|
|
64
|
-
let
|
|
58
|
+
const { hour, meridiem, minute, second } = this.parts;
|
|
59
|
+
let [h, m, sec] = [hour, minute, second];
|
|
65
60
|
if (part === 'minute') {
|
|
66
|
-
|
|
61
|
+
m = Number(to);
|
|
62
|
+
}
|
|
63
|
+
else if (part === 'second') {
|
|
64
|
+
sec = Number(to);
|
|
67
65
|
}
|
|
68
66
|
else if (part === 'hour') {
|
|
69
|
-
|
|
70
|
-
const whole = isTwelveHour(this.locale) ? (shown % 12) + (meridiem === 'PM' ? 12 : 0) : shown;
|
|
71
|
-
next = whole * 60 + minute;
|
|
67
|
+
h = wholeHour(Number(to), isTwelveHour(this.clock) ? meridiem : undefined);
|
|
72
68
|
}
|
|
73
69
|
else {
|
|
74
|
-
|
|
75
|
-
next = (half + (to === 'PM' ? 12 : 0)) * 60 + minute;
|
|
70
|
+
h = (hour % 12) + (to === 'PM' ? 12 : 0);
|
|
76
71
|
}
|
|
77
|
-
const
|
|
78
|
-
if (this.
|
|
72
|
+
const nearest = this.nearest(h * 3600 + m * 60 + sec);
|
|
73
|
+
if (this.barredAt(nearest)) {
|
|
79
74
|
return;
|
|
80
75
|
}
|
|
81
|
-
this.requestValue(
|
|
76
|
+
this.requestValue(this.valueFor(nearest));
|
|
82
77
|
};
|
|
83
78
|
this.readOnly = false;
|
|
84
79
|
this.required = false;
|
|
80
|
+
this.secondLabel = 'Second';
|
|
81
|
+
this.seconds = false;
|
|
85
82
|
this.step = 15;
|
|
86
83
|
this.listRevealed = false;
|
|
87
84
|
this.naming = undefined;
|
|
85
|
+
this.handleColumnsKeydown = (event) => {
|
|
86
|
+
const step = inlineStep(this, event.key);
|
|
87
|
+
if (step === 0 || event.altKey || event.ctrlKey || event.metaKey) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const columns = [...(this.renderRoot?.querySelectorAll('[part="column"]') ?? [])];
|
|
91
|
+
const at = columns.findIndex((column) => event.composedPath().includes(column));
|
|
92
|
+
const next = columns[at + step];
|
|
93
|
+
if (at < 0 || !next) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
next.focus();
|
|
98
|
+
};
|
|
88
99
|
}
|
|
89
100
|
get times() {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return found;
|
|
101
|
+
return offeredTimes(this.min, this.max, this.step, this.seconds);
|
|
102
|
+
}
|
|
103
|
+
get clock() {
|
|
104
|
+
return clockLocale(this.locale, this.hourCycle);
|
|
105
|
+
}
|
|
106
|
+
valueFor(at) {
|
|
107
|
+
return secondsAsValue(at, this.seconds);
|
|
98
108
|
}
|
|
99
109
|
barred(at) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return best;
|
|
110
|
+
const seconds = valueAsSeconds(at);
|
|
111
|
+
return seconds !== null && this.barredAt(seconds);
|
|
112
|
+
}
|
|
113
|
+
barredAt(at) {
|
|
114
|
+
return timeBarred(at, this.disableTimes);
|
|
115
|
+
}
|
|
116
|
+
nearest(at) {
|
|
117
|
+
return nearestTime(this.times, at);
|
|
111
118
|
}
|
|
112
119
|
wholeHourBarred(shown, half) {
|
|
113
|
-
const whole =
|
|
114
|
-
const within = this.times.filter((at) => Math.floor(at /
|
|
115
|
-
return within.length > 0 && within.every((at) => this.
|
|
120
|
+
const whole = wholeHour(shown, half);
|
|
121
|
+
const within = this.times.filter((at) => Math.floor(at / 3600) === whole);
|
|
122
|
+
return within.length > 0 && within.every((at) => this.barredAt(at));
|
|
116
123
|
}
|
|
117
124
|
halfBarred(half) {
|
|
118
|
-
const within = this.times.filter((at) => (half === 'PM') === at >=
|
|
119
|
-
return within.length > 0 && within.every((at) => this.
|
|
125
|
+
const within = this.times.filter((at) => (half === 'PM') === at >= 43_200);
|
|
126
|
+
return within.length > 0 && within.every((at) => this.barredAt(at));
|
|
120
127
|
}
|
|
121
128
|
get held() {
|
|
122
|
-
return this.value
|
|
123
|
-
.split(',')
|
|
124
|
-
.map((one) => one.trim())
|
|
125
|
-
.filter(Boolean);
|
|
129
|
+
return commaList(this.value);
|
|
126
130
|
}
|
|
127
131
|
between(at) {
|
|
128
132
|
if (this.mode !== 'range') {
|
|
@@ -132,17 +136,25 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
|
|
|
132
136
|
return from !== undefined && to !== undefined && at > from && at < to;
|
|
133
137
|
}
|
|
134
138
|
get parts() {
|
|
135
|
-
const at =
|
|
136
|
-
|
|
137
|
-
return { hour, meridiem: hour < 12 ? 'AM' : 'PM', minute: at % 60 };
|
|
139
|
+
const at = valueAsSeconds(this.value) ?? valueAsSeconds(this.min) ?? 0;
|
|
140
|
+
return wheelParts(at, isTwelveHour(this.clock));
|
|
138
141
|
}
|
|
139
142
|
get hours() {
|
|
140
|
-
const found = new Set(this.times.map((at) => Math.floor(at /
|
|
143
|
+
const found = new Set(this.times.map((at) => Math.floor(at / 3600)));
|
|
141
144
|
return [...found];
|
|
142
145
|
}
|
|
143
146
|
get minutes() {
|
|
144
147
|
const hour = this.parts.hour;
|
|
145
|
-
|
|
148
|
+
const found = new Set(this.times
|
|
149
|
+
.filter((at) => Math.floor(at / 3600) === hour)
|
|
150
|
+
.map((at) => Math.floor(at / 60) % 60));
|
|
151
|
+
return [...found];
|
|
152
|
+
}
|
|
153
|
+
get secondsShown() {
|
|
154
|
+
const { hour, minute } = this.parts;
|
|
155
|
+
return this.times
|
|
156
|
+
.filter((at) => Math.floor(at / 60) === hour * 60 + minute)
|
|
157
|
+
.map((at) => at % 60);
|
|
146
158
|
}
|
|
147
159
|
reveal() {
|
|
148
160
|
if (this.mode !== undefined) {
|
|
@@ -163,32 +175,33 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
|
|
|
163
175
|
return super.requestValue(next);
|
|
164
176
|
}
|
|
165
177
|
fault(at) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
178
|
+
switch (timeFault(at, {
|
|
179
|
+
disableTimes: this.disableTimes,
|
|
180
|
+
max: this.max,
|
|
181
|
+
min: this.min,
|
|
182
|
+
offered: this.times,
|
|
183
|
+
})) {
|
|
184
|
+
case 'rangeUnderflow':
|
|
185
|
+
return [
|
|
186
|
+
'rangeUnderflow',
|
|
187
|
+
fillTemplate(this.rangeUnderflowMessage || 'Choose a time at or after {min}.', {
|
|
188
|
+
min: writeClock(this.min, this.clock, this.seconds),
|
|
189
|
+
}),
|
|
190
|
+
];
|
|
191
|
+
case 'rangeOverflow':
|
|
192
|
+
return [
|
|
193
|
+
'rangeOverflow',
|
|
194
|
+
fillTemplate(this.rangeOverflowMessage || 'Choose a time at or before {max}.', {
|
|
195
|
+
max: writeClock(this.max, this.clock, this.seconds),
|
|
196
|
+
}),
|
|
197
|
+
];
|
|
198
|
+
case 'unavailable':
|
|
199
|
+
return ['badInput', this.unavailableMessage || 'Choose a time that is available.'];
|
|
200
|
+
case 'stepMismatch':
|
|
201
|
+
return ['stepMismatch', this.stepMismatchMessage || 'Choose one of the times on offer.'];
|
|
202
|
+
default:
|
|
203
|
+
return undefined;
|
|
190
204
|
}
|
|
191
|
-
return undefined;
|
|
192
205
|
}
|
|
193
206
|
reportRequired() {
|
|
194
207
|
const anchor = this.partNamed('list') ?? undefined;
|
|
@@ -197,11 +210,12 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
|
|
|
197
210
|
return;
|
|
198
211
|
}
|
|
199
212
|
const held = this.held;
|
|
200
|
-
if (held.length > 0 && held.some((one) =>
|
|
213
|
+
if (held.length > 0 && held.some((one) => valueAsSeconds(one) === null)) {
|
|
214
|
+
const shape = this.seconds ? 'HH:mm:ss' : 'HH:mm';
|
|
201
215
|
this.internals.setValidity({ badInput: true }, this.badInputMessage ||
|
|
202
216
|
(this.mode === undefined
|
|
203
|
-
?
|
|
204
|
-
:
|
|
217
|
+
? `Enter a time as ${shape}.`
|
|
218
|
+
: `Enter times as ${shape}, separated by commas.`), anchor);
|
|
205
219
|
return;
|
|
206
220
|
}
|
|
207
221
|
for (const one of held) {
|
|
@@ -297,9 +311,8 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
|
|
|
297
311
|
</${this.tag('listbox')}>`;
|
|
298
312
|
}
|
|
299
313
|
renderSpinner() {
|
|
300
|
-
const twelve = isTwelveHour(this.
|
|
301
|
-
const { hour, meridiem, minute } = this.parts;
|
|
302
|
-
const shownHour = twelve ? String(hour % 12 === 0 ? 12 : hour % 12) : String(hour);
|
|
314
|
+
const twelve = isTwelveHour(this.clock);
|
|
315
|
+
const { hour, meridiem, minute, second, shown } = this.parts;
|
|
303
316
|
const hours = twelve
|
|
304
317
|
? [...new Set(this.hours.map((one) => (one % 12 === 0 ? 12 : one % 12)))].sort((a, b) => a - b)
|
|
305
318
|
: this.hours;
|
|
@@ -308,11 +321,15 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
|
|
|
308
321
|
role="group"
|
|
309
322
|
aria-label=${this.listName}
|
|
310
323
|
data-unset=${this.value === '' ? '' : nothing}
|
|
324
|
+
@keydown=${this.handleColumnsKeydown}
|
|
311
325
|
>
|
|
312
|
-
${this.renderColumn('hour', this.hourLabel, hours.map(String), (one) => (twelve ? one : one.padStart(2, '0')),
|
|
313
|
-
${this.renderColumn('minute', this.minuteLabel, this.minutes.map(String), (one) => one.padStart(2, '0'), String(minute), (one) => this.
|
|
326
|
+
${this.renderColumn('hour', this.hourLabel, hours.map(String), (one) => (twelve ? one : one.padStart(2, '0')), String(shown), (one) => this.wholeHourBarred(Number(one), twelve ? meridiem : undefined))}
|
|
327
|
+
${this.renderColumn('minute', this.minuteLabel, this.minutes.map(String), (one) => one.padStart(2, '0'), String(minute), (one) => this.barredAt(hour * 3600 + Number(one) * 60))}
|
|
328
|
+
${this.seconds
|
|
329
|
+
? this.renderColumn('second', this.secondLabel, this.secondsShown.map(String), (one) => one.padStart(2, '0'), String(second), (one) => this.barredAt(hour * 3600 + minute * 60 + Number(one)))
|
|
330
|
+
: nothing}
|
|
314
331
|
${twelve
|
|
315
|
-
? this.renderColumn('meridiem', this.meridiemLabel, ['AM', 'PM'], (one) => dayPeriod(one, this.
|
|
332
|
+
? this.renderColumn('meridiem', this.meridiemLabel, ['AM', 'PM'], (one) => dayPeriod(one, this.clock), meridiem, (one) => this.halfBarred(one))
|
|
316
333
|
: nothing}
|
|
317
334
|
</div>`;
|
|
318
335
|
}
|
|
@@ -332,8 +349,8 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
|
|
|
332
349
|
@helix-before-change=${this.handleChange}
|
|
333
350
|
@input=${this.handleListInput}
|
|
334
351
|
>
|
|
335
|
-
${this.times.map((
|
|
336
|
-
const at =
|
|
352
|
+
${this.times.map((seconds) => {
|
|
353
|
+
const at = this.valueFor(seconds);
|
|
337
354
|
const between = this.between(at);
|
|
338
355
|
return html `<${this.tag('option')}
|
|
339
356
|
part="option${between ? ' option-between' : ''}"
|
|
@@ -344,7 +361,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
|
|
|
344
361
|
?disabled=${this.barred(at)}
|
|
345
362
|
?selected=${this.held.includes(at)}
|
|
346
363
|
data-between=${between ? '' : nothing}
|
|
347
|
-
>${
|
|
364
|
+
>${writeClock(at, this.clock, this.seconds)}</${this.tag('option')}
|
|
348
365
|
>`;
|
|
349
366
|
})}
|
|
350
367
|
</${this.tag('listbox')}>`;
|
|
@@ -356,16 +373,20 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
|
|
|
356
373
|
return html `${this.renderList()}`;
|
|
357
374
|
}
|
|
358
375
|
get valueAsDate() {
|
|
359
|
-
|
|
376
|
+
const at = valueAsSeconds(this.value);
|
|
377
|
+
return at === null ? null : new Date(at * 1000);
|
|
360
378
|
}
|
|
361
379
|
set valueAsDate(date) {
|
|
362
|
-
this.
|
|
380
|
+
this.valueAsNumber = date ? date.getTime() : Number.NaN;
|
|
363
381
|
}
|
|
364
382
|
get valueAsNumber() {
|
|
365
|
-
|
|
383
|
+
const at = valueAsSeconds(this.value);
|
|
384
|
+
return at === null ? Number.NaN : at * 1000;
|
|
366
385
|
}
|
|
367
386
|
set valueAsNumber(ms) {
|
|
368
|
-
this.value = Number.isNaN(ms)
|
|
387
|
+
this.value = Number.isNaN(ms)
|
|
388
|
+
? ''
|
|
389
|
+
: this.valueFor(Math.floor((((ms % 86_400_000) + 86_400_000) % 86_400_000) / 1000));
|
|
369
390
|
}
|
|
370
391
|
};
|
|
371
392
|
__decorate([
|
|
@@ -374,6 +395,9 @@ __decorate([
|
|
|
374
395
|
__decorate([
|
|
375
396
|
property({ attribute: 'disable-times' })
|
|
376
397
|
], HelixHeadlessTimePicker.prototype, "disableTimes", void 0);
|
|
398
|
+
__decorate([
|
|
399
|
+
property({ attribute: 'hour-cycle' })
|
|
400
|
+
], HelixHeadlessTimePicker.prototype, "hourCycle", void 0);
|
|
377
401
|
__decorate([
|
|
378
402
|
property({ attribute: 'hour-label' })
|
|
379
403
|
], HelixHeadlessTimePicker.prototype, "hourLabel", void 0);
|
|
@@ -416,6 +440,12 @@ __decorate([
|
|
|
416
440
|
__decorate([
|
|
417
441
|
property({ reflect: true, type: Boolean })
|
|
418
442
|
], HelixHeadlessTimePicker.prototype, "required", void 0);
|
|
443
|
+
__decorate([
|
|
444
|
+
property({ attribute: 'second-label' })
|
|
445
|
+
], HelixHeadlessTimePicker.prototype, "secondLabel", void 0);
|
|
446
|
+
__decorate([
|
|
447
|
+
property({ type: Boolean })
|
|
448
|
+
], HelixHeadlessTimePicker.prototype, "seconds", void 0);
|
|
419
449
|
__decorate([
|
|
420
450
|
property({ type: Number })
|
|
421
451
|
], HelixHeadlessTimePicker.prototype, "step", void 0);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ChordPress } from '@fusion.dev/helix-core';
|
|
2
|
+
/** A key and the modifiers held with it. */
|
|
3
|
+
export interface Chord {
|
|
4
|
+
alt: boolean;
|
|
5
|
+
ctrl: boolean;
|
|
6
|
+
key: string;
|
|
7
|
+
meta: boolean;
|
|
8
|
+
shift: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** What a key press carries that a chord is compared against. */
|
|
11
|
+
export type Pressed = ChordPress;
|
|
12
|
+
/**
|
|
13
|
+
* Reads a chord written as keys joined by `+`, such as `F8` or `Alt+T`, case
|
|
14
|
+
* aside. The last part is the key. Empty, or a chord with no key, is none.
|
|
15
|
+
*
|
|
16
|
+
* Split as core's `chordParts` splits every chord, and read more strictly than
|
|
17
|
+
* Kbd reads one: four modifiers by their own names, no `mod`, and Shift held
|
|
18
|
+
* exactly as written, so the key that moves to the stack is the one written.
|
|
19
|
+
*/
|
|
20
|
+
export declare const parseHotkey: (text: string) => Chord | null;
|
|
21
|
+
/** Whether a press is the chord: the same key, case aside, and exactly its modifiers. */
|
|
22
|
+
export declare const matchesHotkey: (pressed: Pressed, chord: Chord | null) => boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { chordParts } from '@fusion.dev/helix-core';
|
|
2
|
+
const MODIFIERS = new Set(['alt', 'ctrl', 'meta', 'shift']);
|
|
3
|
+
export const parseHotkey = (text) => {
|
|
4
|
+
const parts = chordParts(text);
|
|
5
|
+
const key = parts.pop();
|
|
6
|
+
if (!key || MODIFIERS.has(key.toLowerCase())) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const held = new Set(parts.map((one) => one.toLowerCase()));
|
|
10
|
+
return {
|
|
11
|
+
alt: held.has('alt'),
|
|
12
|
+
ctrl: held.has('ctrl'),
|
|
13
|
+
key,
|
|
14
|
+
meta: held.has('meta'),
|
|
15
|
+
shift: held.has('shift'),
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export const matchesHotkey = (pressed, chord) => chord !== null &&
|
|
19
|
+
pressed.key.toLowerCase() === chord.key.toLowerCase() &&
|
|
20
|
+
pressed.altKey === chord.alt &&
|
|
21
|
+
pressed.ctrlKey === chord.ctrl &&
|
|
22
|
+
pressed.metaKey === chord.meta &&
|
|
23
|
+
pressed.shiftKey === chord.shift;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement, type ToneMark } from '@fusion.dev/helix-core';
|
|
1
|
+
import { HelixElement, SwipeController, type ToneMark } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fusion.dev/helix-core").DismissibleElement) & typeof HelixElement;
|
|
4
4
|
/**
|
|
@@ -12,6 +12,16 @@ declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fus
|
|
|
12
12
|
* with each new message, and the last of three messages would stay for nine
|
|
13
13
|
* seconds.
|
|
14
14
|
*
|
|
15
|
+
* **Each message is an `alertdialog` named by its words**, which is how a reader
|
|
16
|
+
* who moves into the stack hears which message they are on. A `danger` message
|
|
17
|
+
* is its own assertive live region, so it is announced at once; every other one
|
|
18
|
+
* is announced politely by the region.
|
|
19
|
+
*
|
|
20
|
+
* A touch or a pen swipes a message toward the end of the line, and past
|
|
21
|
+
* halfway, or quickly, dismisses it through the same `helix-dismiss` as its
|
|
22
|
+
* control. While it moves, the distance is on the `item` part as
|
|
23
|
+
* `--toast-swipe-x`.
|
|
24
|
+
*
|
|
15
25
|
* **Messages are inverted by default.** The item flips `color-scheme`, so a new
|
|
16
26
|
* message stands out from the page without using a color that carries meaning.
|
|
17
27
|
*
|
|
@@ -39,6 +49,7 @@ declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fus
|
|
|
39
49
|
* @csspart timer - The bar showing how long is left, where a duration was given.
|
|
40
50
|
*
|
|
41
51
|
* @cssproperty --toast-duration - How long this message stays, in milliseconds, for a progress hint.
|
|
52
|
+
* @cssproperty --toast-swipe-x - How far a swipe has moved the message across, in pixels, toward the right.
|
|
42
53
|
*/
|
|
43
54
|
export declare class HelixHeadlessToastItem extends HelixHeadlessToastItem_base {
|
|
44
55
|
/**
|
|
@@ -64,6 +75,9 @@ export declare class HelixHeadlessToastItem extends HelixHeadlessToastItem_base
|
|
|
64
75
|
*/
|
|
65
76
|
get open(): boolean;
|
|
66
77
|
set open(next: boolean);
|
|
78
|
+
protected readonly swipes: SwipeController;
|
|
79
|
+
/** Writes how far a swipe has moved the message toward the end of the line. */
|
|
80
|
+
private slide;
|
|
67
81
|
/** When the current run of the timer began, or nothing while it is paused. */
|
|
68
82
|
private startedAt;
|
|
69
83
|
/** What is left of the duration, so resuming continues rather than restarts. */
|
|
@@ -106,6 +120,7 @@ export declare class HelixHeadlessToastItem extends HelixHeadlessToastItem_base
|
|
|
106
120
|
connectedCallback(): void;
|
|
107
121
|
disconnectedCallback(): void;
|
|
108
122
|
protected willUpdate(changed: PropertyValues): void;
|
|
123
|
+
protected updated(changed: PropertyValues): void;
|
|
109
124
|
protected firstUpdated(changed: PropertyValues): void;
|
|
110
125
|
/**
|
|
111
126
|
* The mark for the message's tone, behind a seam.
|
|
@@ -4,14 +4,33 @@ 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, MixinDismissible, requestChange, toneMark, } from '@fusion.dev/helix-core';
|
|
8
|
-
import { x } from '@fusion.dev/helix-icons';
|
|
7
|
+
import { customElement, drawDismiss, drawDismissGlyph, HelixElement, hostAria, isRtl, MixinDismissible, requestChange, SwipeController, settleSwipe, swipeEdge, toneMark, towardEdge, } from '@fusion.dev/helix-core';
|
|
9
8
|
import { html, nothing } from 'lit';
|
|
10
9
|
import { property } from 'lit/decorators.js';
|
|
11
10
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
12
11
|
let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissible(HelixElement) {
|
|
13
12
|
constructor() {
|
|
14
13
|
super(...arguments);
|
|
14
|
+
this.swipes = new SwipeController(this, {
|
|
15
|
+
axis: () => (this.open ? 'x' : undefined),
|
|
16
|
+
end: (dx, _dy, vx) => {
|
|
17
|
+
const edge = swipeEdge('end', isRtl(this));
|
|
18
|
+
const width = this.partNamed('item')?.getBoundingClientRect().width ?? 0;
|
|
19
|
+
const shown = settleSwipe({
|
|
20
|
+
points: [1],
|
|
21
|
+
size: width,
|
|
22
|
+
travel: Math.max(0, towardEdge(edge, dx, 0)),
|
|
23
|
+
velocity: towardEdge(edge, vx, 0),
|
|
24
|
+
});
|
|
25
|
+
if (shown === 0 && this.dismiss()) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
this.slide(0);
|
|
29
|
+
},
|
|
30
|
+
move: (dx) => this.slide(Math.max(0, towardEdge(swipeEdge('end', isRtl(this)), dx, 0))),
|
|
31
|
+
start: (_target, event) => event.pointerType !== 'mouse',
|
|
32
|
+
surface: () => this.partNamed('item'),
|
|
33
|
+
});
|
|
15
34
|
this.openState = undefined;
|
|
16
35
|
this.holds = 0;
|
|
17
36
|
this.handlePause = () => {
|
|
@@ -33,6 +52,10 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
|
|
|
33
52
|
this.openState = next;
|
|
34
53
|
this.requestUpdate('open', previous);
|
|
35
54
|
}
|
|
55
|
+
slide(travel) {
|
|
56
|
+
const physical = isRtl(this) ? -travel : travel;
|
|
57
|
+
this.partNamed('item')?.style.setProperty('--toast-swipe-x', travel === 0 ? '' : `${Math.round(physical)}px`);
|
|
58
|
+
}
|
|
36
59
|
dismiss() {
|
|
37
60
|
if (!this.open) {
|
|
38
61
|
return true;
|
|
@@ -86,6 +109,16 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
|
|
|
86
109
|
super.willUpdate(changed);
|
|
87
110
|
this.toggleAttribute('data-closed', !this.open);
|
|
88
111
|
this.toggleAttribute('hidden', !this.open);
|
|
112
|
+
hostAria(this.internals, { role: 'alertdialog' });
|
|
113
|
+
this.internals.ariaModal = 'false';
|
|
114
|
+
this.internals.ariaLive = this.tone === 'danger' ? 'assertive' : null;
|
|
115
|
+
}
|
|
116
|
+
updated(changed) {
|
|
117
|
+
super.updated(changed);
|
|
118
|
+
const words = this.partNamed('content');
|
|
119
|
+
if (words) {
|
|
120
|
+
hostAria(this.internals, { labelledBy: [words] });
|
|
121
|
+
}
|
|
89
122
|
}
|
|
90
123
|
firstUpdated(changed) {
|
|
91
124
|
super.firstUpdated(changed);
|
|
@@ -108,9 +141,7 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
|
|
|
108
141
|
}
|
|
109
142
|
renderDismissIcon() {
|
|
110
143
|
return html `<slot name="dismiss-icon">
|
|
111
|
-
|
|
112
|
-
${unsafeSVG(x.content)}
|
|
113
|
-
</svg>
|
|
144
|
+
${drawDismissGlyph()}
|
|
114
145
|
</slot>`;
|
|
115
146
|
}
|
|
116
147
|
render() {
|
|
@@ -130,14 +161,11 @@ let HelixHeadlessToastItem = class HelixHeadlessToastItem extends MixinDismissib
|
|
|
130
161
|
<div part="content"><slot></slot></div>
|
|
131
162
|
<div part="actions"><slot name="actions"></slot></div>
|
|
132
163
|
${this.dismissible
|
|
133
|
-
?
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
>
|
|
139
|
-
${this.renderDismissIcon()}
|
|
140
|
-
</button>`
|
|
164
|
+
? drawDismiss({
|
|
165
|
+
label: this.dismissLabel,
|
|
166
|
+
mark: this.renderDismissIcon(),
|
|
167
|
+
onDismiss: () => this.dismiss(),
|
|
168
|
+
})
|
|
141
169
|
: nothing}
|
|
142
170
|
${this.duration
|
|
143
171
|
? html `<div
|
|
@@ -9,9 +9,15 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
9
9
|
* announce a region that arrives already holding its message. `Radio.Group` is
|
|
10
10
|
* part of its family for the same reason.
|
|
11
11
|
*
|
|
12
|
-
* **The region is
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* **The region is a landmark named by `label`, and a polite live region.** A
|
|
13
|
+
* `danger` message is announced assertively, and every other one politely.
|
|
14
|
+
* Each message is an `alertdialog` named by its words, so a reader who moves
|
|
15
|
+
* into the stack hears which message they are on.
|
|
16
|
+
*
|
|
17
|
+
* **`hotkey` takes focus to the stack and back**, F8 unless you set it, and
|
|
18
|
+
* `label` says which key: `{hotkey}` in it is filled in, so the default name
|
|
19
|
+
* is "Notifications (F8)". Pressed while focus is in the stack, it gives focus
|
|
20
|
+
* back to where it was.
|
|
15
21
|
*
|
|
16
22
|
* **The region never creates or removes items.** You render items and remove
|
|
17
23
|
* them. An item requests closing with a cancelable `helix-dismiss` event. A
|
|
@@ -19,9 +25,11 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
19
25
|
* and the DOM stay in sync. It hides on its own unless you set `open`, which
|
|
20
26
|
* makes the state yours.
|
|
21
27
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
28
|
+
* **`add()` shows a message from script**, for a page with no framework state
|
|
29
|
+
* to render them from: it appends a message of the region's own tier and
|
|
30
|
+
* removes it once it has been dismissed and has finished leaving. Messages
|
|
31
|
+
* you render yourself stay yours, and the region never removes them. A touch
|
|
32
|
+
* or a pen swipes a message away toward the end of the line.
|
|
25
33
|
*
|
|
26
34
|
* **The stack is in the top layer.** The `region` part is a `popover="manual"`
|
|
27
35
|
* shown while a message is showing, and shown again above a modal dialog that
|
|
@@ -46,11 +54,17 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
46
54
|
*
|
|
47
55
|
* @event {HelixDismissEvent} helix-dismiss - A message is being dismissed, by its control or by its timer. The message sends it, and you can listen for it on the region. Cancelable: canceling it keeps the message.
|
|
48
56
|
*
|
|
49
|
-
* @csspart region - The stack the messages are in. It is in the top layer while a message shows.
|
|
57
|
+
* @csspart region - The stack the messages are in. It is in the top layer while a message shows, and takes focus from `hotkey` where no message holds a control.
|
|
50
58
|
*/
|
|
51
59
|
export declare class HelixHeadlessToast extends HelixElement {
|
|
52
60
|
/**
|
|
53
|
-
*
|
|
61
|
+
* The key that takes focus to the stack and back, such as `F8` or `Alt+T`:
|
|
62
|
+
* keys joined by `+`, the last one the key. Empty turns it off.
|
|
63
|
+
*/
|
|
64
|
+
hotkey: string;
|
|
65
|
+
/**
|
|
66
|
+
* Accessible name for the region, with `{hotkey}` filled in. Translate it with
|
|
67
|
+
* the rest of your interface.
|
|
54
68
|
*/
|
|
55
69
|
label: string;
|
|
56
70
|
/**
|
|
@@ -83,6 +97,26 @@ export declare class HelixHeadlessToast extends HelixElement {
|
|
|
83
97
|
protected willUpdate(changed: PropertyValues): void;
|
|
84
98
|
/** The element focus came from when it entered the stack. */
|
|
85
99
|
private focusOrigin;
|
|
100
|
+
private readonly scope;
|
|
101
|
+
/** Takes focus to the stack and back, on `hotkey`. */
|
|
102
|
+
private readonly handleHotkey;
|
|
103
|
+
/** Messages `add()` made, which are the region's to remove. */
|
|
104
|
+
private readonly made;
|
|
105
|
+
/**
|
|
106
|
+
* Shows a message from script, and returns the element.
|
|
107
|
+
*
|
|
108
|
+
* The message is the region's own tier of `Toast.Item`, appended to the
|
|
109
|
+
* stack, holding `message` as its text. The region removes it once it has
|
|
110
|
+
* been dismissed and has finished leaving, which is the one case where it
|
|
111
|
+
* removes anything: a message you render stays yours.
|
|
112
|
+
*/
|
|
113
|
+
add(message: string, options?: {
|
|
114
|
+
dismissible?: boolean;
|
|
115
|
+
duration?: number;
|
|
116
|
+
tone?: 'danger' | 'info' | 'success' | 'warning';
|
|
117
|
+
}): HTMLElement;
|
|
118
|
+
/** Removes a message `add()` made, once it has hidden and finished leaving. */
|
|
119
|
+
private sweep;
|
|
86
120
|
/** Watches the messages arrive and hide, to raise or lower the stack. */
|
|
87
121
|
private watcher;
|
|
88
122
|
/** The messages that are showing, in order. */
|