@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
@@ -4,9 +4,12 @@ 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, elementById, HelixElement, searchableById, VirtualizeController, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
7
+ import { AriaForwardController, customElement, elementById, HelixElement, HelixLoadMoreEvent, HelixRangeChangeEvent, searchableById, VirtualizeController, } from '@fusion.dev/helix-core';
8
+ import { html, render } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
+ import { holdControls, releaseControls } from '../controls.js';
11
+ import { fillHoles } from '../fill.js';
12
+ import { stepIndex } from '../keys.js';
10
13
  let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixElement {
11
14
  constructor() {
12
15
  super(...arguments);
@@ -16,20 +19,71 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
16
19
  this.measured = false;
17
20
  this.rows = [];
18
21
  this.source = null;
22
+ this.active = 0;
23
+ this.said = { first: 0, last: 0 };
24
+ this.askedAt = -1;
19
25
  this.aria = new AriaForwardController(this, {
20
26
  label: true,
21
27
  target: () => this.partNamed('viewport'),
22
28
  });
23
29
  this.virtualize = new VirtualizeController(this, {
24
30
  container: () => this.partNamed('viewport'),
25
- count: () => this.rows.length,
31
+ count: () => this.data.length,
26
32
  estimate: () => this.estimate,
27
33
  measured: () => this.measured,
28
34
  rows: () => this.drawn(),
29
35
  });
36
+ this.handleKeydown = (event) => {
37
+ if (event.defaultPrevented ||
38
+ event.altKey ||
39
+ event.ctrlKey ||
40
+ event.metaKey ||
41
+ event.shiftKey) {
42
+ return;
43
+ }
44
+ const at = this.indexOf(event.target);
45
+ if (at === undefined) {
46
+ return;
47
+ }
48
+ const { first, last } = this.virtualize.visible();
49
+ const next = stepIndex(event.key, at, this.data.length, last - first - 1);
50
+ if (next === undefined) {
51
+ return;
52
+ }
53
+ event.preventDefault();
54
+ void this.focusIndex(next);
55
+ };
56
+ this.handleFocusOut = () => {
57
+ this.requestUpdate();
58
+ };
59
+ this.handleFocusIn = (event) => {
60
+ let node = event.target;
61
+ while (node && node.parentNode !== this) {
62
+ node = node.parentNode;
63
+ }
64
+ const index = this.indexOf(node);
65
+ if (index !== undefined) {
66
+ this.active = index;
67
+ }
68
+ this.requestUpdate();
69
+ };
70
+ }
71
+ get range() {
72
+ return this.virtualize.visible();
73
+ }
74
+ scrollToIndex(index, options = {}) {
75
+ const count = this.data.length;
76
+ if (count === 0 || !Number.isFinite(index)) {
77
+ return;
78
+ }
79
+ this.virtualize.reveal(Math.max(0, Math.min(Math.trunc(index), count - 1)), 0, options);
80
+ this.virtualize.measure();
81
+ }
82
+ get data() {
83
+ return this.items ?? this.rows;
30
84
  }
31
85
  drawn() {
32
- return [...this.querySelectorAll('[data-index]')].map((element) => ({
86
+ return [...this.querySelectorAll(':scope > [data-index]')].map((element) => ({
33
87
  element,
34
88
  index: Number(element.dataset.index),
35
89
  }));
@@ -43,6 +97,9 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
43
97
  }
44
98
  connectedCallback() {
45
99
  super.connectedCallback();
100
+ this.addEventListener('keydown', this.handleKeydown);
101
+ this.addEventListener('focusin', this.handleFocusIn);
102
+ this.addEventListener('focusout', this.handleFocusOut);
46
103
  if (typeof MutationObserver === 'undefined') {
47
104
  return;
48
105
  }
@@ -55,6 +112,9 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
55
112
  }
56
113
  disconnectedCallback() {
57
114
  super.disconnectedCallback();
115
+ this.removeEventListener('keydown', this.handleKeydown);
116
+ this.removeEventListener('focusin', this.handleFocusIn);
117
+ this.removeEventListener('focusout', this.handleFocusOut);
58
118
  this.watcher?.disconnect();
59
119
  this.watcher = undefined;
60
120
  }
@@ -76,6 +136,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
76
136
  super.updated?.(changed);
77
137
  this.virtualize.measure();
78
138
  this.place();
139
+ this.report();
79
140
  }
80
141
  read() {
81
142
  const source = this.for === '' ? null : elementById(this, this.for);
@@ -93,6 +154,47 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
93
154
  this.rows = [];
94
155
  }
95
156
  }
157
+ report() {
158
+ const { first, last } = this.virtualize.visible();
159
+ if (first !== this.said.first || last !== this.said.last) {
160
+ this.said = { first, last };
161
+ this.dispatchEvent(new HelixRangeChangeEvent({ first, last }));
162
+ }
163
+ const box = this.partNamed('viewport');
164
+ const count = this.data.length;
165
+ const screen = box?.clientHeight ?? 0;
166
+ if (count > 0 &&
167
+ screen > 0 &&
168
+ this.askedAt !== count &&
169
+ this.virtualize.remaining() <= screen) {
170
+ this.askedAt = count;
171
+ this.dispatchEvent(new HelixLoadMoreEvent());
172
+ }
173
+ }
174
+ indexOf(node) {
175
+ if (!(node instanceof HTMLElement) || node.parentElement !== this) {
176
+ return undefined;
177
+ }
178
+ const index = node.dataset.index;
179
+ return index === undefined ? undefined : Number(index);
180
+ }
181
+ rowFor(index) {
182
+ return this.querySelector(`:scope > [data-index="${index}"]`);
183
+ }
184
+ async focusIndex(index) {
185
+ this.active = index;
186
+ this.focusing = index;
187
+ this.virtualize.reveal(index);
188
+ this.virtualize.measure();
189
+ this.requestUpdate();
190
+ try {
191
+ await this.updateComplete;
192
+ }
193
+ finally {
194
+ this.focusing = undefined;
195
+ }
196
+ this.rowFor(index)?.focus();
197
+ }
96
198
  focusedIndex() {
97
199
  const root = this.getRootNode();
98
200
  let node = root.activeElement;
@@ -103,36 +205,51 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
103
205
  return index === undefined ? undefined : Number(index);
104
206
  }
105
207
  keptIndex() {
106
- const index = this.focusedIndex();
208
+ const index = this.focusing ?? this.focusedIndex();
107
209
  const { first, last } = this.virtualize.window;
108
- return index !== undefined && index < this.rows.length && (index < first || index >= last)
210
+ return index !== undefined && index < this.data.length && (index < first || index >= last)
109
211
  ? index
110
212
  : undefined;
111
213
  }
112
- fill(elements, row, at) {
113
- const total = String(this.rows.length);
214
+ stamp(element, at) {
215
+ element.setAttribute('role', 'listitem');
216
+ element.setAttribute('aria-posinset', String(at + 1));
217
+ element.setAttribute('aria-setsize', String(this.data.length));
218
+ element.setAttribute('data-index', String(at));
219
+ }
220
+ fill(elements, at) {
221
+ const item = this.data[at];
114
222
  for (const element of elements) {
115
- for (const hole of [
116
- ...(element.matches('[data-field]') ? [element] : []),
117
- ...element.querySelectorAll('[data-field]'),
118
- ]) {
119
- hole.textContent = row[hole.dataset.field ?? ''] ?? '';
223
+ if (this.renderItem) {
224
+ render(this.renderItem(item, at), element);
120
225
  }
121
- element.setAttribute('role', 'listitem');
122
- element.setAttribute('aria-posinset', String(at + 1));
123
- element.setAttribute('aria-setsize', total);
124
- element.setAttribute('data-index', String(at));
226
+ else {
227
+ fillHoles(element, item);
228
+ }
229
+ this.stamp(element, at);
125
230
  }
126
231
  }
232
+ draw(template, at) {
233
+ const fragment = document.createDocumentFragment();
234
+ if (this.renderItem) {
235
+ fragment.append(document.createElement('div'));
236
+ }
237
+ else if (template) {
238
+ fragment.append(template.content.cloneNode(true));
239
+ }
240
+ this.fill([...fragment.children], at);
241
+ return fragment;
242
+ }
127
243
  place() {
128
- const template = this.querySelector('template');
129
- if (!template) {
244
+ const template = this.querySelector(':scope > template');
245
+ if (!template && !this.renderItem) {
130
246
  return;
131
247
  }
248
+ const count = this.data.length;
132
249
  const { first, last } = this.virtualize.window;
133
250
  const kept = this.keptIndex();
134
251
  const wanted = [];
135
- for (let at = first; at < Math.min(last, this.rows.length); at += 1) {
252
+ for (let at = first; at < Math.min(last, count); at += 1) {
136
253
  wanted.push(at);
137
254
  }
138
255
  if (kept !== undefined) {
@@ -140,48 +257,66 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
140
257
  wanted.sort((a, b) => a - b);
141
258
  }
142
259
  const keep = new Set(wanted);
260
+ const redraw = (this.filledBy === undefined) !== (this.renderItem === undefined);
143
261
  const drawn = new Map();
144
262
  for (const child of [...this.children]) {
145
263
  if (child === template) {
146
264
  continue;
147
265
  }
148
266
  const index = Number(child.dataset.index);
149
- if (!Number.isInteger(index) || !keep.has(index)) {
267
+ if (redraw || !Number.isInteger(index) || !keep.has(index)) {
150
268
  child.remove();
151
269
  continue;
152
270
  }
153
271
  drawn.set(index, [...(drawn.get(index) ?? []), child]);
154
272
  }
155
- const refill = this.filledFrom !== this.rows;
156
- this.filledFrom = this.rows;
273
+ const refill = this.filledFrom !== this.data || this.filledBy !== this.renderItem;
274
+ this.filledFrom = this.data;
275
+ this.filledBy = this.renderItem;
276
+ const fresh = new Set();
157
277
  let anchor = drawn.values().next().value?.[0] ?? null;
158
278
  for (const at of wanted) {
159
- const row = this.rows[at];
160
279
  const group = drawn.get(at);
161
280
  if (group) {
162
281
  if (refill) {
163
- this.fill(group, row, at);
282
+ this.fill(group, at);
283
+ fresh.add(at);
164
284
  }
165
285
  anchor = group.at(-1).nextElementSibling;
166
286
  continue;
167
287
  }
168
- const clone = template.content.cloneNode(true);
169
- this.fill([...clone.children], row, at);
170
- this.insertBefore(clone, anchor);
288
+ const fragment = this.draw(template, at);
289
+ drawn.set(at, [...fragment.children]);
290
+ fresh.add(at);
291
+ this.insertBefore(fragment, anchor);
292
+ }
293
+ if (!keep.has(this.active)) {
294
+ const onScreen = this.virtualize.visible().first;
295
+ this.active = Math.max(Math.min(onScreen, Math.min(last, count) - 1), first, 0);
296
+ }
297
+ for (const [at, group] of drawn) {
298
+ const stop = at === this.active;
299
+ group[0]?.setAttribute('tabindex', stop ? '0' : '-1');
300
+ if (stop && (fresh.has(at) || this.released !== at)) {
301
+ releaseControls(group);
302
+ }
303
+ else if (!stop && (fresh.has(at) || this.released === at)) {
304
+ holdControls(group);
305
+ }
171
306
  }
307
+ this.released = this.active;
172
308
  }
173
309
  render() {
174
- const window = this.virtualize.window;
175
- const kept = this.hasUpdated ? this.keptIndex() : undefined;
176
- const room = kept === undefined ? 0 : this.virtualize.sizeOf(kept);
177
- const before = window.before - (kept !== undefined && kept < window.first ? room : 0);
178
- const after = window.after - (kept !== undefined && kept >= window.last ? room : 0);
310
+ const { after, before } = this.virtualize.spacers(this.hasUpdated ? this.keptIndex() : undefined);
179
311
  return html `<div
180
312
  data-scroller
181
313
  part="viewport"
182
314
  role="list"
183
315
  aria-label=${this.label}
184
- @scroll=${() => this.virtualize.measure()}
316
+ @scroll=${() => {
317
+ this.virtualize.measure();
318
+ this.report();
319
+ }}
185
320
  >
186
321
  <div part="before" style="block-size: ${before}px"></div>
187
322
  <slot></slot>
@@ -195,12 +330,18 @@ __decorate([
195
330
  __decorate([
196
331
  property()
197
332
  ], HelixHeadlessVirtualList.prototype, "for", void 0);
333
+ __decorate([
334
+ property({ attribute: false })
335
+ ], HelixHeadlessVirtualList.prototype, "items", void 0);
198
336
  __decorate([
199
337
  property()
200
338
  ], HelixHeadlessVirtualList.prototype, "label", void 0);
201
339
  __decorate([
202
340
  property({ type: Boolean })
203
341
  ], HelixHeadlessVirtualList.prototype, "measured", void 0);
342
+ __decorate([
343
+ property({ attribute: false })
344
+ ], HelixHeadlessVirtualList.prototype, "renderItem", void 0);
204
345
  __decorate([
205
346
  state()
206
347
  ], HelixHeadlessVirtualList.prototype, "rows", void 0);