@fusion.dev/helix-headless 0.25.1 → 0.30.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 (89) hide show
  1. package/custom-elements.json +4966 -755
  2. package/dist/accordion/root/accordion-root.d.ts +5 -6
  3. package/dist/accordion/root/accordion-root.js +13 -36
  4. package/dist/announcement/root/announcement-root.js +1 -1
  5. package/dist/avatar/root/avatar-root.d.ts +17 -3
  6. package/dist/avatar/root/avatar-root.js +27 -8
  7. package/dist/blockquote/root/blockquote-root.js +1 -1
  8. package/dist/button/root/button-root.js +1 -1
  9. package/dist/carousel/root/carousel-root.js +2 -2
  10. package/dist/chat/root/chat-root.js +1 -1
  11. package/dist/checkbox/root/checkbox-root.js +2 -2
  12. package/dist/checkbox-group/root/checkbox-group-root.js +2 -2
  13. package/dist/color-input/root/color-input-root.d.ts +12 -14
  14. package/dist/color-input/root/color-input-root.js +4 -52
  15. package/dist/color-picker/root/color-picker-root.js +2 -2
  16. package/dist/combobox/root/combobox-root.js +11 -14
  17. package/dist/command/root/command-root.js +2 -3
  18. package/dist/data-table/root/data-table-root.js +1 -1
  19. package/dist/date-input/root/date-input-root.d.ts +5 -77
  20. package/dist/date-input/root/date-input-root.js +16 -108
  21. package/dist/date-picker/root/date-picker-root.js +2 -2
  22. package/dist/divider/root/divider-root.js +1 -1
  23. package/dist/drawer/root/drawer-root.js +1 -1
  24. package/dist/drilldown/item/drilldown-item.js +1 -1
  25. package/dist/file-input/root/file-input-root.js +4 -7
  26. package/dist/filter-input/field/filter-field.d.ts +1 -1
  27. package/dist/filter-input/root/filter-input-root.d.ts +2 -2
  28. package/dist/filter-input/root/filter-input-root.js +12 -15
  29. package/dist/heatmap/root/heatmap-root.js +1 -1
  30. package/dist/image/root/image-root.d.ts +7 -0
  31. package/dist/image/root/image-root.js +6 -2
  32. package/dist/input/root/input-root.js +3 -3
  33. package/dist/listbox/root/listbox-root.js +3 -3
  34. package/dist/megamenu/root/megamenu-root.d.ts +12 -2
  35. package/dist/megamenu/root/megamenu-root.js +20 -15
  36. package/dist/menu/item/menu-item.js +1 -1
  37. package/dist/menu/root/menu-root.js +3 -10
  38. package/dist/navigation/root/navigation-root.d.ts +1 -2
  39. package/dist/navigation/root/navigation-root.js +3 -9
  40. package/dist/number-input/root/number-input-root.js +5 -5
  41. package/dist/object-inspector/root/object-inspector-root.js +4 -4
  42. package/dist/outline/root/outline-root.d.ts +0 -1
  43. package/dist/outline/root/outline-root.js +6 -12
  44. package/dist/pin-input/root/pin-input-root.js +2 -2
  45. package/dist/popover/root/popover-root.js +2 -10
  46. package/dist/radio/root/radio-root.js +1 -1
  47. package/dist/scatter-chart/root/scatter-chart-root.js +1 -1
  48. package/dist/scroll-area/root/scroll-area-root.js +1 -1
  49. package/dist/select/root/select-root.js +8 -9
  50. package/dist/slider/root/slider-root.js +2 -2
  51. package/dist/sparkline/root/sparkline-root.d.ts +2 -1
  52. package/dist/sparkline/root/sparkline-root.js +2 -2
  53. package/dist/speed-dial/item/speed-dial-item.js +1 -1
  54. package/dist/speed-dial/root/speed-dial-root.d.ts +2 -2
  55. package/dist/speed-dial/root/speed-dial-root.js +4 -7
  56. package/dist/switch/root/switch-root.js +3 -3
  57. package/dist/table/column/table-column.js +1 -1
  58. package/dist/tabs/root/tabs-root.d.ts +2 -17
  59. package/dist/tabs/root/tabs-root.js +3 -21
  60. package/dist/tabs/tab/tab.js +1 -1
  61. package/dist/textarea/root/textarea-root.js +3 -3
  62. package/dist/time-input/root/time-input-root.d.ts +5 -64
  63. package/dist/time-input/root/time-input-root.js +15 -107
  64. package/dist/time-picker/root/time-picker-root.js +2 -2
  65. package/dist/toggle/group/toggle-group.d.ts +0 -13
  66. package/dist/toggle/group/toggle-group.js +3 -23
  67. package/dist/toggle/root/toggle-root.js +1 -1
  68. package/dist/tooltip/root/tooltip-root.d.ts +0 -13
  69. package/dist/tooltip/root/tooltip-root.js +8 -16
  70. package/dist/tour/root/tour-root.js +6 -6
  71. package/dist/tour/stop/tour-stop.js +2 -2
  72. package/dist/video/root/captions.d.ts +85 -0
  73. package/dist/video/root/captions.js +45 -0
  74. package/dist/video/root/fullscreen.d.ts +28 -0
  75. package/dist/video/root/fullscreen.js +28 -0
  76. package/dist/video/root/idle.d.ts +34 -0
  77. package/dist/video/root/idle.js +24 -0
  78. package/dist/video/root/media-session.d.ts +54 -0
  79. package/dist/video/root/media-session.js +49 -0
  80. package/dist/video/root/remote-playback.d.ts +34 -0
  81. package/dist/video/root/remote-playback.js +38 -0
  82. package/dist/video/root/video-root.d.ts +11 -89
  83. package/dist/video/root/video-root.js +45 -139
  84. package/dist/video-playlist/menu/video-playlist-menu.d.ts +2 -1
  85. package/dist/video-playlist/menu/video-playlist-menu.js +8 -10
  86. package/dist/virtual-list/root/virtual-list-root.js +1 -1
  87. package/package.json +8 -7
  88. package/types/jsx.d.ts +264 -192
  89. package/types/vue.d.ts +78 -69
