@fusion.dev/helix-headless 0.26.0 → 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
@@ -1,4 +1,4 @@
1
- import { HelixElement } from '@fusion.dev/helix-core';
1
+ import { HelixElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
2
2
  import { type TemplateResult } from 'lit';
3
3
  import { HelixHeadlessAccordionItem } from '../item/accordion-item.js';
4
4
  /**
@@ -20,8 +20,9 @@ import { HelixHeadlessAccordionItem } from '../item/accordion-item.js';
20
20
  * Items are matched with `instanceof HelixHeadlessAccordionItem`, never by tag
21
21
  * name. Matching `helix-accordion-item` would tie this tier to the styled one.
22
22
  *
23
- * Every listener is attached on connect, so none of it runs on the server,
24
- * where there is no tree to walk and no events to hear.
23
+ * The keyboard listener is the controller's and is attached on connect, so
24
+ * none of it runs on the server, where there is no tree to walk and no events
25
+ * to hear.
25
26
  *
26
27
  * @since 0.1.0
27
28
  * @status experimental
@@ -45,9 +46,7 @@ export declare class HelixHeadlessAccordion extends HelixElement {
45
46
  mode?: 'single' | 'single-collapsible';
46
47
  /** Direct items, in light-DOM order. Client only: the server has no tree. */
47
48
  get items(): HelixHeadlessAccordionItem[];
48
- private readonly handleKeydown;
49
- connectedCallback(): void;
50
- disconnectedCallback(): void;
49
+ protected readonly keys: KeyboardNavigationController;
51
50
  protected render(): TemplateResult;
52
51
  }
53
52
  declare global {
@@ -4,52 +4,29 @@ 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 } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, KeyboardNavigationController, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessAccordionItem } from '../item/accordion-item.js';
11
- const NAVIGATION = new Set(['ArrowDown', 'ArrowUp', 'Home', 'End']);
11
+ const navigable = (item) => ({
12
+ contains: (node) => node === item,
13
+ disabled: false,
14
+ focusItem: () => item.focusTrigger(),
15
+ select: () => { },
16
+ selected: false,
17
+ });
12
18
  let HelixHeadlessAccordion = class HelixHeadlessAccordion extends HelixElement {
13
19
  constructor() {
14
20
  super(...arguments);
15
- this.handleKeydown = (event) => {
16
- if (!NAVIGATION.has(event.key)) {
17
- return;
18
- }
19
- const items = this.items;
20
- const current = items.indexOf(event.target);
21
- if (current === -1) {
22
- return;
23
- }
24
- const last = items.length - 1;
25
- let next = current;
26
- if (event.key === 'ArrowDown') {
27
- next = current === last ? 0 : current + 1;
28
- }
29
- else if (event.key === 'ArrowUp') {
30
- next = current === 0 ? last : current - 1;
31
- }
32
- else if (event.key === 'Home') {
33
- next = 0;
34
- }
35
- else {
36
- next = last;
37
- }
38
- event.preventDefault();
39
- items[next]?.focusTrigger();
40
- };
21
+ this.keys = new KeyboardNavigationController(this, {
22
+ items: () => this.items.map(navigable),
23
+ origin: 'focus',
24
+ selects: () => false,
25
+ });
41
26
  }
42
27
  get items() {
43
28
  return [...this.children].filter((child) => child instanceof HelixHeadlessAccordionItem);
44
29
  }
45
- connectedCallback() {
46
- super.connectedCallback();
47
- this.addEventListener('keydown', this.handleKeydown);
48
- }
49
- disconnectedCallback() {
50
- super.disconnectedCallback();
51
- this.removeEventListener('keydown', this.handleKeydown);
52
- }
53
30
  render() {
54
31
  return html `<slot></slot>`;
55
32
  }
@@ -20,7 +20,7 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
20
20
  this.markEmpty();
21
21
  }
