@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,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 { valueAsDate as asDate, customElement, fillTemplate, dateAsValue as fromDate, HelixFormElement, HelixNavigateEvent, hostAria, inlineStep, MixinControllerValue, reportValues, requestChange,
|
|
7
|
+
import { addDays, addMonths, valueAsDate as asDate, barredBy, customElement, fillTemplate, dateAsValue as fromDate, HelixFormElement, HelixNavigateEvent, hostAria, inlineStep, commaList as list, localToday, MixinControllerValue, monthOf, monthOut, periodCells, periodKey, reportValues, requestChange, sameMonth, weekStartOf, YEARS_SHOWN, yearOut, yearPageOf, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretLeft, caretRight } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -21,34 +21,6 @@ const KEYS = new Set([
|
|
|
21
21
|
'PageUp',
|
|
22
22
|
' ',
|
|
23
23
|
]);
|
|
24
|
-
const localToday = () => {
|
|
25
|
-
const now = new Date();
|
|
26
|
-
const month = `${now.getMonth() + 1}`.padStart(2, '0');
|
|
27
|
-
const day = `${now.getDate()}`.padStart(2, '0');
|
|
28
|
-
return `${now.getFullYear()}-${month}-${day}`;
|
|
29
|
-
};
|
|
30
|
-
const list = (value) => value
|
|
31
|
-
.split(',')
|
|
32
|
-
.map((entry) => entry.trim())
|
|
33
|
-
.filter(Boolean);
|
|
34
|
-
const weekStartOf = (locale) => {
|
|
35
|
-
try {
|
|
36
|
-
const tag = new Intl.Locale(locale);
|
|
37
|
-
const first = tag.getWeekInfo?.().firstDay ?? tag.weekInfo?.firstDay;
|
|
38
|
-
return first === undefined ? 0 : first % 7;
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return 0;
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
const monthOf = (date) => `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, '0')}`;
|
|
45
|
-
const addDays = (date, days) => new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days));
|
|
46
|
-
const sameMonth = (left, right) => left.getUTCFullYear() === right.getUTCFullYear() && left.getUTCMonth() === right.getUTCMonth();
|
|
47
|
-
const addMonths = (date, months) => {
|
|
48
|
-
const target = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + months, 1));
|
|
49
|
-
const last = new Date(Date.UTC(target.getUTCFullYear(), target.getUTCMonth() + 1, 0)).getUTCDate();
|
|
50
|
-
return new Date(Date.UTC(target.getUTCFullYear(), target.getUTCMonth(), Math.min(date.getUTCDate(), last)));
|
|
51
|
-
};
|
|
52
24
|
let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControllerValue(HelixFormElement) {
|
|
53
25
|
constructor() {
|
|
54
26
|
super(...arguments);
|
|
@@ -62,11 +34,20 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
62
34
|
this.min = '';
|
|
63
35
|
this.monthState = undefined;
|
|
64
36
|
this.months = 1;
|
|
37
|
+
this.monthViewLabel = '{heading}. Choose a month';
|
|
65
38
|
this.nextLabel = 'Next month';
|
|
39
|
+
this.nextYearLabel = 'Next year';
|
|
40
|
+
this.nextYearsLabel = 'Later years';
|
|
66
41
|
this.previousLabel = 'Previous month';
|
|
42
|
+
this.previousYearLabel = 'Previous year';
|
|
43
|
+
this.previousYearsLabel = 'Earlier years';
|
|
67
44
|
this.focused = '';
|
|
68
45
|
this.choosing = false;
|
|
69
46
|
this.handleKeydown = (event) => {
|
|
47
|
+
if (this.view !== 'day') {
|
|
48
|
+
this.handlePeriodKeydown(event);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
70
51
|
if (!KEYS.has(event.key) || this.isDisabled) {
|
|
71
52
|
return;
|
|
72
53
|
}
|
|
@@ -101,9 +82,27 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
101
82
|
}
|
|
102
83
|
event.preventDefault();
|
|
103
84
|
};
|
|
85
|
+
this.pageYear = undefined;
|
|
86
|
+
this.periodFocus = undefined;
|
|
87
|
+
this.handleHeading = () => {
|
|
88
|
+
if (this.isDisabled) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (this.view === 'day') {
|
|
92
|
+
this.pageYear = this.shown.getUTCFullYear();
|
|
93
|
+
this.periodFocus = this.shown.getUTCMonth();
|
|
94
|
+
this.zoom('month');
|
|
95
|
+
}
|
|
96
|
+
else if (this.view === 'month' && this.maxView === 'year') {
|
|
97
|
+
this.periodFocus = this.periodYear;
|
|
98
|
+
this.zoom('year');
|
|
99
|
+
}
|
|
100
|
+
};
|
|
104
101
|
this.readOnly = false;
|
|
105
102
|
this.required = false;
|
|
106
103
|
this.step = 1;
|
|
104
|
+
this.view = 'day';
|
|
105
|
+
this.yearViewLabel = '{heading}. Choose a year';
|
|
107
106
|
}
|
|
108
107
|
get month() {
|
|
109
108
|
return this.monthState ?? (this.defaultMonth || fromDate(this.derivedMonth).slice(0, 7));
|
|
@@ -170,18 +169,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
170
169
|
if (this.max !== '' && iso > this.max) {
|
|
171
170
|
return true;
|
|
172
171
|
}
|
|
173
|
-
|
|
174
|
-
return true;
|
|
175
|
-
}
|
|
176
|
-
const step = Math.max(1, Math.floor(this.step) || 1);
|
|
177
|
-
if (step > 1) {
|
|
178
|
-
const base = this.min === '' ? 0 : (asDate(this.min)?.getTime() ?? 0);
|
|
179
|
-
const days = Math.round((date.getTime() - base) / 86_400_000);
|
|
180
|
-
if (days % step !== 0) {
|
|
181
|
-
return true;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return writtenList(this.disableDates).includes(iso);
|
|
172
|
+
return barredBy(date, this) !== null;
|
|
185
173
|
}
|
|
186
174
|
clamp(date) {
|
|
187
175
|
const iso = fromDate(date);
|
|
@@ -252,8 +240,93 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
252
240
|
this.choosing = true;
|
|
253
241
|
this.requestValue(next.join(','));
|
|
254
242
|
}
|
|
243
|
+
get periodYear() {
|
|
244
|
+
return this.pageYear ?? this.shown.getUTCFullYear();
|
|
245
|
+
}
|
|
246
|
+
get periodStop() {
|
|
247
|
+
if (this.periodFocus !== undefined) {
|
|
248
|
+
return this.periodFocus;
|
|
249
|
+
}
|
|
250
|
+
return this.view === 'year' ? this.periodYear : this.shown.getUTCMonth();
|
|
251
|
+
}
|
|
252
|
+
monthOut(first) {
|
|
253
|
+
return monthOut(first, this.min, this.max);
|
|
254
|
+
}
|
|
255
|
+
yearOut(year) {
|
|
256
|
+
return yearOut(year, this.min, this.max);
|
|
257
|
+
}
|
|
258
|
+
zoom(view) {
|
|
259
|
+
this.view = view;
|
|
260
|
+
void this.updateComplete.then(() => this.focus());
|
|
261
|
+
}
|
|
262
|
+
choosePeriod(at) {
|
|
263
|
+
if (this.isDisabled) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (this.view === 'year') {
|
|
267
|
+
if (this.yearOut(at)) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.pageYear = at;
|
|
271
|
+
this.periodFocus = at === this.shown.getUTCFullYear() ? this.shown.getUTCMonth() : 0;
|
|
272
|
+
this.zoom('month');
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const first = new Date(Date.UTC(this.periodYear, at, 1));
|
|
276
|
+
if (this.monthOut(first)) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
this.show(first);
|
|
280
|
+
if (this.month !== monthOf(first)) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
this.pageYear = undefined;
|
|
284
|
+
this.periodFocus = undefined;
|
|
285
|
+
this.focused = '';
|
|
286
|
+
this.zoom('day');
|
|
287
|
+
}
|
|
288
|
+
turnPage(direction) {
|
|
289
|
+
const by = this.view === 'year' ? YEARS_SHOWN : 1;
|
|
290
|
+
this.pageYear = this.periodYear + direction * by;
|
|
291
|
+
if (this.view === 'year') {
|
|
292
|
+
this.periodFocus = this.periodStop + direction * by;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
handlePeriodKeydown(event) {
|
|
296
|
+
if (this.isDisabled) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (event.key === 'Escape') {
|
|
300
|
+
event.preventDefault();
|
|
301
|
+
event.stopPropagation();
|
|
302
|
+
this.pageYear = undefined;
|
|
303
|
+
this.periodFocus = undefined;
|
|
304
|
+
this.zoom('day');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const grid = this.partNamed('periods');
|
|
308
|
+
if (!grid || !event.composedPath().includes(grid)) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const stop = this.periodStop;
|
|
312
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
313
|
+
event.preventDefault();
|
|
314
|
+
this.choosePeriod(stop);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const moved = periodKey(this.view === 'year' ? 'year' : 'month', stop, this.periodYear, event.key, inlineStep(this, event.key));
|
|
318
|
+
if (moved === null) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
event.preventDefault();
|
|
322
|
+
this.periodFocus = moved.focus;
|
|
323
|
+
this.pageYear = moved.pageYear;
|
|
324
|
+
void this.updateComplete.then(() => this.focus());
|
|
325
|
+
}
|
|
255
326
|
focus(options) {
|
|
256
|
-
this.renderRoot
|
|
327
|
+
this.renderRoot
|
|
328
|
+
?.querySelector('[part~="cell"][tabindex="0"], [part~="period"][tabindex="0"]')
|
|
329
|
+
?.focus(options);
|
|
257
330
|
}
|
|
258
331
|
connectedCallback() {
|
|
259
332
|
super.connectedCallback();
|
|
@@ -291,12 +364,11 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
291
364
|
];
|
|
292
365
|
}
|
|
293
366
|
const date = asDate(iso);
|
|
294
|
-
|
|
367
|
+
const why = date ? barredBy(date, this) : null;
|
|
368
|
+
if (why === null) {
|
|
295
369
|
return undefined;
|
|
296
370
|
}
|
|
297
|
-
|
|
298
|
-
writtenList(this.disableDays).includes(String(date.getUTCDay()));
|
|
299
|
-
return listed
|
|
371
|
+
return why === 'listed'
|
|
300
372
|
? ['badInput', this.unavailableMessage || 'Choose a date that is available.']
|
|
301
373
|
: ['stepMismatch', this.stepMismatchMessage || 'Choose one of the dates the calendar offers.'];
|
|
302
374
|
}
|
|
@@ -363,7 +435,9 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
363
435
|
heading: new Intl.DateTimeFormat(locale, { month: 'long', timeZone: 'UTC', year: 'numeric' }),
|
|
364
436
|
locale: this.locale,
|
|
365
437
|
long: new Intl.DateTimeFormat(locale, { dateStyle: 'long', timeZone: 'UTC' }),
|
|
438
|
+
monthName: new Intl.DateTimeFormat(locale, { month: 'short', timeZone: 'UTC' }),
|
|
366
439
|
weekday: new Intl.DateTimeFormat(locale, { timeZone: 'UTC', weekday: 'short' }),
|
|
440
|
+
year: new Intl.DateTimeFormat(locale, { timeZone: 'UTC', year: 'numeric' }),
|
|
367
441
|
};
|
|
368
442
|
this.cachedFormats = built;
|
|
369
443
|
return built;
|
|
@@ -404,7 +478,9 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
404
478
|
</button>`
|
|
405
479
|
: nothing}
|
|
406
480
|
${''}
|
|
407
|
-
<span part="heading" aria-live="polite"
|
|
481
|
+
<span part="heading" aria-live="polite"
|
|
482
|
+
>${this.renderHeadingText(heading.format(month), this.monthViewLabel)}</span
|
|
483
|
+
>
|
|
408
484
|
${last
|
|
409
485
|
? html `<button
|
|
410
486
|
part="next"
|
|
@@ -452,7 +528,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
452
528
|
@click=${outside ? nothing : () => this.select(date)}
|
|
453
529
|
><span
|
|
454
530
|
part="day${!outside && selected && !middle ? ' day-selected' : ''}${iso === today ? ' day-today' : ''}${outside ? ' day-outside' : ''}${disabled ? ' day-disabled' : ''}"
|
|
455
|
-
>${
|
|
531
|
+
>${this.renderDay(date, outside)}</span
|
|
456
532
|
></span
|
|
457
533
|
>`;
|
|
458
534
|
})}
|
|
@@ -460,7 +536,86 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
460
536
|
</div>
|
|
461
537
|
</div>`;
|
|
462
538
|
}
|
|
539
|
+
renderDay(date, outside) {
|
|
540
|
+
return this.dayContent && !outside ? this.dayContent(fromDate(date)) : date.getUTCDate();
|
|
541
|
+
}
|
|
542
|
+
renderHeadingText(text, label) {
|
|
543
|
+
const steps = this.maxView !== undefined &&
|
|
544
|
+
(this.view === 'day' || (this.view === 'month' && this.maxView === 'year'));
|
|
545
|
+
if (!steps) {
|
|
546
|
+
return text;
|
|
547
|
+
}
|
|
548
|
+
return html `<button
|
|
549
|
+
part="heading-button"
|
|
550
|
+
type="button"
|
|
551
|
+
aria-label=${fillTemplate(label, { heading: text })}
|
|
552
|
+
?disabled=${this.isDisabled}
|
|
553
|
+
@click=${this.handleHeading}
|
|
554
|
+
>${text}</button>`;
|
|
555
|
+
}
|
|
556
|
+
renderPeriods() {
|
|
557
|
+
const { monthName, year: yearName } = this.formats;
|
|
558
|
+
const years = this.view === 'year';
|
|
559
|
+
const page = years ? yearPageOf(this.periodYear) : this.periodYear;
|
|
560
|
+
const heading = years
|
|
561
|
+
? `${yearName.format(new Date(Date.UTC(page, 0, 1)))} – ${yearName.format(new Date(Date.UTC(page + YEARS_SHOWN - 1, 0, 1)))}`
|
|
562
|
+
: yearName.format(new Date(Date.UTC(page, 0, 1)));
|
|
563
|
+
const stop = this.periodStop;
|
|
564
|
+
const cells = periodCells({
|
|
565
|
+
max: this.max,
|
|
566
|
+
min: this.min,
|
|
567
|
+
page: this.periodYear,
|
|
568
|
+
shown: this.shown,
|
|
569
|
+
today: localToday(),
|
|
570
|
+
view: years ? 'year' : 'month',
|
|
571
|
+
}).map(({ at, chosen, current, first, out }) => {
|
|
572
|
+
const words = years ? yearName.format(first) : monthName.format(first);
|
|
573
|
+
return html `<span
|
|
574
|
+
part="period${chosen ? ' period-selected' : ''}${current ? ' period-current' : ''}${out ? ' period-disabled' : ''}"
|
|
575
|
+
role="gridcell"
|
|
576
|
+
aria-current=${current ? 'date' : nothing}
|
|
577
|
+
aria-disabled=${out ? 'true' : nothing}
|
|
578
|
+
aria-label=${years ? words : `${words} ${yearName.format(first)}`}
|
|
579
|
+
aria-selected=${chosen ? 'true' : nothing}
|
|
580
|
+
tabindex=${at === stop && !this.isDisabled ? '0' : '-1'}
|
|
581
|
+
@click=${() => this.choosePeriod(at)}
|
|
582
|
+
>${words}</span
|
|
583
|
+
>`;
|
|
584
|
+
});
|
|
585
|
+
const rows = [0, 3, 6, 9].map((from) => html `<div part="period-row" role="row">${cells.slice(from, from + 3)}</div>`);
|
|
586
|
+
return html `<div part="month">
|
|
587
|
+
<div part="header">
|
|
588
|
+
<button
|
|
589
|
+
part="previous"
|
|
590
|
+
type="button"
|
|
591
|
+
aria-label=${years ? this.previousYearsLabel : this.previousYearLabel}
|
|
592
|
+
?disabled=${this.isDisabled || this.yearOut(page - 1)}
|
|
593
|
+
@click=${() => this.turnPage(-1)}
|
|
594
|
+
>
|
|
595
|
+
${this.renderPreviousMark()}
|
|
596
|
+
</button>
|
|
597
|
+
<span part="heading" aria-live="polite"
|
|
598
|
+
>${this.renderHeadingText(heading, this.yearViewLabel)}</span
|
|
599
|
+
>
|
|
600
|
+
<button
|
|
601
|
+
part="next"
|
|
602
|
+
type="button"
|
|
603
|
+
aria-label=${years ? this.nextYearsLabel : this.nextYearLabel}
|
|
604
|
+
?disabled=${this.isDisabled || this.yearOut(years ? page + YEARS_SHOWN : page + 1)}
|
|
605
|
+
@click=${() => this.turnPage(1)}
|
|
606
|
+
>
|
|
607
|
+
${this.renderNextMark()}
|
|
608
|
+
</button>
|
|
609
|
+
</div>
|
|
610
|
+
<div part="periods" role="grid" aria-label=${heading}>${rows}</div>
|
|
611
|
+
</div>`;
|
|
612
|
+
}
|
|
463
613
|
render() {
|
|
614
|
+
if (this.view !== 'day') {
|
|
615
|
+
return html `<div part="months" style="--date-picker-column-count: 1">
|
|
616
|
+
${this.renderPeriods()}
|
|
617
|
+
</div>`;
|
|
618
|
+
}
|
|
464
619
|
const shown = this.shown;
|
|
465
620
|
const count = this.monthCount;
|
|
466
621
|
return html `<div part="months" style="--date-picker-column-count: ${Math.min(count, 2)}">
|
|
@@ -483,6 +638,9 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
|
|
|
483
638
|
__decorate([
|
|
484
639
|
property({ attribute: 'bad-input-message' })
|
|
485
640
|
], HelixHeadlessDatePicker.prototype, "badInputMessage", void 0);
|
|
641
|
+
__decorate([
|
|
642
|
+
property({ attribute: false })
|
|
643
|
+
], HelixHeadlessDatePicker.prototype, "dayContent", void 0);
|
|
486
644
|
__decorate([
|
|
487
645
|
property({ attribute: 'default-month' })
|
|
488
646
|
], HelixHeadlessDatePicker.prototype, "defaultMonth", void 0);
|
|
@@ -498,6 +656,9 @@ __decorate([
|
|
|
498
656
|
__decorate([
|
|
499
657
|
property({ reflect: true, type: Boolean })
|
|
500
658
|
], HelixHeadlessDatePicker.prototype, "invalid", void 0);
|
|
659
|
+
__decorate([
|
|
660
|
+
property({ attribute: false })
|
|
661
|
+
], HelixHeadlessDatePicker.prototype, "isDateUnavailable", void 0);
|
|
501
662
|
__decorate([
|
|
502
663
|
property()
|
|
503
664
|
], HelixHeadlessDatePicker.prototype, "label", void 0);
|
|
@@ -507,6 +668,9 @@ __decorate([
|
|
|
507
668
|
__decorate([
|
|
508
669
|
property()
|
|
509
670
|
], HelixHeadlessDatePicker.prototype, "max", void 0);
|
|
671
|
+
__decorate([
|
|
672
|
+
property({ attribute: 'max-view' })
|
|
673
|
+
], HelixHeadlessDatePicker.prototype, "maxView", void 0);
|
|
510
674
|
__decorate([
|
|
511
675
|
property()
|
|
512
676
|
], HelixHeadlessDatePicker.prototype, "min", void 0);
|
|
@@ -519,18 +683,39 @@ __decorate([
|
|
|
519
683
|
__decorate([
|
|
520
684
|
property({ type: Number })
|
|
521
685
|
], HelixHeadlessDatePicker.prototype, "months", void 0);
|
|
686
|
+
__decorate([
|
|
687
|
+
property({ attribute: 'month-view-label' })
|
|
688
|
+
], HelixHeadlessDatePicker.prototype, "monthViewLabel", void 0);
|
|
522
689
|
__decorate([
|
|
523
690
|
property({ attribute: 'next-label' })
|
|
524
691
|
], HelixHeadlessDatePicker.prototype, "nextLabel", void 0);
|
|
692
|
+
__decorate([
|
|
693
|
+
property({ attribute: 'next-year-label' })
|
|
694
|
+
], HelixHeadlessDatePicker.prototype, "nextYearLabel", void 0);
|
|
695
|
+
__decorate([
|
|
696
|
+
property({ attribute: 'next-years-label' })
|
|
697
|
+
], HelixHeadlessDatePicker.prototype, "nextYearsLabel", void 0);
|
|
525
698
|
__decorate([
|
|
526
699
|
property({ attribute: 'previous-label' })
|
|
527
700
|
], HelixHeadlessDatePicker.prototype, "previousLabel", void 0);
|
|
701
|
+
__decorate([
|
|
702
|
+
property({ attribute: 'previous-year-label' })
|
|
703
|
+
], HelixHeadlessDatePicker.prototype, "previousYearLabel", void 0);
|
|
704
|
+
__decorate([
|
|
705
|
+
property({ attribute: 'previous-years-label' })
|
|
706
|
+
], HelixHeadlessDatePicker.prototype, "previousYearsLabel", void 0);
|
|
528
707
|
__decorate([
|
|
529
708
|
property({ attribute: 'range-overflow-message' })
|
|
530
709
|
], HelixHeadlessDatePicker.prototype, "rangeOverflowMessage", void 0);
|
|
531
710
|
__decorate([
|
|
532
711
|
state()
|
|
533
712
|
], HelixHeadlessDatePicker.prototype, "focused", void 0);
|
|
713
|
+
__decorate([
|
|
714
|
+
state()
|
|
715
|
+
], HelixHeadlessDatePicker.prototype, "pageYear", void 0);
|
|
716
|
+
__decorate([
|
|
717
|
+
state()
|
|
718
|
+
], HelixHeadlessDatePicker.prototype, "periodFocus", void 0);
|
|
534
719
|
__decorate([
|
|
535
720
|
property({ attribute: 'range-underflow-message' })
|
|
536
721
|
], HelixHeadlessDatePicker.prototype, "rangeUnderflowMessage", void 0);
|
|
@@ -552,6 +737,12 @@ __decorate([
|
|
|
552
737
|
__decorate([
|
|
553
738
|
property({ attribute: 'value-missing-message' })
|
|
554
739
|
], HelixHeadlessDatePicker.prototype, "valueMissingMessage", void 0);
|
|
740
|
+
__decorate([
|
|
741
|
+
property()
|
|
742
|
+
], HelixHeadlessDatePicker.prototype, "view", void 0);
|
|
743
|
+
__decorate([
|
|
744
|
+
property({ attribute: 'year-view-label' })
|
|
745
|
+
], HelixHeadlessDatePicker.prototype, "yearViewLabel", void 0);
|
|
555
746
|
HelixHeadlessDatePicker = __decorate([
|
|
556
747
|
customElement('helix-headless-date-picker')
|
|
557
748
|
], HelixHeadlessDatePicker);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The elements at the start of a slot that hold words and no control, up to the
|
|
3
|
+
* first one that is or holds a control.
|
|
4
|
+
*
|
|
5
|
+
* What a dialog is described by. A question above a form describes the dialog,
|
|
6
|
+
* and the form does not: a description that read out every field label would
|
|
7
|
+
* repeat what a reader hears again as they reach each field.
|
|
8
|
+
*/
|
|
9
|
+
export declare const wordsIn: (slot: HTMLSlotElement | null) => Element[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const CONTROL = 'a[href], audio[controls], button, details, form, iframe, input, select, textarea, video[controls], [contenteditable], [tabindex]';
|
|
2
|
+
const isControl = (element) => element.localName.includes('-') || element.matches(CONTROL);
|
|
3
|
+
export const wordsIn = (slot) => {
|
|
4
|
+
const words = [];
|
|
5
|
+
for (const one of slot?.assignedElements({ flatten: true }) ?? []) {
|
|
6
|
+
if (isControl(one) || [...one.querySelectorAll('*')].some(isControl)) {
|
|
7
|
+
break;
|
|
8
|
+
}
|
|
9
|
+
if ((one.textContent ?? '').trim() !== '') {
|
|
10
|
+
words.push(one);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return words;
|
|
14
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AriaForwardController, CommandController, HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { AriaForwardController, CommandController, HelixElement, OpenPhaseController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
declare const HelixHeadlessDialog_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & typeof HelixElement;
|
|
4
4
|
/**
|
|
@@ -23,6 +23,16 @@ declare const HelixHeadlessDialog_base: (new (...args: any[]) => import("@fusion
|
|
|
23
23
|
* the `header` slot, or the elements an `aria-labelledby` on the host names.
|
|
24
24
|
* While it is open, the page behind it does not scroll.
|
|
25
25
|
*
|
|
26
|
+
* The dialog is described by the words at the start of its body: the elements
|
|
27
|
+
* in the default slot before the first one that is or holds a control. A
|
|
28
|
+
* question above a form describes the dialog, and the form does not. An
|
|
29
|
+
* `aria-describedby` on the host replaces it.
|
|
30
|
+
*
|
|
31
|
+
* Set `alert` for a dialog that interrupts to ask for an answer, such as a
|
|
32
|
+
* confirmation before a deletion. It is an `alertdialog`, which a screen reader
|
|
33
|
+
* announces with its description as it opens. Set `dismissible` to draw a
|
|
34
|
+
* close control, labeled by `dismiss-label`.
|
|
35
|
+
*
|
|
26
36
|
* @since 0.1.0
|
|
27
37
|
* @status stable
|
|
28
38
|
* @category overlays
|
|
@@ -30,6 +40,7 @@ declare const HelixHeadlessDialog_base: (new (...args: any[]) => import("@fusion
|
|
|
30
40
|
* @documentation https://fusion.dev/helix/components/dialog
|
|
31
41
|
*
|
|
32
42
|
* @slot - The dialog's content.
|
|
43
|
+
* @slot dismiss-icon - Replaces the glyph inside the close control.
|
|
33
44
|
* @slot footer - The dialog's actions.
|
|
34
45
|
* @slot header - The title, and anything beside it.
|
|
35
46
|
* @slot icon - An icon above the title, for a warning dialog.
|
|
@@ -39,11 +50,30 @@ declare const HelixHeadlessDialog_base: (new (...args: any[]) => import("@fusion
|
|
|
39
50
|
* @event {HelixBeforeOpenEvent} helix-before-open - Fired before it opens, and cancelable.
|
|
40
51
|
* @event {ToggleEvent} toggle - Sent when the dialog opens or closes, with `newState` and `oldState`.
|
|
41
52
|
*
|
|
53
|
+
* @csspart body - The slot the content is in. It draws no box until you give it one.
|
|
42
54
|
* @csspart dialog - The native dialog element.
|
|
55
|
+
* @csspart dismiss - The close control, drawn when the dialog is `dismissible`.
|
|
56
|
+
* @csspart dismiss-mark - The glyph on the close control, where none is slotted.
|
|
57
|
+
* @csspart footer - The slot the actions are in. It draws no box until you give it one.
|
|
58
|
+
* @csspart header - The slot the title is in. It draws no box until you give it one.
|
|
43
59
|
*
|
|
60
|
+
* @cssstate entering - From the moment the dialog opens until its entrance has finished.
|
|
61
|
+
* @cssstate exiting - From the moment the dialog closes until its exit has finished.
|
|
44
62
|
* @cssstate open - While the dialog is open.
|
|
45
63
|
*/
|
|
46
64
|
export declare class HelixHeadlessDialog extends HelixHeadlessDialog_base {
|
|
65
|
+
/**
|
|
66
|
+
* Makes the dialog an alert dialog, for a message that interrupts to ask for
|
|
67
|
+
* an answer, such as a confirmation before something is deleted.
|
|
68
|
+
*
|
|
69
|
+
* The `<dialog>` takes `role="alertdialog"`, so a screen reader announces the
|
|
70
|
+
* dialog and its description as it opens. Everything else stays the same:
|
|
71
|
+
* the dialog is modal, Escape asks to close it, and `persistent` still
|
|
72
|
+
* refuses that.
|
|
73
|
+
*
|
|
74
|
+
* Not reflected, because no stylesheet selects on it.
|
|
75
|
+
*/
|
|
76
|
+
alert: boolean;
|
|
47
77
|
/**
|
|
48
78
|
* Which close requests the browser handles on its own.
|
|
49
79
|
*
|
|
@@ -57,11 +87,24 @@ export declare class HelixHeadlessDialog extends HelixHeadlessDialog_base {
|
|
|
57
87
|
* that a resting value is an attribute's absence.
|
|
58
88
|
*/
|
|
59
89
|
closedBy?: 'any' | 'closerequest' | 'none';
|
|
90
|
+
/**
|
|
91
|
+
* Draws a control that closes the dialog, in the corner where the title is.
|
|
92
|
+
*
|
|
93
|
+
* Pressing it is `close()`, so it sends `helix-before-close` like every other
|
|
94
|
+
* way out, and `persistent` does not disable it.
|
|
95
|
+
*/
|
|
96
|
+
dismissible: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* The close control's accessible name. Translate it with the rest of your
|
|
99
|
+
* interface.
|
|
100
|
+
*/
|
|
101
|
+
dismissLabel: string;
|
|
60
102
|
/**
|
|
61
103
|
* Accessible name for the dialog. When it is empty, the heading in the
|
|
62
104
|
* `header` slot names the dialog.
|
|
63
105
|
*/
|
|
64
106
|
label: string;
|
|
107
|
+
protected readonly phases: OpenPhaseController;
|
|
65
108
|
protected readonly aria: AriaForwardController;
|
|
66
109
|
protected readonly commands: CommandController;
|
|
67
110
|
/**
|
|
@@ -147,6 +190,19 @@ export declare class HelixHeadlessDialog extends HelixHeadlessDialog_base {
|
|
|
147
190
|
* either. A browser without element reflection keeps the fallback name.
|
|
148
191
|
*/
|
|
149
192
|
private nameByHeader;
|
|
193
|
+
/** Whether the body's words are what currently describe the dialog. */
|
|
194
|
+
private describedByBody;
|
|
195
|
+
/**
|
|
196
|
+
* Describes the dialog by the words at the start of its body, where nothing
|
|
197
|
+
* else describes it.
|
|
198
|
+
*
|
|
199
|
+
* An element reference, for the reason `nameByHeader` gives. Only the leading
|
|
200
|
+
* run of elements that hold no control, so a dialog asking a question above a
|
|
201
|
+
* form is described by the question: a description that read out every field
|
|
202
|
+
* label would repeat what a reader hears again as they reach each field. An
|
|
203
|
+
* `aria-describedby` on the host comes first.
|
|
204
|
+
*/
|
|
205
|
+
private describeByBody;
|
|
150
206
|
protected willUpdate(changed: PropertyValues): void;
|
|
151
207
|
connectedCallback(): void;
|
|
152
208
|
disconnectedCallback(): void;
|
|
@@ -203,6 +259,13 @@ export declare class HelixHeadlessDialog extends HelixHeadlessDialog_base {
|
|
|
203
259
|
private refuse;
|
|
204
260
|
/** Bounds the refusal listener to one, whether or not it ever fires. */
|
|
205
261
|
private refusal;
|
|
262
|
+
/**
|
|
263
|
+
* The glyph on the close control, behind a seam.
|
|
264
|
+
*
|
|
265
|
+
* Slot fallback, so a consumer replaces it by slotting, and a method, so a
|
|
266
|
+
* tier above replaces it by overriding. The same pair `Toast.Item` draws.
|
|
267
|
+
*/
|
|
268
|
+
protected renderDismissIcon(): TemplateResult;
|
|
206
269
|
protected render(): TemplateResult;
|
|
207
270
|
}
|
|
208
271
|
declare global {
|