@@ -64,8 +64,6 @@ export declare class HelixHeadlessTabs extends HelixHeadlessTabs_base {
64
64
  * consumer's server markup does not already have.
65
65
  */
66
66
  variant?: 'segmented' | 'underlined';
67
- /** Watches for anything that moves a tab, since none of it is a property. */
68
- private observer;
69
67
  private get tabs();
70
68
  private get panels();
71
69
  private readonly handleSelection;
@@ -74,23 +72,10 @@ export declare class HelixHeadlessTabs extends HelixHeadlessTabs_base {
74
72
  connectedCallback(): void;
75
73
  disconnectedCallback(): void;
76
74
  protected firstUpdated(changed: PropertyValues): void;
77
- /** The host's own size, which moves every tab and therefore the mark. */
78
- private measure;
79
75
  /**
80
- * Watches every tab for a change of size.
76
+ * The set changed: pair it up and measure it.
81
77
  *
82
- * Called again whenever the set changes, because `tabs` reads `this.children`
83
- * and filters on `instanceof`, so it is empty until the tab elements upgrade.
84
- * At `firstUpdated` it frequently is empty, and observing nothing is silent.
85
- *
86
- * `observe` is idempotent per element, so re-running this costs nothing and
87
- * needs no bookkeeping about which tabs are already watched.
88
- */
89
- private watch;
90
- /**
91
- * The set changed: pair it up, watch it, and measure it.
92
- *
93
- * All three, and dropping `place()` leaves an indicator unplaced for the life
78
+ * Both, and dropping `place()` leaves an indicator unplaced for the life
94
79
  * of the page. The order of events is what does it: the host renders and
95
80
  * measures before its tabs have upgraded, so there is no selected tab to
96
81
  * measure and `place` refuses; the ResizeObserver's one initial callback
@@ -19,8 +19,9 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
19
19
  });
20
20
  this.indicator = new IndicatorController(this, {
21
21
  active: () => this.tabs.find((tab) => tab.selected),
22
- indicator: () => this.renderRoot?.querySelector('[part="indicator"]'),
23
- surface: () => this.renderRoot?.querySelector('[part="tabs"]'),
22
+ indicator: () => this.partNamed('indicator'),
23
+ observe: () => this.tabs,
24
+ surface: () => this.partNamed('tabs'),
24
25
  });
25
26
  this.label = 'Tabs';
26
27
  this.manual = false;
@@ -29,7 +30,6 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
29
30
  };
30
31
  this.handleSlotChange = () => {
31
32
  this.sync();
32
- this.watch();
33
33
  this.indicator.place();
34
34
  };
35
35
  }
@@ -54,34 +54,16 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
54
54
  connectedCallback() {
55
55
  super.connectedCallback();
56
56
  this.addEventListener('helix-select', this.handleSelection);
57
- if (this.hasUpdated) {
58
- this.measure();
59
- this.watch();
60
- }
61
57
  }
62
58
  disconnectedCallback() {
63
59
  super.disconnectedCallback();
64
60
  this.removeEventListener('helix-select', this.handleSelection);
65
- this.observer?.disconnect();
66
- this.observer = undefined;
67
61
  }
68
62
  firstUpdated(changed) {
69
63
  super.firstUpdated(changed);
70
64
  this.sync();
71
65
  this.indicator.place();
72
66
  requestAnimationFrame(() => this.indicator.place());
73
- this.measure();
74
- this.watch();
75
- }
76
- measure() {
77
- this.observer?.disconnect();
78
- this.observer = new ResizeObserver(() => this.indicator.place());
79
- this.observer.observe(this);
80
- }
81
- watch() {
82
- for (const tab of this.tabs) {
83
- this.observer?.observe(tab);
84
- }
85
67
  }
86
68
  updated(changed) {
87
69
  super.updated(changed);
@@ -15,7 +15,7 @@ let HelixHeadlessTab = class HelixHeadlessTab extends HelixElement {
15
15
  this.value = '';
16
16
  }
17
17
  focusItem() {
18
- this.renderRoot.querySelector('[part="tab"]')?.focus();
18
+ this.partNamed('tab')?.focus();
19
19
  }
20
20
  select() {
21
21
  if (this.disabled) {
@@ -11,7 +11,7 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.aria = new AriaForwardController(this, {
14
- target: () => this.renderRoot?.querySelector('[part="control"]') ?? null,
14
+ target: () => this.partNamed('control'),
15
15
  });
16
16
  this.announcesInput = true;
17
17
  this.autocomplete = '';
@@ -29,7 +29,7 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
29
29
  };
30
30
  this.handleChange = (event) => {
31
31
  event.stopPropagation();
32
- this.dispatchEvent(new Event('change', { bubbles: true, composed: false }));
32
+ this.announceChange();
33
33
  };
34
34
  this.rows = 3;
35
35
  this.wrap = 'soft';
@@ -39,7 +39,7 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
39
39
  delegatesFocus: true,
40
40
  }; }
41
41
  get control() {
42
- return this.renderRoot.querySelector('[part="control"]');
42
+ return this.partNamed('control');
43
43
  }
44
44
  grow() {
45
45
  const control = this.control;
@@ -1,9 +1,9 @@
1
- import { AriaForwardController, HelixFormElement, PopoverController } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../icon/index.js';
4
4
  import '../../tag/index.js';
5
5
  import '../../time-picker/index.js';
6
- declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinTokensInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
6
+ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinPickerEditingInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinPickerFieldInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinTokensInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
7
7
  /**
8
8
  * @summary A time, typed or picked from a list.
9
9
  *
@@ -122,20 +122,6 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
122
122
  * What the field says before a time is in it.
123
123
  */
124
124
  placeholder: string;
125
- /**
126
- * Whether the control refuses changes while still submitting its value.
127
- *
128
- * **Enforced by refusing every request in one place**, which is the seam this
129
- * component already has: `requestValue` is what every path here comes through,
130
- * and gating it once is what stops the next call site from being written
131
- * without the check.
132
- *
133
- * Not `disabled`, which is the confusion worth naming: a disabled control
134
- * submits nothing and leaves the tab order, and a read-only one submits its
135
- * value and stays reachable, which is what a form showing a value somebody
136
- * else set needs.
137
- */
138
- readOnly: boolean;
139
125
  /**
140
126
  * How far apart the offered times are, in minutes.
141
127
  */
@@ -153,19 +139,12 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
153
139
  * Accessible name for the control that opens the list.
154
140
  */
155
141
  triggerLabel: string;
156
- /**
157
- * Whether the caret is in the field, which is when it holds the value.
158
- */
159
- private editing;
160
- /** The overlay, placed against the field for the reason Date Input gives. */
161
- protected readonly panel: PopoverController;
162
- private get control();
163
142
  /** Every time in the value, which may hold more than one. */
164
143
  private get held();
165
144
  /** One time, written the way `time-style` asks for. */
166
145
  private written;
167
146
  /** What the field shows: the value while it is being edited, a reading when not. */
168
- private get display();
147
+ protected get display(): string;
169
148
  /**
170
149
  * What is being typed but not yet held, for `multiple` only.
171
150
  *
@@ -192,41 +171,8 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
192
171
  private get picked();
193
172
  private readonly handleInput;
194
173
  private readonly handlePicked;
195
- /**
196
- * Puts the caret back in the field without the field reopening the list.
197
- *
198
- * Focus opens the list, so handing focus back after a pick would reopen the
199
- * thing that was just answered, and a reader could not get out of it. Input
200
- * Date guards the same case by asking where focus came from, which is enough
201
- * there and is not enough here: the option that had focus is gone by the time
202
- * this runs, so `relatedTarget` is whatever the document fell back to, which
203
- * is outside this element and reads as a genuine arrival.
204
- *
205
- * A flag rather than a better guess, because this element is the one moving
206
- * the focus and it is the only thing that can know so. Cleared synchronously,
207
- * since `focus()` dispatches before it returns.
208
- *
209
- * The path cannot be exercised from a spec: `focus()` dispatches no event in
210
- * an unfocused document, which is what both the test runtime and the browser
211
- * pane here are.
212
- */
213
- private returning;
214
- private returnFocus;
215
- private readonly handleBlur;
216
- /**
217
- * Focusing the field opens the list, and only from outside the element.
218
- *
219
- * The guard is the one Date Input needed: picking a time shuts the panel and
220
- * puts the caret back in the field, and without it the list came straight
221
- * back and a reader could not get out of it.
222
- */
223
- private readonly handleFocus;
224
- /** Puts the field back where the answer differs from what was typed. */
225
- private restore;
226
- /** Reports the value and the validity the host stands behind. */
227
- private sync;
228
- /** Escape shuts the list without moving the time. */
229
- private readonly handleKeydown;
174
+ /** What is wrong with the times, which the text input cannot say. */
175
+ protected validate(): string;
230
176
  /**
231
177
  * Whether the panel is one list of times or a column each.
232
178
  *
@@ -243,12 +189,7 @@ export declare class HelixHeadlessTimeInput extends HelixHeadlessTimeInput_base
243
189
  * only the departure has to be named.
244
190
  */
245
191
  variant?: 'list' | 'wheels';
246
- /** Refuses every change while read-only. See `readOnly`. */
247
- requestValue(next: string): boolean;
248
- protected firstUpdated(changed: PropertyValues): void;
249
192
  protected updated(changed: PropertyValues): void;
250
- /** Puts the caret in the field, the way a label pointing at one would. */
251
- focus(options?: FocusOptions): void;
252
193
  /**
253
194
  * The clock the field opens, behind a seam the styled tier can move.
254
195
  *
@@ -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 { AriaForwardController, valueAsTime as asTime, customElement, timeAsValue as fromTime, HelixFormElement, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, PopoverController, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, valueAsTime as asTime, customElement, timeAsValue as fromTime, HelixFormElement, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerEditing, MixinPickerField, MixinTokens, } from '@fusion.dev/helix-core';
8
8
  import { clock } from '@fusion.dev/helix-icons';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -14,33 +14,20 @@ import '../../icon/index.js';
14
14
  import '../../tag/index.js';
15
15
  import '../../time-picker/index.js';
16
16
  import { isTime, toMinutes, writeTime } from '../../time-picker/root/time-picker-root.js';
17
- let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinControllerOpen(MixinTokens(MixinControllerValue(MixinField(HelixFormElement)))) {
17
+ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinPickerEditing(MixinPickerField(MixinControllerOpen(MixinTokens(MixinControllerValue(MixinField(HelixFormElement)))))) {
18
18
  constructor() {
19
19
  super(...arguments);
20
20
  this.aria = new AriaForwardController(this, {
21
- target: () => this.renderRoot?.querySelector('[part="control"]') ?? null,
21
+ target: () => this.partNamed('control'),
22
22
  });
23
23
  this.autocomplete = '';
24
24
  this.locale = '';
25
25
  this.max = '23:59';
26
26
  this.min = '00:00';
27
27
  this.placeholder = 'HH:mm';
28
- this.readOnly = false;
29
28
  this.step = 15;
30
29
  this.timeStyle = 'local';
31
30
  this.triggerLabel = 'Choose a time';
32
- this.editing = false;
33
- this.panel = new PopoverController(this, {
34
- anchor: () => this.renderRoot?.querySelector('[part="field"]'),
35
- boundary: () => [
36
- this.renderRoot?.querySelector('[part="surface"]'),
37
- this.renderRoot?.querySelector('[part="trigger"]'),
38
- ],
39
- offset: () => 8,
40
- onDismiss: () => this.requestOpen(false),
41
- placement: 'bottom-start',
42
- surface: () => this.renderRoot?.querySelector('[part="surface"]'),
43
- });
44
31
  this.draft = '';
45
32
  this.handleDraftKey = (event) => {
46
33
  if (this.mode !== 'multiple') {
@@ -87,34 +74,6 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinControlle
87
74
  this.updateComplete.then(() => this.returnFocus());
88
75
  }
89
76
  };
90
- this.returning = false;
91
- this.handleBlur = () => {
92
- this.editing = false;
93
- };
94
- this.handleFocus = (event) => {
95
- this.editing = !this.returning;
96
- const from = event.relatedTarget;
97
- if (this.open || this.isDisabled || this.returning) {
98
- return;
99
- }
100
- if (from instanceof Node && this.renderRoot?.contains?.(from)) {
101
- return;
102
- }
103
- this.requestOpen(true);
104
- };
105
- this.handleKeydown = (event) => {
106
- if (event.key === 'Escape' && this.open) {
107
- event.stopPropagation();
108
- this.requestOpen(false);
109
- this.returnFocus();
110
- }
111
- };
112
- }
113
- get control() {
114
- if (typeof this.renderRoot?.querySelector !== 'function') {
115
- return null;
116
- }
117
- return this.renderRoot.querySelector('[part="control"]');
118
77
  }
119
78
  get held() {
120
79
  return this.value
@@ -148,77 +107,32 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinControlle
148
107
  const held = this.held;
149
108
  return held.length > 0 && held.every(isTime) ? held.join(',') : '';
150
109
  }
151
- returnFocus() {
152
- this.returning = true;
153
- this.control?.focus();
154
- this.returning = false;
155
- }
156
- restore() {
157
- const control = this.control;
158
- if (control && control.value !== this.display) {
159
- control.value = this.display;
160
- }
161
- this.sync();
162
- }
163
- sync() {
164
- this.internals.setFormValue(this.value);
165
- const control = this.control;
166
- if (!control) {
167
- return;
168
- }
110
+ validate() {
169
111
  const held = this.held;
170
112
  const floor = toMinutes(this.min) ?? 0;
171
113
  const ceiling = toMinutes(this.max) ?? 1439;
172
- let message = '';
173
114
  if (held.length > 0 && !held.every(isTime)) {
174
- message =
175
- this.mode === undefined
176
- ? 'Enter a time as HH:mm.'
177
- : 'Enter times as HH:mm, separated by commas.';
178
- }
179
- else if (held.some((one) => (toMinutes(one) ?? 0) < floor)) {
180
- message = `Choose a time at or after ${writeTime(this.min, this.locale)}.`;
115
+ return this.mode === undefined
116
+ ? 'Enter a time as HH:mm.'
117
+ : 'Enter times as HH:mm, separated by commas.';
181
118
  }
182
- else if (held.some((one) => (toMinutes(one) ?? 0) > ceiling)) {
183
- message = `Choose a time at or before ${writeTime(this.max, this.locale)}.`;
119
+ if (held.some((one) => (toMinutes(one) ?? 0) < floor)) {
120
+ return `Choose a time at or after ${writeTime(this.min, this.locale)}.`;
184
121
  }
185
- else if (this.mode === 'range' && held.length > 2) {
186
- message = 'A range is two times.';
122
+ if (held.some((one) => (toMinutes(one) ?? 0) > ceiling)) {
123
+ return `Choose a time at or before ${writeTime(this.max, this.locale)}.`;
187
124
  }
188
- if (message === '') {
189
- this.internals.setValidity(control.validity, control.validationMessage, control);
190
- }
191
- else {
192
- this.internals.setValidity({ badInput: true }, message, control);
193
- }
194
- this.refreshFeedback();
195
- }
196
- requestValue(next) {
197
- if (this.readOnly) {
198
- return false;
125
+ if (this.mode === 'range' && held.length > 2) {
126
+ return 'A range is two times.';
199
127
  }
200
- return super.requestValue(next);
201
- }
202
- firstUpdated(changed) {
203
- super.firstUpdated(changed);
204
- this.sync();
128
+ return '';
205
129
  }
206
130
  updated(changed) {
207
131
  super.updated(changed);
208
- this.sync();
209
132
  if (this.open) {
210
- this.panel.show();
211
- this.renderRoot
212
- ?.querySelector('[part="picker"]')
213
- ?.reveal?.();
214
- }
215
- else {
216
- this.panel.hide();
133
+ this.partNamed('picker')?.reveal?.();
217
134
  }
218
135
  }
219
- focus(options) {
220
- this.control?.focus(options);
221
- }
222
136
  renderPicker() {
223
137
  return html `<${this.tag('time-picker')}
224
138
  part="picker"
@@ -341,9 +255,6 @@ __decorate([
341
255
  __decorate([
342
256
  property()
343
257
  ], HelixHeadlessTimeInput.prototype, "placeholder", void 0);
344
- __decorate([
345
- property({ attribute: 'readonly', reflect: true, type: Boolean })
346
- ], HelixHeadlessTimeInput.prototype, "readOnly", void 0);
347
258
  __decorate([
348
259
  property({ type: Number })
349
260
  ], HelixHeadlessTimeInput.prototype, "step", void 0);
@@ -353,9 +264,6 @@ __decorate([
353
264
  __decorate([
354
265
  property({ attribute: 'trigger-label' })
355
266
  ], HelixHeadlessTimeInput.prototype, "triggerLabel", void 0);
356
- __decorate([
357
- state()
358
- ], HelixHeadlessTimeInput.prototype, "editing", void 0);
359
267
  __decorate([
360
268
  state()
361
269
  ], HelixHeadlessTimeInput.prototype, "draft", void 0);
@@ -167,7 +167,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
167
167
  return super.requestValue(next);
168
168
  }
169
169
  reportRequired() {
170
- const anchor = this.renderRoot?.querySelector('[part="list"]') ?? undefined;
170
+ const anchor = this.partNamed('list') ?? undefined;
171
171
  if (this.required && this.value === '') {
172
172
  this.internals.setValidity({ valueMissing: true }, 'Choose a time.', anchor);
173
173
  return;
@@ -212,7 +212,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
212
212
  void this.revealWhenSettled();
213
213
  }
214
214
  focus(options) {
215
- this.renderRoot?.querySelector('[part="list"]')?.focus(options);
215
+ this.partNamed('list')?.focus(options);
216
216
  }
217
217
  renderColumn(part, label, values, written, value, barred = () => false) {
218
218
  return html `<${this.tag('listbox')}
@@ -39,26 +39,13 @@ export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_b
39
39
  /** Marks the chosen item, separately from placing the indicator. */
40
40
  private sync;
41
41
  private readonly indicator;
42
- private observer;
43
42
  protected readonly keys: KeyboardNavigationController;
44
43
  protected readonly cascade: DisabledCascadeController;
45
44
  private get items();
46
- /**
47
- * Watches the group and every item it currently holds.
48
- *
49
- * Called again on `slotchange`, because the set was read once at first paint:
50
- * an item added later was measured by nothing, so a group whose options
51
- * arrive with the data had a thumb that stopped following the moment one of
52
- * them changed width. Observing the same element twice is a no-op, so there
53
- * is nothing to unwind first.
54
- */
55
- private watch;
56
45
  private readonly handleSelection;
57
46
  connectedCallback(): void;
58
47
  disconnectedCallback(): void;
59
48
  protected firstUpdated(changed: PropertyValues): void;
60
- /** The observer the items are registered on, rebuilt with them. */
61
- private measure;
62
49
  protected updated(changed: PropertyValues): void;
63
50
  protected willUpdate(changed: PropertyValues): void;
64
51
  /**
@@ -13,8 +13,9 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
13
13
  super(...arguments);
14
14
  this.indicator = new IndicatorController(this, {
15
15
  active: () => this.items.find((item) => item.value === this.value),
16
- indicator: () => this.renderRoot?.querySelector('[part="indicator"]'),
17
- surface: () => this.renderRoot?.querySelector('[part="track"]'),
16
+ indicator: () => this.partNamed('indicator'),
17
+ observe: () => this.items,
18
+ surface: () => this.partNamed('track'),
18
19
  });
19
20
  this.keys = new KeyboardNavigationController(this, { items: () => this.items });
20
21
  this.cascade = new DisabledCascadeController(this, {
@@ -35,42 +36,22 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
35
36
  get items() {
36
37
  return [...this.children].filter((child) => child instanceof HelixHeadlessToggle);
37
38
  }
38
- watch() {
39
- this.observer?.observe(this);
40
- for (const item of this.items) {
41
- this.observer?.observe(item);
42
- }
43
- }
44
39
  connectedCallback() {
45
40
  super.connectedCallback();
46
41
  this.addEventListener('helix-select', this.handleSelection);
47
- if (this.hasUpdated) {
48
- this.measure();
49
- this.watch();
50
- }
51
42
  }
52
43
  disconnectedCallback() {
53
44
  super.disconnectedCallback();
54
45
  this.removeEventListener('helix-select', this.handleSelection);
55
- this.observer?.disconnect();
56
46
  }
57
47
  firstUpdated(changed) {
58
48
  super.firstUpdated(changed);
59
49
  this.sync();
60
50
  this.indicator.place();
61
- this.measure();
62
- this.watch();
63
- }
64
- measure() {
65
- this.observer?.disconnect();
66
- this.observer = new ResizeObserver(() => this.indicator.place());
67
51
  }
68
52
  updated(changed) {
69
53
  super.updated(changed);
70
54
  this.sync();
71
- if (changed.has('value')) {
72
- this.indicator.place();
73
- }
74
55
  this.validate();
75
56
  this.linkHost();
76
57
  }
@@ -93,7 +74,6 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
93
74
  <div part="indicator" aria-hidden="true"></div>
94
75
  <slot
95
76
  @slotchange=${() => {
96
- this.watch();
97
77
  this.sync();
98
78
  this.indicator.place();
99
79
  }}
@@ -23,7 +23,7 @@ let HelixHeadlessToggle = class HelixHeadlessToggle extends HelixElement {
23
23
  this.seeded = next;
24
24
  }
25
25
  focusItem() {
26
- this.renderRoot.querySelector('[part="item"]')?.focus();
26
+ this.partNamed('item')?.focus();
27
27
  }
28
28
  select() {
29
29
  if (this.disabled) {
@@ -289,18 +289,6 @@ export declare class HelixHeadlessTooltip extends HelixHeadlessTooltip_base {
289
289
  * really is still over the trigger. See the property.
290
290
  */
291
291
  private readonly handleActivate;
292
- /**
293
- * Escape closes the bubble from wherever the reader is.
294
- *
295
- * On the document, not on the host: a tooltip opened by hover has focus
296
- * somewhere else entirely, so a press never reaches this element and Escape
297
- * would work for everything except the case the pattern is named for, a
298
- * reader escaping a tooltip obscuring what is under it.
299
- *
300
- * Not stopped from propagating: a tooltip is not what the reader was trying
301
- * to escape if it sits inside something else that also closes.
302
- */
303
- private readonly handleKeydown;
304
292
  /**
305
293
  * What the pointer and focus listeners are currently on.
306
294
  *
@@ -345,7 +333,6 @@ export declare class HelixHeadlessTooltip extends HelixHeadlessTooltip_base {
345
333
  * this should not be the thing to settle it.
346
334
  */
347
335
  private auditDescription;
348
- connectedCallback(): void;
349
336
  disconnectedCallback(): void;
350
337
  protected firstUpdated(changed: PropertyValues): void;
351
338
  protected updated(changed: PropertyValues): void;
@@ -23,15 +23,18 @@ let HelixHeadlessTooltip = class HelixHeadlessTooltip extends MixinControllerOpe
23
23
  this.holds = new Set();
24
24
  this.bubbleLayer = new PopoverController(this, {
25
25
  anchor: () => this.trigger,
26
- arrow: () => this.renderRoot.querySelector('[part="arrow"]'),
26
+ arrow: () => this.partNamed('arrow'),
27
27
  boundary: () => [this, this.named],
28
28
  offset: () => {
29
- const point = this.renderRoot.querySelector('[part="arrow"]');
29
+ const point = this.partNamed('arrow');
30
30
  return this.offset + (point?.offsetWidth ?? 0) / Math.SQRT2;
31
31
  },
32
- onDismiss: () => this.hide(),
32
+ onDismiss: () => {
33
+ this.holds.clear();
34
+ this.hide();
35
+ },
33
36
  placement: () => this.placement,
34
- surface: () => this.renderRoot.querySelector('[part="tooltip"]'),
37
+ surface: () => this.partNamed('tooltip'),
35
38
  within: () => this.enclosure,
36
39
  });
37
40
  this.handleContentChange = () => {
@@ -66,12 +69,6 @@ let HelixHeadlessTooltip = class HelixHeadlessTooltip extends MixinControllerOpe
66
69
  this.holds.clear();
67
70
  this.hide();
68
71
  };
69
- this.handleKeydown = (event) => {
70
- if (event.key === 'Escape' && this.open) {
71
- this.holds.clear();
72
- this.hide();
73
- }
74
- };
75
72
  this.audited = false;
76
73
  }
77
74
  get trigger() {
@@ -101,7 +98,7 @@ let HelixHeadlessTooltip = class HelixHeadlessTooltip extends MixinControllerOpe
101
98
  : null;
102
99
  }
103
100
  get bubble() {
104
- return this.renderRoot.querySelector('[part="tooltip"]');
101
+ return this.partNamed('tooltip');
105
102
  }
106
103
  get hasContent() {
107
104
  if (this.content !== '') {
@@ -204,14 +201,9 @@ let HelixHeadlessTooltip = class HelixHeadlessTooltip extends MixinControllerOpe
204
201
  '`aria-describedby="<that id>"` to the element it names. Written in your own markup ' +
205
202
  'it survives server rendering, which is why this element will not write it for you.');
206
203
  }
207
- connectedCallback() {
208
- super.connectedCallback();
209
- document.addEventListener('keydown', this.handleKeydown);
210
- }
211
204
  disconnectedCallback() {
212
205
  super.disconnectedCallback();
213
206
  this.unbind();
214
- document.removeEventListener('keydown', this.handleKeydown);
215
207
  this.holds.clear();
216
208
  clearTimeout(this.timer);
217
209
  }
@@ -95,9 +95,9 @@ let HelixHeadlessTour = class HelixHeadlessTour extends HelixElement {
95
95
  connectedCallback() {
96
96
  super.connectedCallback();
97
97
  this.addEventListener('helix-tour-ask', this.handleAsk);
98
- this.ownerDocument.addEventListener('keydown', this.handleKeydown);
99
- globalThis.addEventListener('resize', this.handleViewport, { passive: true });
100
- globalThis.addEventListener('scroll', this.handleViewport, { capture: true, passive: true });
98
+ document.addEventListener('keydown', this.handleKeydown);
99
+ window.addEventListener('resize', this.handleViewport, { passive: true });
100
+ window.addEventListener('scroll', this.handleViewport, { capture: true, passive: true });
101
101
  if (this.hasUpdated) {
102
102
  this.watch(this.lit);
103
103
  }
@@ -105,9 +105,9 @@ let HelixHeadlessTour = class HelixHeadlessTour extends HelixElement {
105
105
  disconnectedCallback() {
106
106
  super.disconnectedCallback();
107
107
  this.removeEventListener('helix-tour-ask', this.handleAsk);
108
- this.ownerDocument.removeEventListener('keydown', this.handleKeydown);
109
- globalThis.removeEventListener('resize', this.handleViewport);
110
- globalThis.removeEventListener('scroll', this.handleViewport, { capture: true });
108
+ document.removeEventListener('keydown', this.handleKeydown);
109
+ window.removeEventListener('resize', this.handleViewport);
110
+ window.removeEventListener('scroll', this.handleViewport, { capture: true });
111
111
  this.sizer?.disconnect();
112
112
  this.sized = undefined;
113
113
  }