@fusion.dev/helix-headless 0.154.0 → 0.156.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 (104) hide show
  1. package/custom-elements.json +3213 -179
  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/breadcrumbs/breadcrumb/breadcrumb.d.ts +36 -3
  7. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +42 -2
  8. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +15 -5
  9. package/dist/breadcrumbs/root/breadcrumbs-root.js +29 -4
  10. package/dist/button-group/root/button-group-root.d.ts +2 -0
  11. package/dist/button-group/root/button-group-root.js +1 -0
  12. package/dist/canvas/node/canvas-node.js +1 -1
  13. package/dist/canvas/root/canvas-root.d.ts +17 -2
  14. package/dist/canvas/root/canvas-root.js +23 -6
  15. package/dist/chat/composer/chat-composer.js +1 -1
  16. package/dist/chat/root/chat-root.d.ts +14 -1
  17. package/dist/chat/root/chat-root.js +14 -4
  18. package/dist/color-input/root/color-input-root.d.ts +9 -3
  19. package/dist/color-input/root/color-input-root.js +3 -1
  20. package/dist/color-picker/channels.d.ts +5 -3
  21. package/dist/color-picker/channels.js +79 -21
  22. package/dist/color-picker/root/color-picker-root.d.ts +58 -5
  23. package/dist/color-picker/root/color-picker-root.js +75 -13
  24. package/dist/combobox/root/combobox-root.d.ts +10 -0
  25. package/dist/combobox/root/combobox-root.js +18 -3
  26. package/dist/command/root/command-root.d.ts +116 -1
  27. package/dist/command/root/command-root.js +124 -15
  28. package/dist/data-table/root/data-table-root.d.ts +25 -0
  29. package/dist/data-table/root/data-table-root.js +13 -3
  30. package/dist/date-input/root/date-input-root.d.ts +33 -1
  31. package/dist/date-input/root/date-input-root.js +9 -2
  32. package/dist/date-picker/root/date-picker-root.d.ts +43 -13
  33. package/dist/date-picker/root/date-picker-root.js +54 -31
  34. package/dist/element-events.generated.js +3 -1
  35. package/dist/graph/root/graph-root.d.ts +13 -0
  36. package/dist/graph/root/graph-root.js +16 -0
  37. package/dist/list/root/list-root.d.ts +74 -1
  38. package/dist/list/root/list-root.js +92 -4
  39. package/dist/list/selection.js +3 -3
  40. package/dist/map/root/map-root.d.ts +6 -0
  41. package/dist/map/root/map-root.js +8 -1
  42. package/dist/navigation/root/navigation-root.d.ts +26 -0
  43. package/dist/navigation/root/navigation-root.js +58 -1
  44. package/dist/number-input/root/number-input-root.d.ts +63 -6
  45. package/dist/number-input/root/number-input-root.js +96 -11
  46. package/dist/object-inspector/root/object-inspector-root.d.ts +36 -1
  47. package/dist/object-inspector/root/object-inspector-root.js +26 -2
  48. package/dist/option/root/option-root.d.ts +32 -0
  49. package/dist/option/root/option-root.js +44 -5
  50. package/dist/outline/root/outline-root.d.ts +6 -2
  51. package/dist/outline/root/outline-root.js +7 -3
  52. package/dist/select/root/select-root.d.ts +29 -3
  53. package/dist/select/root/select-root.js +34 -9
  54. package/dist/slider/root/slider-root.d.ts +71 -9
  55. package/dist/slider/root/slider-root.js +129 -42
  56. package/dist/speed-dial/item/speed-dial-item.d.ts +29 -1
  57. package/dist/speed-dial/item/speed-dial-item.js +48 -7
  58. package/dist/speed-dial/root/speed-dial-root.d.ts +32 -6
  59. package/dist/speed-dial/root/speed-dial-root.js +50 -5
  60. package/dist/splitter/root/splitter-root.d.ts +22 -0
  61. package/dist/splitter/root/splitter-root.js +36 -7
  62. package/dist/steps/reach.d.ts +5 -1
  63. package/dist/steps/reach.js +2 -2
  64. package/dist/steps/root/steps-root.d.ts +37 -2
  65. package/dist/steps/root/steps-root.js +49 -7
  66. package/dist/steps/step/step.d.ts +6 -0
  67. package/dist/steps/step/step.js +4 -0
  68. package/dist/table/root/table-root.d.ts +20 -0
  69. package/dist/table/root/table-root.js +13 -3
  70. package/dist/tabs/root/tabs-root.d.ts +37 -0
  71. package/dist/tabs/root/tabs-root.js +92 -2
  72. package/dist/tabs/tab/tab.d.ts +38 -2
  73. package/dist/tabs/tab/tab.js +45 -4
  74. package/dist/tag/group/tag-group.d.ts +29 -3
  75. package/dist/tag/group/tag-group.js +42 -11
  76. package/dist/time-input/root/time-input-root.d.ts +32 -1
  77. package/dist/time-input/root/time-input-root.js +9 -2
  78. package/dist/time-picker/root/time-picker-root.d.ts +63 -1
  79. package/dist/time-picker/root/time-picker-root.js +58 -7
  80. package/dist/timeline/item/timeline-item.d.ts +25 -0
  81. package/dist/timeline/item/timeline-item.js +27 -2
  82. package/dist/toggle/root/toggle-root.d.ts +2 -0
  83. package/dist/toggle/root/toggle-root.js +1 -1
  84. package/dist/tree/item/tree-item.d.ts +10 -0
  85. package/dist/tree/item/tree-item.js +12 -0
  86. package/dist/tree/root/tree-root.d.ts +40 -1
  87. package/dist/tree/root/tree-root.js +120 -4
  88. package/dist/tree/selection.d.ts +6 -0
  89. package/dist/tree/selection.js +11 -0
  90. package/dist/video/root/keys.d.ts +6 -2
  91. package/dist/video/root/keys.js +18 -1
  92. package/dist/video/root/video-root.d.ts +60 -1
  93. package/dist/video/root/video-root.js +70 -2
  94. package/dist/video-playlist/item/video-playlist-item.d.ts +15 -1
  95. package/dist/video-playlist/item/video-playlist-item.js +7 -0
  96. package/dist/video-playlist/root/video-playlist-root.d.ts +43 -4
  97. package/dist/video-playlist/root/video-playlist-root.js +69 -2
  98. package/dist/virtual-list/root/virtual-list-root.d.ts +8 -0
  99. package/dist/virtual-list/root/virtual-list-root.js +3 -0
  100. package/lint.json +1 -1
  101. package/package.json +5 -5
  102. package/types/jsx.d.ts +3885 -383
  103. package/types/vue.d.ts +1045 -107
  104. package/types/words.d.ts +146 -1
