@fusion.dev/helix-headless 0.142.1 → 0.143.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 (217) hide show
  1. package/custom-elements.json +16710 -707
  2. package/dist/accordion/item/accordion-item.d.ts +38 -2
  3. package/dist/accordion/item/accordion-item.js +38 -6
  4. package/dist/accordion/root/accordion-root.d.ts +23 -1
  5. package/dist/accordion/root/accordion-root.js +39 -3
  6. package/dist/announcement/root/announcement-root.d.ts +11 -7
  7. package/dist/announcement/root/announcement-root.js +1 -1
  8. package/dist/app-header/root/app-header-root.d.ts +21 -0
  9. package/dist/app-header/root/app-header-root.js +31 -5
  10. package/dist/area-chart/root/area-chart-root.js +16 -5
  11. package/dist/avatar/group/avatar-group.d.ts +24 -3
  12. package/dist/avatar/group/avatar-group.js +34 -2
  13. package/dist/avatar/root/avatar-root.d.ts +3 -1
  14. package/dist/avatar/root/avatar-root.js +8 -1
  15. package/dist/badge/root/badge-root.d.ts +8 -0
  16. package/dist/bar-chart/root/bar-chart-root.d.ts +19 -3
  17. package/dist/bar-chart/root/bar-chart-root.js +43 -13
  18. package/dist/blockquote/root/blockquote-root.d.ts +12 -7
  19. package/dist/blockquote/root/blockquote-root.js +6 -4
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
  22. package/dist/button/root/button-root.d.ts +37 -0
  23. package/dist/button/root/button-root.js +44 -0
  24. package/dist/callout/root/callout-root.d.ts +14 -5
  25. package/dist/callout/root/callout-root.js +11 -3
  26. package/dist/canvas/root/canvas-root.d.ts +1 -0
  27. package/dist/canvas/root/canvas-root.js +10 -1
  28. package/dist/carousel/root/carousel-root.d.ts +47 -5
  29. package/dist/carousel/root/carousel-root.js +53 -21
  30. package/dist/chat/composer/chat-composer.d.ts +13 -11
  31. package/dist/chat/composer/chat-composer.js +6 -12
  32. package/dist/chat/message/chat-message.js +8 -1
  33. package/dist/chat/root/chat-root.d.ts +1 -0
  34. package/dist/chat/root/chat-root.js +2 -1
  35. package/dist/checkbox/root/checkbox-root.d.ts +7 -0
  36. package/dist/checkbox/root/checkbox-root.js +7 -3
  37. package/dist/checkbox-group/root/checkbox-group-root.d.ts +65 -32
  38. package/dist/checkbox-group/root/checkbox-group-root.js +85 -37
  39. package/dist/code/root/code-root.d.ts +1 -1
  40. package/dist/code-block/root/code-block-root.d.ts +19 -0
  41. package/dist/code-block/root/code-block-root.js +21 -1
  42. package/dist/color-input/root/color-input-root.d.ts +26 -1
  43. package/dist/color-input/root/color-input-root.js +32 -3
  44. package/dist/color-picker/root/color-picker-root.d.ts +37 -7
  45. package/dist/color-picker/root/color-picker-root.js +83 -6
  46. package/dist/combobox/root/combobox-root.d.ts +90 -2
  47. package/dist/combobox/root/combobox-root.js +160 -19
  48. package/dist/command/root/command-root.d.ts +17 -6
  49. package/dist/command/root/command-root.js +12 -1
  50. package/dist/copy-button/root/copy-button-root.d.ts +21 -6
  51. package/dist/copy-button/root/copy-button-root.js +11 -1
  52. package/dist/data-table/root/data-table-root.d.ts +164 -10
  53. package/dist/data-table/root/data-table-root.js +488 -57
  54. package/dist/date-input/root/date-input-root.d.ts +53 -8
  55. package/dist/date-input/root/date-input-root.js +70 -18
  56. package/dist/date-picker/root/date-picker-root.d.ts +84 -17
  57. package/dist/date-picker/root/date-picker-root.js +125 -20
  58. package/dist/dialog/root/dialog-root.d.ts +38 -10
  59. package/dist/dialog/root/dialog-root.js +55 -2
  60. package/dist/donut-chart/root/donut-chart-root.d.ts +39 -15
  61. package/dist/donut-chart/root/donut-chart-root.js +30 -10
  62. package/dist/drawer/root/drawer-root.d.ts +32 -7
  63. package/dist/drawer/root/drawer-root.js +50 -5
  64. package/dist/drilldown/item/drilldown-item.d.ts +16 -13
  65. package/dist/drilldown/item/drilldown-item.js +6 -2
  66. package/dist/drilldown/root/drilldown-root.d.ts +21 -5
  67. package/dist/drilldown/root/drilldown-root.js +26 -10
  68. package/dist/element-events.generated.js +35 -9
  69. package/dist/file-input/root/file-input-root.d.ts +18 -1
  70. package/dist/file-input/root/file-input-root.js +34 -8
  71. package/dist/filter-input/menu/filter-menu.d.ts +9 -0
  72. package/dist/filter-input/menu/filter-menu.js +8 -0
  73. package/dist/filter-input/root/filter-input-root.d.ts +5 -2
  74. package/dist/filter-input/root/filter-input-root.js +22 -7
  75. package/dist/format-date/root/format-date-root.d.ts +11 -3
  76. package/dist/format-date/root/format-date-root.js +26 -5
  77. package/dist/graph/root/graph-root.d.ts +24 -3
  78. package/dist/graph/root/graph-root.js +66 -12
  79. package/dist/heatmap/root/buckets.d.ts +2 -1
  80. package/dist/heatmap/root/buckets.js +5 -1
  81. package/dist/heatmap/root/heatmap-root.d.ts +27 -6
  82. package/dist/heatmap/root/heatmap-root.js +23 -6
  83. package/dist/icon/root/icon-root.d.ts +24 -3
  84. package/dist/icon/root/icon-root.js +34 -1
  85. package/dist/icon-button/root/icon-button-root.d.ts +1 -1
  86. package/dist/image/root/image-root.d.ts +9 -2
  87. package/dist/image/root/image-root.js +8 -2
  88. package/dist/input/root/input-root.d.ts +51 -9
  89. package/dist/input/root/input-root.js +46 -5
  90. package/dist/kbd/root/kbd-root.d.ts +12 -4
  91. package/dist/kbd/root/kbd-root.js +10 -11
  92. package/dist/legend/root/legend-root.d.ts +7 -0
  93. package/dist/legend/root/legend-root.js +7 -2
  94. package/dist/lightbox/item/lightbox-item.d.ts +2 -1
  95. package/dist/lightbox/root/lightbox-root.d.ts +16 -5
  96. package/dist/lightbox/root/lightbox-root.js +24 -3
  97. package/dist/line-chart/root/line-chart-root.d.ts +19 -2
  98. package/dist/line-chart/root/line-chart-root.js +40 -13
  99. package/dist/list/item/list-item.d.ts +11 -5
  100. package/dist/list/item/list-item.js +5 -6
  101. package/dist/listbox/root/listbox-root.d.ts +111 -2
  102. package/dist/listbox/root/listbox-root.js +144 -16
  103. package/dist/map/root/map-root.d.ts +19 -4
  104. package/dist/map/root/map-root.js +36 -7
  105. package/dist/megamenu/root/megamenu-root.d.ts +10 -1
  106. package/dist/megamenu/root/megamenu-root.js +10 -1
  107. package/dist/menu/item/menu-item.d.ts +41 -12
  108. package/dist/menu/item/menu-item.js +46 -3
  109. package/dist/menu/root/menu-root.d.ts +32 -2
  110. package/dist/menu/root/menu-root.js +48 -8
  111. package/dist/meter/root/meter-root.d.ts +6 -0
  112. package/dist/meter/root/meter-root.js +18 -2
  113. package/dist/navigation/root/navigation-root.d.ts +10 -2
  114. package/dist/navigation/root/navigation-root.js +13 -2
  115. package/dist/notifications/group/notification-group.d.ts +1 -0
  116. package/dist/notifications/notification/notification.d.ts +12 -0
  117. package/dist/notifications/notification/notification.js +31 -2
  118. package/dist/notifications/root/notifications-root.d.ts +7 -0
  119. package/dist/notifications/root/notifications-root.js +17 -2
  120. package/dist/number-input/root/number-input-root.d.ts +62 -7
  121. package/dist/number-input/root/number-input-root.js +68 -16
  122. package/dist/object-inspector/root/object-inspector-root.d.ts +48 -5
  123. package/dist/object-inspector/root/object-inspector-root.js +91 -11
  124. package/dist/option/root/option-root.d.ts +4 -1
  125. package/dist/option/root/option-root.js +1 -0
  126. package/dist/outline/root/outline-root.d.ts +13 -2
  127. package/dist/outline/root/outline-root.js +4 -2
  128. package/dist/pagination/root/pagination-root.d.ts +19 -1
  129. package/dist/pagination/root/pagination-root.js +49 -9
  130. package/dist/pagination/size/pagination-size.d.ts +13 -3
  131. package/dist/pagination/size/pagination-size.js +7 -0
  132. package/dist/pin-input/root/pin-input-root.d.ts +16 -0
  133. package/dist/pin-input/root/pin-input-root.js +19 -5
  134. package/dist/popover/root/popover-root.d.ts +19 -2
  135. package/dist/popover/root/popover-root.js +22 -1
  136. package/dist/progress/root/progress-root.d.ts +16 -1
  137. package/dist/progress/root/progress-root.js +31 -7
  138. package/dist/qr-code/root/qr-code-root.d.ts +19 -0
  139. package/dist/qr-code/root/qr-code-root.js +29 -1
  140. package/dist/radio/group/radio-group.d.ts +41 -0
  141. package/dist/radio/group/radio-group.js +50 -14
  142. package/dist/radio/root/radio-root.d.ts +8 -5
  143. package/dist/radio/root/radio-root.js +1 -2
  144. package/dist/rating/root/rating-root.d.ts +24 -1
  145. package/dist/rating/root/rating-root.js +24 -7
  146. package/dist/scatter-chart/root/scatter-chart-root.js +15 -7
  147. package/dist/select/root/select-root.d.ts +69 -3
  148. package/dist/select/root/select-root.js +92 -8
  149. package/dist/skeleton/root/skeleton-root.d.ts +14 -3
  150. package/dist/skeleton/root/skeleton-root.js +17 -3
  151. package/dist/skip-link/root/skip-link-root.d.ts +10 -0
  152. package/dist/skip-link/root/skip-link-root.js +15 -1
  153. package/dist/slider/root/slider-root.d.ts +20 -2
  154. package/dist/slider/root/slider-root.js +41 -3
  155. package/dist/sparkline/root/sparkline-root.d.ts +9 -10
  156. package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
  157. package/dist/speed-dial/item/speed-dial-item.js +7 -2
  158. package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
  159. package/dist/speed-dial/root/speed-dial-root.js +19 -5
  160. package/dist/splitter/root/splitter-root.d.ts +28 -6
  161. package/dist/splitter/root/splitter-root.js +35 -1
  162. package/dist/stat/root/stat-root.d.ts +7 -1
  163. package/dist/stat/root/stat-root.js +14 -1
  164. package/dist/steps/root/steps-root.js +1 -1
  165. package/dist/switch/root/switch-root.d.ts +2 -0
  166. package/dist/switch/root/switch-root.js +1 -0
  167. package/dist/table/cell/table-cell.d.ts +24 -4
  168. package/dist/table/cell/table-cell.js +21 -1
  169. package/dist/table/column/table-column.d.ts +32 -6
  170. package/dist/table/column/table-column.js +36 -6
  171. package/dist/table/root/table-root.d.ts +94 -3
  172. package/dist/table/root/table-root.js +128 -5
  173. package/dist/table/row/table-row.d.ts +7 -0
  174. package/dist/table/row/table-row.js +9 -1
  175. package/dist/tabs/root/tabs-root.d.ts +6 -1
  176. package/dist/tabs/root/tabs-root.js +6 -2
  177. package/dist/tag/root/tag-root.d.ts +23 -1
  178. package/dist/tag/root/tag-root.js +16 -2
  179. package/dist/textarea/root/textarea-root.d.ts +4 -0
  180. package/dist/textarea/root/textarea-root.js +1 -1
  181. package/dist/time-input/root/time-input-root.d.ts +28 -2
  182. package/dist/time-input/root/time-input-root.js +46 -10
  183. package/dist/time-picker/root/time-picker-root.d.ts +101 -18
  184. package/dist/time-picker/root/time-picker-root.js +115 -16
  185. package/dist/toast/root/toast-root.d.ts +64 -1
  186. package/dist/toast/root/toast-root.js +147 -2
  187. package/dist/toggle/group/toggle-group.d.ts +38 -1
  188. package/dist/toggle/group/toggle-group.js +34 -8
  189. package/dist/toggle/root/toggle-root.d.ts +14 -10
  190. package/dist/toggle/root/toggle-root.js +3 -3
  191. package/dist/toolbar/root/toolbar-root.d.ts +55 -15
  192. package/dist/toolbar/root/toolbar-root.js +99 -16
  193. package/dist/tooltip/root/tooltip-root.d.ts +37 -4
  194. package/dist/tooltip/root/tooltip-root.js +48 -0
  195. package/dist/tour/root/tour-root.d.ts +34 -5
  196. package/dist/tour/root/tour-root.js +51 -14
  197. package/dist/tour/stop/tour-stop.d.ts +18 -1
  198. package/dist/tour/stop/tour-stop.js +25 -5
  199. package/dist/tree/item/tree-item.d.ts +21 -5
  200. package/dist/tree/item/tree-item.js +14 -4
  201. package/dist/tree/root/tree-root.d.ts +29 -5
  202. package/dist/tree/root/tree-root.js +64 -8
  203. package/dist/trend/root/trend-root.d.ts +23 -1
  204. package/dist/trend/root/trend-root.js +28 -7
  205. package/dist/video/root/keys.d.ts +5 -0
  206. package/dist/video/root/keys.js +13 -1
  207. package/dist/video/root/video-root.d.ts +19 -0
  208. package/dist/video/root/video-root.js +48 -4
  209. package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
  210. package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
  211. package/dist/video-playlist/root/video-playlist-root.js +6 -3
  212. package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
  213. package/dist/virtual-list/root/virtual-list-root.js +103 -21
  214. package/lint.json +1 -1
  215. package/package.json +5 -5
  216. package/types/jsx.d.ts +4348 -965
  217. package/types/vue.d.ts +972 -232
