@fusion.dev/helix-headless 0.144.0 → 0.145.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 (155) hide show
  1. package/custom-elements.json +8312 -881
  2. package/dist/accordion/item/accordion-item.d.ts +48 -12
  3. package/dist/accordion/item/accordion-item.js +26 -5
  4. package/dist/accordion/root/accordion-root.d.ts +6 -0
  5. package/dist/accordion/root/accordion-root.js +7 -1
  6. package/dist/announcement/root/announcement-root.d.ts +21 -1
  7. package/dist/announcement/root/announcement-root.js +36 -1
  8. package/dist/app-header/root/app-header-root.d.ts +7 -0
  9. package/dist/app-header/root/app-header-root.js +5 -1
  10. package/dist/app-header/sectioning.d.ts +7 -0
  11. package/dist/app-header/sectioning.js +26 -0
  12. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +32 -1
  13. package/dist/breadcrumbs/root/breadcrumbs-root.js +25 -2
  14. package/dist/canvas/root/canvas-root.d.ts +38 -3
  15. package/dist/canvas/root/canvas-root.js +121 -40
  16. package/dist/carousel/root/carousel-root.d.ts +90 -0
  17. package/dist/carousel/root/carousel-root.js +134 -12
  18. package/dist/chat/composer/chat-composer.d.ts +38 -6
  19. package/dist/chat/composer/chat-composer.js +42 -9
  20. package/dist/chat/message/chat-message.d.ts +32 -1
  21. package/dist/chat/message/chat-message.js +25 -9
  22. package/dist/chat/naming.d.ts +22 -0
  23. package/dist/chat/naming.js +12 -0
  24. package/dist/chat/relative-clock.d.ts +39 -0
  25. package/dist/chat/relative-clock.js +31 -0
  26. package/dist/chat/root/chat-root.d.ts +36 -0
  27. package/dist/chat/root/chat-root.js +24 -1
  28. package/dist/code-block/root/code-block-root.d.ts +8 -1
  29. package/dist/code-block/root/code-block-root.js +5 -5
  30. package/dist/copy-button/root/copy-button-root.d.ts +21 -1
  31. package/dist/copy-button/root/copy-button-root.js +2 -2
  32. package/dist/data-table/root/data-table-root.d.ts +150 -2
  33. package/dist/data-table/root/data-table-root.js +303 -29
  34. package/dist/data-table/tree.d.ts +40 -0
  35. package/dist/data-table/tree.js +55 -0
  36. package/dist/description-list/item/description-list-item.d.ts +17 -2
  37. package/dist/description-list/item/description-list-item.js +15 -2
  38. package/dist/description-list/root/description-list-root.d.ts +27 -1
  39. package/dist/description-list/root/description-list-root.js +26 -1
  40. package/dist/drilldown/item/drilldown-item.d.ts +11 -6
  41. package/dist/drilldown/item/drilldown-item.js +8 -5
  42. package/dist/drilldown/root/drilldown-root.d.ts +11 -0
  43. package/dist/drilldown/root/drilldown-root.js +8 -3
  44. package/dist/element-events.generated.js +53 -8
  45. package/dist/filter-input/menu/filter-menu.d.ts +3 -0
  46. package/dist/filter-input/menu/filter-menu.js +8 -4
  47. package/dist/image/root/image-root.js +9 -1
  48. package/dist/input/root/input-root.d.ts +1 -1
  49. package/dist/input/root/input-root.js +2 -2
  50. package/dist/list/item/list-item.d.ts +68 -1
  51. package/dist/list/item/list-item.js +66 -4
  52. package/dist/list/numbering.d.ts +14 -0
  53. package/dist/list/numbering.js +8 -0
  54. package/dist/list/root/list-root.d.ts +106 -9
  55. package/dist/list/root/list-root.js +189 -4
  56. package/dist/list/selection.d.ts +19 -0
  57. package/dist/list/selection.js +21 -0
  58. package/dist/listbox/root/listbox-root.js +1 -2
  59. package/dist/map/root/map-root.d.ts +6 -2
  60. package/dist/map/root/map-root.js +5 -2
  61. package/dist/megamenu/root/megamenu-root.d.ts +21 -0
  62. package/dist/megamenu/root/megamenu-root.js +16 -2
  63. package/dist/menu/index.d.ts +1 -0
  64. package/dist/menu/index.js +1 -0
  65. package/dist/menu/item/menu-item.d.ts +38 -11
  66. package/dist/menu/item/menu-item.js +33 -1
  67. package/dist/menu/root/menu-root.d.ts +25 -1
  68. package/dist/menu/root/menu-root.js +63 -6
  69. package/dist/menu/section/menu-section.d.ts +55 -0
  70. package/dist/menu/section/menu-section.js +60 -0
  71. package/dist/navigation/root/navigation-root.d.ts +16 -1
  72. package/dist/navigation/root/navigation-root.js +27 -1
  73. package/dist/notifications/root/notifications-root.d.ts +18 -0
  74. package/dist/notifications/root/notifications-root.js +17 -1
  75. package/dist/object-inspector/root/object-inspector-root.d.ts +97 -16
  76. package/dist/object-inspector/root/object-inspector-root.js +179 -79
  77. package/dist/object-inspector/rows.d.ts +57 -0
  78. package/dist/object-inspector/rows.js +86 -0
  79. package/dist/outline/levels.d.ts +31 -0
  80. package/dist/outline/levels.js +23 -0
  81. package/dist/outline/root/outline-root.d.ts +68 -3
  82. package/dist/outline/root/outline-root.js +73 -8
  83. package/dist/pagination/size/pagination-size.d.ts +11 -2
  84. package/dist/pagination/size/pagination-size.js +19 -4
  85. package/dist/radio/group/radio-group.d.ts +4 -10
  86. package/dist/radio/group/radio-group.js +5 -10
  87. package/dist/rating/root/rating-root.d.ts +1 -1
  88. package/dist/rating/root/rating-root.js +2 -2
  89. package/dist/scroll-area/root/scroll-area-root.d.ts +113 -0
  90. package/dist/scroll-area/root/scroll-area-root.js +218 -3
  91. package/dist/scroll-area/scrollbar.d.ts +65 -0
  92. package/dist/scroll-area/scrollbar.js +24 -0
  93. package/dist/select/root/select-root.js +1 -1
  94. package/dist/splitter/root/splitter-root.d.ts +13 -0
  95. package/dist/splitter/root/splitter-root.js +20 -2
  96. package/dist/splitter/snap.d.ts +9 -0
  97. package/dist/splitter/snap.js +12 -0
  98. package/dist/steps/reach.d.ts +15 -0
  99. package/dist/steps/reach.js +12 -0
  100. package/dist/steps/root/steps-root.d.ts +30 -0
  101. package/dist/steps/root/steps-root.js +53 -0
  102. package/dist/steps/step/step.d.ts +38 -2
  103. package/dist/steps/step/step.js +28 -6
  104. package/dist/table/column/table-column.d.ts +11 -0
  105. package/dist/table/column/table-column.js +6 -1
  106. package/dist/table/root/table-root.d.ts +101 -0
  107. package/dist/table/root/table-root.js +166 -5
  108. package/dist/table/row/table-row.d.ts +47 -1
  109. package/dist/table/row/table-row.js +39 -2
  110. package/dist/tabs/panel/tab-panel.d.ts +9 -5
  111. package/dist/tabs/panel/tab-panel.js +3 -4
  112. package/dist/tabs/root/tabs-root.d.ts +25 -1
  113. package/dist/tabs/root/tabs-root.js +21 -3
  114. package/dist/tabs/tab/tab.d.ts +36 -4
  115. package/dist/tabs/tab/tab.js +46 -3
  116. package/dist/timeline/group/timeline-group.d.ts +46 -0
  117. package/dist/timeline/group/timeline-group.js +37 -0
  118. package/dist/timeline/index.d.ts +1 -0
  119. package/dist/timeline/index.js +1 -0
  120. package/dist/timeline/root/timeline-root.d.ts +10 -3
  121. package/dist/toast/item/toast-item.d.ts +10 -17
  122. package/dist/toast/item/toast-item.js +15 -36
  123. package/dist/toolbar/root/toolbar-root.d.ts +13 -0
  124. package/dist/toolbar/root/toolbar-root.js +15 -5
  125. package/dist/tree/item/tree-item.d.ts +54 -1
  126. package/dist/tree/item/tree-item.js +89 -20
  127. package/dist/tree/root/tree-root.d.ts +37 -0
  128. package/dist/tree/root/tree-root.js +51 -6
  129. package/dist/tree/selection.d.ts +24 -0
  130. package/dist/tree/selection.js +26 -0
  131. package/dist/video/media-events.d.ts +49 -0
  132. package/dist/video/media-events.js +39 -0
  133. package/dist/video/root/video-root.d.ts +99 -13
  134. package/dist/video/root/video-root.js +84 -47
  135. package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -0
  136. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  137. package/dist/video-playlist/order.d.ts +41 -0
  138. package/dist/video-playlist/order.js +34 -0
  139. package/dist/video-playlist/root/video-playlist-root.d.ts +70 -2
  140. package/dist/video-playlist/root/video-playlist-root.js +60 -16
  141. package/dist/virtual-list/controls.d.ts +4 -0
  142. package/dist/virtual-list/controls.js +30 -0
  143. package/dist/virtual-list/fill.d.ts +30 -0
  144. package/dist/virtual-list/fill.js +39 -0
  145. package/dist/virtual-list/keys.d.ts +15 -0
  146. package/dist/virtual-list/keys.js +24 -0
  147. package/dist/virtual-list/root/virtual-list-root.d.ts +155 -23
  148. package/dist/virtual-list/root/virtual-list-root.js +175 -34
  149. package/lint.json +1 -1
  150. package/package.json +5 -5
  151. package/types/jsx.d.ts +3778 -801
  152. package/types/svelte.d.ts +2 -0
  153. package/types/vue.d.ts +782 -215
  154. package/dist/listbox/range.d.ts +0 -17
  155. package/dist/listbox/range.js +0 -5