22
22
  markEmpty() {
23
- const region = this.renderRoot?.querySelector('[part="actions"]');
23
+ const region = this.partNamed('actions');
24
24
  const empty = !slotFilled(this.renderRoot?.querySelector('slot[name="actions"]'));
25
25
  region?.toggleAttribute('data-empty', empty);
26
26
  region?.toggleAttribute('hidden', empty);
@@ -45,15 +45,29 @@ export declare class HelixHeadlessAvatar extends HelixElement {
45
45
  */
46
46
  src: string;
47
47
  /**
48
- * Whether the image failed.
48
+ * Where the image stands: still on its way, arrived, or failed and replaced
49
+ * by the initials.
49
50
  *
50
- * The element's own observation, never set.
51
+ * Read-only to a consumer: it is a report rather than a setting, and the same
52
+ * report `Image` gives, for the same reason. `load` and `error` fire once, so
53
+ * a listener bound after the picture settled hears neither, which is the
54
+ * platform on an `<img>` too, and this is what that listener asks instead.
55
+ * Mirrored to `data-status` rather than reflected, as `Image` explains.
51
56
  */
52
- private failed;
57
+ status: 'error' | 'loaded' | 'loading';
58
+ /**
59
+ * Whether the picture failed, which is what the fallback draws from.
60
+ *
61
+ * **Separate from `status`, because the two move at different moments** for
62
+ * a picture a server sent: the fallback belongs on screen at once, while the
63
+ * report waits for the event it reports, below.
64
+ */
65
+ private broken;
53
66
  private readonly handleError;
54
67
  private readonly handleLoad;
55
68
  protected willUpdate(changed: PropertyValues): void;
56
69
  protected firstUpdated(changed: PropertyValues): void;
70
+ protected updated(changed: PropertyValues): void;
57
71
  protected render(): TemplateResult;
58
72
  /**
59
73
  * The mark shown where no image loaded, behind a seam a tier can move.
@@ -22,19 +22,23 @@ let HelixHeadlessAvatar = class HelixHeadlessAvatar extends HelixElement {
22
22
  super(...arguments);
23
23
  this.name = '';
24
24
  this.src = '';
25
- this.failed = false;
25
+ this.status = 'loading';
26
+ this.broken = false;
26
27
  this.handleError = (event) => {
27
- this.failed = true;
28
+ this.broken = true;
29
+ this.status = 'error';
28
30
  this.relayNativeEvent(event);
29
31
  };
30
32
  this.handleLoad = (event) => {
33
+ this.status = 'loaded';
31
34
  this.relayNativeEvent(event);
32
35
  };
33
36
  }
34
37
  willUpdate(changed) {
35
38
  super.willUpdate(changed);
36
39
  if (changed.has('src')) {
37
- this.failed = false;
40
+ this.broken = false;
41
+ this.status = 'loading';
38
42
  }
39
43
  hostAria(this.internals, {
40
44
  label: this.name,
@@ -46,14 +50,26 @@ let HelixHeadlessAvatar = class HelixHeadlessAvatar extends HelixElement {
46
50
  const image = this.serverRendered ? this.renderRoot.querySelector('img') : null;
47
51
  if (image?.complete) {
48
52
  const broken = image.naturalWidth === 0;
49
- if (broken) {
50
- this.failed = true;
53
+ this.broken = broken;
54
+ setTimeout(() => {
55
+ this.status = broken ? 'error' : 'loaded';
56
+ this.relayNativeEvent(new Event(broken ? 'error' : 'load'));
57
+ });
58
+ }
59
+ }
60
+ updated(changed) {
61
+ super.updated(changed);
62
+ if (changed.has('status')) {
63
+ if (this.status === 'loading') {
64
+ this.removeAttribute('data-status');
65
+ }
66
+ else {
67
+ this.setAttribute('data-status', this.status);
51
68
  }
52
- setTimeout(() => this.relayNativeEvent(new Event(broken ? 'error' : 'load')));
53
69
  }
54
70
  }
55
71
  render() {
56
- if (this.src !== '' && !this.failed) {
72
+ if (this.src !== '' && !this.broken) {
57
73
  return html `<img
58
74
  part="image"
59
75
  src=${this.src}
@@ -76,9 +92,12 @@ __decorate([
76
92
  __decorate([
77
93
  property()
78
94
  ], HelixHeadlessAvatar.prototype, "src", void 0);
95
+ __decorate([
96
+ property({ attribute: false })
97
+ ], HelixHeadlessAvatar.prototype, "status", void 0);
79
98
  __decorate([
80
99
  state()
81
- ], HelixHeadlessAvatar.prototype, "failed", void 0);
100
+ ], HelixHeadlessAvatar.prototype, "broken", void 0);
82
101
  HelixHeadlessAvatar = __decorate([
83
102
  customElement('helix-headless-avatar')
84
103
  ], HelixHeadlessAvatar);
@@ -13,7 +13,7 @@ let HelixHeadlessBlockquote = class HelixHeadlessBlockquote extends HelixElement
13
13
  this.cite = '';
14
14
  }
15
15
  markEmpty() {
16
- const footer = this.renderRoot?.querySelector('[part="attribution"]');
16
+ const footer = this.partNamed('attribution');
17
17
  const empty = !slotFilled(this.renderRoot?.querySelector('slot[name="attribution"]'));
18
18
  footer?.toggleAttribute('data-empty', empty);
19
19
  footer?.toggleAttribute('hidden', empty);
@@ -11,7 +11,7 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.aria = new AriaForwardController(this, {
14
- target: () => this.renderRoot?.querySelector('[part="button"]') ?? null,
14
+ target: () => this.partNamed('button'),
15
15
  });
16
16
  this.command = '';
17
17
  this.commandFor = '';
@@ -127,7 +127,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
127
127
  }
128
128
  scrollToIndex() {
129
129
  const slide = this.panes[this.index];
130
- const track = this.renderRoot.querySelector('[part="track"]');
130
+ const track = this.partNamed('track');
131
131
  if (!slide || !track) {
132
132
  return;
133
133
  }
@@ -160,7 +160,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
160
160
  this.timer = setInterval(() => this.goTo(this.index + 1), this.interval);
161
161
  }
162
162
  nearest() {
163
- const track = this.renderRoot.querySelector('[part="track"]');
163
+ const track = this.partNamed('track');
164
164
  if (!track) {
165
165
  return undefined;
166
166
  }
@@ -108,7 +108,7 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
108
108
  return this.knownChildren.filter((child) => 'isChatMessage' in child && child.isChatMessage === true);
109
109
  }
110
110
  get log() {
111
- return this.renderRoot?.querySelector?.('[part="log"]') ?? null;
111
+ return this.partNamed('log');
112
112
  }
113
113
  get atBottom() {
114
114
  const log = this.log;
@@ -11,7 +11,7 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
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.indeterminate = false;
17
17
  this.value = 'on';
@@ -53,7 +53,7 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
53
53
  const slot = this.renderRoot.querySelector('[part="label"] slot');
54
54
  const filled = (slot?.assignedNodes({ flatten: true }) ?? []).some((node) => node.textContent?.trim() !== '' || node.nodeType === Node.ELEMENT_NODE);
55
55
  this.toggleState('has-label', filled);
56
- this.renderRoot.querySelector('[part="label"]')?.toggleAttribute('hidden', !filled);
56
+ this.partNamed('label')?.toggleAttribute('hidden', !filled);
57
57
  }
58
58
  render() {
59
59
  return html `<label part="field">
@@ -32,8 +32,8 @@ let HelixHeadlessCheckboxGroup = class HelixHeadlessCheckboxGroup extends MixinF
32
32
  return;
33
33
  }
34
34
  this.value = next;
35
- this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
36
- this.dispatchEvent(new Event('change', { bubbles: true, composed: false }));
35
+ this.announceInput();
36
+ this.announceChange();
37
37
  };
38
38
  this.handleMemberNative = (event) => {
39
39
  if (event.target !== this) {
@@ -1,8 +1,8 @@
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 '../../color-picker/index.js';
4
4
  import { type ColorFormat } from '../../color-picker/color.js';
5
- declare const HelixHeadlessColorInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
5
+ declare const HelixHeadlessColorInput_base: (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").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
6
6
  /**
7
7
  * @summary A color, typed in any syntax or picked from a panel.
8
8
  *
@@ -151,8 +151,6 @@ export declare class HelixHeadlessColorInput extends HelixHeadlessColorInput_bas
151
151
  * where the color is the answer rather than a setting.
152
152
  */
153
153
  variant?: 'swatch';
154
- /** The overlay, placed against the field for the reason Date Input gives. */
155
- protected readonly panel: PopoverController;
156
154
  /**
157
155
  * The last value that was a color.
158
156
  *
@@ -177,8 +175,7 @@ export declare class HelixHeadlessColorInput extends HelixHeadlessColorInput_bas
177
175
  * focusable to name, and in `swatch` the trigger is the only control in the
178
176
  * field. It carries `id="control"` there for the same reason.
179
177
  */
180
- private get anchor();
181
- private get control();
178
+ protected get anchor(): HTMLElement | null;
182
179
  /** What the picker should show, which is only ever a color. */
183
180
  private get picked();
184
181
  /** What the trigger's swatch paints, which is only ever a color. */
@@ -196,15 +193,16 @@ export declare class HelixHeadlessColorInput extends HelixHeadlessColorInput_bas
196
193
  */
197
194
  private readonly handleSampleStart;
198
195
  private readonly handleSampleEnd;
199
- private restore;
200
- /** Reports the value and the validity the host stands behind. */
201
- private sync;
202
- /** Escape shuts the panel without moving the color. */
203
- private readonly handleKeydown;
204
- protected firstUpdated(changed: PropertyValues): void;
196
+ /**
197
+ * Reports the value and the validity the host stands behind.
198
+ *
199
+ * The family's own rather than the mixin's, because in `swatch` there is no
200
+ * text control: the mixin stops where it finds none, and here the verdict
201
+ * hangs off the trigger instead and `required` is checked by hand, a block of
202
+ * color having no native validity to adopt.
203
+ */
204
+ protected sync(): void;
205
205
  protected updated(changed: PropertyValues): void;
206
- /** Puts the caret in the field, the way a label pointing at one would. */
207
- focus(options?: FocusOptions): void;
208
206
  /**
209
207
  * The picker the field opens, behind a seam the styled tier can move.
210
208
  *
@@ -4,17 +4,17 @@ 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, customElement, HelixFormElement, MixinControllerOpen, MixinControllerValue, MixinField, PopoverController, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, HelixFormElement, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerField, } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
11
11
  import '../../color-picker/index.js';
12
12
  import { parseColor, toCss } from '../../color-picker/color.js';
13
- let HelixHeadlessColorInput = class HelixHeadlessColorInput extends MixinControllerOpen(MixinControllerValue(MixinField(HelixFormElement))) {
13
+ let HelixHeadlessColorInput = class HelixHeadlessColorInput extends MixinPickerField(MixinControllerOpen(MixinControllerValue(MixinField(HelixFormElement)))) {
14
14
  constructor() {
15
15
  super(...arguments);
16
16
  this.aria = new AriaForwardController(this, {
17
- target: () => this.renderRoot?.querySelector('[part="control"]') ?? null,
17
+ target: () => this.partNamed('control'),
18
18
  });
19
19
  this.alpha = false;
20
20
  this.autocomplete = 'off';
@@ -22,17 +22,6 @@ let HelixHeadlessColorInput = class HelixHeadlessColorInput extends MixinControl
22
22
  this.placeholder = '#000000';
23
23
  this.swatches = '';
24
24
  this.triggerLabel = 'Choose a color';
25
- this.panel = new PopoverController(this, {
26
- anchor: () => this.renderRoot?.querySelector('[part="field"]'),
27
- boundary: () => [
28
- this.renderRoot?.querySelector('[part="surface"]'),
29
- this.renderRoot?.querySelector('[part="trigger"]'),
30
- ],
31
- offset: () => 8,
32
- onDismiss: () => this.requestOpen(false),
33
- placement: 'bottom-start',
34
- surface: () => this.renderRoot?.querySelector('[part="surface"]'),
35
- });
36
25
  this.settled = '';
37
26
  this.handleInput = (event) => {
38
27
  event.stopPropagation();
@@ -50,25 +39,9 @@ let HelixHeadlessColorInput = class HelixHeadlessColorInput extends MixinControl
50
39
  this.handleSampleEnd = () => {
51
40
  this.requestOpen(true);
52
41
  };
53
- this.handleKeydown = (event) => {
54
- if (event.key === 'Escape' && this.open) {
55
- event.stopPropagation();
56
- this.requestOpen(false);
57
- this.control?.focus();
58
- }
59
- };
60
42
  }
61
43
  get anchor() {
62
- if (typeof this.renderRoot?.querySelector !== 'function') {
63
- return null;
64
- }
65
- return this.control ?? this.renderRoot.querySelector('[part="trigger"]');
66
- }
67
- get control() {
68
- if (typeof this.renderRoot?.querySelector !== 'function') {
69
- return null;
70
- }
71
- return this.renderRoot.querySelector('[part="control"]');
44
+ return this.control ?? this.partNamed('trigger');
72
45
  }
73
46
  get picked() {
74
47
  return parseColor(this.value) ? this.value : this.settled;
@@ -77,13 +50,6 @@ let HelixHeadlessColorInput = class HelixHeadlessColorInput extends MixinControl
77
50
  const parsed = parseColor(this.value);
78
51
  return parsed ? toCss(parsed) : 'transparent';
79
52
  }
80
- restore() {
81
- const control = this.control;
82
- if (control && control.value !== this.value) {
83
- control.value = this.value;
84
- }
85
- this.sync();
86
- }
87
53
  sync() {
88
54
  this.internals.setFormValue(this.value);
89
55
  const anchor = this.anchor;
@@ -102,25 +68,11 @@ let HelixHeadlessColorInput = class HelixHeadlessColorInput extends MixinControl
102
68
  }
103
69
  this.refreshFeedback();
104
70
  }
105
- firstUpdated(changed) {
106
- super.firstUpdated(changed);
107
- this.sync();
108
- }
109
71
  updated(changed) {
110
72
  super.updated(changed);
111
- this.sync();
112
73
  if (changed.has('value') && parseColor(this.value)) {
113
74
  this.settled = this.value;
114
75
  }
115
- if (this.open) {
116
- this.panel.show();
117
- }
118
- else {
119
- this.panel.hide();
120
- }
121
- }
122
- focus(options) {
123
- this.anchor?.focus(options);
124
76
  }
125
77
  renderPicker() {
126
78
  return html `<${this.tag('color-picker')}
@@ -77,7 +77,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
77
77
  const grid = event.currentTarget;
78
78
  if (event.type === 'pointerdown') {
79
79
  capturePointer(grid, event.pointerId);
80
- this.renderRoot?.querySelector('[part="grid-saturation"]')?.focus();
80
+ this.partNamed('grid-saturation')?.focus();
81
81
  }
82
82
  else if (!grid.hasPointerCapture(event.pointerId)) {
83
83
  return;
@@ -120,7 +120,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
120
120
  }
121
121
  }
122
122
  focus(options) {
123
- this.renderRoot?.querySelector('[part="grid-saturation"]')?.focus(options);
123
+ this.partNamed('grid-saturation')?.focus(options);
124
124
  }
125
125
  renderChannel(channel, label, max, value) {
126
126
  return html `<${this.tag('slider')}
@@ -18,7 +18,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
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 = 'off';
24
24
  this.creatable = false;
@@ -34,15 +34,16 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
34
34
  this.typed = '';
35
35
  this.suggestion = '';
36
36
  this.panel = new PopoverController(this, {
37
- anchor: () => this.renderRoot?.querySelector('[part="field"]'),
37
+ anchor: () => this.partNamed('field'),
38
38
  boundary: () => [
39
- this.renderRoot?.querySelector('[part="surface"]'),
40
- this.renderRoot?.querySelector('[part="field"]'),
39
+ this.partNamed('surface'),
40
+ this.partNamed('field'),
41
41
  ],
42
42
  offset: () => 4,
43
43
  onDismiss: () => this.close(),
44
+ open: () => this.open,
44
45
  placement: 'bottom-start',
45
- surface: () => this.renderRoot?.querySelector('[part="surface"]'),
46
+ surface: () => this.partNamed('surface'),
46
47
  });
47
48
  this.handleInput = (event) => {
48
49
  event.stopPropagation();
@@ -109,7 +110,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
109
110
  .some((node) => node instanceof HTMLElement && node.localName === 'helix-tag')) {
110
111
  return;
111
112
  }
112
- this.renderRoot.querySelector('[part="control"]')?.focus();
113
+ this.partNamed('control')?.focus();
113
114
  if (!this.open) {
114
115
  this.requestOpen(true);
115
116
  }
@@ -133,7 +134,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
133
134
  return this.multiple ? 'select-multiple' : 'select-one';
134
135
  }
135
136
  get listbox() {
136
- return this.renderRoot?.querySelector('[part="listbox"]') ?? null;
137
+ return this.partNamed('listbox');
137
138
  }
138
139
  get values() {
139
140
  return this.value === '' ? [] : this.value.split(',');
@@ -195,7 +196,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
195
196
  return;
196
197
  }
197
198
  this.requestOpen(false);
198
- this.renderRoot.querySelector('[part="control"]')?.focus();
199
+ this.partNamed('control')?.focus();
199
200
  }
200
201
  completionFor(typed) {
201
202
  if (typed === '') {
@@ -226,7 +227,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
226
227
  updated(changed) {
227
228
  super.updated?.(changed);
228
229
  this.sync();
229
- const control = this.renderRoot.querySelector('[part="control"]');
230
+ const control = this.partNamed('control');
230
231
  const wanted = this.multiple || this.open ? this.suggestion || this.typed : this.word;
231
232
  if (control && control.value !== wanted) {
232
233
  control.value = wanted;
@@ -234,11 +235,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
234
235
  if (control && this.suggestion !== '') {
235
236
  control.setSelectionRange(this.typed.length, this.suggestion.length);
236
237
  }
237
- if (this.open) {
238
- this.panel.show();
239
- }
240
- else {
241
- this.panel.hide();
238
+ if (!this.open) {
242
239
  this.typed = '';
243
240
  this.suggestion = '';
244
241
  this.listbox?.requestFilter('');
@@ -76,7 +76,7 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
76
76
  };
77
77
  }
78
78
  get listbox() {
79
- return this.renderRoot?.querySelector('[part="listbox"]') ?? null;
79
+ return this.partNamed('listbox');
80
80
  }
81
81
  get activeOption() {
82
82
  return this.querySelector('[data-active]');
@@ -111,8 +111,7 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
111
111
  return;
112
112
  }
113
113
  if (this.open) {
114
- this.renderRoot
115
- .querySelector('[part="listbox"]')
114
+ this.partNamed('listbox')
116
115
  ?.renderRoot?.querySelector('[part="search-field"]')
117
116
  ?.focus();
118
117
  }
@@ -42,7 +42,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
42
42
  this.dragOrder = undefined;
43
43
  this.warnedAboutKey = false;
44
44
  this.virtualize = new VirtualizeController(this, {
45
- container: () => this.windowed ? this.renderRoot.querySelector('[part="table"]') : null,
45
+ container: () => (this.windowed ? this.partNamed('table') : null),
46
46
  count: () => this.available,
47
47
  estimate: () => this.estimate,
48
48
  measured: () => this.windowed,