@@ -30,10 +30,16 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
30
30
  this.loadingText = 'Loading';
31
31
  this.multiple = false;
32
32
  this.placeholder = '';
33
+ this.readOnly = false;
33
34
  this.removeLabel = 'Remove {name}';
34
35
  this.suggest = false;
35
36
  this.typed = '';
36
37
  this.suggestion = '';
38
+ this.editing = false;
39
+ this.onCreate = false;
40
+ this.emptied = false;
41
+ this.opening = undefined;
42
+ this.watcher = undefined;
37
43
  this.panel = new PopoverController(this, {
38
44
  anchor: () => this.partNamed('field'),
39
45
  boundary: () => [
@@ -41,7 +47,10 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
41
47
  this.partNamed('field'),
42
48
  ],
43
49
  offset: () => 4,
44
- onDismiss: () => this.close(),
50
+ onDismiss: () => {
51
+ this.commitEmptied();
52
+ this.close();
53
+ },
45
54
  open: () => this.open,
46
55
  placement: 'bottom-start',
47
56
  surface: () => this.partNamed('surface'),
@@ -50,6 +59,9 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
50
59
  event.stopPropagation();
51
60
  const typed = event.target.value;
52
61
  this.typed = typed;
62
+ this.editing = true;
63
+ this.onCreate = false;
64
+ this.emptied = typed.trim() === '';
53
65
  const narrowing = new HelixFilterEvent({ value: typed });
54
66
  if (this.dispatchEvent(narrowing)) {
55
67
  this.listbox?.requestFilter(typed);
@@ -65,6 +77,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
65
77
  return;
66
78
  }
67
79
  if (event.key === 'Escape') {
80
+ this.emptied = false;
68
81
  if (this.open) {
69
82
  event.preventDefault();
70
83
  this.close();
@@ -72,6 +85,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
72
85
  return;
73
86
  }
74
87
  if (event.key === 'Tab') {
88
+ this.commitEmptied();
75
89
  if (this.open) {
76
90
  this.requestOpen(false);
77
91
  }
@@ -80,23 +94,39 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
80
94
  if (this.multiple && this.tokenKeydown(event, this.typed)) {
81
95
  return;
82
96
  }
83
- if (event.key === 'Enter' && this.creation !== '' && !this.showing) {
97
+ if (event.key === 'Enter' && this.creation !== '' && (this.onCreate || !this.showing)) {
84
98
  event.preventDefault();
85
99
  this.create();
86
100
  return;
87
101
  }
88
- if (!this.open && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
89
- this.requestOpen(true);
102
+ if (!this.open) {
103
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
104
+ event.preventDefault();
105
+ const fallback = event.key === 'ArrowDown' ? 'first' : 'last';
106
+ this.opening = this.values.length > 0 ? 'selected' : fallback;
107
+ this.requestOpen(true);
108
+ }
109
+ return;
110
+ }
111
+ if (this.moveAroundCreate(event)) {
112
+ void this.updateComplete.then(() => this.linkActive());
113
+ return;
90
114
  }
91
115
  this.listbox?.navigate(event);
92
116
  void this.listbox?.updateComplete.then(() => this.linkActive());
93
117
  };
118
+ this.handleKeypress = (event) => {
119
+ if (event.key === 'Enter' && !this.open && !event.defaultPrevented) {
120
+ this.submitImplicitly();
121
+ }
122
+ };
94
123
  this.handleChange = (event) => {
95
124
  event.stopPropagation();
96
125
  const { value } = event.detail;
97
126
  if (!this.requestValue(value)) {
98
127
  return;
99
128
  }
129
+ this.emptied = false;
100
130
  this.typed = '';
101
131
  this.suggestion = '';
102
132
  this.listbox?.requestFilter('');
@@ -122,14 +152,23 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
122
152
  this.drop(tag.dataset.value ?? '');
123
153
  };
124
154
  }
155
+ requestValue(next) {
156
+ if (this.readOnly) {
157
+ return false;
158
+ }
159
+ return super.requestValue(next);
160
+ }
161
+ requestOpen(next) {
162
+ if (next && this.readOnly) {
163
+ return false;
164
+ }
165
+ return super.requestOpen(next);
166
+ }
125
167
  get selectedIndex() {
126
168
  return this.listbox?.selectedIndex ?? -1;
127
169
  }
128
170
  set selectedIndex(at) {
129
- const listbox = this.listbox;
130
- if (listbox) {
131
- listbox.selectedIndex = at;
132
- }
171
+ this.value = this.listbox?.options[at]?.value ?? '';
133
172
  }
134
173
  get type() {
135
174
  return this.multiple ? 'select-multiple' : 'select-one';
@@ -202,6 +241,37 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
202
241
  this.requestOpen(false);
203
242
  this.partNamed('control')?.focus();
204
243
  }
244
+ commitEmptied() {
245
+ if (this.emptied && !this.multiple && this.value !== '') {
246
+ this.requestValue('');
247
+ }
248
+ this.emptied = false;
249
+ }
250
+ get reachable() {
251
+ return (this.listbox?.options ?? []).filter((option) => !option.hidden && !option.disabled);
252
+ }
253
+ moveAroundCreate(event) {
254
+ if (this.creation === '' || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) {
255
+ return false;
256
+ }
257
+ const down = event.key === 'ArrowDown';
258
+ if (this.onCreate) {
259
+ event.preventDefault();
260
+ this.onCreate = false;
261
+ this.listbox?.highlight(down ? 'first' : 'last');
262
+ return true;
263
+ }
264
+ const reachable = this.reachable;
265
+ const at = reachable.findIndex((option) => option.active);
266
+ const edge = down ? reachable.length - 1 : 0;
267
+ if (reachable.length === 0 || at === edge) {
268
+ event.preventDefault();
269
+ this.onCreate = true;
270
+ this.listbox?.highlight('none');
271
+ return true;
272
+ }
273
+ return false;
274
+ }
205
275
  completionFor(typed) {
206
276
  if (typed === '') {
207
277
  return '';
@@ -210,14 +280,19 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
210
280
  const match = (this.listbox?.options ?? []).find((option) => !option.hidden && !option.disabled && option.text.toLowerCase().startsWith(needle));
211
281
  return match && match.text.length > typed.length ? match.text : '';
212
282
  }
283
+ get blocksImplicitSubmission() {
284
+ return true;
285
+ }
213
286
  linkActive() {
214
287
  const control = this.partNamed('control');
215
288
  if (!control || !('ariaActiveDescendantElement' in control)) {
216
289
  return;
217
290
  }
218
- const active = this.open
219
- ? (this.listbox?.options ?? []).find((option) => option.active && !option.hidden)
220
- : undefined;
291
+ const active = !this.open
292
+ ? undefined
293
+ : this.onCreate
294
+ ? this.partNamed('create')
295
+ : (this.listbox?.options ?? []).find((option) => option.active && !option.hidden);
221
296
  control.ariaActiveDescendantElement = active ?? null;
222
297
  }
223
298
  removeLastToken() {
@@ -235,6 +310,35 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
235
310
  }
236
311
  this.reportRequired(this.value === '', 'Choose one to continue.', 'control');
237
312
  }
313
+ connectedCallback() {
314
+ super.connectedCallback();
315
+ this.watcher = new MutationObserver(() => this.requestUpdate());
316
+ this.watcher.observe(this, {
317
+ attributeFilter: ['label', 'value'],
318
+ characterData: true,
319
+ childList: true,
320
+ subtree: true,
321
+ });
322
+ }
323
+ disconnectedCallback() {
324
+ super.disconnectedCallback();
325
+ this.watcher?.disconnect();
326
+ this.watcher = undefined;
327
+ }
328
+ willUpdate(changed) {
329
+ super.willUpdate(changed);
330
+ if (this.childrenKnown && this.defaultValue === '' && !this.hasAttribute('default-value')) {
331
+ const declared = optionsAmong(this.knownChildren)
332
+ .filter((option) => option.defaultSelected)
333
+ .map((option) => option.value);
334
+ if (declared.length > 0) {
335
+ this.defaultValue = this.multiple ? declared.join(',') : (declared.at(-1) ?? '');
336
+ }
337
+ }
338
+ if (this.creation === '') {
339
+ this.onCreate = false;
340
+ }
341
+ }
238
342
  firstUpdated(changed) {
239
343
  super.firstUpdated(changed);
240
344
  this.finishHydration();
@@ -244,7 +348,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
244
348
  this.sync();
245
349
  this.linkActive();
246
350
  const control = this.partNamed('control');
247
- const wanted = this.multiple || this.open ? this.suggestion || this.typed : this.word;
351
+ const wanted = this.fieldText;
248
352
  if (control && control.value !== wanted) {
249
353
  control.value = wanted;
250
354
  }
@@ -254,8 +358,26 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
254
358
  if (!this.open) {
255
359
  this.typed = '';
256
360
  this.suggestion = '';
361
+ this.editing = false;
362
+ this.onCreate = false;
257
363
  this.listbox?.requestFilter('');
258
364
  }
365
+ else if (changed.has('open')) {
366
+ if (this.opening !== undefined) {
367
+ this.listbox?.highlight(this.opening);
368
+ this.opening = undefined;
369
+ void this.listbox?.updateComplete.then(() => this.linkActive());
370
+ }
371
+ if (control && !this.editing && !this.multiple && control.value !== '') {
372
+ control.select();
373
+ }
374
+ }
375
+ }
376
+ get fieldText() {
377
+ if (this.multiple || (this.open && this.editing)) {
378
+ return this.suggestion || this.typed;
379
+ }
380
+ return this.word;
259
381
  }
260
382
  renderListbox() {
261
383
  return html `<${this.tag('listbox')}
@@ -290,7 +412,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
290
412
  exportparts="dismiss: tag-dismiss, dismiss-mark: tag-dismiss-mark, label: tag-label, start: tag-start"
291
413
  data-value=${option.value}
292
414
  dismiss-label=${fillTemplate(this.removeLabel, { name: option.text })}
293
- ?dismissible=${!this.isDisabled}
415
+ ?dismissible=${!this.isDisabled && !this.readOnly}
294
416
  @helix-dismiss=${this.handleDismiss}
295
417
  >${option.text}</${this.tag('tag')}
296
418
  >`;
@@ -302,6 +424,18 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
302
424
  </svg>
303
425
  </slot>`;
304
426
  }
427
+ renderCreate() {
428
+ if (this.creation === '') {
429
+ return nothing;
430
+ }
431
+ return this.onCreate
432
+ ? html `<button part="create create-active" id="create" type="button" @click=${() => this.create()}>
433
+ ${this.createLabel} "${this.creation}"
434
+ </button>`
435
+ : html `<button part="create" id="create" type="button" @click=${() => this.create()}>
436
+ ${this.createLabel} "${this.creation}"
437
+ </button>`;
438
+ }
305
439
  render() {
306
440
  return html `${this.renderLabel()}
307
441
  <div part="field" @click=${this.handleFieldClick}>
@@ -323,20 +457,18 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
323
457
  aria-invalid=${this.invalidity ?? nothing}
324
458
  placeholder=${(this.chosen.length > 0 || this.unresolved ? '' : this.placeholder) || nothing}
325
459
  ?disabled=${this.isDisabled}
326
- .value=${this.multiple || this.open ? this.suggestion || this.typed : this.word}
460
+ ?readonly=${this.readOnly}
461
+ .value=${this.fieldText}
327
462
  @input=${this.handleInput}
328
463
  @keydown=${this.handleKeydown}
464
+ @keypress=${this.handleKeypress}
329
465
  />
330
466
  <slot name="end"></slot>
331
467
  ${this.renderIndicator()}
332
468
  </div>
333
469
  <div part="surface" popover="manual" ?hidden=${!this.open}>
334
470
  ${this.renderListbox()}
335
- ${this.creation === ''
336
- ? nothing
337
- : html `<button part="create" type="button" @click=${() => this.create()}>
338
- ${this.createLabel} "${this.creation}"
339
- </button>`}
471
+ ${this.renderCreate()}
340
472
  </div>
341
473
  ${this.renderHint()}${this.renderFeedback()}`;
342
474
  }
@@ -368,6 +500,9 @@ __decorate([
368
500
  __decorate([
369
501
  property()
370
502
  ], HelixHeadlessCombobox.prototype, "placeholder", void 0);
503
+ __decorate([
504
+ property({ attribute: 'readonly', reflect: true, type: Boolean })
505
+ ], HelixHeadlessCombobox.prototype, "readOnly", void 0);
371
506
  __decorate([
372
507
  property({ attribute: 'remove-label' })
373
508
  ], HelixHeadlessCombobox.prototype, "removeLabel", void 0);
@@ -380,6 +515,12 @@ __decorate([
380
515
  __decorate([
381
516
  state()
382
517
  ], HelixHeadlessCombobox.prototype, "suggestion", void 0);
518
+ __decorate([
519
+ state()
520
+ ], HelixHeadlessCombobox.prototype, "editing", void 0);
521
+ __decorate([
522
+ state()
523
+ ], HelixHeadlessCombobox.prototype, "onCreate", void 0);
383
524
  HelixHeadlessCombobox = __decorate([
384
525
  customElement('helix-headless-combobox')
385
526
  ], HelixHeadlessCombobox);
@@ -35,8 +35,9 @@ declare const HelixHeadlessCommand_base: (new (...args: any[]) => import("@fusio
35
35
  *
36
36
  * @event {HelixBeforeCloseEvent} helix-before-close - The palette is about to close. Cancelable: cancel it to keep it open.
37
37
  * @event {HelixBeforeOpenEvent} helix-before-open - The palette is about to open. Cancelable: cancel it to keep it closed.
38
+ * @event {HelixFilterEvent} helix-filter - Fired as the user types, with the text the list is filtered by. Cancelable: cancel it to leave the list unfiltered and filter the commands yourself, for example on a server while `loading` is set.
38
39
  * @event {HelixSelectEvent} helix-select - A command was chosen. Cancelable: cancel it to keep the palette open.
39
- * @event {Event} toggle - The palette opened or closed. The event carries `newState` and `oldState`.
40
+ * @event {ToggleEvent} toggle - The palette opened or closed. The event carries `newState` and `oldState`.
40
41
  *
41
42
  * @csspart dialog - The box of the dialog the palette opens in, forwarded from `Dialog`.
42
43
  * @csspart footer - The row under the list.
@@ -72,6 +73,17 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
72
73
  * Accessible name for the overlay, which has no visible heading.
73
74
  */
74
75
  label: string;
76
+ /**
77
+ * Whether the commands are still loading, for a palette that asks a server
78
+ * for them as the user types.
79
+ *
80
+ * Passed to the list, which shows `loading-text` and tells screen readers.
81
+ */
82
+ loading: boolean;
83
+ /**
84
+ * The text shown while the commands load.
85
+ */
86
+ loadingText: string;
75
87
  /**
76
88
  * The text shown in the filter field before anything is typed.
77
89
  */
@@ -119,12 +131,11 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
119
131
  */
120
132
  private readonly handleChange;
121
133
  /**
122
- * The row under the keyboard, read from the attribute the listbox writes.
134
+ * The row under the keyboard, read from the `active` property the listbox
135
+ * writes on each option.
123
136
  *
124
- * `data-active` rather than a reference the listbox hands over, because the
125
- * options are this element's own light DOM children and the attribute is
126
- * already published: asking the listbox would mean a second channel saying
127
- * what the markup says.
137
+ * Off the options rather than a reference the listbox hands over, because
138
+ * they are this element's own light DOM children and already say it.
128
139
  */
129
140
  private get activeOption();
130
141
  /**
@@ -24,6 +24,8 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
24
24
  this.emptyText = 'No commands';
25
25
  this.hotkey = 'mod+k';
26
26
  this.label = 'Commands';
27
+ this.loading = false;
28
+ this.loadingText = 'Loading';
27
29
  this.placeholder = 'Search commands';
28
30
  this.footed = false;
29
31
  this.handleHotkey = (event) => {
@@ -80,7 +82,8 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
80
82
  return this.partNamed('listbox');
81
83
  }
82
84
  get activeOption() {
83
- return this.querySelector('[data-active]');
85
+ return (this.listbox?.options.find((option) => option.active && !option.hidden) ??
86
+ this.querySelector('[data-active]'));
84
87
  }
85
88
  connectedCallback() {
86
89
  super.connectedCallback();
@@ -140,6 +143,8 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
140
143
  aria-label=${this.label}
141
144
  empty-text=${this.emptyText}
142
145
  filter-label=${this.placeholder}
146
+ loading-text=${this.loadingText}
147
+ ?loading=${this.loading}
143
148
  filterable
144
149
  @helix-before-change=${this.handleChange}
145
150
  @change=${this.stopInner}
@@ -170,6 +175,12 @@ __decorate([
170
175
  __decorate([
171
176
  property()
172
177
  ], HelixHeadlessCommand.prototype, "label", void 0);
178
+ __decorate([
179
+ property({ reflect: true, type: Boolean })
180
+ ], HelixHeadlessCommand.prototype, "loading", void 0);
181
+ __decorate([
182
+ property({ attribute: 'loading-text' })
183
+ ], HelixHeadlessCommand.prototype, "loadingText", void 0);
173
184
  __decorate([
174
185
  property()
175
186
  ], HelixHeadlessCommand.prototype, "placeholder", void 0);
@@ -14,6 +14,12 @@ import '../../tooltip/index.js';
14
14
  * that is not focused, or after a gesture that does not qualify. A refused
15
15
  * write leaves the button unchanged.
16
16
  *
17
+ * It replaces Button's render and its press, so it never submits a form or
18
+ * sends a command: `type`, `command`, `commandfor` and the `form*` overrides
19
+ * have no effect, and the `start` and `end` slots and the `text` part are not
20
+ * rendered. An `aria-label` on the element names it before a copy, in place of
21
+ * `label`.
22
+ *
17
23
  * @since 0.1.0
18
24
  * @status stable
19
25
  * @category actions
@@ -57,12 +63,8 @@ export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
57
63
  /**
58
64
  * The text copied to the clipboard.
59
65
  *
60
- * **Narrows `Button.value`, which is the submit value, and the two agree.**
61
- * A copy button is `type="button"` and never submits, so the inherited
62
- * meaning has nothing to act on; where a consumer changes the type, what it
63
- * submits is what it copies, which is the reading anybody would expect of one
64
- * property called `value`. Declared here rather than inherited because the
65
- * clipboard is what this component is about and its own page should say so.
66
+ * It replaces `Button.value`, the submit value: a copy button never submits,
67
+ * whatever its `type`. Declared here so its own page says what it holds.
66
68
  */
67
69
  value: string;
68
70
  /**
@@ -76,6 +78,19 @@ export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
76
78
  * makes the announcement happen at all.
77
79
  */
78
80
  private get says();
81
+ /**
82
+ * @internal The host's `aria-label`, observed so a new one renders, as
83
+ * `MixinField` observes it. Not reflected: the attribute is the consumer's.
84
+ */
85
+ protected namedBy: string | null;
86
+ /**
87
+ * @internal The name is written here, from `aria-label` too, so it can
88
+ * change to `copiedLabel`: carried down as well, a host `aria-label` would
89
+ * cover the confirmation.
90
+ */
91
+ protected get forwardsLabel(): boolean;
92
+ /** @internal A copy button never submits, so it is no form's default button. */
93
+ protected get submitsForm(): boolean;
79
94
  /**
80
95
  * The write, the confirmation, the timer and the event, all of them shared.
81
96
  *
@@ -20,6 +20,7 @@ let HelixHeadlessCopyButton = HelixHeadlessCopyButton_1 = class HelixHeadlessCop
20
20
  this.copiedLabel = 'Copied';
21
21
  this.label = 'Copy';
22
22
  this.value = '';
23
+ this.namedBy = null;
23
24
  this.clipboard = new CopyController(this, { after: CONFIRM });
24
25
  this.handleCopy = async () => {
25
26
  if (this.isDisabled) {
@@ -29,7 +30,13 @@ let HelixHeadlessCopyButton = HelixHeadlessCopyButton_1 = class HelixHeadlessCop
29
30
  };
30
31
  }
31
32
  get says() {
32
- return this.copied ? this.copiedLabel : this.label;
33
+ return this.copied ? this.copiedLabel : this.namedBy || this.label;
34
+ }
35
+ get forwardsLabel() {
36
+ return false;
37
+ }
38
+ get submitsForm() {
39
+ return false;
33
40
  }
34
41
  get copied() {
35
42
  return this.clipboard.copied;
@@ -79,6 +86,9 @@ __decorate([
79
86
  __decorate([
80
87
  property()
81
88
  ], HelixHeadlessCopyButton.prototype, "value", void 0);
89
+ __decorate([
90
+ property({ attribute: 'aria-label' })
91
+ ], HelixHeadlessCopyButton.prototype, "namedBy", void 0);
82
92
  HelixHeadlessCopyButton = HelixHeadlessCopyButton_1 = __decorate([
83
93
  customElement('helix-headless-copy-button')
84
94
  ], HelixHeadlessCopyButton);