@fusion.dev/helix-headless 0.153.0 → 0.155.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 (81) hide show
  1. package/custom-elements.json +1885 -140
  2. package/dist/accordion/item/accordion-item.d.ts +15 -0
  3. package/dist/accordion/item/accordion-item.js +12 -1
  4. package/dist/accordion/root/accordion-root.d.ts +2 -1
  5. package/dist/accordion/root/accordion-root.js +1 -0
  6. package/dist/badge/root/badge-root.d.ts +15 -2
  7. package/dist/badge/root/badge-root.js +20 -3
  8. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +36 -3
  9. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +42 -2
  10. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +15 -5
  11. package/dist/breadcrumbs/root/breadcrumbs-root.js +29 -4
  12. package/dist/button-group/root/button-group-root.d.ts +2 -0
  13. package/dist/button-group/root/button-group-root.js +1 -0
  14. package/dist/card/root/card-root.d.ts +31 -11
  15. package/dist/card/root/card-root.js +25 -11
  16. package/dist/code/root/code-root.d.ts +7 -0
  17. package/dist/code/root/code-root.js +1 -1
  18. package/dist/code-block/root/code-block-root.d.ts +37 -1
  19. package/dist/code-block/root/code-block-root.js +38 -5
  20. package/dist/color-input/root/color-input-root.d.ts +9 -3
  21. package/dist/color-input/root/color-input-root.js +3 -1
  22. package/dist/color-picker/channels.d.ts +5 -3
  23. package/dist/color-picker/channels.js +79 -21
  24. package/dist/color-picker/root/color-picker-root.d.ts +58 -5
  25. package/dist/color-picker/root/color-picker-root.js +75 -13
  26. package/dist/date-input/root/date-input-root.d.ts +1 -0
  27. package/dist/date-input/root/date-input-root.js +2 -1
  28. package/dist/date-picker/root/date-picker-root.d.ts +43 -13
  29. package/dist/date-picker/root/date-picker-root.js +54 -31
  30. package/dist/element-events.generated.js +2 -1
  31. package/dist/empty/root/empty-root.d.ts +19 -1
  32. package/dist/empty/root/empty-root.js +13 -4
  33. package/dist/icon/fetch.d.ts +20 -0
  34. package/dist/icon/fetch.js +70 -0
  35. package/dist/icon/root/icon-root.d.ts +27 -5
  36. package/dist/icon/root/icon-root.js +41 -17
  37. package/dist/navigation/root/navigation-root.d.ts +26 -0
  38. package/dist/navigation/root/navigation-root.js +58 -1
  39. package/dist/quote/root/quote-root.d.ts +17 -1
  40. package/dist/quote/root/quote-root.js +15 -2
  41. package/dist/select/root/select-root.d.ts +29 -3
  42. package/dist/select/root/select-root.js +34 -9
  43. package/dist/skeleton/root/skeleton-root.d.ts +7 -0
  44. package/dist/skeleton/root/skeleton-root.js +4 -1
  45. package/dist/slider/root/slider-root.d.ts +71 -9
  46. package/dist/slider/root/slider-root.js +129 -42
  47. package/dist/speed-dial/item/speed-dial-item.d.ts +29 -1
  48. package/dist/speed-dial/item/speed-dial-item.js +48 -7
  49. package/dist/speed-dial/root/speed-dial-root.d.ts +32 -6
  50. package/dist/speed-dial/root/speed-dial-root.js +50 -5
  51. package/dist/splitter/root/splitter-root.d.ts +22 -0
  52. package/dist/splitter/root/splitter-root.js +36 -7
  53. package/dist/stat/group/stat-group.d.ts +23 -4
  54. package/dist/stat/group/stat-group.js +13 -1
  55. package/dist/stat/root/stat-root.d.ts +46 -1
  56. package/dist/stat/root/stat-root.js +29 -5
  57. package/dist/steps/reach.d.ts +5 -1
  58. package/dist/steps/reach.js +2 -2
  59. package/dist/steps/root/steps-root.d.ts +37 -2
  60. package/dist/steps/root/steps-root.js +49 -7
  61. package/dist/steps/step/step.d.ts +6 -0
  62. package/dist/steps/step/step.js +4 -0
  63. package/dist/tabs/root/tabs-root.d.ts +37 -0
  64. package/dist/tabs/root/tabs-root.js +92 -2
  65. package/dist/tabs/tab/tab.d.ts +38 -2
  66. package/dist/tabs/tab/tab.js +45 -4
  67. package/dist/tag/group/tag-group.d.ts +29 -3
  68. package/dist/tag/group/tag-group.js +42 -11
  69. package/dist/toggle/root/toggle-root.d.ts +2 -0
  70. package/dist/toggle/root/toggle-root.js +1 -1
  71. package/dist/tree/item/tree-item.d.ts +10 -0
  72. package/dist/tree/item/tree-item.js +12 -0
  73. package/dist/tree/root/tree-root.d.ts +40 -1
  74. package/dist/tree/root/tree-root.js +120 -4
  75. package/dist/tree/selection.d.ts +6 -0
  76. package/dist/tree/selection.js +11 -0
  77. package/lint.json +1 -1
  78. package/package.json +5 -5
  79. package/types/jsx.d.ts +1412 -207
  80. package/types/vue.d.ts +294 -65
  81. package/types/words.d.ts +49 -1