@@ -0,0 +1,55 @@
1
+ import { at } from '../table/column/table-column.js';
2
+ import { filter, parse, sort } from './root/order.js';
3
+ export const childrenOf = (row, field) => {
4
+ const value = at(row, field);
5
+ return Array.isArray(value) ? value : [];
6
+ };
7
+ export const grow = (rows, axes, { field, isOpen, query, spec, }) => {
8
+ const searching = query.trim() !== '';
9
+ const order = parse(spec);
10
+ const every = [];
11
+ const kept = new Set();
12
+ const seen = new Set();
13
+ const keep = (row) => {
14
+ if (seen.has(row)) {
15
+ return kept.has(row);
16
+ }
17
+ seen.add(row);
18
+ every.push(row);
19
+ let below = false;
20
+ for (const child of childrenOf(row, field)) {
21
+ below = keep(child) || below;
22
+ }
23
+ const stays = below || !searching || filter([row], query, axes).length > 0;
24
+ if (stays) {
25
+ kept.add(row);
26
+ }
27
+ return stays;
28
+ };
29
+ for (const row of rows) {
30
+ keep(row);
31
+ }
32
+ const placed = new Map();
33
+ const shown = [];
34
+ const lay = (level, depth, parent) => {
35
+ const siblings = sort(level.filter((row) => kept.has(row) && !placed.has(row)), order, axes);
36
+ for (const [index, row] of siblings.entries()) {
37
+ const children = childrenOf(row, field).filter((child) => kept.has(child));
38
+ const open = children.length > 0 && (searching || isOpen(row));
39
+ placed.set(row, {
40
+ branch: children.length > 0,
41
+ level: depth,
42
+ open,
43
+ parent,
44
+ position: index + 1,
45
+ siblings: siblings.length,
46
+ });
47
+ shown.push(row);
48
+ if (open) {
49
+ lay(children, depth + 1, row);
50
+ }
51
+ }
52
+ };
53
+ lay(rows, 1, undefined);
54
+ return { every, placed, rows: shown, total: every.length };
55
+ };
@@ -1,5 +1,5 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
- import { type TemplateResult } from 'lit';
2
+ import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * @summary One label and its value.
5
5
  *
