@fusion.dev/helix-headless 0.159.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.
@@ -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, valueAsTime as asTime, clockLocale, customElement, formatWords, timeAsValue as fromTime, HelixFormElement, handedLocale, isClockValue, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerEditing, MixinPickerField, MixinSegmentedField, MixinTokens, timeFault, words, wordsLocale, writeClock, } from '@fusion.dev/helix-core';
8
- import { clock } from '@fusion.dev/helix-icons';
7
+ import { AriaForwardController, valueAsTime as asTime, clockLocale, customElement, drawIcon, formatWords, timeAsValue as fromTime, HelixClearEvent, HelixFormElement, handedLocale, isClockValue, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerEditing, MixinPickerField, MixinSegmentedField, MixinTokens, readWrittenTime, timeFault, words, wordsLocale, writeClock, } from '@fusion.dev/helix-core';
8
+ import { clock, 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';
@@ -19,6 +19,8 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
19
19
  target: () => this.partNamed('control') ?? this.segmentGroup,
20
20
  });
21
21
  this.autocomplete = '';
22
+ this.clearable = false;
23
+ this.clearLabel = 'Clear';
22
24
  this.announcesInput = true;
23
25
  this.disableTimes = '';
24
26
  this.locale = '';
@@ -34,16 +36,28 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
34
36
  this.timeStyle = 'local';
35
37
  this.triggerLabel = 'Choose a time';
36
38
  this.unavailableMessage = 'Choose a time that is available.';
39
+ this.unreadableMessage = '{mode, select, single {Enter a time as {shape}.} other {Enter times as {shape}, separated by commas.}}';
37
40
  this.draft = '';
41
+ this.handleTextCommit = (event) => {
42
+ if (this.mode !== 'multiple') {
43
+ const held = this.held;
44
+ const read = held.map((one) => this.read(one));
45
+ if (held.length > 0 && !held.every((one) => this.isTime(one)) && read.every(Boolean)) {
46
+ this.requestValue(read.join(','));
47
+ }
48
+ }
49
+ this.handleCommit(event);
50
+ };
38
51
  this.handleDraftKey = (event) => {
39
52
  if (this.mode !== 'multiple') {
40
53
  return;
41
54
  }
42
55
  const held = this.held;
43
- if (event.key === 'Enter' && this.isTime(this.draft)) {
56
+ const draft = this.read(this.draft.trim());
57
+ if (event.key === 'Enter' && draft !== '') {
44
58
  event.preventDefault();
45
- if (!held.includes(this.draft)) {
46
- this.requestPicked([...held, this.draft].sort().join(','));
59
+ if (!held.includes(draft)) {
60
+ this.requestPicked([...held, draft].sort().join(','));
47
61
  }
48
62
  this.draft = '';
49
63
  }
@@ -91,6 +105,13 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
91
105
  this.returnFocus();
92
106
  }
93
107
  };
108
+ this.handleClear = () => {
109
+ if (this.value !== '' && this.requestValue('')) {
110
+ this.announceChange();
111
+ this.dispatchEvent(new HelixClearEvent());
112
+ }
113
+ void this.updateComplete.then(() => this.returnFocus());
114
+ };
94
115
  }
95
116
  static { this.words = 'helix-time-input'; }
96
117
  get language() {
@@ -104,7 +125,10 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
104
125
  .filter(Boolean);
105
126
  }
106
127
  get segmentable() {
107
- return this.mode === undefined;
128
+ return this.mode !== 'multiple';
129
+ }
130
+ get segmentGroups() {
131
+ return this.mode === 'range' ? 2 : 1;
108
132
  }