@@ -15,7 +15,7 @@ import { areaAt, hueAt } from '../wheel.js';
15
15
  import '../../button/index.js';
16
16
  import '../../button-group/index.js';
17
17
  import '../../slider/index.js';
18
- const FORMATS = ['hex', 'rgb', 'hsl'];
18
+ const FORMATS = ['hex', 'rgb', 'hsl', 'oklch'];
19
19
  const START = { alpha: 100, brightness: 100, hue: 0, saturation: 100 };
20
20
  let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPickerFeedback(MixinControllerValue(HelixFormElement)) {
21
21
  constructor() {
@@ -26,13 +26,16 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
26
26
  this.blueLabel = 'Blue';
27
27
  this.brightnessLabel = 'Brightness';
28
28
  this.channelFields = false;
29
+ this.chromaLabel = 'Chroma';
29
30
  this.colorWords = {};
30
31
  this.copiedLabel = 'Copied';
31
32
  this.copyLabel = 'Copy {value}';
32
33
  this.eyedropperLabel = 'Sample a color from the screen';
33
34
  this.formatLabel = 'Syntax: {format}. Change it.';
34
35
  this.greenLabel = 'Green';
36
+ this.hint = '';
35
37
  this.hueLabel = 'Hue';
38
+ this.label = '';
36
39
  this.lightnessLabel = 'Lightness';
37
40
  this.noEyedropper = false;
38
41
  this.noFormat = false;
@@ -50,6 +53,11 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
50
53
  this.valueMissingMessage = 'Choose a color.';
51
54
  this.wheel = false;
52
55
  this.sampling = false;
56
+ this.handleNamingSlot = () => {
57
+ this.requestUpdate();
58
+ };
59
+ this.dragging = false;
60
+ this.picking = false;
53
61
  this.owned = false;
54
62
  this.announcesInput = true;
55
63
  this.unsettled = false;
@@ -132,6 +140,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
132
140
  return;
133
141
  }
134
142
  this.dispatchEvent(new HelixSampleStartEvent());
143
+ this.picking = true;
135
144
  try {
136
145
  const { sRGBHex } = await new Dropper().open();
137
146
  const parsed = parseColor(sRGBHex);
@@ -142,6 +151,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
142
151
  }
143
152
  catch {
144
153
  }
154
+ this.picking = false;
145
155
  this.dispatchEvent(new HelixSampleEndEvent());
146
156
  };
147
157
  this.handleGridPointer = (event) => {
@@ -150,12 +160,14 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
150
160
  }
151
161
  const grid = event.currentTarget;
152
162
  if (event.type === 'pointerup' || event.type === 'pointercancel') {
163
+ this.dragging = false;
153
164
  this.settle();
154
165
  return;
155
166
  }
156
167
  if (event.type === 'pointerdown') {
168
+ this.dragging = true;
157
169
  capturePointer(grid, event.pointerId);
158
- this.partNamed('grid-saturation')?.focus();
170
+ this.partNamed('grid-saturation')?.focus({ preventScroll: true });
159
171
  }
160
172
  else if (!grid.hasPointerCapture(event.pointerId)) {
161
173
  return;
@@ -181,7 +193,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
181
193
  }
182
194
  if (event.type === 'pointerdown') {
183
195
  capturePointer(wheel, event.pointerId);
184
- this.partNamed('wheel-hue')?.focus();
196
+ this.partNamed('wheel-hue')?.focus({ preventScroll: true });
185
197
  }
186
198
  else if (!wheel.hasPointerCapture(event.pointerId)) {
187
199
  return;
@@ -232,6 +244,23 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
232
244
  };
233
245
  }
234
246
  static { this.words = 'helix-color-picker'; }
