@fusion.dev/helix-headless 0.158.0 → 0.160.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/custom-elements.json +1925 -174
  2. package/dist/app-header/root/app-header-root.d.ts +59 -12
  3. package/dist/app-header/root/app-header-root.js +68 -29
  4. package/dist/button/root/button-root.d.ts +8 -0
  5. package/dist/button/root/button-root.js +4 -0
  6. package/dist/button-group/root/button-group-root.d.ts +20 -7
  7. package/dist/button-group/root/button-group-root.js +30 -5
  8. package/dist/date-input/root/date-input-root.d.ts +108 -19
  9. package/dist/date-input/root/date-input-root.js +112 -13
  10. package/dist/date-picker/root/date-picker-root.d.ts +81 -16
  11. package/dist/date-picker/root/date-picker-root.js +140 -36
  12. package/dist/element-events.generated.js +9 -1
  13. package/dist/format-date/fields.js +1 -0
  14. package/dist/format-date/options.d.ts +3 -1
  15. package/dist/format-date/options.js +4 -1
  16. package/dist/format-date/root/format-date-root.d.ts +27 -11
  17. package/dist/format-date/root/format-date-root.js +6 -0
  18. package/dist/lightbox/item/lightbox-item.d.ts +7 -1
  19. package/dist/lightbox/item/lightbox-item.js +15 -2
  20. package/dist/lightbox/root/lightbox-root.d.ts +8 -4
  21. package/dist/lightbox/root/lightbox-root.js +9 -1
  22. package/dist/lightbox/zoom-controller.d.ts +13 -4
  23. package/dist/lightbox/zoom-controller.js +17 -0
  24. package/dist/listbox/root/listbox-root.js +5 -0
  25. package/dist/notifications/notification/notification.d.ts +1 -0
  26. package/dist/notifications/notification/notification.js +2 -1
  27. package/dist/slider/root/slider-root.d.ts +54 -3
  28. package/dist/slider/root/slider-root.js +124 -2
  29. package/dist/steps/root/steps-root.js +8 -6
  30. package/dist/steps/step/step.d.ts +9 -0
  31. package/dist/steps/step/step.js +19 -2
  32. package/dist/tabs/panel/tab-panel.d.ts +22 -1
  33. package/dist/tabs/panel/tab-panel.js +22 -1
  34. package/dist/tabs/root/tabs-root.d.ts +21 -5
  35. package/dist/tabs/root/tabs-root.js +24 -13
  36. package/dist/tabs/tab/tab.d.ts +4 -2
  37. package/dist/tabs/tab/tab.js +3 -1
  38. package/dist/time-input/root/time-input-root.d.ts +55 -7
  39. package/dist/time-input/root/time-input-root.js +70 -10
  40. package/dist/time-picker/root/time-picker-root.d.ts +27 -5
  41. package/dist/time-picker/root/time-picker-root.js +52 -13
  42. package/dist/toast/item/toast-item.d.ts +27 -8
  43. package/dist/toast/item/toast-item.js +47 -11
  44. package/dist/toast/root/toast-root.d.ts +24 -1
  45. package/dist/toast/root/toast-root.js +54 -0
  46. package/dist/toggle/group/toggle-group.d.ts +18 -1
  47. package/dist/toggle/group/toggle-group.js +8 -2
  48. package/dist/toggle/root/toggle-root.d.ts +2 -2
  49. package/dist/toggle/root/toggle-root.js +2 -1
  50. package/dist/tour/root/tour-root.d.ts +34 -0
  51. package/dist/tour/root/tour-root.js +92 -3
  52. package/dist/tour/stop/tour-stop.d.ts +33 -1
  53. package/dist/tour/stop/tour-stop.js +12 -1
  54. package/lint.json +1 -1
  55. package/package.json +5 -5
  56. package/types/jsx.d.ts +1119 -218
  57. package/types/vue.d.ts +278 -51
  58. package/types/words.d.ts +72 -0
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, type DateCalendar, HelixFormElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, type DateCalendar, HelixFormElement, type MonthCalendar, type MonthSystem } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../date-picker/index.js';
4
4
  import '../../tag/index.js';