109
133
  get segmentedKind() {
110
134
  return 'time';
@@ -141,6 +165,9 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
141
165
  get restCount() {
142
166
  return Math.max(0, this.held.length - this.shownTimes.length);
143
167
  }
168
+ read(text) {
169
+ return this.isTime(text) ? text : readWrittenTime(text, this.language, this.seconds);
170
+ }
144
171
  get picked() {
145
172
  const held = this.held;
146
173
  return held.length > 0 && held.every((one) => this.isTime(one)) ? held.join(',') : '';
@@ -157,9 +184,7 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
157
184
  const shape = this.seconds ? 'HH:mm:ss' : 'HH:mm';
158
185
  if (held.length > 0 && !held.every((one) => this.isTime(one))) {
159
186
  return (this.badInputMessage ||
160
- (this.mode === undefined
161
- ? `Enter a time as ${shape}.`
162
- : `Enter times as ${shape}, separated by commas.`));
187
+ formatWords(this, 'unreadableMessage', { mode: this.mode ?? 'single', shape }));
163
188
  }
164
189
  const rules = { disableTimes: this.disableTimes, max: this.max, min: this.min };
165
190
  const faults = held.map((one) => timeFault(one, rules));
@@ -249,6 +274,12 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
249
274
  ${unsafeSVG(clock.content)}
250
275
  </svg></slot>`;
251
276
  }
277
+ get showsClear() {
278
+ return this.clearable && this.value !== '' && !this.isDisabled && !this.readOnly;
279
+ }
280
+ renderClearIcon() {
281
+ return html `<slot name="clear-icon">${drawIcon(x)}</slot>`;
282
+ }
252
283
  renderControl() {
253
284
  return html `<input
254
285
  part="control"
@@ -257,6 +288,10 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
257
288
  .value=${this.display}
258
289
  name=${this.name || nothing}
259
290
  placeholder=${this.mode === 'multiple' && this.held.length > 0 ? nothing : this.placeholder || nothing}
291
+ role="combobox"
292
+ aria-controls="surface"
293
+ aria-expanded=${this.open ? 'true' : 'false'}
294
+ aria-haspopup="dialog"
260
295
  autocomplete=${this.autocomplete || nothing}
261
296
  aria-describedby=${this.describedBy ?? nothing}
262
297
  aria-invalid=${this.invalidity ?? nothing}
@@ -264,13 +299,17 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
264
299
  ?readonly=${this.readOnly}
265
300
  ?required=${this.required}
266
301
  @blur=${this.handleBlur}
267
- @change=${this.handleCommit}
302
+ @change=${this.handleTextCommit}
268
303
  @focus=${this.handleFocus}
269
304
  @input=${this.handleInput}
270
305
  @keydown=${this.handleDraftKey}
271
306
  @keypress=${this.handleKeypress}
272
307
  />`;
273
308
  }
309
+ willUpdate(changed) {
310
+ super.willUpdate(changed);
311
+ this.toggleState('blank', this.value === '');
312
+ }
274
313
  render() {
275
314
  return html `${this.renderLabel()}
276
315
  <div part="field" @keydown=${this.handleKeydown}>
@@ -280,6 +319,16 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
280
319
  : nothing}
281
320
  ${this.segmentsShown ? this.renderSegments() : this.renderControl()}
282
321
  <slot name="end"></slot>
322
+ ${this.showsClear
323
+ ? html `<button
324
+ part="clear"
325
+ type="button"
326
+ aria-label=${this.clearLabel}
327
+ @click=${this.handleClear}
328
+ >
329
+ ${this.renderClearIcon()}
330
+ </button>`
331
+ : nothing}
283
332
  <button
284
333
  part="trigger"
285
334
  type="button"
@@ -327,6 +376,13 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinSegmented
327
376
  __decorate([
328
377
  property()
329
378
  ], HelixHeadlessTimeInput.prototype, "autocomplete", void 0);
379
+ __decorate([
380
+ property({ type: Boolean })
381
+ ], HelixHeadlessTimeInput.prototype, "clearable", void 0);
382
+ __decorate([
383
+ words,
384
+ property({ attribute: 'clear-label' })
385
+ ], HelixHeadlessTimeInput.prototype, "clearLabel", void 0);
330
386
  __decorate([
331
387
  property({ attribute: 'disable-times' })
332
388
  ], HelixHeadlessTimeInput.prototype, "disableTimes", void 0);