@@ -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' })
@@ -6,6 +6,7 @@ registerElementEvents({
6
6
  'helix-headless-area-chart': ['helix-reading-change', 'helix-reading-press'],
7
7
  'helix-headless-avatar': ['error', 'load'],
8
8
  'helix-headless-bar-chart': ['helix-reading-change', 'helix-reading-press'],
9
+ 'helix-headless-breadcrumb': ['helix-select'],
9
10
  'helix-headless-button': ['helix-press'],
10
11
  'helix-headless-button-group': ['change', 'helix-before-change'],
11
12
  'helix-headless-callout': ['helix-dismiss'],
@@ -140,6 +141,7 @@ registerElementEvents({
140
141
  'helix-action',
141
142
  'helix-before-change',
142
143
  'helix-before-reorder',
144
+ 'helix-load-more',
143
145
  'helix-reorder',
144
146
  ],
145
147
  'helix-headless-list-item': ['helix-action'],
@@ -241,7 +243,7 @@ registerElementEvents({
241
243
  'helix-headless-step': ['helix-select'],
242
244
  'helix-headless-steps': ['helix-activate', 'helix-before-activate', 'helix-select'],
243
245
  'helix-headless-switch': ['change', 'helix-before-toggle', 'input'],
244
- 'helix-headless-tab': ['helix-select'],
246
+ 'helix-headless-tab': ['helix-dismiss', 'helix-select'],
245
247
  'helix-headless-table': [
246
248
  'helix-before-reorder',
247
249
  'helix-reorder',
@@ -39,6 +39,7 @@ declare const HelixHeadlessGraph_base: (new (...args: any[]) => import("@fusion.
39
39
  * @documentation https://fusion.dev/helix/components/graph
40
40
  *
41
41
  * @slot - The renderer's box, which the drawing is made in.
42
+ * @slot empty - What the panel shows when the list holds no notes. Defaults to `empty-label`.
42
43
  * @slot filter-icon - The icon in the search field. Defaults to a magnifying glass.
43
44
  * @slot footer - Content below the graph.
44
45
  * @slot fullscreen-icon - Replaces the glyph inside the fullscreen button.
@@ -65,6 +66,7 @@ declare const HelixHeadlessGraph_base: (new (...args: any[]) => import("@fusion.
65
66
  * @csspart body - The drawing beside the list, or above it where the frame is narrow.
66
67
  * @csspart controls - The group the pan and zoom buttons sit in.
67
68
  * @csspart count - How many notes the list holds, which a search narrows, at the end of the search bar.
69
+ * @csspart empty - The box in place of the list when it holds no notes, holding the `empty` slot.
68
70
  * @csspart filter-icon - The icon beside the search field.
69
71
  * @csspart filter-mark - The default icon beside the search field, shown when none is slotted.
70
72
  * @csspart footer - The strip below the graph.
@@ -135,6 +137,11 @@ export declare class HelixHeadlessGraph extends HelixHeadlessGraph_base {
135
137
  * shows every note. The list holds what the drawing shows.
136
138
  */
137
139
  depth?: number;
140
+ /**
141
+ * What the panel shows when the list holds no notes: the graph has none, or
142
+ * a search or the legend hid them all. A slotted `empty` replaces it.
143
+ */
144
+ emptyLabel: string;
138
145
  /**
139
146
  * The graph's name where `label` is empty and the host has no
140
147
  * `aria-label`.
@@ -180,6 +187,12 @@ export declare class HelixHeadlessGraph extends HelixHeadlessGraph_base {
180
187
  noPanel: boolean;
181
188
  /** The list's name, read by screen readers, and the name of the button that opens it with `no-panel`. */
182
189
  notesLabel: string;
190
+ /**
191
+ * What a screen reader calls the graph in place of its role, after its
192
+ * name: "Research notes, graph". Said only where the graph has a name of its
193
+ * own, since the fallback name is the same word. Empty leaves the role.
194
+ */
195
+ roleLabel: string;
183
196
  /** The search field's name. */
184
197
  searchLabel: string;
185
198
  /** How the selected note is described in the list. */
@@ -17,6 +17,7 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
17
17
  this.activateHintLabel = 'Press the graph to use it';
18
18
  this.controlsLabel = 'Graph controls';
19
19
  this.countLabel = '{count, plural, one {# note} other {# notes}}';
20
+ this.emptyLabel = 'No notes';
20
21
  this.fallbackLabel = 'Graph';
21
22
  this.hiddenGroups = [];
22
23
  this.label = '';
@@ -33,6 +34,7 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
33
34
  this.nodes = [];
34
35
  this.noPanel = false;
35
36
  this.notesLabel = 'Notes';
37
+ this.roleLabel = 'graph';
36
38
  this.searchLabel = 'Find a note';
37
39
  this.selectedLabel = 'Selected';
38
40
  this.twoFingerHintLabel = 'Use two fingers to move the graph';
@@ -409,6 +411,8 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
409
411
  part="region"
410
412
  role="group"
411
413
  aria-label=${this.label || this.getAttribute?.('aria-label') || this.fallbackLabel}
414
+ aria-roledescription=${(this.label || this.getAttribute?.('aria-label')) && this.roleLabel ? this.roleLabel : nothing}
415
+ aria-keyshortcuts="ArrowUp ArrowDown ArrowLeft ArrowRight Plus Minus"
412
416
  tabindex="0"
413
417
  @focusin=${this.idle.stir}
414
418
  @keydown=${this.handleKeydown}
@@ -454,6 +458,7 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
454
458
  data-scroller
455
459
  aria-label=${this.notesLabel}
456
460
  aria-activedescendant=${cursor}
461
+ aria-keyshortcuts="ArrowUp ArrowDown Home End PageUp PageDown Enter Space"
457
462
  @keydown=${this.handleListKeydown}
458
463
  @scroll=${() => this.virtualize.measure()}
459
464
  >
@@ -461,6 +466,9 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
461
466
  ${this.renderRows()}
462
467
  <div part="after" role="presentation" style="block-size: ${after}px"></div>
463
468
  </div>
469
+ ${this.entries.length === 0
470
+ ? html `<div part="empty"><slot name="empty">${this.emptyLabel}</slot></div>`
471
+ : nothing}
464
472
  </div>
465
473
  </div>
466
474
  <div part="footer">
@@ -485,6 +493,10 @@ __decorate([
485
493
  __decorate([
486
494
  property({ type: Number })
487
495
  ], HelixHeadlessGraph.prototype, "depth", void 0);
496
+ __decorate([
497
+ words,
498
+ property({ attribute: 'empty-label' })
499
+ ], HelixHeadlessGraph.prototype, "emptyLabel", void 0);
488
500
  __decorate([
489
501
  words,
490
502
  property({ attribute: 'fallback-label' })
@@ -543,6 +555,10 @@ __decorate([
543
555
  words,
544
556
  property({ attribute: 'notes-label' })
545
557
  ], HelixHeadlessGraph.prototype, "notesLabel", void 0);
558
+ __decorate([
559
+ words,
560
+ property({ attribute: 'role-label' })
561
+ ], HelixHeadlessGraph.prototype, "roleLabel", void 0);
546
562
  __decorate([
547
563
  words,
548
564
  property({ attribute: 'search-label' })
@@ -69,6 +69,23 @@ import { type ListSelectionBehavior, type ListSelectionMode } from '../selection
69
69
  * no `start` counts down from the number of rows, which a server cannot know,
70
70
  * so served markup counts from one until the list runs.
71
71
  *
72
+ * **An empty list.** Set `empty-label`, or put your own content in the `empty`
73
+ * slot, for a row shown while the list has no rows and none are loading, such
74
+ * as "No files yet" and a link to add one. Without either, an empty list shows
75
+ * nothing. The list takes the `empty` state while it has no rows, whatever it
76
+ * shows.
77
+ *
78
+ * **Loading more.** `load-more` sends `helix-load-more` once the end of the
79
+ * list comes within a screen of view, in the box that scrolls it or the page,
80
+ * once for each number of rows: add rows and it can send it again. While you
81
+ * fetch, set `loading`: the list is busy, and a row after the last says
82
+ * "Loading more" (`loading-label`, or your own content in the `loading`
83
+ * slot). The list asks for nothing while `loading` is set.
84
+ *
85
+ * An interactive list with no name is reported once in a development build,
86
+ * since a screen reader announces it as a grid and nothing more. Name it with
87
+ * `label` or `aria-label`.
88
+ *
72
89
  * @since 0.1.0
73
90
  * @status stable
74
91
  * @category data
@@ -76,18 +93,26 @@ import { type ListSelectionBehavior, type ListSelectionMode } from '../selection
76
93
  * @documentation https://fusion.dev/helix/components/list
77
94
  *
78
95
  * @slot - The rows.
96
+ * @slot empty - Content for the row shown while the list has no rows and none are loading. Falls back to `empty-label`.
97
+ * @slot loading - Content for the row after the last while `loading` is set. Falls back to `loading-label`.
79
98
  *
80
99
  * @event {Event} change - Fired once a press has changed which rows are selected.
81
100
  * @event {HelixActionEvent} helix-action - Sent from a row when the reader acts on it: Enter, or a press in a list that selects nothing. Read the row from `target`. A row with `href` follows its link instead.
82
101
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired before a press changes which rows are selected, with the `value` it would leave. Cancelable.
83
102
  * @event {HelixBeforeReorderEvent} helix-before-reorder - Fired when a row is dropped at a new place, in a `reorderable` list, with every row's value in the new order. Cancelable: cancel it to keep the row where it is.
103
+ * @event {HelixLoadMoreEvent} helix-load-more - Fired, in a list with `load-more`, when the end of the list comes within a screen of view, once for each number of rows.
84
104
  * @event {HelixReorderEvent} helix-reorder - Fired after `helix-before-reorder` when nobody canceled it. Move your rows to the order in `keys`. Cancelable: cancel it to say the move did not happen.
85
105
  *
86
106
  * @csspart drag-preview - A copy of the row a pointer drags, which follows the pointer in the top layer until the row is let go, in a `reorderable` list. Hidden from assistive technology.
107
+ * @csspart empty - The row shown while the list has no rows and none are loading, where `empty-label` or the `empty` slot gives it content. A grid row with one cell in a list that selects.
87
108
  * @csspart live - The region that says each step of a move to screen readers, in a `reorderable` list. Drawn from the first move. Visually hidden in the styled tier.
109
+ * @csspart loading - The row after the last while `loading` is set. A grid row with one cell in a list that selects.
88
110
  *
89
111
  * @cssproperty --list-first - The number of the first row, written where `start` or `reversed` moves it.
90
112
  * @cssproperty --list-step - What each row adds to the number before it: `1`, or `-1` where `reversed`.
113
+ *
114
+ * @cssstate empty - While the list has no rows.
115
+ * @cssstate loading - While `loading` is set.
91
116
  */
92
117
  export declare class HelixHeadlessList extends HelixElement {
93
118
  /**
@@ -102,6 +127,11 @@ export declare class HelixHeadlessList extends HelixElement {
102
127
  canceledLabel: string;
103
128
  /** What screen readers hear when a row is dropped, in a `reorderable` list. */
104
129
  droppedLabel: string;
130
+ /**
131
+ * The text of the row shown while the list has no rows, where the `empty`
132
+ * slot is empty. Empty, the default, shows no row.
133
+ */
134
+ emptyLabel: string;
105
135
  /**
106
136
  * The kind of list, which follows from the content.
107
137
  *
@@ -129,6 +159,18 @@ export declare class HelixHeadlessList extends HelixElement {
129
159
  * The list's name, read by screen readers.
130
160
  */
131
161
  label: string;
162
+ /**
163
+ * Marks the list as busy fetching more rows, and draws a row after the last
164
+ * that says so. The list asks for no more while it is set.
165
+ */
166
+ loading: boolean;
167
+ /** The text of the row drawn while `loading` is set, where the `loading` slot is empty. */
168
+ loadingLabel: string;
169
+ /**
170
+ * Sends `helix-load-more` once the end of the list comes within a screen of
171
+ * view, so you can add the next rows as a reader nears them.
172
+ */
173
+ loadMore: boolean;
132
174
  /**
133
175
  * What screen readers hear each time an arrow key moves a held row, in a
134
176
  * `reorderable` list. `{name}` is the row's words, `{position}` the place it
@@ -185,7 +227,7 @@ export declare class HelixHeadlessList extends HelixElement {
185
227
  */
186
228
  type?: '1' | 'a' | 'A' | 'i' | 'I';
187
229
  /**
188
- * The selected rows' values, separated by commas, in a list that selects.
230
+ * The selected rows' values, separated by commas with no spaces, such as `a,c`, in a list that selects.
189
231
  *
190
232
  * Set it to start with rows selected. A row's value is its `value`, or its
191
233
  * words where it has none.
@@ -303,6 +345,37 @@ export declare class HelixHeadlessList extends HelixElement {
303
345
  */
304
346
  private get numbers();
305
347
  protected render(): TemplateResult;
348
+ /**
349
+ * The row shown while the list has no rows: a list item, or a grid row with
350
+ * one cell in a list that selects. Drawn only once the children are known,
351
+ * so a server and a hydrating render never claim an empty list, and hidden
352
+ * while it has neither `empty-label` nor content in the `empty` slot.
353
+ */
354
+ private drawEmpty;
355
+ /**
356
+ * The row after the last while `loading` is set: a list item, or a grid row
357
+ * with one cell in a list that selects. It is not one of the rows, so it
358
+ * carries no value and a key never lands on it.
359
+ */
360
+ private drawLoading;
361
+ /** Watches the end of a `load-more` list coming near the box that scrolls it. */
362
+ private endWatcher;
363
+ /** The end being watched, so a new one is watched again. */
364
+ private watchedEnd;
365
+ /** Whether the end of the list was in reach when last measured. */
366
+ private endInView;
367
+ /** The number of rows `helix-load-more` was last sent for. */
368
+ private askedAt;
369
+ /**
370
+ * Starts or stops watching the end of the list. The box it is measured
371
+ * against is the one that scrolls the list, or the page, and a screen of
372
+ * room is added below it, so the next rows are asked for before a reader
373
+ * reaches the last.
374
+ */
375
+ private watchEnd;
376
+ /** Sends `helix-load-more`, once for each number of rows, and never while loading. */
377
+ private askForMore;
378
+ private readonly handleEmptyChange;
306
379
  private readonly handleSlotChange;
307
380
  }
308
381
  declare global {