@@ -12,11 +12,17 @@ import { type TemplateResult } from 'lit';
12
12
  * such as a tag, an avatar, or a link. The label is almost always text, so it
13
13
  * is also a property.
14
14
  *
15
+ * **For a label with several values, write each value as a `<dd>`**, such as
16
+ * the authors of a paper. The item then puts your `<dd>` elements after its
17
+ * `<dt>` in place of its own, so a screen reader hears one term and each of
18
+ * its descriptions, as in a native `<dl>`. A server cannot see your elements,
19
+ * so the served item wraps them in its own `<dd>` until it runs.
20
+ *
15
21
  * @since 0.1.0
16
22
  * @status stable
17
23
  * @documentation https://fusion.dev/helix/components/description-list
18
24
  *
19
- * @slot - The value.
25
+ * @slot - The value, or several `<dd>` elements for several values.
20
26
  * @slot label - The label, when it is more than text. Falls back to `label`.
21
27
  *
22
28
  * @csspart label - The `<dt>`, holding the label.
@@ -31,7 +37,16 @@ export declare class HelixHeadlessDescriptionListItem extends HelixElement {
31
37
  * that varies and is often more than text.
32
38
  */
33
39
  label: string;
40
+ /**
41
+ * Whether the values are the consumer's own `<dd>` elements, which the item
42
+ * then does not wrap. Unknown on a server and while hydrating, where the
43
+ * item draws its own.
44
+ */
45
+ private get several();
46
+ protected firstUpdated(changed: PropertyValues): void;
34
47
  protected render(): TemplateResult;
48
+ /** A value arriving or leaving can change whether the item wraps them. */
49
+ private readonly handleSlotChange;
35
50
  }