@@ -382,6 +438,10 @@ __decorate([
382
438
  words,
383
439
  property({ attribute: 'unavailable-message' })
384
440
  ], HelixHeadlessTimeInput.prototype, "unavailableMessage", void 0);
441
+ __decorate([
442
+ words,
443
+ property({ attribute: 'unreadable-message' })
444
+ ], HelixHeadlessTimeInput.prototype, "unreadableMessage", void 0);
385
445
  __decorate([
386
446
  state()
387
447
  ], HelixHeadlessTimeInput.prototype, "draft", void 0);
@@ -1,4 +1,4 @@
1
- import { HelixFormElement, type HourCycle } from '@fusion.dev/helix-core';
1
+ import { HelixFormElement, type HourCycle, type PickerFailure } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../listbox/index.js';
4
4
  import '../../option/index.js';
@@ -51,7 +51,10 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
51
51
  * host's `aria-label` or its `aria-labelledby`. A development build warns
52
52
  * where all four are empty. The list is described by the hint, or by the
53
53
  * message in `feedback` in its place, and carries `aria-multiselectable` with
54
- * a `mode` and `aria-invalid` while `feedback` says what is wrong. The wheels
54
+ * a `mode`. It carries `aria-invalid` while `feedback` says what is wrong,
55
+ * while it holds a time outside `min` and `max` or one the picker does not
56
+ * offer, and, for a `required` picker with no time, once the reader has
57
+ * chosen one or a form has refused the picker. The wheels
55
58
  * are a `group` with that name, holding one `listbox` per wheel, named "Hour",
56
59
  * "Minute", "Second" and "AM/PM" in the page's language, and each described
57
60
  * the same way.
@@ -100,7 +103,7 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
100
103
  * @csspart list-loading - The text shown while the options load.
101
104
  * @csspart list-search - The region that holds the filter field.
102
105
  * @csspart list-search-field - The filter field.
103
- * @csspart option - One time in the list. It carries `data-between` inside a range.
106
+ * @csspart option - One time in the list. It carries `data-between` inside a range, and inside the run to the time under the pointer while a range waits for its second end.
104
107
  * @csspart option-between - A time inside a chosen range, excluding its ends, beside `option`.
105
108
  * @csspart option-description - The caption under the time's label, when there is one.
106
109
  * @csspart option-item - The row.
@@ -110,7 +113,8 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
110
113
  * @csspart option-text - The label and the caption, as one column.
111
114
  *
112
115
  * @cssstate blank - While no time is chosen.
113
- * @cssstate invalid - While `invalid` is set or `feedback` says what is wrong.
116
+ * @cssstate invalid - While `invalid` is set, `feedback` says what is wrong, or the list carries `aria-invalid`.
117
+ * @cssstate range-pending - While a `range` holds its first time and waits for the second.
114
118
  */