247
+ slottedIn(name) {
248
+ return this.knownChildren.filter((child) => child.slot === name);
249
+ }
250
+ get named() {
251
+ return (this.label ||
252
+ this.slottedIn('label')
253
+ .map((child) => child.textContent ?? '')
254
+ .join(' ')
255
+ .trim());
256
+ }
257
+ get hinted() {
258
+ return this.hint !== '' || this.slottedIn('hint').length > 0;
259
+ }
260
+ get describedBy() {
261
+ const ids = [this.hinted ? 'hint' : '', this.feedback === '' ? '' : 'feedback'].filter(Boolean);
262
+ return ids.length > 0 ? ids.join(' ') : nothing;
263
+ }
235
264
  settle() {
236
265
  if (this.unsettled) {
237
266
  this.unsettled = false;
@@ -299,6 +328,8 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
299
328
  return {
300
329
  alpha: this.opacityLabel,
301
330
  blue: this.blueLabel,
331
+ brightness: this.brightnessLabel,
332
+ chroma: this.chromaLabel,
302
333
  green: this.greenLabel,
303
334
  hue: this.hueLabel,
304
335
  lightness: this.lightnessLabel,
@@ -327,10 +358,10 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
327
358
  <input
328
359
  part="field-input"
329
360
  type="number"
330
- inputmode="numeric"
361
+ inputmode=${field.step < 1 ? 'decimal' : 'numeric'}
331
362
  min=${field.min}
332
363
  max=${field.max}
333
- step="1"
364
+ step=${field.step}
334
365
  .value=${live(String(field.value))}
335
366
  ?disabled=${this.isDisabled}
336
367
  ?readonly=${this.readOnly}
@@ -350,7 +381,8 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
350
381
  exportparts="control: channel-control, feedback: channel-feedback, field: channel-field,
351
382
  hint: channel-hint, label: channel-label, marker: channel-marker,
352
383
  marker-label: channel-marker-label, markers: channel-markers, output: channel-output,
353
- tooltip: channel-tooltip, tooltips: channel-tooltips"
384
+ thumb: channel-thumb, thumbs: channel-thumbs, tooltip: channel-tooltip,
385
+ tooltips: channel-tooltips"
354
386
  data-channel=${channel}
355
387
  aria-invalid=${this.marked ? 'true' : nothing}
356
388
  label=${label}
@@ -391,10 +423,18 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
391
423
  willUpdate(changed) {
392
424
  super.willUpdate(changed);
393
425
  const outside = [...(this.internals.labels ?? [])];
394
- hostAria(this.internals, { labelledBy: outside, role: outside.length > 0 ? 'group' : '' });
426
+ const named = this.named;
427
+ hostAria(this.internals, {
428
+ label: outside.length > 0 ? '' : named,
429
+ labelledBy: outside,
430
+ role: outside.length > 0 || named !== '' ? 'group' : '',
431
+ });
395
432
  if (!this.hydrating) {
396
433
  this.sampling = 'EyeDropper' in globalThis;
397
434
  }
435
+ this.toggleState('copied', this.copier.copied);
436
+ this.toggleState('dragging', this.dragging);
437
+ this.toggleState('sampling', this.picking);
398
438
  }
399
439
  renderTools() {
400
440
  return html `<${this.tag('button-group')} part="actions" label=${this.toolsLabel}>
@@ -471,7 +511,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
471
511
  const reversed = square('brightness');
472
512
  const hue = hueName({ alpha: 100, brightness: 100, hue: color.hue, saturation: 100 }, this.colorWords, locale);
473
513
  const hueSaid = (value) => new Intl.ListFormat(locale, { style: 'short', type: 'unit' }).format([String(value), hue]);
474
- const owner = this.getAttribute('aria-label')?.trim();
514
+ const owner = this.getAttribute('aria-label')?.trim() || this.named;
475
515
  const axisName = (channel) => (owner ? `${owner}, ${channel}` : channel);
476
516
  const area = html `<div
477
517
  part="grid"
@@ -488,7 +528,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
488
528
  min="0"
489
529
  max="100"
490
530
  aria-label=${axisName(this.saturationLabel)}
491
- aria-describedby=${this.feedback === '' ? nothing : 'feedback'}
531
+ aria-describedby=${this.describedBy}
492
532
  aria-valuetext=${both}
493
533
  aria-invalid=${this.marked ? 'true' : nothing}
494
534
  .value=${String(color.saturation)}
@@ -505,7 +545,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
505
545
  tabindex="-1"
506
546
  style="writing-mode: vertical-lr; direction: rtl"
507
547
  aria-label=${axisName(this.brightnessLabel)}
508
- aria-describedby=${this.feedback === '' ? nothing : 'feedback'}
548
+ aria-describedby=${this.describedBy}
509
549
  aria-valuetext=${reversed}
510
550
  aria-invalid=${this.marked ? 'true' : nothing}
511
551
  .value=${String(color.brightness)}
@@ -514,7 +554,10 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
514
554
  @change=${() => this.settle()}
515
555
  />
516
556
  </div>`;
517
- return html `${this.wheel
557
+ return html `<div part="label" ?hidden=${this.named === ''}>
558
+ <slot name="label" @slotchange=${this.handleNamingSlot}>${this.label}</slot>
559
+ </div>
560
+ ${this.wheel
518
561
  ? html `<div
519
562
  part="wheel"
520
563
  style="--color-picker-hue: ${color.hue}"
@@ -531,7 +574,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
531
574
  max="360"
532
575
  dir="ltr"
533
576
  aria-label=${axisName(this.hueLabel)}
534
- aria-describedby=${this.feedback === '' ? nothing : 'feedback'}
577
+ aria-describedby=${this.describedBy}
535
578
  aria-valuetext=${hueSaid(color.hue)}
536
579
  aria-invalid=${this.marked ? 'true' : nothing}
537
580
  .value=${String(color.hue)}
@@ -574,7 +617,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
574
617
  part="value"
575
618
  type="text"
576
619
  aria-label=${this.valueLabel}
577
- aria-describedby=${this.feedback === '' ? nothing : 'feedback'}
620
+ aria-describedby=${this.describedBy}
578
621
  aria-invalid=${this.marked ? 'true' : nothing}
579
622
  autocomplete=${this.autocomplete || nothing}
580
623
  spellcheck="false"
@@ -591,6 +634,9 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
591
634
  </div>
592
635
 
593
636
  ${this.presets.length === 0 ? nothing : this.renderSwatches(painted)}
637
+ <div part="hint" id="hint" ?hidden=${!this.hinted}>
638
+ <slot name="hint" @slotchange=${this.handleNamingSlot}>${this.hint}</slot>
639
+ </div>
594
640
  ${this.renderPickerFeedback()}`;
595
641
  }
596
642
  };
@@ -615,6 +661,10 @@ __decorate([
615
661
  __decorate([
616
662
  property({ attribute: 'channel-fields', type: Boolean })
617
663
  ], HelixHeadlessColorPicker.prototype, "channelFields", void 0);
664
+ __decorate([
665
+ words,
666
+ property({ attribute: 'chroma-label' })
667
+ ], HelixHeadlessColorPicker.prototype, "chromaLabel", void 0);
618
668
  __decorate([
619
669
  property({ attribute: 'color-words', converter: wordsConverter })
620
670
  ], HelixHeadlessColorPicker.prototype, "colorWords", void 0);
@@ -641,10 +691,16 @@ __decorate([
641
691
  words,
642
692
  property({ attribute: 'green-label' })
643
693
  ], HelixHeadlessColorPicker.prototype, "greenLabel", void 0);
694
+ __decorate([
695
+ property()
696
+ ], HelixHeadlessColorPicker.prototype, "hint", void 0);
644
697
  __decorate([
645
698
  words,
646
699
  property({ attribute: 'hue-label' })
647
700
  ], HelixHeadlessColorPicker.prototype, "hueLabel", void 0);
701
+ __decorate([
702
+ property()
703
+ ], HelixHeadlessColorPicker.prototype, "label", void 0);
648
704
  __decorate([
649
705
  words,
650
706
  property({ attribute: 'lightness-label' })
@@ -711,6 +767,12 @@ __decorate([
711
767
  __decorate([
712
768
  state()
713
769
  ], HelixHeadlessColorPicker.prototype, "swatchAt", void 0);
770
+ __decorate([
771
+ state()
772
+ ], HelixHeadlessColorPicker.prototype, "dragging", void 0);
773
+ __decorate([
774
+ state()
775
+ ], HelixHeadlessColorPicker.prototype, "picking", void 0);
714
776
  HelixHeadlessColorPicker = __decorate([
715
777
  customElement('helix-headless-color-picker')
716
778
  ], HelixHeadlessColorPicker);
@@ -72,6 +72,7 @@ declare const HelixHeadlessDateInput_base: (new (...args: any[]) => import("@fus
72
72
  * @csspart picker-day-outside - A day from a neighboring month. Applied with `picker-day`.
73
73
  * @csspart picker-day-selected - The chosen day, or either end of a chosen range. Applied with `picker-day`.
74
74
  * @csspart picker-day-today - Today. Applied with `picker-day`.
75
+ * @csspart picker-day-weekend - A day of the weekend where the calendar's language is spoken, beside `picker-day`.
75
76
  * @csspart picker-feedback - The calendar's own message, under it, while the calendar's `feedback` is set.
76
77
  * @csspart picker-grid - The grid of days.
77
78
  * @csspart picker-header - The month name and its controls.
@@ -218,7 +218,8 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
218
218
  exportparts="cell: picker-cell, cell-range-end: picker-cell-range-end,
219
219
  cell-range-middle: picker-cell-range-middle, cell-range-start: picker-cell-range-start,
220
220
  day: picker-day, day-disabled: picker-day-disabled, day-outside: picker-day-outside,
221
- day-selected: picker-day-selected, day-today: picker-day-today, feedback: picker-feedback, grid: picker-grid,
221
+ day-selected: picker-day-selected, day-today: picker-day-today, day-weekend: picker-day-weekend,
222
+ feedback: picker-feedback, grid: picker-grid,
222
223
  header: picker-header, heading: picker-heading, heading-button: picker-heading-button,
223
224
  month: picker-month, months: picker-months, next: picker-next,
224
225
  next-mark: picker-next-mark, period: picker-period, period-current: picker-period-current,
@@ -1,4 +1,4 @@
1
- import { type CalendarView, type DateCalendar, HelixFormElement } from '@fusion.dev/helix-core';
1
+ import { type CalendarView, type DateCalendar, HelixFormElement, type MonthCalendar } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessDatePicker_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinPickerFeedbackInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & typeof HelixFormElement;
4
4
  /**
@@ -47,6 +47,8 @@ declare const HelixHeadlessDatePicker_base: (new (...args: any[]) => import("@fu
47
47
  * @aliases Calendar, Date grid, Month view
48
48
  * @documentation https://fusion.dev/helix/components/date-picker
49
49
  *
50
+ * @slot footer - Content under the calendar, such as buttons for today or a common range.
51
+ * @slot header - Content above the calendar, such as a note on what the date is for.
50
52
  * @slot next-icon - The glyph on the control that moves forward a month, usually a caret.
51
53
  * @slot previous-icon - The glyph on the control that moves back a month, usually a caret.
52
54
  *
@@ -64,6 +66,7 @@ declare const HelixHeadlessDatePicker_base: (new (...args: any[]) => import("@fu
64
66
  * @csspart day-outside - A day from a neighboring month, filling the grid out, beside `day`.
65
67
  * @csspart day-selected - The chosen day, or either end of a chosen range, beside `day`.
66
68
  * @csspart day-today - Today, beside `day`.
69
+ * @csspart day-weekend - A day of the weekend where the calendar's language is spoken, beside `day`: Saturday and Sunday in `en-US`, Friday and Saturday in `ar-EG`.
67
70
  * @csspart feedback - The message under the calendar, while `feedback` is set.
68
71
  * @csspart grid - The grid of days.
69
72
  * @csspart header - The month name and its controls.
@@ -82,7 +85,7 @@ declare const HelixHeadlessDatePicker_base: (new (...args: any[]) => import("@fu
82
85
  * @csspart previous - The control that moves to the previous month.
83
86
  * @csspart previous-mark - The glyph on the previous month control, where none is slotted.
84
87
  * @csspart week - One row of days.
85
- * @csspart weekday - A column header naming a day of the week.
88
+ * @csspart weekday - A column header naming a day of the week. It shows the short name and says the full one.
86
89
  *
87
90
  * @cssproperty --date-picker-column-count - How many month columns are drawn, capped at two. `--date-picker-columns` overrides it.
88
91
  *
@@ -105,17 +108,24 @@ export declare class HelixHeadlessDatePicker extends HelixHeadlessDatePicker_bas
105
108
  */
106
109
  badInputMessage: string;
107
110
  /**
108
- * The calendar the heading, the days, and the months are written in:
109
- * `gregory`, `buddhist`, `japanese`, `roc`, or `iso8601`, the five whose
110
- * months are the Gregorian months.
111
+ * The calendar the heading, the days, and the months are written in.
111
112
  *
112
- * Absent is the locale's own where it is one of the five, such as
113
- * `buddhist` for `th-TH` or `japanese` for `ja-JP-u-ca-japanese`, and
114
- * `gregory` for every other, such as `persian` for `fa-IR`, so the heading
115
- * names the Gregorian month the grid shows. The value is `YYYY-MM-DD` in
116
- * every calendar.
113
+ * `gregory`, `buddhist`, `japanese`, `roc`, and `iso8601` are the five
114
+ * whose months are the Gregorian months. Absent is the locale's own where
115
+ * it is one of the five, such as `buddhist` for `th-TH` or `japanese` for
116
+ * `ja-JP-u-ca-japanese`, and `gregory` for every other, such as `persian`
117
+ * for `fa-IR`.
118
+ *
119
+ * `persian`, `hebrew`, `islamic-umalqura`, `islamic-civil`, `ethiopic`,
120
+ * `coptic`, and `indian` draw a calendar with months of its own, as React
121
+ * Aria's calendars do: each grid is one of that calendar's months, the
122
+ * Hebrew leap month and Ethiopia's five-day thirteenth month included, and
123
+ * Previous, Next and the page keys move by those months. `month` and
124
+ * `helix-navigate` then name a month by its first day, `YYYY-MM-DD`. The
125
+ * heading stays text, as the months and years views are drawn in Gregorian
126
+ * months only. The value is `YYYY-MM-DD` in every calendar.
117
127
  */
118
- calendar?: DateCalendar;
128
+ calendar?: DateCalendar | MonthCalendar;
119
129
  /**
120
130
  * What each day shows, in place of its number: a function given the date as
121
131
  * `YYYY-MM-DD` that returns text, an element or a Lit template.
@@ -198,8 +208,9 @@ export declare class HelixHeadlessDatePicker extends HelixHeadlessDatePicker_bas
198
208
  /** The month on show, whoever set it, and `undefined` until somebody has. */
199
209
  private monthState;
200
210
  /**
201
- * The month shown, as `YYYY-MM`. When it is not set, it follows `value`,
202
- * then the current month.
211
+ * The month shown, as `YYYY-MM`, or as its first day, `YYYY-MM-DD`, under a
212
+ * `calendar` with months of its own. When it is not set, it follows
213
+ * `value`, then the current month.
203
214
  *
204
215
  * Separate from `value` on purpose, so paging selects nothing and a consumer
205
216
  * can drive the view. Reading gives the month on show; writing sets it, and
@@ -258,6 +269,8 @@ export declare class HelixHeadlessDatePicker extends HelixHeadlessDatePicker_bas
258
269
  private focused;
259
270
  /** The selected dates, in order. */
260
271
  private get selected();
272
+ /** The days of the weekend, `0` for Sunday: the locale's, or Saturday and Sunday where none is named. */
273
+ private get weekend();
261
274
  /** The day the week starts on: `firstDayOfWeek`, or the locale's. */
262
275
  private get weekStart();
263
276
  /**
@@ -274,6 +287,13 @@ export declare class HelixHeadlessDatePicker extends HelixHeadlessDatePicker_bas
274
287
  private isSelected;
275
288
  /** The month being shown, resolved from `month`, then `value`, then today. */
276
289
  private get shown();
290
+ /**
291
+ * The months the calendar pages through: its own under a `calendar` with
292
+ * months of its own, and the Gregorian months otherwise.
293
+ */
294
+ private get system();
295
+ /** Whether `calendar` names a calendar with months of its own. */
296
+ private get ownMonths();
277
297
  /**
278
298
  * The month to open on when nothing has declared one.
279
299
  *
@@ -432,6 +452,16 @@ export declare class HelixHeadlessDatePicker extends HelixHeadlessDatePicker_bas
432
452
  * The validation message for a date that `step` skips.
433
453
  */
434
454
  stepMismatchMessage: string;
455
+ /**
456
+ * The time zone today is read in, such as `America/New_York`, as React
457
+ * Aria's `today(timeZone)` reads it. Empty is the reader's own.
458
+ *
459
+ * Dates are plain `YYYY-MM-DD` and carry no zone, so this moves only today:
460
+ * the day marked `aria-current` and `day-today`, and the month a picker
461
+ * with no value opens on. A desk in Tokyo booking for an office in New York
462
+ * sets `America/New_York`, and today is the office's.
463
+ */
464
+ timeZone: string;
435
465
  /**
436
466
  * The validation message for a date that `disable-dates` or `disable-days`
437
467
  * bars.
@@ -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 { addDays, addMonths, valueAsDate as asDate, barredBy, calendarLocale, customElement, formatWords, dateAsValue as fromDate, HelixFormElement, HelixNavigateEvent, handedLocale, hostAria, inlineStep, commaList as list, localToday, MixinControllerValue, MixinPickerFeedback, monthOf, monthOut, periodCells, periodKey, reportValues, requestChange, sameMonth, weekStartOf, words, wordsLocale, YEARS_SHOWN, yearOut, yearPageOf, } from '@fusion.dev/helix-core';
7
+ import { addDays, addMonths, valueAsDate as asDate, barredBy, calendarLocale, customElement, formatWords, dateAsValue as fromDate, HelixFormElement, HelixNavigateEvent, handedLocale, hostAria, inlineStep, commaList as list, MixinControllerValue, MixinPickerFeedback, MONTH_CALENDARS, monthOf, monthOut, monthSystem, periodCells, periodKey, reportValues, requestChange, todayIn, weekendOf, weekStartOf, words, wordsLocale, 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';
@@ -46,7 +46,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
46
46
  this.focused = '';
47
47
  this.choosing = false;
48
48
  this.handleKeydown = (event) => {
49
- if (this.view !== 'day') {
49
+ if (this.view !== 'day' && !this.ownMonths) {
50
50
  this.handlePeriodKeydown(event);
51
51
  return;
52
52
  }
@@ -80,7 +80,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
80
80
  }
81
81
  else {
82
82
  const step = event.key === 'PageUp' ? -1 : 1;
83
- this.moveTo(addMonths(from, event.shiftKey ? step * 12 : step));
83
+ this.moveTo(this.system.add(from, event.shiftKey ? step * 12 : step));
84
84
  }
85
85
  event.preventDefault();
86
86
  };
@@ -105,6 +105,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
105
105
  this.required = false;
106
106
  this.step = 1;
107
107
  this.stepMismatchMessage = 'Choose one of the dates the calendar offers.';
108
+ this.timeZone = '';
108
109
  this.unavailableMessage = 'Choose a date that is available.';
109
110
  this.valueMissingMessage = 'Choose a date.';
110
111
  this.view = 'day';
@@ -115,7 +116,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
115
116
  return wordsLocale(this);
116
117
  }
117
118
  get month() {
118
- return this.monthState ?? (this.defaultMonth || fromDate(this.derivedMonth).slice(0, 7));
119
+ return this.monthState ?? (this.defaultMonth || this.system.key(this.derivedMonth));
119
120
  }
120
121
  set month(next) {
121
122
  const previous = this.month;
@@ -125,6 +126,9 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
125
126
  get selected() {
126
127
  return list(this.value);
127
128
  }
129
+ get weekend() {
130
+ return handedLocale(this) === '' ? [0, 6] : weekendOf(this.language);
131
+ }
128
132
  get weekStart() {
129
133
  if (this.firstDayOfWeek !== undefined && Number.isFinite(this.firstDayOfWeek)) {
130
134
  return ((Math.floor(this.firstDayOfWeek) % 7) + 7) % 7;
@@ -147,24 +151,31 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
147
151
  return this.selected.includes(iso);
148
152
  }
149
153
  get shown() {
150
- const explicit = /^(\d{4})-(\d{2})$/.exec(this.monthState ?? this.defaultMonth);
154
+ const explicit = /^(\d{4})-(\d{2})(?:-(\d{2}))?$/.exec(this.monthState ?? this.defaultMonth);
151
155
  if (explicit) {
152
- return firstOf(Number(explicit[1]), Number(explicit[2]) - 1);
156
+ const first = firstOf(Number(explicit[1]), Number(explicit[2]) - 1);
157
+ return this.system.start(explicit[3] ? addDays(first, Number(explicit[3]) - 1) : first);
153
158
  }
154
159
  return this.derivedMonth;
155
160
  }
161
+ get system() {
162
+ return monthSystem(this.calendar);
163
+ }
164
+ get ownMonths() {
165
+ return MONTH_CALENDARS.includes(this.calendar ?? '');
166
+ }
156
167
  get derivedMonth() {
157
- const from = asDate(this.selected[0] ?? '') ?? asDate(localToday()) ?? new Date();
158
- return firstOf(from.getUTCFullYear(), from.getUTCMonth());
168
+ const from = asDate(this.selected[0] ?? '') ?? asDate(todayIn(this.timeZone)) ?? new Date();
169
+ return this.system.start(from);
159
170
  }
160
171
  get anchor() {
161
172
  const shown = this.shown;
162
173
  for (const candidate of [
163
174
  asDate(this.focused),
164
175
  asDate(this.selected[0] ?? ''),
165
- asDate(localToday()),
176
+ asDate(todayIn(this.timeZone)),
166
177
  ]) {
167
- if (candidate && sameMonth(candidate, shown)) {
178
+ if (candidate && this.system.same(candidate, shown)) {
168
179
  return candidate;
169
180
  }
170
181
  }
@@ -198,17 +209,17 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
198
209
  return date;
199
210
  }
200
211
  get atStart() {
201
- return asDate(this.min) !== null && monthOf(addMonths(this.shown, -1)) < this.min.slice(0, 7);
212
+ return asDate(this.min) !== null && fromDate(addDays(this.shown, -1)) < this.min;
202
213
  }
203
214
  get atEnd() {
204
215
  return (asDate(this.max) !== null &&
205
- monthOf(addMonths(this.shown, this.monthCount)) > this.max.slice(0, 7));
216
+ fromDate(this.system.start(this.system.add(this.shown, this.monthCount))) > this.max);
206
217
  }
207
218
  show(date) {
208
219
  if (this.isDisabled) {
209
220
  return;
210
221
  }
211
- const next = monthOf(date);
222
+ const next = this.system.key(date);
212
223
  if (next === this.month) {
213
224
  return;
214
225
  }
@@ -437,7 +448,9 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
437
448
  return Math.max(1, Math.floor(this.months) || 1);
438
449
  }
439
450
  get formats() {
440
- const language = calendarLocale(this.language, this.calendar);
451
+ const language = this.ownMonths
452
+ ? `${new Intl.Locale(this.language || new Intl.DateTimeFormat().resolvedOptions().locale, { calendar: this.calendar ?? 'gregory' })}`
453
+ : calendarLocale(this.language, this.calendar);
441
454
  if (this.cachedFormats?.locale === language) {
442
455
  return this.cachedFormats;
443
456
  }
@@ -450,6 +463,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
450
463
  monthName: utc({ month: 'short' }),
451
464
  monthYear: utc({ month: 'short', year: 'numeric' }),
452
465
  weekday: utc({ weekday: 'short' }),
466
+ weekdayName: utc({ weekday: 'long' }),
453
467
  year: utc({ year: 'numeric' }),
454
468
  };
455
469
  this.cachedFormats = built;
@@ -470,12 +484,13 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
470
484
  </slot>`;
471
485
  }
472
486
  renderMonth(month, index) {
473
- const { full, heading: named, weekday } = this.formats;
487
+ const { full, heading: named, weekday, weekdayName } = this.formats;
474
488
  const heading = named.format(month);
475
489
  const weeks = this.weeksOf(month);
476
490
  const anchor = fromDate(this.anchor);
477
- const today = localToday();
491
+ const today = todayIn(this.timeZone);
478
492
  const range = this.range;
493
+ const weekend = this.weekend;
479
494
  const first = index === 0;
480
495
  const last = index === this.monthCount - 1;
481
496
  return html `<div part="month">
@@ -486,7 +501,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
486
501
  type="button"
487
502
  aria-label=${this.previousLabel}
488
503
  ?disabled=${this.isDisabled || this.atStart}
489
- @click=${() => this.show(addMonths(this.shown, -1))}
504
+ @click=${() => this.show(this.system.add(this.shown, -1))}
490
505
  >
491
506
  ${this.renderPreviousMark()}
492
507
  </button>`
@@ -501,7 +516,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
501
516
  type="button"
502
517
  aria-label=${this.nextLabel}
503
518
  ?disabled=${this.isDisabled || this.atEnd}
504
- @click=${() => this.show(addMonths(this.shown, 1))}
519
+ @click=${() => this.show(this.system.add(this.shown, 1))}
505
520
  >
506
521
  ${this.renderNextMark()}
507
522
  </button>`
@@ -515,14 +530,16 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
515
530
  data-unset=${this.value === '' ? '' : nothing}
516
531
  >
517
532
  <div part="week" role="row">
518
- ${weeks[0]?.map((date) => html `<span part="weekday" role="columnheader">${weekday.format(date)}</span>`)}
533
+ ${weeks[0]?.map((date) => html `<span part="weekday" role="columnheader" aria-label=${weekdayName.format(date)}
534
+ >${weekday.format(date)}</span
535
+ >`)}
519
536
  </div>
520
537
  ${weeks.map((week) => html `<div part="week" role="row">
521
538
  ${week.map((date) => {
522
539
  const iso = fromDate(date);
523
540
  const disabled = this.barred(date);
524
541
  const selected = this.isSelected(iso);
525
- const outside = !sameMonth(date, month);
542
+ const outside = !this.system.same(date, month);
526
543
  const start = !outside && range !== null && iso === range[0];
527
544
  const end = !outside && range !== null && iso === range[1];
528
545
  const middle = !outside && range !== null && iso > range[0] && iso < range[1];
@@ -538,10 +555,11 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
538
555
  data-range-middle=${middle ? '' : nothing}
539
556
  data-range-start=${start ? '' : nothing}
540
557
  data-today=${iso === today ? '' : nothing}
558
+ data-weekend=${weekend.includes(date.getUTCDay()) ? '' : nothing}
541
559
  tabindex=${!outside && iso === anchor && !this.isDisabled ? '0' : '-1'}
542
560
  @click=${outside ? nothing : () => this.select(date)}
543
561
  ><span
544
- part="day${!outside && selected && !middle ? ' day-selected' : ''}${iso === today ? ' day-today' : ''}${outside ? ' day-outside' : ''}${disabled ? ' day-disabled' : ''}"
562
+ part="day${!outside && selected && !middle ? ' day-selected' : ''}${iso === today ? ' day-today' : ''}${outside ? ' day-outside' : ''}${disabled ? ' day-disabled' : ''}${weekend.includes(date.getUTCDay()) ? ' day-weekend' : ''}"
545
563
  >${this.renderDay(date, outside)}</span
546
564
  ></span
547
565
  >`;
@@ -551,10 +569,11 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
551
569
  </div>`;
552
570
  }
553
571
  renderDay(date, outside) {
554
- return this.dayContent && !outside ? this.dayContent(fromDate(date)) : date.getUTCDate();
572
+ return this.dayContent && !outside ? this.dayContent(fromDate(date)) : this.system.day(date);
555
573
  }
556
574
  renderHeadingText(text, label) {
557
575
  const steps = this.maxView !== undefined &&
576
+ !this.ownMonths &&
558
577
  (this.view === 'day' || (this.view === 'month' && this.maxView === 'year'));
559
578
  if (!steps) {
560
579
  return text;
@@ -580,7 +599,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
580
599
  min: this.min,
581
600
  page: this.periodYear,
582
601
  shown: this.shown,
583
- today: localToday(),
602
+ today: todayIn(this.timeZone),
584
603
  view: years ? 'year' : 'month',
585
604
  }).map(({ at, chosen, current, first, out }) => {
586
605
  const words = years ? yearName.format(first) : monthName.format(first);
@@ -625,17 +644,18 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinPickerF
625
644
  </div>`;
626
645
  }
627
646
  render() {
628
- if (this.view !== 'day') {
629
- return html `<div part="months" style="--date-picker-column-count: 1">
630
- ${this.renderPeriods()}
631
- </div>
632
- ${this.renderPickerFeedback()}`;
647
+ if (this.view !== 'day' && !this.ownMonths) {
648
+ return html `<slot name="header"></slot>
649
+ <div part="months" style="--date-picker-column-count: 1">${this.renderPeriods()}</div>
650
+ <slot name="footer"></slot>${this.renderPickerFeedback()}`;
633
651
  }
634
652
  const shown = this.shown;
635
653
  const count = this.monthCount;
636
- return html `<div part="months" style="--date-picker-column-count: ${Math.min(count, 2)}">
637
- ${Array.from({ length: count }, (_, index) => this.renderMonth(addMonths(shown, index), index))}
638
- </div>${this.renderPickerFeedback()}`;
654
+ return html `<slot name="header"></slot>
655
+ <div part="months" style="--date-picker-column-count: ${Math.min(count, 2)}">
656
+ ${Array.from({ length: count }, (_, index) => this.renderMonth(this.system.add(shown, index), index))}
657
+ </div>
658
+ <slot name="footer"></slot>${this.renderPickerFeedback()}`;
639
659
  }
640
660
  get valueAsDate() {
641
661
  return asDate(this.value);
@@ -758,6 +778,9 @@ __decorate([
758
778
  words,
759
779
  property({ attribute: 'step-mismatch-message' })
760
780
  ], HelixHeadlessDatePicker.prototype, "stepMismatchMessage", void 0);
781
+ __decorate([
782
+ property({ attribute: 'time-zone' })
783
+ ], HelixHeadlessDatePicker.prototype, "timeZone", void 0);
761
784
  __decorate([
762
785
  words,
763
786
  property({ attribute: 'unavailable-message' })