36
51
  declare global {
37
52
  interface HTMLElementTagNameMap {
@@ -11,10 +11,23 @@ let HelixHeadlessDescriptionListItem = class HelixHeadlessDescriptionListItem ex
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.label = '';
14
+ this.handleSlotChange = () => {
15
+ this.requestUpdate();
16
+ };
17
+ }
18
+ get several() {
19
+ return this.knownChildren.some((child) => child.localName === 'dd');
20
+ }
21
+ firstUpdated(changed) {
22
+ super.firstUpdated(changed);
23
+ this.finishHydration();
14
24
  }
15
25
  render() {
16
- return html `<dt part="label"><slot name="label">${this.label}</slot></dt>
17
- <dd part="value"><slot></slot></dd>`;
26
+ const label = html `<dt part="label"><slot name="label">${this.label}</slot></dt>`;
27
+ return this.several
28
+ ? html `${label}<slot @slotchange=${this.handleSlotChange}></slot>`
29
+ : html `${label}
30
+ <dd part="value"><slot @slotchange=${this.handleSlotChange}></slot></dd>`;
18
31
  }
19
32
  };
20
33
  __decorate([
@@ -1,5 +1,5 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
- import { type TemplateResult } from 'lit';
2
+ import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * @summary A list of labels and their values, such as a details panel.
5
5
  *
@@ -15,6 +15,11 @@ import { type TemplateResult } from 'lit';
15
15
  * matches a plain `<dl>` with the same content node for node, both with a
16
16
  * slotted label and with an item set to `display: contents`.
17
17
  *
18
+ * **A named list is a named group.** A `<dl>` takes no name in ARIA 1.2, so
19
+ * the list becomes a group around it when it has `label`, `aria-label` or
20
+ * `aria-labelledby`, and the name lands on the group. Unnamed, the host adds
21
+ * nothing to the tree.
22
+ *
18
23
  * @since 0.1.0
19
24
  * @status stable
20
25
  * @category data
@@ -42,6 +47,27 @@ export declare class HelixHeadlessDescriptionList extends HelixElement {
42
47
  * and React reports that as a hydration mismatch.
43
48
  */
44
49
  direction?: 'row';
50
+ /**
51
+ * The list's name, read by screen readers, such as "Server details". It
52
+ * makes the list a named group.
53
+ */
54
+ label: string;
55
+ /**
56
+ * Hears a name written on the element, which no property mirrors.
57
+ *
58
+ * Lit observes the attributes of its own properties only, and `ariaLabel`
59
+ * and `ariaLabelledByElements` belong to the platform.
60
+ */
61
+ static get observedAttributes(): string[];
62
+ /**
63
+ * Called by the browser when an observed attribute changes. A name written
64
+ * or taken off asks for an update, because it decides whether the list is a
65
+ * group.
66
+ */
67
+ attributeChangedCallback(name: string, old: string | null, value: string | null): void;
68
+ /** Whether anything names the list. */
69
+ private get named();
70
+ protected willUpdate(changed: PropertyValues): void;
45
71
  protected render(): TemplateResult;
46
72
  }
47
73
  declare global {
@@ -4,10 +4,32 @@ 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, hostAria } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessDescriptionList = class HelixHeadlessDescriptionList extends HelixElement {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.label = '';
14
+ }
15
+ static get observedAttributes() {
16
+ return [...super.observedAttributes, 'aria-label', 'aria-labelledby'];
17
+ }
18
+ attributeChangedCallback(name, old, value) {
19
+ super.attributeChangedCallback(name, old, value);
20
+ if (name === 'aria-label' || name === 'aria-labelledby') {
21
+ this.requestUpdate();
22
+ }
23
+ }
24
+ get named() {
25
+ return (this.label !== '' ||
26
+ Boolean(this.getAttribute?.('aria-label')) ||
27
+ Boolean(this.getAttribute?.('aria-labelledby')));
28
+ }
29
+ willUpdate(changed) {
30
+ super.willUpdate(changed);
31
+ hostAria(this.internals, { label: this.label, role: this.named ? 'group' : '' });
32
+ }
11
33
  render() {
12
34
  return html `<dl part="list"><slot></slot></dl>`;
13
35
  }
@@ -15,6 +37,9 @@ let HelixHeadlessDescriptionList = class HelixHeadlessDescriptionList extends He
15
37
  __decorate([
16
38
  property({ reflect: true })
17
39
  ], HelixHeadlessDescriptionList.prototype, "direction", void 0);
40
+ __decorate([
41
+ property()
42
+ ], HelixHeadlessDescriptionList.prototype, "label", void 0);
18
43
  HelixHeadlessDescriptionList = __decorate([
19
44
  customElement('helix-headless-description-list')
20
45
  ], HelixHeadlessDescriptionList);
@@ -29,6 +29,7 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
29
29
  * @documentation https://fusion.dev/helix/components/drilldown
30
30
  *
31
31
  * @slot - The row's label.
32
+ * @slot chevron - Replaces the caret of a row that opens a page, such as with your own icon.
32
33
  * @slot end - Content after the label, usually an icon or a count.
33
34
  * @slot page - The rows of the page this row opens.
34
35
  * @slot start - Content before the label, usually an icon.
@@ -65,15 +66,14 @@ export declare class HelixHeadlessDrilldownItem extends HelixElement {
65
66
  */
66
67
  readonly isDrilldownItem = true;
67
68
  /**
68
- * Whether the page showing is the one this row is on.
69
- *
70
- * Written by the stack.
69
+ * @internal Whether the page showing is the one this row is on. Written by
70
+ * the stack, and not a consumer's to set: a framework reading `onPage` as an
71
+ * event handler is why it is kept off the typings.
71
72
  */
72
73
  onPage: boolean;
73
74
  /**
74
- * Whether this row's own page is the one showing.
75
- *
76
- * Written by the stack.
75
+ * @internal Whether this row's own page is the one showing. Written by the
76
+ * stack.
77
77
  */
78
78
  showingPage: boolean;
79
79
  /**
@@ -134,6 +134,11 @@ export declare class HelixHeadlessDrilldownItem extends HelixElement {
134
134
  get selected(): boolean;
135
135
  /** Fires `helix-select` for this row. */
136
136
  select(): void;
137
+ /**
138
+ * The caret of a row that opens a page, as the `chevron` slot's fallback, so
139
+ * an icon of your own replaces it by being slotted.
140
+ */
141
+ protected renderMark(): TemplateResult;
137
142
  protected render(): TemplateResult | typeof nothing;
138
143
  }
139
144
  declare global {
@@ -50,6 +50,13 @@ let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixE
50
50
  }
51
51
  this.dispatchEvent(new HelixSelectEvent({ value: this.value }));
52
52
  }
53
+ renderMark() {
54
+ return html `<slot name="chevron"
55
+ ><svg part="mark" data-mirror fill="currentColor" viewBox=${caretRight.viewBox} aria-hidden="true">
56
+ ${unsafeSVG(caretRight.content)}
57
+ </svg></slot
58
+ >`;
59
+ }
53
60
  render() {
54
61
  if (this.showingPage) {
55
62
  return html `<slot name="page" @slotchange=${this.readPage}></slot>`;
@@ -69,11 +76,7 @@ let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixE
69
76
  <slot name="start"></slot>
70
77
  <span part="label"><slot></slot></span>
71
78
  <slot name="end"></slot>
72
- ${this.page
73
- ? html `<svg part="mark" data-mirror fill="currentColor" viewBox=${caretRight.viewBox} aria-hidden="true">
74
- ${unsafeSVG(caretRight.content)}
75
- </svg>`
76
- : nothing}
79
+ ${this.page ? this.renderMark() : nothing}
77
80
  </button>
78
81
  <div hidden><slot name="page" @slotchange=${this.readPage}></slot></div>`;
79
82
  }
@@ -20,6 +20,11 @@ declare const HelixHeadlessDrilldown_base: (new (...args: any[]) => import("@fus
20
20
  * and reads the rest from the DOM. The behavior lives in `DrilldownController`,
21
21
  * which `Menu` shares.
22
22
  *
23
+ * After each move the host carries `data-direction`, `forward` into a page or
24
+ * `back` out of one, so a stylesheet can slide the new page in from the side
25
+ * the reader is going. The styled tier does, and holds still under reduced
26
+ * motion.
27
+ *
23
28
  * The rows of the page showing are a `menu`, named by `label` on the top page
24
29
  * and by the page's title below it. The back button and the title sit above
25
30
  * the menu. Disabled rows stay reachable with the arrow keys, and typing a
@@ -32,6 +37,7 @@ declare const HelixHeadlessDrilldown_base: (new (...args: any[]) => import("@fus
32
37
  * @documentation https://fusion.dev/helix/components/drilldown
33
38
  *
34
39
  * @slot - The rows of the top page.
40
+ * @slot back-icon - Replaces the icon on the back button.
35
41
  *
36
42
  * @event {HelixSelectEvent} helix-select - Fired when a row with no page is chosen. Cancelable: canceling it stops the change and leaves the decision to you.
37
43
  *
@@ -135,6 +141,11 @@ export declare class HelixHeadlessDrilldown extends HelixHeadlessDrilldown_base
135
141
  protected updated(changed: PropertyValues): void;
136
142
  /** Walks the tree marking which rows are on the page showing. */
137
143
  private mark;
144
+ /**
145
+ * The back button's icon, as the `back-icon` slot's fallback, so an icon of
146
+ * your own replaces it by being slotted.
147
+ */
148
+ protected renderBackIcon(): TemplateResult;
138
149
  protected render(): TemplateResult;
139
150
  }
140
151
  declare global {
@@ -150,14 +150,19 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
150
150
  }
151
151
  }
152
152
  }
153
+ renderBackIcon() {
154
+ return html `<slot name="back-icon"
155
+ ><svg part="back-mark" data-mirror fill="currentColor" viewBox=${caretLeft.viewBox} aria-hidden="true">
156
+ ${unsafeSVG(caretLeft.content)}
157
+ </svg></slot
158
+ >`;
159
+ }
153
160
  render() {
154
161
  return html `${this.drilldown.atRoot
155
162
  ? ''
156
163
  : html `<div part="header">
157
164
  <button part="back" type="button" aria-label=${this.backLabel} @click=${() => this.back()}>
158
- <svg part="back-mark" data-mirror fill="currentColor" viewBox=${caretLeft.viewBox} aria-hidden="true">
159
- ${unsafeSVG(caretLeft.content)}
160
- </svg>
165
+ ${this.renderBackIcon()}
161
166
  </button>
162
167
  <span id="title" part="title">${this.drilldown.label}</span>
163
168
  </div>`}
@@ -22,7 +22,7 @@ registerElementEvents({
22
22
  ],
23
23
  'helix-headless-carousel': ['helix-activate', 'helix-select'],
24
24
  'helix-headless-chat': ['helix-before-pin', 'helix-history', 'helix-pin', 'helix-send'],
25
- 'helix-headless-chat-composer': ['helix-send', 'input'],
25
+ 'helix-headless-chat-composer': ['helix-send', 'helix-typing', 'input'],
26
26
  'helix-headless-chat-message': ['helix-send'],
27
27
  'helix-headless-checkbox': ['change', 'helix-before-toggle', 'input'],
28
28
  'helix-headless-checkbox-group': ['change', 'helix-before-change', 'input'],
@@ -62,8 +62,10 @@ registerElementEvents({
62
62
  'helix-headless-copy-button': ['helix-copy', 'helix-copy-error'],
63
63
  'helix-headless-data-table': [
64
64
  'helix-activate',
65
+ 'helix-expand',
65
66
  'helix-filter',
66
67
  'helix-filtered',
68
+ 'helix-load-more',
67
69
  'helix-navigate',
68
70
  'helix-reorder',
69
71
  'helix-reordered',
@@ -124,6 +126,8 @@ registerElementEvents({
124
126
  'helix-before-open',
125
127
  'toggle',
126
128
  ],
129
+ 'helix-headless-list': ['change', 'helix-action', 'helix-before-change'],
130
+ 'helix-headless-list-item': ['helix-action'],
127
131
  'helix-headless-listbox': ['change', 'helix-before-change', 'helix-filter', 'input'],
128
132
  'helix-headless-map': [
129
133
  'change',
@@ -137,6 +141,7 @@ registerElementEvents({
137
141
  'helix-map-rotate',
138
142
  'helix-map-tilt',
139
143
  'helix-map-zoom',
144
+ 'helix-toggle',
140
145
  ],
141
146
  'helix-headless-map-area': ['helix-map-part-change'],
142
147
  'helix-headless-map-buildings': ['helix-map-part-change'],
@@ -153,9 +158,15 @@ registerElementEvents({
153
158
  'helix-headless-menu-item': ['helix-before-toggle', 'helix-select'],
154
159
  'helix-headless-notification': ['helix-dismiss', 'helix-read'],
155
160
  'helix-headless-notification-group': ['helix-before-close', 'helix-before-open', 'toggle'],
156
- 'helix-headless-notifications': ['helix-dismiss', 'helix-read'],
161
+ 'helix-headless-notifications': ['helix-dismiss', 'helix-load-more', 'helix-read'],
157
162
  'helix-headless-number-input': ['change', 'helix-before-change', 'input'],
158
- 'helix-headless-object-inspector': ['helix-copy'],
163
+ 'helix-headless-object-inspector': [
164
+ 'helix-activate',
165
+ 'helix-before-activate',
166
+ 'helix-copy',
167
+ 'helix-filter',
168
+ 'helix-filtered',
169
+ ],
159
170
  'helix-headless-option': ['helix-select'],
160
171
  'helix-headless-outline': ['helix-activate'],
161
172
  'helix-headless-pagination': ['helix-activate', 'helix-navigate'],
@@ -166,6 +177,7 @@ registerElementEvents({
166
177
  'helix-headless-radio': ['helix-select'],
167
178
  'helix-headless-radio-group': ['change', 'helix-before-change', 'helix-select', 'input'],
168
179
  'helix-headless-rating': ['change', 'helix-before-change', 'helix-clear', 'input'],
180
+ 'helix-headless-scroll-area': ['scroll', 'scrollend'],
169
181
  'helix-headless-select': [
170
182
  'change',
171
183
  'helix-before-change',
@@ -193,8 +205,8 @@ registerElementEvents({
193
205
  'helix-headless-steps': ['helix-activate', 'helix-before-activate', 'helix-select'],
194
206
  'helix-headless-switch': ['change', 'helix-before-toggle', 'input'],
195
207
  'helix-headless-tab': ['helix-select'],
196
- 'helix-headless-table': ['helix-sort'],
197
- 'helix-headless-table-column': ['helix-before-resize'],
208
+ 'helix-headless-table': ['helix-select', 'helix-selected', 'helix-sort', 'helix-sorted'],
209
+ 'helix-headless-table-column': ['helix-before-resize', 'helix-resized'],
198
210
  'helix-headless-tabs': ['helix-activate', 'helix-before-activate', 'helix-select'],
199
211
  'helix-headless-tag': ['helix-dismiss'],
200
212
  'helix-headless-tag-group': ['change', 'helix-before-change', 'helix-dismiss'],
@@ -224,9 +236,42 @@ registerElementEvents({
224
236
  ],
225
237
  'helix-headless-tour-stop': ['helix-tour-ask'],
226
238
  'helix-headless-tree': ['helix-activate', 'helix-before-activate', 'helix-select'],
227
- 'helix-headless-tree-item': ['helix-before-close', 'helix-before-open', 'helix-select', 'toggle'],
228
- 'helix-headless-video': ['ended', 'helix-next', 'helix-previous', 'pause', 'play'],
229
- 'helix-headless-video-playlist': ['helix-activate', 'helix-before-activate'],
239
+ 'helix-headless-tree-item': [
240
+ 'helix-before-close',
241
+ 'helix-before-open',
242
+ 'helix-load',
243
+ 'helix-select',
244
+ 'toggle',
245
+ ],
246
+ 'helix-headless-video': [
247
+ 'abort',
248
+ 'canplay',
249
+ 'canplaythrough',
250
+ 'durationchange',
251
+ 'emptied',
252
+ 'ended',
253
+ 'error',
254
+ 'helix-next',
255
+ 'helix-previous',
256
+ 'loadeddata',
257
+ 'loadedmetadata',
258
+ 'loadstart',
259
+ 'pause',
260
+ 'play',
261
+ 'playing',
262
+ 'progress',
263
+ 'ratechange',
264
+ 'resize',
265
+ 'seeked',
266
+ 'seeking',
267
+ 'stalled',
268
+ 'suspend',
269
+ 'timeupdate',
270
+ 'volumechange',
271
+ 'waiting',
272
+ ],
273
+ 'helix-headless-video-playlist': ['helix-activate', 'helix-before-activate', 'helix-end'],
230
274
  'helix-headless-video-playlist-item': ['helix-select'],
231
275
  'helix-headless-video-playlist-menu': ['helix-select'],
276
+ 'helix-headless-virtual-list': ['helix-load-more', 'helix-range-change'],
232
277
  });
@@ -66,8 +66,11 @@ import '../../menu/index.js';
66
66
  * @csspart divider-content - The content set into the line.
67
67
  * @csspart divider-line - The line itself.
68
68
  * @csspart entry - One row: a field, its comparison, an operator, or a value.
69
+ * @csspart entry-end - The trailing column of a row, which holds its shortcut.
69
70
  * @csspart entry-item - The row itself.
70
71
  * @csspart entry-mark - The mark at the end of a row: a check mark on a chosen value, and a caret on a field.
72
+ * @csspart entry-shortcut - The key drawn for a row's shortcut. The filter menu sets none, so it renders only where a subclass does.
73
+ * @csspart entry-shortcut-kbd - The key inside it.
71
74
  * @csspart entry-start - The leading column.
72
75
  * @csspart menu - The menu the fields are offered in.
73
76
  * @csspart menu-back - The button that goes back from a field's page.
@@ -137,7 +137,8 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
137
137
  const offered = using?.arity === 'none' ? [] : values;
138
138
  return html `<${item}
139
139
  part="entry"
140
- exportparts="item: entry-item, mark: entry-mark, start: entry-start"
140
+ exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
141
+ shortcut-kbd: entry-shortcut-kbd, start: entry-start"
141
142
  data-facet="field"
142
143
  data-field=${name}
143
144
  >
@@ -145,7 +146,8 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
145
146
  ${operators.length > 1
146
147
  ? html `<${item}
147
148
  part="entry"
148
- exportparts="item: entry-item, mark: entry-mark, start: entry-start"
149
+ exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
150
+ shortcut-kbd: entry-shortcut-kbd, start: entry-start"
149
151
  slot="submenu"
150
152
  data-facet="comparison"
151
153
  data-field=${name}
@@ -153,7 +155,8 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
153
155
  ${using?.label ?? ''}
154
156
  ${operators.map((operator) => html `<${item}
155
157
  part="entry"
156
- exportparts="item: entry-item, mark: entry-mark, start: entry-start"
158
+ exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
159
+ shortcut-kbd: entry-shortcut-kbd, start: entry-start"
157
160
  slot="submenu"
158
161
  checkable
159
162
  exclusive
@@ -175,7 +178,8 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
175
178
  : nothing}
176
179
  ${offered.map((option) => html `<${item}
177
180
  part="entry"
178
- exportparts="item: entry-item, mark: entry-mark, start: entry-start"
181
+ exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
182
+ shortcut-kbd: entry-shortcut-kbd, start: entry-start"
179
183
  slot="submenu"
180
184
  checkable
181
185
  ?exclusive=${using?.arity === 'one'}
@@ -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, DelayController, HelixElement } from '@fusion.dev/helix-core';
7
+ import { customElement, DelayController, HelixElement, reportRuntime } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  let HelixHeadlessImage = class HelixHeadlessImage extends HelixElement {
@@ -100,6 +100,14 @@ let HelixHeadlessImage = class HelixHeadlessImage extends HelixElement {
100
100
  firstUpdated(changed) {
101
101
  super.firstUpdated?.(changed);
102
102
  this.settle();
103
+ if (this.alt === undefined && this.getAttribute('aria-hidden') !== 'true') {
104
+ reportRuntime({
105
+ kind: 'missing-alt',
106
+ message: `<${this.localName}> has no \`alt\`, so a screen reader announces its file name. ` +
107
+ 'Describe the picture in `alt`, or write `alt=""` for one that is decoration.',
108
+ tag: this.localName,
109
+ });
110
+ }
103
111
  }
104
112
  get image() {
105
113
  return this.renderRoot?.querySelector('img') ?? null;
@@ -36,7 +36,7 @@ declare const HelixHeadlessInput_base: (new (...args: any[]) => import("@fusion.
36
36
  *
37
37
  * @event {Event} change - Sent when the user finishes changing the value. Relayed from the native input.
38
38
  * @event {HelixBeforeChangeEvent} helix-before-change - The text is about to change. Cancelable: cancel it to keep the current text.
39
- * @event {Event} helix-clear - Fired after the clear button has emptied the field, after `input` and `change`.
39
+ * @event {HelixClearEvent} helix-clear - Fired after the clear button has emptied the field, after `input` and `change`.
40
40
  * @event {Event} input - Fired as the value changes.
41
41
  *
42
42
  * @csspart clear - The button that empties the field.
@@ -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, customElement, drawIcon, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, drawIcon, HelixClearEvent, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
8
8
  import { eye, eyeSlash, x } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -38,7 +38,7 @@ let HelixHeadlessInput = class HelixHeadlessInput extends MixinControllerValue(M
38
38
  this.handleClear = () => {
39
39
  if (this.value !== '' && this.requestValue('')) {
40
40
  this.announceChange();
41
- this.dispatchEvent(new Event('helix-clear', { bubbles: true, composed: true }));
41
+ this.dispatchEvent(new HelixClearEvent());
42
42
  }
43
43
  const control = this.control;
44
44
  if (control) {