115
119
  export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_base {
116
120
  /**
@@ -318,7 +322,18 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
318
322
  * sequence.
319
323
  */
320
324
  private readonly handleChange;
321
- /** Whether a time falls inside a range, which is what gets banded. */
325
+ /**
326
+ * The time under the pointer while a range holds one end, which the list
327
+ * bands the range to before it is chosen, as Date Picker does.
328
+ */
329
+ private previewed;
330
+ private readonly handleListHover;
331
+ private readonly handleListLeave;
332
+ /**
333
+ * Whether a time falls inside a range, which is what gets banded: the
334
+ * chosen range, or while one end waits, the run to the time under the
335
+ * pointer.
336
+ */
322
337
  private between;
323
338
  /** The readings the wheels show, from the value or from `min`. */
324
339
  private get parts();
@@ -463,6 +478,13 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
463
478
  * browser points its bubble at.
464
479
  */
465
480
  private reportRequired;
481
+ /**
482
+ * What is wrong with the answer, as validity flags and a message, or nothing.
483
+ * `reportRequired` hands it to the form, and `failure` to the mark.
484
+ */
485
+ private get problem();
486
+ /** A missing time waits for the reader; a refused one is marked at once. */
487
+ protected get failure(): PickerFailure | undefined;
466
488
  /**
467
489
  * Reports the times to the form, one entry each.
468
490
  *
@@ -53,6 +53,19 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
53
53
  const added = next.find((one) => !before.has(one));
54
54
  this.requestValue(added ?? '');
55
55
  };
56
+ this.previewed = '';
57
+ this.handleListHover = (event) => {
58
+ const option = event
59
+ .composedPath()
60
+ .find((one) => one instanceof HTMLElement && one.hasAttribute('value'));
61
+ const at = option?.getAttribute('value') ?? '';
62
+ if (this.mode === 'range' && this.held.length === 1 && at !== this.previewed) {
63
+ this.previewed = at;
64
+ }
65
+ };
66
+ this.handleListLeave = () => {
67
+ this.previewed = '';
68
+ };
56
69
  this.moveColumn = (part, to) => {
57
70
  if (this.isDisabled) {
58
71
  return;
@@ -146,8 +159,11 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
146
159
  if (this.mode !== 'range') {
147
160
  return false;
148
161
  }
149
- const [from, to] = this.held;
150
- return from !== undefined && to !== undefined && at > from && at < to;
162
+ const [from, to = this.previewed] = this.held;
163
+ if (from === undefined || to === '' || this.held.length > 2) {
164
+ return false;
165
+ }
166
+ return at > (from < to ? from : to) && at < (from < to ? to : from);
151
167
  }
152
168
  get parts() {
153
169
  const at = valueAsSeconds(this.value) ?? valueAsSeconds(this.min) ?? 0;
@@ -218,27 +234,41 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
218
234
  }
219
235
  }
220
236
  reportRequired() {
221
- const anchor = this.partNamed('list') ?? undefined;
222
- if (this.required && this.value === '') {
223
- this.internals.setValidity({ valueMissing: true }, this.valueMissingMessage, anchor);
237
+ const problem = this.problem;
238
+ if (problem) {
239
+ this.internals.setValidity(problem[0], problem[1], this.partNamed('list') ?? undefined);
224
240
  return;
225
241
  }
242
+ this.internals.setValidity({});
243
+ }
244
+ get problem() {
245
+ if (this.required && this.value === '') {
246
+ return [{ valueMissing: true }, this.valueMissingMessage];
247
+ }
226
248
  const held = this.held;
227
249
  if (held.length > 0 && held.some((one) => valueAsSeconds(one) === null)) {
228
- this.internals.setValidity({ badInput: true }, formatWords(this, 'badInputMessage', {
229
- mode: this.mode ?? 'single',
230
- shape: this.seconds ? 'HH:mm:ss' : 'HH:mm',
231
- }), anchor);
232
- return;
250
+ return [
251
+ { badInput: true },
252
+ formatWords(this, 'badInputMessage', {
253
+ mode: this.mode ?? 'single',
254
+ shape: this.seconds ? 'HH:mm:ss' : 'HH:mm',
255
+ }),
256
+ ];
233
257
  }
234
258
  for (const one of held) {
235
259
  const fault = this.fault(one);
236
260
  if (fault) {
237
- this.internals.setValidity({ [fault[0]]: true }, fault[1], anchor);
238
- return;
261
+ return [{ [fault[0]]: true }, fault[1]];
239
262
  }
240
263
  }
241
- this.internals.setValidity({});
264
+ return undefined;
265
+ }
266
+ get failure() {
267
+ const problem = this.problem;
268
+ if (!problem) {
269
+ return undefined;
270
+ }
271
+ return problem[0].valueMissing ? 'missing' : 'refused';
242
272
  }
243
273
  report() {
244
274
  if (this.isDisabled) {
@@ -290,7 +320,11 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
290
320
  willUpdate(changed) {
291
321
  super.willUpdate(changed);
292
322
  this.report();
323
+ if (changed.has('value')) {
324
+ this.previewed = '';
325
+ }
293
326
  this.toggleState('blank', this.value === '');
327
+ this.toggleState('range-pending', this.mode === 'range' && this.held.length === 1);
294
328
  }
295
329
  firstUpdated(changed) {
296
330
  super.firstUpdated(changed);
@@ -386,6 +420,8 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
386
420
  .value=${this.value}
387
421
  @helix-before-change=${this.handleChange}
388
422
  @input=${this.handleListInput}
423
+ @pointerleave=${this.handleListLeave}
424
+ @pointerover=${this.handleListHover}
389
425
  >
390
426
  ${this.times.map((seconds) => {
391
427
  const at = this.valueFor(seconds);
@@ -481,6 +517,9 @@ __decorate([
481
517
  words,
482
518
  property({ attribute: 'range-underflow-message' })
483
519
  ], HelixHeadlessTimePicker.prototype, "rangeUnderflowMessage", void 0);
520
+ __decorate([
521
+ state()
522
+ ], HelixHeadlessTimePicker.prototype, "previewed", void 0);
484
523
  __decorate([
485
524
  property({ attribute: 'readonly', reflect: true, type: Boolean })
486
525
  ], HelixHeadlessTimePicker.prototype, "readOnly", void 0);
@@ -37,7 +37,7 @@ declare const HelixHeadlessToggleGroup_base: (new (...args: any[]) => import("@f
37
37
  * | Right, Down | Moves to the next item, from the last back to the first. In a single group, chooses it. |
38
38
  * | Left, Up | Moves to the item before, from the first round to the last. In a single group, chooses it. |
39
39
  * | Home, End | Moves to the first or the last item. In a single group, chooses it. |
40
- * | Space, Enter | Chooses the focused item, or presses or releases it in a multiple group. |
40
+ * | Space, Enter | Chooses the focused item, or presses or releases it in a multiple group. With `allow-empty-selection`, takes back the chosen item. |
41
41
  *
42
42
  * Left and Right swap on a right-to-left page, and a disabled item is passed
43
43
  * over. A single group is a `radiogroup` of items with `role="radio"` and
@@ -117,6 +117,15 @@ export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_b
117
117
  * `MixinCollection` records for the same reason.
118
118
  */
119
119
  private adopt;
120
+ /**
121
+ * The items whose `selected` this group has written, which `adopt` passes
122
+ * over: the mark is this group's answer, and the markup declared nothing.
123
+ *
124
+ * Measured before this: in a group with no default, choosing List and then
125
+ * resetting the form left List chosen and submitted, because `adopt` read
126
+ * the mark `sync` had put on it as a declaration and made it the default.
127
+ */
128
+ private readonly marked;
120
129
  /** Marks the chosen item, separately from placing the indicator. */
121
130
  private sync;
122
131
  /**
@@ -139,6 +148,14 @@ export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_b
139
148
  private get chosenItem();
140
149
  /** Whether several items can be pressed at once. */
141
150
  private get multiple();
151
+ /**
152
+ * Whether a press on the chosen item, or Space or Enter on it, takes it
153
+ * back so nothing is chosen, as React Aria's `ToggleButtonGroup` and
154
+ * Radix's `ToggleGroup` do in a single group. Absent, a single group always
155
+ * keeps its choice once it has one, as a radio group does. A multiple group
156
+ * releases a pressed item either way.
157
+ */
158
+ allowEmptySelection: boolean;
142
159
  /**
143
160
  * Whether the group holds a tab stop of its own.
144
161
  *
@@ -11,6 +11,7 @@ import { HelixHeadlessToggle } from '../root/toggle-root.js';
11
11
  let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinControllerValue(MixinField(HelixFormElement)) {
12
12
  constructor() {
13
13
  super(...arguments);
14
+ this.marked = new WeakSet();
14
15
  this.lastFocused = undefined;
15
16
  this.noteFocus = (event) => {
16
17
  const item = event.target;
@@ -19,6 +20,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
19
20
  this.sync();
20
21
  }
21
22
  };
23
+ this.allowEmptySelection = false;
22
24
  this.focusable = true;
23
25
  this.indicator = new IndicatorController(this, {
24
26
  active: () => this.chosenItem,
@@ -40,7 +42,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
40
42
  }
41
43
  const value = event.detail.value;
42
44
  if (!this.multiple) {
43
- this.requestValue(value);
45
+ this.requestValue(this.allowEmptySelection && value === this.value ? '' : value);
44
46
  return;
45
47
  }
46
48
  const next = toggleValue(this.values, value, this.items.map((item) => item.value));
@@ -63,7 +65,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
63
65
  if (this.value !== '' || this.children === undefined) {
64
66
  return;
65
67
  }
66
- const declared = this.items.find((item) => item.selected);
68
+ const declared = this.items.find((item) => item.defaultSelected || (item.selected && !this.marked.has(item)));
67
69
  if (declared) {
68
70
  this.defaultValue = declared.value;
69
71
  }
@@ -73,6 +75,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
73
75
  const pressed = new Set(this.values);
74
76
  const entry = this.focusable ? this.entry() : undefined;
75
77
  for (const item of this.items) {
78
+ this.marked.add(item);
76
79
  item.selected = item === active;
77
80
  item.pressed = this.multiple ? pressed.has(item.value) : undefined;
78
81
  item.focusable = item === entry;
@@ -192,6 +195,9 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
192
195
  return html `${inside ? nothing : label}<div part="row"><slot name="start"></slot><div part="track">${inside ? label : nothing}<div part="indicator" aria-hidden="true"></div><slot @slotchange=${this.handleItems}></slot></div><slot name="end"></slot></div>${this.renderHint()}${this.renderFeedback()}`;
193
196
  }
194
197
  };
198
+ __decorate([
199
+ property({ attribute: 'allow-empty-selection', type: Boolean })
200
+ ], HelixHeadlessToggleGroup.prototype, "allowEmptySelection", void 0);
195
201
  __decorate([
196
202
  property({ attribute: false })
197
203
  ], HelixHeadlessToggleGroup.prototype, "focusable", void 0);
@@ -31,9 +31,9 @@ declare const HelixHeadlessToggle_base: (new (...args: any[]) => import("@fusion
31
31
  * @event {Event} change - Fired after a toggle button on its own is pressed or released.
32
32
  * @event {HelixSelectEvent} helix-select - Fired when the item is chosen. Cancelable: call `preventDefault()` to keep the current choice.
33
33
  *
34
- * @csspart item - The radio button.
34
+ * @csspart item - The button: a radio in a group where one is chosen, and a button with `aria-pressed` on its own or in a group where several can be.
35
35
  *
36
- * @cssstate pressed - The item is pressed, in a group where several can be.
36
+ * @cssstate pressed - The item is pressed, on its own or in a group where several can be.
37
37
  * @cssstate selected - The item is chosen.
38
38
  */
39
39
  export declare class HelixHeadlessToggle extends HelixHeadlessToggle_base {
@@ -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 { customElement, HelixElement, HostClickController, MixinSelectableItem, } from '@fusion.dev/helix-core';
7
+ import { auditName, customElement, HelixElement, HostClickController, MixinSelectableItem, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(HelixElement) {
@@ -39,6 +39,7 @@ let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(
39
39
  firstUpdated(changed) {
40
40
  super.firstUpdated(changed);
41
41
  this.finishHydration();
42
+ auditName(this, () => this.partNamed('item'), 'Give it words in its default slot, an `aria-label`, or a label on the icon it holds.');
42
43
  }
43
44
  render() {
44
45
  const alone = this.alone;