@@ -15,17 +15,24 @@ declare const HelixHeadlessDateInput_base: (new (...args: any[]) => import("@fus
15
15
  * gives a picker and locale formatting but allows almost no styling, so the
16
16
  * field is a text field. Typing sends `input` on each keystroke and `change`
17
17
  * when you leave the field, as a text field does. A date picked from the
18
- * calendar sends both.
18
+ * calendar sends both. The field reads ISO, `2026-08-04`, and a date written
19
+ * in the order, the digits, and the month names of `locale`, `8/4/2026` or
20
+ * `Aug 4, 2026` in `en-US` and `4.8.2026` in `de-DE`, which becomes the value
21
+ * as you leave the field.
19
22
  *
20
23
  * The trigger and ArrowDown open the calendar with focus on the selected day,
21
24
  * Tab moves between the calendar's controls without leaving it, and Escape
22
25
  * closes it and returns focus to the field. A press into the field opens the
23
26
  * calendar too. Tab into the field opens nothing, and Tab out of the field
24
- * closes the calendar.
27
+ * closes the calendar. The text field is a `combobox` with
28
+ * `aria-haspopup="dialog"`, `aria-expanded`, and `aria-controls`, as in the
29
+ * APG date picker combobox, since Down opens the calendar from it.
25
30
  *
26
31
  * With `segmented`, the field is a row of segments in place of the text: a
27
32
  * month, a day and a year in the order the locale writes them, each set with
28
- * digits or the arrow keys. Alt+Down opens the calendar from a segment.
33
+ * digits or the arrow keys. Alt+Down opens the calendar from a segment. A
34
+ * `range` is two rows with a dash between them, the start and the end, as
35
+ * React Aria's DateRangePicker draws them, and `multiple` keeps the text.
29
36
  *
30
37
  * Text that is not a date gives the form an empty value, as a native date
31
38
  * field does, and `value` keeps the text so it can be corrected.
@@ -57,6 +64,7 @@ declare const HelixHeadlessDateInput_base: (new (...args: any[]) => import("@fus
57
64
  * @aliases DatePicker, DateField, InputDate
58
65
  * @documentation https://fusion.dev/helix/components/date-input
59
66
  *
67
+ * @slot clear-icon - Replaces the cross on the clear button, under `clearable`.
60
68
  * @slot end - Content after the date, before the trigger, such as a clear button.
61
69
  * @slot feedback - The error message under the field.
62
70
  * @slot hint - The help text under the field.
@@ -72,9 +80,11 @@ declare const HelixHeadlessDateInput_base: (new (...args: any[]) => import("@fus
72
80
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired before the date changes. Cancelable: call `preventDefault()` to keep the current date.
73
81
  * @event {HelixBeforeCloseEvent} helix-before-close - Fired before the calendar closes. Cancelable: call `preventDefault()` to keep it open.
74
82
  * @event {HelixBeforeOpenEvent} helix-before-open - Fired before the calendar opens. Cancelable: call `preventDefault()` to keep it closed.
83
+ * @event {HelixClearEvent} helix-clear - Fired after the clear button has emptied the field, after `input` and `change`.
75
84
  * @event {Event} input - Fired after the value changes.
76
85
  * @event {ToggleEvent} toggle - Fired when the calendar opens or closes, with `newState` and `oldState`.
77
86
  *
87
+ * @csspart clear - The button that empties the field, under `clearable`, while it holds a date.
78
88
  * @csspart control - The text input.
79
89
  * @csspart date - One held date, as a tag, in `multiple`.
80
90
  * @csspart date-cell - The tag's grid cell, which a tag draws only inside a tag group.
@@ -116,8 +126,10 @@ declare const HelixHeadlessDateInput_base: (new (...args: any[]) => import("@fus
116
126
  * @csspart picker-periods - The grid of months or years.
117
127
  * @csspart picker-previous - The control that moves to the previous month.
118
128
  * @csspart picker-previous-mark - The icon on the previous month control.
129
+ * @csspart picker-selection - The chosen date or range in words, which screen readers hear when it changes. Drawn for screen readers only.
119
130
  * @csspart picker-week - One row of days.
120
131
  * @csspart picker-weekday - A column header naming a day of the week.
132
+ * @csspart range-separator - The dash between a range's two rows of segments, in `segmented`, beside `literal`.
121
133
  * @csspart rest - The count of dates beyond the ones shown. It is a `<button>` with `expandable` and a `<span>` without it.
122
134
  * @csspart segment - One part of the date, a year, a month or a day, in `segmented`.
123
135
  * @csspart segment-placeholder - A segment with nothing in it yet. Applied with `segment`.
@@ -127,6 +139,7 @@ declare const HelixHeadlessDateInput_base: (new (...args: any[]) => import("@fus
127
139
  * @csspart trigger - The control that opens the calendar.
128
140
  * @csspart trigger-icon - The icon on the button that opens the calendar.
129
141
  *
142
+ * @cssstate blank - The field holds no date.
130
143
  * @cssstate invalid - The field has an error to show, from the browser's validation or from your `feedback`.
131
144
  * @cssstate open - The calendar is open.
132
145
  */
@@ -138,28 +151,43 @@ export declare class HelixHeadlessDateInput extends HelixHeadlessDateInput_base
138
151
  static words: string;
139
152
  protected readonly aria: AriaForwardController;
140
153
  /**
141
- * What the browser may autofill here, as one of the platform's own tokens.
154
+ * What the browser may autofill here, as one of the platform's own tokens,
155
+ * such as `bday`.
142
156
  *
143
157
  * Empty leaves the attribute off, which is the browser's default and is what
144
158
  * lets autofill work at all: hardcoding `off` here means a birthday or a
145
- * postal code can never be filled in for a reader.
159
+ * postal code can never be filled in for a reader. With `segmented`, a
160
+ * hidden native date input carries it, since no browser fills the segments.
146
161
  */
147
162
  autocomplete: string;
148
163
  /**
149
- * The calendar the written date and the calendar's days are in: `gregory`,
150
- * `buddhist`, `japanese`, `roc`, or `iso8601`.
164
+ * The calendar the written date, the segments, and the calendar's days are
165
+ * in: `gregory`, `buddhist`, `japanese`, `roc`, or `iso8601`, or a calendar
166
+ * with months of its own that Date Picker's `calendar` names, such as
167
+ * `persian`, `hebrew`, `islamic-umalqura`, `ethiopic`, or `chinese`.
151
168
  *
152
- * Absent is the locale's own where it is one of the five, and `gregory` for
153
- * every other, as Date Picker's `calendar` is. The value is `YYYY-MM-DD` in
154
- * every calendar, and a focused field is ISO in every calendar. In
155
- * `segmented`, the segments are the Gregorian day, month, and year, in the
156
- * order the locale writes them, or ISO order for `iso8601`.
169
+ * Absent is the locale's own where its months are the Gregorian months, and
170
+ * `gregory` for every other, as Date Picker's `calendar` is. The value is
171
+ * `YYYY-MM-DD` in every calendar, and a focused text field is ISO in every
172
+ * calendar. In `segmented`, the segments are the day, month, and year as
173
+ * the calendar counts them, in the order the locale writes them: Mehr 14,
174
+ * 1405 is month 7 and day 14 of 1405 under `persian`. Under the five with
175
+ * Gregorian months they are the Gregorian day, month, and year, or ISO order
176
+ * for `iso8601`.
157
177
  */
158
- calendar?: DateCalendar;
178
+ calendar?: DateCalendar | MonthCalendar;
159
179
  /**
160
180
  * The name of the calendar's grid of days, read by screen readers.
161
181
  */
162
182
  calendarLabel: string;
183
+ /**
184
+ * Whether the field shows a button that empties it, as Input's `clearable`
185
+ * does. Shown only while the field holds something to clear and is neither
186
+ * disabled nor read-only.
187
+ */
188
+ clearable: boolean;
189
+ /** The accessible name of the clear button, which shows only an icon. */
190
+ clearLabel: string;
163
191
  /** Typed, so a keystroke is announced as `input` alone. See `announcesInput`. */
164
192
  protected readonly announcesInput = true;
165
193
  /**
@@ -168,8 +196,9 @@ export declare class HelixHeadlessDateInput extends HelixHeadlessDateInput_base
168
196
  * **The field renders when it is idle and holds the value when it is being
169
197
  * edited.** A written date is a rendering, and a document being edited must
170
198
  * not be a rendering of something else: `Intl.DateTimeFormat` formats and does
171
- * not parse, so `4 August 2026` cannot be typed back, and writing a parser per
172
- * locale is how a field ends up rejecting what it just displayed.
199
+ * not parse, so a rendering such as `Tuesday, August 4, 2026` is no text to
200
+ * edit. The field does read a date typed in the locale's order and digits as
201
+ * you leave it, which covers what a reader types.
173
202
  *
174
203
  * The way out is not to choose between the two but to separate them in time.
175
204
  * Idle, the field shows `4 August 2026`, which is what a date is for reading.
@@ -348,6 +377,12 @@ export declare class HelixHeadlessDateInput extends HelixHeadlessDateInput_base
348
377
  * removes rather than as "Remove" said once per date.
349
378
  */
350
379
  removeLabel: string;
380
+ /**
381
+ * What a screen reader hears about the dates chosen in the calendar, with
382
+ * `{kind}`, `{start}`, `{end}` and `{count}` filled in as Date Picker's
383
+ * `selection-label` says.
384
+ */
385
+ selectionLabel: string;
351
386
  /**
352
387
  * How far apart the selectable dates are, in days.
353
388
  *
@@ -361,6 +396,12 @@ export declare class HelixHeadlessDateInput extends HelixHeadlessDateInput_base
361
396
  * The message shown when a date is not on `step`.
362
397
  */
363
398
  stepMismatchMessage: string;
399
+ /**
400
+ * The time zone today is read in, such as `America/New_York`, handed to the
401
+ * calendar: the day marked today, and the month an empty field opens on.
402
+ * Empty is the reader's own. Dates are plain `YYYY-MM-DD` and carry no zone.
403
+ */
404
+ timeZone: string;
364
405
  /**
365
406
  * The name of the button that opens the calendar, read by screen readers.
366
407
  */
@@ -375,17 +416,23 @@ export declare class HelixHeadlessDateInput extends HelixHeadlessDateInput_base
375
416
  */
376
417
  private get picked();
377
418
  /**
378
- * Segments hold one date, so `range` and `multiple` keep the line of text,
379
- * whose separators and tags are how those modes write several.
419
+ * A row of segments holds one date, and `range` takes two rows with a dash
420
+ * between them, as React Aria's range fields do. `multiple` keeps the line
421
+ * of text, whose tags are how it writes several.
380
422
  */
381
423
  protected get segmentable(): boolean;
424
+ protected get segmentGroups(): number;
382
425
  protected get segmentedKind(): 'date';
383
426
  /**
384
427
  * The segments are the Gregorian day, month and year, which is what they
385
428
  * hold, in ISO order where the calendar is `iso8601`. A year shown as 2569
386
- * or Reiwa 8 would be a year the segment does not hold.
429
+ * or Reiwa 8 would be a year the segment does not hold. Under a calendar
430
+ * with months of its own they hold that calendar's parts, so they are laid
431
+ * out and named in it.
387
432
  */
388
433
  protected get segmentLocale(): string;
434
+ /** The calendar the segments count in, where it has months of its own. */
435
+ protected get segmentSystem(): MonthSystem | undefined;
389
436
  /** One date, written the way `dateStyle` asks for. */
390
437
  private written;
391
438
  protected get display(): string;
@@ -401,6 +448,20 @@ export declare class HelixHeadlessDateInput extends HelixHeadlessDateInput_base
401
448
  private draft;
402
449
  /** Relayed under this element's name, so a consumer hears one control. */
403
450
  private readonly handleInput;
451
+ /**
452
+ * A date as the reader wrote it, as `YYYY-MM-DD`: ISO as it is, and the
453
+ * order, the digits, and the month names of the field's locale and calendar
454
+ * read back, so `8/4/2026` in `en-US` is August 4, 2026. Empty where it is
455
+ * no date.
456
+ */
457
+ private read;
458
+ /**
459
+ * The text the reader leaves, read in the field's locale: a date written as
460
+ * the locale writes one becomes the value, as ISO, when the field commits.
461
+ * Not on each key, where `8/4/202` is a date in the year 202 and taking it
462
+ * would move the caret mid-year.
463
+ */
464
+ private readonly handleTextCommit;
404
465
  /** Adds what was typed to the set, or takes the last one back out. */
405
466
  private readonly handleDraftKey;
406
467
  /** The dates that get a tag, which is all of them until the cap says not. */
@@ -423,6 +484,11 @@ export declare class HelixHeadlessDateInput extends HelixHeadlessDateInput_base
423
484
  * The message shown when `disable-dates` or `disable-days` bars a date.
424
485
  */
425
486
  unavailableMessage: string;
487
+ /**
488
+ * The message for text that is no date, where `bad-input-message` is not
489
+ * set. `{mode}` is `single`, `range`, or `multiple`.
490
+ */
491
+ unreadableMessage: string;
426
492
  /**
427
493
  * The name of the calendar's heading where it shows a page of years, with
428
494
  * `{heading}` filled in.
@@ -477,6 +543,29 @@ export declare class HelixHeadlessDateInput extends HelixHeadlessDateInput_base
477
543
  * and no mark whatsoever.
478
544
  */
479
545
  protected renderTrigger(): TemplateResult;
546
+ /**
547
+ * A native date input a browser's autofill can fill, where the field shows
548
+ * segments: a row of spans that no browser fills. React Aria's
549
+ * `HiddenDateInput` is the same answer for its segmented field.
550
+ *
551
+ * Drawn and never seen, a pixel and see-through, as Select's hidden select
552
+ * is, since Safari skips a field that is `display: none`. `aria-hidden` and
553
+ * out of the tab order, and in this shadow root, so the form still takes
554
+ * the value once, from the host. A fill is taken as a pick, with `input`
555
+ * and `change` from the host.
556
+ */
557
+ private renderAutofill;
558
+ private readonly handleAutofill;
559
+ /** Whether there is a clear button on screen right now. */
560
+ private get showsClear();
561
+ /**
562
+ * Empties the field, through `requestValue` so a consumer who owns the
563
+ * value decides, then says `change` and `helix-clear` and puts focus back
564
+ * in the field, the button having gone.
565
+ */
566
+ private readonly handleClear;
567
+ /** The cross on the clear button, behind a seam a tier can move. */
568
+ protected renderClearIcon(): TemplateResult;
480
569
  /** The line of text the dates are typed into, where the field shows no segments. */
481
570
  private renderControl;
482
571
  protected render(): TemplateResult;
@@ -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, valueAsDate as asDate, barredBy, calendarLocale, customElement, formatWords, dateAsValue as fromDate, HelixFormElement, handedLocale, isDateValue, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerEditing, MixinPickerField, MixinSegmentedField, MixinTokens, words, wordsLocale, } from '@fusion.dev/helix-core';
8
- import { calendarBlank } from '@fusion.dev/helix-icons';
7
+ import { AriaForwardController, valueAsDate as asDate, barredBy, calendarLocale, customElement, drawIcon, formatWords, dateAsValue as fromDate, HelixClearEvent, HelixFormElement, handedLocale, isDateValue, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerEditing, MixinPickerField, MixinSegmentedField, MixinTokens, MONTH_CALENDARS, monthSystem, readWrittenDate, words, wordsLocale, } from '@fusion.dev/helix-core';
8
+ import { calendarBlank, 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';
@@ -25,6 +25,8 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
25
25
  });
26
26
  this.autocomplete = '';
27
27
  this.calendarLabel = 'Date Picker';
28
+ this.clearable = false;
29
+ this.clearLabel = 'Clear';
28
30
  this.announcesInput = true;
29
31
  this.dateStyle = 'long';
30
32
  this.disableDates = '';
@@ -47,8 +49,10 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
47
49
  this.rangeOverflowMessage = 'Choose a date on or before {max}.';
48
50
  this.rangeUnderflowMessage = 'Choose a date on or after {min}.';
49
51
  this.removeLabel = 'Remove {name}';
52
+ this.selectionLabel = '{kind, select, date {Selected date: {start}} start {Range starts {start}. Choose the day it ends.} range {Selected range: {start} to {end}} other {{count, plural, one {# date selected} other {# dates selected}}}}';
50
53
  this.step = 1;
51
54
  this.stepMismatchMessage = 'Choose one of the dates the calendar offers.';
55
+ this.timeZone = '';
52
56
  this.triggerLabel = 'Choose a date';
53
57
  this.draft = '';
54
58
  this.handleInput = (event) => {
@@ -64,15 +68,28 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
64
68
  this.requestValue(whole ? held.join(',') : typed);
65
69
  this.restore();
66
70
  };
71
+ this.handleTextCommit = (event) => {
72
+ if (this.mode !== 'multiple') {
73
+ const held = this.mode === 'range' && /\s[–-]\s/.test(this.value)
74
+ ? this.value.split(/\s+[–-]\s+/).map((one) => one.trim())
75
+ : dates(this.value);
76
+ const read = held.map((one) => this.read(one));
77
+ if (held.length > 0 && !held.every(isDateValue) && read.every(Boolean)) {
78
+ this.requestValue(read.join(','));
79
+ }
80
+ }
81
+ this.handleCommit(event);
82
+ };
67
83
  this.handleDraftKey = (event) => {
68
84
  if (this.mode !== 'multiple') {
69
85
  return;
70
86
  }
71
87
  const held = dates(this.value);
72
- if (event.key === 'Enter' && isDateValue(this.draft)) {
88
+ const draft = this.read(this.draft.trim());
89
+ if (event.key === 'Enter' && draft !== '') {
73
90
  event.preventDefault();
74
- if (!held.includes(this.draft)) {
75
- this.requestPicked([...held, this.draft].join(','));
91
+ if (!held.includes(draft)) {
92
+ this.requestPicked([...held, draft].join(','));
76
93
  }
77
94
  this.draft = '';
78
95
  }
@@ -100,10 +117,24 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
100
117
  }
101
118
  };
102
119
  this.unavailableMessage = 'Choose a date that is available.';
120
+ this.unreadableMessage = '{mode, select, single {Enter a date as YYYY-MM-DD.} other {Enter dates as YYYY-MM-DD, separated by commas.}}';
103
121
  this.yearViewLabel = '{heading}. Choose a year';
104
122
  this.handleIconChange = () => {
105
123
  this.requestUpdate();
106
124
  };
125
+ this.handleAutofill = (event) => {
126
+ const filled = event.currentTarget.value;
127
+ if (isDateValue(filled)) {
128
+ this.requestPicked(filled);
129
+ }
130
+ };
131
+ this.handleClear = () => {
132
+ if (this.value !== '' && this.requestValue('')) {
133
+ this.announceChange();
134
+ this.dispatchEvent(new HelixClearEvent());
135
+ }
136
+ void this.updateComplete.then(() => this.returnFocus());
137
+ };
107
138
  }
108
139
  static { this.words = 'helix-date-input'; }
109
140
  get language() {
@@ -125,24 +156,34 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
125
156
  }
126
157
  willUpdate(changed) {
127
158
  super.willUpdate(changed);
159
+ this.toggleState('blank', this.value === '');
128
160
  if (changed.has('months') || changed.has('locale') || changed.has('calendar')) {
129
161
  this.fitted = undefined;
130
162
  this.wide = undefined;
131
163
  }
164
+ if (changed.has('open') && this.open) {
165
+ this.partNamed('picker')?.showChosen?.();
166
+ }
132
167
  }
133
168
  get picked() {
134
169
  const held = dates(this.value);
135
170
  return held.length > 0 && held.every(isDateValue) ? held.join(',') : '';
136
171
  }
137
172
  get segmentable() {
138
- return this.mode === undefined;
173
+ return this.mode !== 'multiple';
174
+ }
175
+ get segmentGroups() {
176
+ return this.mode === 'range' ? 2 : 1;
139
177
  }
140
178
  get segmentedKind() {
141
179
  return 'date';
142
180
  }
143
181
  get segmentLocale() {
144
182
  const tag = calendarLocale(this.language, this.calendar);
145
- return tag.includes('-ca-iso8601') ? tag : calendarLocale(tag, 'gregory');
183
+ return tag.includes('-ca-iso8601') || this.segmentSystem ? tag : calendarLocale(tag, 'gregory');
184
+ }
185
+ get segmentSystem() {
186
+ return MONTH_CALENDARS.includes(this.calendar ?? '') ? monthSystem(this.calendar) : undefined;
146
187
  }
147
188
  written(date) {
148
189
  if (this.dateStyle === 'iso' || !isDateValue(date)) {
@@ -167,6 +208,9 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
167
208
  const written = held.map((one) => this.written(one));
168
209
  return this.mode === 'range' ? written.join(' – ') : written.join(', ');
169
210
  }
211
+ read(text) {
212
+ return isDateValue(text) ? text : readWrittenDate(text, this.segmentLocale, this.segmentSystem);
213
+ }
170
214
  get shownDates() {
171
215
  const held = dates(this.value);
172
216
  return this.shownLimit > 0 ? held.slice(0, this.shownLimit) : held;
@@ -185,9 +229,7 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
185
229
  const held = dates(this.value);
186
230
  if (held.length > 0 && !held.every(isDateValue)) {
187
231
  return (this.badInputMessage ||
188
- (this.mode === undefined
189
- ? 'Enter a date as YYYY-MM-DD.'
190
- : 'Enter dates as YYYY-MM-DD, separated by commas.'));
232
+ formatWords(this, 'unreadableMessage', { mode: this.mode ?? 'single' }));
191
233
  }
192
234
  if (this.min !== '' && held.some((one) => one < this.min)) {
193
235
  return [
@@ -230,7 +272,7 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
230
272
  period-disabled: picker-period-disabled, period-row: picker-period-row,
231
273
  period-selected: picker-period-selected, periods: picker-periods,
232
274
  previous: picker-previous, previous-mark: picker-previous-mark,
233
- week: picker-week, weekday: picker-weekday"
275
+ selection: picker-selection, week: picker-week, weekday: picker-weekday"
234
276
  ?invalid=${this.resolvedFeedback !== ''}
235
277
  mode=${this.mode ?? nothing}
236
278
  months=${this.fitted ?? this.months}
@@ -251,7 +293,9 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
251
293
  previous-label=${this.previousLabel}
252
294
  previous-year-label=${this.previousYearLabel}
253
295
  previous-years-label=${this.previousYearsLabel}
296
+ selection-label=${this.selectionLabel}
254
297
  step=${this.step}
298
+ time-zone=${this.timeZone || nothing}
255
299
  year-view-label=${this.yearViewLabel}
256
300
  .dayContent=${this.dayContent}
257
301
  .isDateUnavailable=${this.isDateUnavailable}
@@ -290,6 +334,29 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
290
334
  ${unsafeSVG(calendarBlank.content)}
291
335
  </svg></slot>`;
292
336
  }
337
+ renderAutofill() {
338
+ if (this.autocomplete === '' || this.autocomplete === 'off') {
339
+ return nothing;
340
+ }
341
+ return html `<input
342
+ type="date"
343
+ aria-hidden="true"
344
+ autocomplete=${this.autocomplete}
345
+ name=${this.name || nothing}
346
+ style="position:fixed;top:0;left:0;width:1px;height:1px;opacity:0;pointer-events:none"
347
+ tabindex="-1"
348
+ .value=${isDateValue(this.value) ? this.value : ''}
349
+ ?disabled=${this.isDisabled}
350
+ @change=${this.handleAutofill}
351
+ @input=${this.stopInner}
352
+ />`;
353
+ }
354
+ get showsClear() {
355
+ return this.clearable && this.value !== '' && !this.isDisabled && !this.readOnly;
356
+ }
357
+ renderClearIcon() {
358
+ return html `<slot name="clear-icon">${drawIcon(x)}</slot>`;
359
+ }
293
360
  renderControl() {
294
361
  return html `<input
295
362
  part="control"
@@ -300,6 +367,10 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
300
367
  placeholder=${this.mode === 'multiple' && dates(this.value).length > 0
301
368
  ? nothing
302
369
  : this.placeholder || nothing}
370
+ role="combobox"
371
+ aria-controls="surface"
372
+ aria-expanded=${this.open ? 'true' : 'false'}
373
+ aria-haspopup="dialog"
303
374
  autocomplete=${this.autocomplete || nothing}
304
375
  inputmode="numeric"
305
376
  aria-describedby=${this.describedBy ?? nothing}
@@ -308,7 +379,7 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
308
379
  ?readonly=${this.readOnly}
309
380
  ?required=${this.required}
310
381
  @blur=${this.handleBlur}
311
- @change=${this.handleCommit}
382
+ @change=${this.handleTextCommit}
312
383
  @focus=${this.handleFocus}
313
384
  @input=${this.handleInput}
314
385
  @keydown=${this.handleDraftKey}
@@ -322,8 +393,18 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
322
393
  ${this.mode === 'multiple'
323
394
  ? this.renderTokenRow(this.shownDates.map((date) => this.renderTag(date)), this.restCount)
324
395
  : nothing}
325
- ${this.segmentsShown ? this.renderSegments() : this.renderControl()}
396
+ ${this.segmentsShown ? html `${this.renderSegments()}${this.renderAutofill()}` : this.renderControl()}
326
397
  <slot name="end"></slot>
398
+ ${this.showsClear
399
+ ? html `<button
400
+ part="clear"
401
+ type="button"
402
+ aria-label=${this.clearLabel}
403
+ @click=${this.handleClear}
404
+ >
405
+ ${this.renderClearIcon()}
406
+ </button>`
407
+ : nothing}
327
408
  <button
328
409
  part="trigger"
329
410
  type="button"
@@ -377,6 +458,13 @@ __decorate([
377
458
  words,
378
459
  property({ attribute: 'calendar-label' })
379
460
  ], HelixHeadlessDateInput.prototype, "calendarLabel", void 0);
461
+ __decorate([
462
+ property({ type: Boolean })
463
+ ], HelixHeadlessDateInput.prototype, "clearable", void 0);
464
+ __decorate([
465
+ words,
466
+ property({ attribute: 'clear-label' })
467
+ ], HelixHeadlessDateInput.prototype, "clearLabel", void 0);
380
468
  __decorate([
381
469
  property({ attribute: 'date-style' })
382
470
  ], HelixHeadlessDateInput.prototype, "dateStyle", void 0);
@@ -464,6 +552,10 @@ __decorate([
464
552
  words,
465
553
  property({ attribute: 'remove-label' })
466
554
  ], HelixHeadlessDateInput.prototype, "removeLabel", void 0);
555
+ __decorate([
556
+ words,
557
+ property({ attribute: 'selection-label' })
558
+ ], HelixHeadlessDateInput.prototype, "selectionLabel", void 0);
467
559
  __decorate([
468
560
  property({ type: Number })
469
561
  ], HelixHeadlessDateInput.prototype, "step", void 0);
@@ -471,6 +563,9 @@ __decorate([
471
563
  words,
472
564
  property({ attribute: 'step-mismatch-message' })
473
565
  ], HelixHeadlessDateInput.prototype, "stepMismatchMessage", void 0);
566
+ __decorate([
567
+ property({ attribute: 'time-zone' })
568
+ ], HelixHeadlessDateInput.prototype, "timeZone", void 0);
474
569
  __decorate([
475
570
  words,
476
571
  property({ attribute: 'trigger-label' })
@@ -482,6 +577,10 @@ __decorate([
482
577
  words,
483
578
  property({ attribute: 'unavailable-message' })
484
579
  ], HelixHeadlessDateInput.prototype, "unavailableMessage", void 0);
580
+ __decorate([
581
+ words,
582
+ property({ attribute: 'unreadable-message' })
583
+ ], HelixHeadlessDateInput.prototype, "unreadableMessage", void 0);
485
584
  __decorate([
486
585
  words,
487
586
  property({ attribute: 'year-view-label' })