@fusion.dev/helix-headless 0.146.0 → 0.147.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 (239) hide show
  1. package/custom-elements.json +5266 -785
  2. package/dist/accordion/item/accordion-item.d.ts +38 -19
  3. package/dist/accordion/item/accordion-item.js +31 -9
  4. package/dist/accordion/root/accordion-root.d.ts +8 -2
  5. package/dist/accordion/root/accordion-root.js +3 -3
  6. package/dist/announcement/root/announcement-root.d.ts +13 -4
  7. package/dist/announcement/root/announcement-root.js +34 -6
  8. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  9. package/dist/area-chart/root/area-chart-root.js +11 -3
  10. package/dist/avatar/root/avatar-root.d.ts +31 -1
  11. package/dist/avatar/root/avatar-root.js +11 -1
  12. package/dist/badge/root/badge-root.d.ts +57 -6
  13. package/dist/badge/root/badge-root.js +88 -5
  14. package/dist/bar-chart/root/bar-chart-root.js +11 -20
  15. package/dist/bar-chart/stack.d.ts +10 -0
  16. package/dist/bar-chart/stack.js +8 -0
  17. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  18. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  19. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -2
  20. package/dist/button/root/button-root.d.ts +24 -38
  21. package/dist/button/root/button-root.js +6 -37
  22. package/dist/button/submitter.d.ts +27 -0
  23. package/dist/button/submitter.js +25 -0
  24. package/dist/card/root/card-root.d.ts +12 -1
  25. package/dist/card/root/card-root.js +11 -4
  26. package/dist/chat/root/chat-root.d.ts +15 -1
  27. package/dist/chat/root/chat-root.js +11 -2
  28. package/dist/checkbox/root/checkbox-root.js +5 -7
  29. package/dist/checkbox-group/root/checkbox-group-root.js +4 -3
  30. package/dist/checkbox-group/select-all.d.ts +0 -6
  31. package/dist/checkbox-group/select-all.js +0 -5
  32. package/dist/code-block/root/code-block-root.d.ts +35 -14
  33. package/dist/code-block/root/code-block-root.js +17 -3
  34. package/dist/color-input/root/color-input-root.d.ts +11 -0
  35. package/dist/color-input/root/color-input-root.js +12 -5
  36. package/dist/color-picker/channels.js +1 -2
  37. package/dist/color-picker/root/color-picker-root.d.ts +32 -24
  38. package/dist/color-picker/root/color-picker-root.js +101 -51
  39. package/dist/color-picker/wheel.d.ts +17 -0
  40. package/dist/color-picker/wheel.js +6 -0
  41. package/dist/combobox/root/combobox-root.d.ts +13 -0
  42. package/dist/combobox/root/combobox-root.js +14 -1
  43. package/dist/command/root/command-root.d.ts +3 -1
  44. package/dist/command/root/command-root.js +2 -19
  45. package/dist/copy-button/root/copy-button-root.d.ts +16 -1
  46. package/dist/copy-button/root/copy-button-root.js +7 -2
  47. package/dist/data-table/keys.d.ts +0 -18
  48. package/dist/data-table/keys.js +1 -11
  49. package/dist/data-table/root/data-table-root.js +13 -7
  50. package/dist/date-input/root/date-input-root.d.ts +8 -1
  51. package/dist/date-input/root/date-input-root.js +8 -5
  52. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  53. package/dist/date-picker/root/date-picker-root.js +21 -16
  54. package/dist/description-list/item/description-list-item.d.ts +18 -4
  55. package/dist/description-list/item/description-list-item.js +8 -4
  56. package/dist/donut-chart/root/donut-chart-root.js +13 -6
  57. package/dist/donut-chart/slice-label.d.ts +35 -0
  58. package/dist/donut-chart/slice-label.js +11 -0
  59. package/dist/drawer/root/drawer-root.d.ts +4 -2
  60. package/dist/drawer/root/drawer-root.js +15 -3
  61. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  62. package/dist/drilldown/item/drilldown-item.js +39 -4
  63. package/dist/drilldown/root/drilldown-root.js +3 -2
  64. package/dist/element-events.generated.js +4 -1
  65. package/dist/empty/root/empty-root.d.ts +31 -1
  66. package/dist/empty/root/empty-root.js +26 -5
  67. package/dist/file-input/root/file-input-root.d.ts +34 -6
  68. package/dist/file-input/root/file-input-root.js +54 -19
  69. package/dist/file-input/status.d.ts +24 -0
  70. package/dist/file-input/status.js +10 -0
  71. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  72. package/dist/filter-input/chip/filter-chip.js +11 -2
  73. package/dist/filter-input/root/filter-input-root.d.ts +8 -0
  74. package/dist/filter-input/root/filter-input-root.js +17 -27
  75. package/dist/format-date/options.d.ts +3 -4
  76. package/dist/format-date/options.js +2 -2
  77. package/dist/format-date/root/format-date-root.d.ts +18 -1
  78. package/dist/format-date/root/format-date-root.js +19 -23
  79. package/dist/heatmap/root/buckets.d.ts +21 -0
  80. package/dist/heatmap/root/buckets.js +34 -0
  81. package/dist/heatmap/root/heatmap-root.d.ts +64 -3
  82. package/dist/heatmap/root/heatmap-root.js +89 -19
  83. package/dist/icon/index.d.ts +1 -1
  84. package/dist/icon/index.js +1 -1
  85. package/dist/icon/registry.d.ts +26 -1
  86. package/dist/icon/registry.js +12 -1
  87. package/dist/icon/root/icon-root.d.ts +13 -0
  88. package/dist/icon/root/icon-root.js +23 -2
  89. package/dist/icon/root/index.d.ts +1 -1
  90. package/dist/icon/root/index.js +1 -1
  91. package/dist/image/root/image-root.d.ts +9 -0
  92. package/dist/image/root/image-root.js +6 -1
  93. package/dist/input/root/input-root.d.ts +23 -2
  94. package/dist/input/root/input-root.js +12 -8
  95. package/dist/kbd/keys.d.ts +29 -0
  96. package/dist/kbd/keys.js +52 -0
  97. package/dist/kbd/root/kbd-root.d.ts +20 -3
  98. package/dist/kbd/root/kbd-root.js +24 -50
  99. package/dist/legend/hidden-series.d.ts +10 -2
  100. package/dist/legend/hidden-series.js +5 -2
  101. package/dist/legend/root/legend-root.d.ts +25 -7
  102. package/dist/legend/root/legend-root.js +22 -10
  103. package/dist/lightbox/root/lightbox-root.d.ts +50 -5
  104. package/dist/lightbox/root/lightbox-root.js +76 -8
  105. package/dist/lightbox/zoom.d.ts +2 -0
  106. package/dist/lightbox/zoom.js +5 -3
  107. package/dist/line-chart/root/line-chart-root.d.ts +16 -0
  108. package/dist/line-chart/root/line-chart-root.js +10 -2
  109. package/dist/list/root/list-root.d.ts +10 -2
  110. package/dist/list/root/list-root.js +26 -5
  111. package/dist/list/selection.d.ts +17 -0
  112. package/dist/list/selection.js +16 -0
  113. package/dist/listbox/root/listbox-root.d.ts +9 -5
  114. package/dist/listbox/root/listbox-root.js +29 -37
  115. package/dist/megamenu/hand-over.d.ts +75 -0
  116. package/dist/megamenu/hand-over.js +67 -0
  117. package/dist/megamenu/root/megamenu-root.d.ts +38 -14
  118. package/dist/megamenu/root/megamenu-root.js +36 -50
  119. package/dist/menu/item/menu-item.js +4 -4
  120. package/dist/menu/root/menu-root.d.ts +3 -0
  121. package/dist/meter/root/meter-root.d.ts +42 -0
  122. package/dist/meter/root/meter-root.js +33 -7
  123. package/dist/notifications/root/notifications-root.d.ts +10 -1
  124. package/dist/notifications/root/notifications-root.js +11 -1
  125. package/dist/number-input/grain.d.ts +28 -0
  126. package/dist/number-input/grain.js +14 -0
  127. package/dist/number-input/root/number-input-root.d.ts +17 -1
  128. package/dist/number-input/root/number-input-root.js +29 -18
  129. package/dist/object-inspector/root/object-inspector-root.d.ts +13 -6
  130. package/dist/object-inspector/root/object-inspector-root.js +18 -17
  131. package/dist/option/index.d.ts +1 -0
  132. package/dist/option/index.js +1 -0
  133. package/dist/option/separator/option-separator.d.ts +30 -0
  134. package/dist/option/separator/option-separator.js +21 -0
  135. package/dist/outline/root/outline-root.d.ts +12 -0
  136. package/dist/outline/root/outline-root.js +5 -19
  137. package/dist/pagination/size/pagination-size.d.ts +5 -0
  138. package/dist/pagination/size/pagination-size.js +4 -2
  139. package/dist/pin-input/code.d.ts +28 -1
  140. package/dist/pin-input/code.js +12 -0
  141. package/dist/pin-input/root/pin-input-root.d.ts +98 -121
  142. package/dist/pin-input/root/pin-input-root.js +157 -148
  143. package/dist/popover/hide-outside.js +2 -9
  144. package/dist/popover/root/popover-root.d.ts +7 -1
  145. package/dist/popover/root/popover-root.js +9 -1
  146. package/dist/progress/root/progress-root.d.ts +27 -0
  147. package/dist/progress/root/progress-root.js +25 -7
  148. package/dist/qr-code/export.d.ts +19 -0
  149. package/dist/qr-code/export.js +14 -0
  150. package/dist/qr-code/name.d.ts +11 -0
  151. package/dist/qr-code/name.js +7 -0
  152. package/dist/qr-code/root/qr-code-root.d.ts +17 -6
  153. package/dist/qr-code/root/qr-code-root.js +10 -12
  154. package/dist/radio/group/radio-group.d.ts +15 -0
  155. package/dist/radio/root/radio-root.js +2 -2
  156. package/dist/rating/root/rating-root.d.ts +22 -9
  157. package/dist/rating/root/rating-root.js +15 -39
  158. package/dist/scatter-chart/root/scatter-chart-root.d.ts +30 -0
  159. package/dist/scatter-chart/root/scatter-chart-root.js +34 -7
  160. package/dist/scroll-area/scrollbar.d.ts +0 -8
  161. package/dist/scroll-area/scrollbar.js +1 -1
  162. package/dist/select/align.d.ts +47 -0
  163. package/dist/select/align.js +7 -0
  164. package/dist/select/root/select-root.d.ts +59 -3
  165. package/dist/select/root/select-root.js +72 -12
  166. package/dist/slider/root/slider-root.d.ts +18 -1
  167. package/dist/slider/root/slider-root.js +22 -0
  168. package/dist/sparkline/readings.d.ts +30 -12
  169. package/dist/sparkline/readings.js +32 -15
  170. package/dist/sparkline/root/sparkline-root.d.ts +46 -3
  171. package/dist/sparkline/root/sparkline-root.js +60 -21
  172. package/dist/speed-dial/item/speed-dial-item.d.ts +1 -1
  173. package/dist/speed-dial/item/speed-dial-item.js +2 -2
  174. package/dist/speed-dial/root/speed-dial-root.d.ts +6 -0
  175. package/dist/speed-dial/root/speed-dial-root.js +9 -1
  176. package/dist/splitter/root/splitter-root.d.ts +3 -0
  177. package/dist/splitter/root/splitter-root.js +2 -0
  178. package/dist/steps/root/steps-root.d.ts +8 -1
  179. package/dist/steps/root/steps-root.js +6 -1
  180. package/dist/steps/step/step.d.ts +1 -0
  181. package/dist/steps/step/step.js +1 -1
  182. package/dist/table/column/table-column.d.ts +6 -0
  183. package/dist/table/column/table-column.js +5 -1
  184. package/dist/table/root/table-root.js +13 -6
  185. package/dist/table/row/table-row.d.ts +24 -0
  186. package/dist/table/row/table-row.js +37 -1
  187. package/dist/tabs/root/tabs-root.d.ts +5 -3
  188. package/dist/tag/group/tag-group.d.ts +16 -4
  189. package/dist/tag/group/tag-group.js +19 -7
  190. package/dist/textarea/root/textarea-root.d.ts +14 -2
  191. package/dist/textarea/root/textarea-root.js +6 -7
  192. package/dist/time-input/root/time-input-root.d.ts +5 -1
  193. package/dist/time-input/root/time-input-root.js +10 -7
  194. package/dist/time-picker/root/time-picker-root.d.ts +11 -14
  195. package/dist/time-picker/root/time-picker-root.js +15 -23
  196. package/dist/toast/item/toast-item.d.ts +3 -3
  197. package/dist/toast/item/toast-item.js +0 -1
  198. package/dist/toast/root/toast-root.d.ts +28 -4
  199. package/dist/toast/root/toast-root.js +58 -12
  200. package/dist/toast/words.d.ts +12 -0
  201. package/dist/toast/words.js +25 -0
  202. package/dist/toggle/group/toggle-group.d.ts +29 -5
  203. package/dist/toggle/group/toggle-group.js +26 -41
  204. package/dist/toggle/root/toggle-root.js +2 -4
  205. package/dist/toolbar/root/toolbar-root.d.ts +21 -5
  206. package/dist/toolbar/root/toolbar-root.js +52 -8
  207. package/dist/tooltip/root/tooltip-root.d.ts +7 -1
  208. package/dist/tooltip/root/tooltip-root.js +10 -1
  209. package/dist/tour/root/tour-root.d.ts +15 -0
  210. package/dist/tour/root/tour-root.js +20 -1
  211. package/dist/tour/stop/tour-stop.d.ts +3 -0
  212. package/dist/tour/stop/tour-stop.js +3 -3
  213. package/dist/tree/root/tree-root.js +2 -2
  214. package/dist/trend/figure.d.ts +16 -0
  215. package/dist/trend/figure.js +8 -0
  216. package/dist/trend/root/trend-root.d.ts +41 -2
  217. package/dist/trend/root/trend-root.js +29 -3
  218. package/dist/video/root/extra-controls.d.ts +12 -0
  219. package/dist/video/root/extra-controls.js +6 -2
  220. package/dist/video/root/video-root.d.ts +5 -1
  221. package/dist/video-playlist/root/video-playlist-root.d.ts +11 -1
  222. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  223. package/dist/virtual-list/fill.d.ts +11 -0
  224. package/dist/virtual-list/fill.js +9 -0
  225. package/dist/virtual-list/keys.d.ts +16 -7
  226. package/dist/virtual-list/keys.js +17 -6
  227. package/dist/virtual-list/plan.d.ts +17 -1
  228. package/dist/virtual-list/plan.js +13 -2
  229. package/dist/virtual-list/root/virtual-list-root.d.ts +25 -3
  230. package/dist/virtual-list/root/virtual-list-root.js +48 -24
  231. package/lint.json +1 -1
  232. package/package.json +5 -5
  233. package/types/jsx.d.ts +2979 -738
  234. package/types/svelte.d.ts +1 -0
  235. package/types/vue.d.ts +619 -202
  236. package/dist/color-picker/swatches.d.ts +0 -12
  237. package/dist/color-picker/swatches.js +0 -26
  238. package/dist/rating/steps.d.ts +0 -7
  239. package/dist/rating/steps.js +0 -9
@@ -4,12 +4,13 @@ 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 { CommandController, customElement, deepActiveElement, deepContains, drawDismiss, drawDismissGlyph, FullscreenController, fillTemplate, HelixElement, hostAria, IdleController, inlineStep, isRtl, keyClaimed, lockScroll, MixinControllerActive, MixinControllerOpen, resolveActiveItem, SwipeController, swipeStep, unlockScroll, whenHydrated, } from '@fusion.dev/helix-core';
8
- import { caretLeft, caretRight } from '@fusion.dev/helix-icons';
7
+ import { CommandController, customElement, DelayController, deepActiveElement, deepContains, drawDismiss, drawDismissGlyph, FullscreenController, fillTemplate, HelixElement, hostAria, IdleController, inlineStep, isRtl, keyClaimed, lockScroll, MixinControllerActive, MixinControllerOpen, resolveActiveItem, SwipeController, swipeStep, unlockScroll, whenHydrated, } from '@fusion.dev/helix-core';
8
+ import { caretLeft, caretRight, minus, plus } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
12
  import { HelixHeadlessLightboxItem } from '../item/lightbox-item.js';
13
+ import { percentOf } from '../zoom.js';
13
14
  import { ZoomController } from '../zoom-controller.js';
14
15
  let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerActive(MixinControllerOpen(HelixElement)) {
15
16
  constructor() {
@@ -26,9 +27,15 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
26
27
  this.slideLabel = '{index} of {count}';
27
28
  this.slideRoleLabel = 'slide';
28
29
  this.statusLabel = '{index} of {count}';
30
+ this.zoomInLabel = 'Zoom in';
31
+ this.zoomOutLabel = 'Zoom out';
32
+ this.zoomStatusLabel = 'Zoomed to {percent}%';
29
33
  this.resting = false;
30
34
  this.filled = false;
31
35
  this.announcement = '';
36
+ this.zoomScale = 1;
37
+ this.zoomSaid = new DelayController(this, { renders: false });
38
+ this.quietZoom = false;
32
39
  this.swipes = new SwipeController(this, {
33
40
  axis: () => (this.open && !this.zoomer.engaged ? 'x' : undefined),
34
41
  end: (dx, _dy, vx) => {
@@ -126,13 +133,26 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
126
133
  viewport?.style.setProperty('--lightbox-pan-y', zoomed ? `${Math.round(pan.y)}px` : '');
127
134
  this.toggleState('zoomed', zoomed);
128
135
  this.idle.stir();
136
+ if (scale !== this.zoomScale) {
137
+ this.zoomScale = scale;
138
+ this.sayZoom();
139
+ }
140
+ }
141
+ sayZoom() {
142
+ if (this.quietZoom || !this.open || this.zoomStatusLabel === '') {
143
+ this.zoomSaid.cancel();
144
+ return;
145
+ }
146
+ this.zoomSaid.start(400, () => {
147
+ this.announcement = fillTemplate(this.zoomStatusLabel, { percent: percentOf(this.zoomScale) });
148
+ });
129
149
  }
130
150
  zoomTo(scale) {
131
151
  this.zoomer.zoomTo(scale);
132
152
  }
133
153
  chromeHasFocus() {
134
154
  const active = deepActiveElement();
135
- const own = this.shadowRoot?.activeElement?.matches('[part="previous"], [part="next"], [part="dismiss"]');
155
+ const own = this.shadowRoot?.activeElement?.matches('[part="previous"], [part="next"], [part="dismiss"], [part="zoom-in"], [part="zoom-out"]');
136
156
  return (own === true ||
137
157
  this.knownChildren.some((child) => (child.slot === 'header' || child.slot === 'footer') && deepContains(child, active)));
138
158
  }
@@ -178,12 +198,11 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
178
198
  if (this.maxZoom <= 1) {
179
199
  return false;
180
200
  }
181
- const scale = this.zoomer.scale;
182
201
  if (key === '+' || key === '=') {
183
- this.zoomer.zoomTo(scale * 1.5);
202
+ this.zoomIn();
184
203
  }
185
204
  else if (key === '-') {
186
- this.zoomer.zoomTo(scale / 1.5);
205
+ this.zoomOut();
187
206
  }
188
207
  else if (key === '0') {
189
208
  this.zoomer.reset();
@@ -193,6 +212,12 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
193
212
  }
194
213
  return true;
195
214
  }
215
+ zoomIn() {
216
+ this.zoomer.zoomTo(this.zoomer.scale * 1.5);
217
+ }
218
+ zoomOut() {
219
+ this.zoomer.zoomTo(this.zoomer.scale / 1.5);
220
+ }
196
221
  toEnd(at) {
197
222
  const target = this.items[at];
198
223
  if (target && target !== this.shown) {
@@ -209,7 +234,9 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
209
234
  this.toggleState('idle', this.resting);
210
235
  this.toggleState('zoomable', this.maxZoom > 1);
211
236
  if (changed.has('active') || (changed.has('open') && !this.open)) {
237
+ this.quietZoom = true;
212
238
  this.zoomer.reset();
239
+ this.quietZoom = false;
213
240
  }
214
241
  this.say(changed);
215
242
  }
@@ -305,11 +332,11 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
305
332
  @pointerleave=${this.idle.leave}
306
333
  @pointermove=${this.idle.stir}
307
334
  >
308
- <slot name="header"></slot>
335
+ <slot name="header" part="header"></slot>
309
336
  <div part="viewport">
310
337
  <slot></slot>
311
338
  </div>
312
- <slot name="footer"></slot>
339
+ <slot name="footer" part="footer"></slot>
313
340
  ${this.controls ? this.renderControls() : nothing}
314
341
  ${this.counter ? html `<span part="counter" aria-hidden="true">${this.place()}</span>` : nothing}
315
342
  <span part="live" role="status" aria-live="polite">${this.announcement || nothing}</span>
@@ -342,12 +369,41 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
342
369
  </svg>
343
370
  </slot>
344
371
  </button>
372
+ ${this.maxZoom > 1 ? this.renderZoomControls() : nothing}
345
373
  ${drawDismiss({
346
374
  label: this.dismissLabel,
347
375
  mark: html `<slot name="dismiss-icon">${drawDismissGlyph()}</slot>`,
348
376
  onDismiss: () => this.close(),
349
377
  })}`;
350
378
  }
379
+ renderZoomControls() {
380
+ return html `<button
381
+ part="zoom-out"
382
+ type="button"
383
+ aria-label=${this.zoomOutLabel}
384
+ aria-disabled=${this.zoomScale <= 1 ? 'true' : nothing}
385
+ @click=${() => this.zoomOut()}
386
+ >
387
+ <slot name="zoom-out-icon">
388
+ <svg part="zoom-out-mark" fill="currentColor" viewBox=${minus.viewBox} aria-hidden="true">
389
+ ${unsafeSVG(minus.content)}
390
+ </svg>
391
+ </slot>
392
+ </button>
393
+ <button
394
+ part="zoom-in"
395
+ type="button"
396
+ aria-label=${this.zoomInLabel}
397
+ aria-disabled=${this.zoomScale >= this.maxZoom ? 'true' : nothing}
398
+ @click=${() => this.zoomIn()}
399
+ >
400
+ <slot name="zoom-in-icon">
401
+ <svg part="zoom-in-mark" fill="currentColor" viewBox=${plus.viewBox} aria-hidden="true">
402
+ ${unsafeSVG(plus.content)}
403
+ </svg>
404
+ </slot>
405
+ </button>`;
406
+ }
351
407
  };
352
408
  __decorate([
353
409
  property({ reflect: true, type: Boolean })
@@ -385,6 +441,15 @@ __decorate([
385
441
  __decorate([
386
442
  property({ attribute: 'status-label' })
387
443
  ], HelixHeadlessLightbox.prototype, "statusLabel", void 0);
444
+ __decorate([
445
+ property({ attribute: 'zoom-in-label' })
446
+ ], HelixHeadlessLightbox.prototype, "zoomInLabel", void 0);
447
+ __decorate([
448
+ property({ attribute: 'zoom-out-label' })
449
+ ], HelixHeadlessLightbox.prototype, "zoomOutLabel", void 0);
450
+ __decorate([
451
+ property({ attribute: 'zoom-status-label' })
452
+ ], HelixHeadlessLightbox.prototype, "zoomStatusLabel", void 0);
388
453
  __decorate([
389
454
  state()
390
455
  ], HelixHeadlessLightbox.prototype, "resting", void 0);
@@ -394,6 +459,9 @@ __decorate([
394
459
  __decorate([
395
460
  state()
396
461
  ], HelixHeadlessLightbox.prototype, "announcement", void 0);
462
+ __decorate([
463
+ state()
464
+ ], HelixHeadlessLightbox.prototype, "zoomScale", void 0);
397
465
  HelixHeadlessLightbox = __decorate([
398
466
  customElement('helix-headless-lightbox')
399
467
  ], HelixHeadlessLightbox);
@@ -20,3 +20,5 @@ export declare const zoomAt: (at: Offset, from: number, to: number, pan: Offset)
20
20
  export declare const spread: (a: Offset, b: Offset) => number;
21
21
  /** The point halfway between two fingers. */
22
22
  export declare const between: (a: Offset, b: Offset) => Offset;
23
+ /** A scale as a whole percent, which is how a reader hears it: 1.5 is 150. */
24
+ export declare const percentOf: (scale: number) => number;
@@ -1,9 +1,10 @@
1
- const clamp = (value, low, high) => Math.min(Math.max(value, low), high) || 0;
2
- export const clampScale = (scale, max) => clamp(scale, 1, Math.max(max, 1));
1
+ import { clamp } from '@fusion.dev/helix-core';
2
+ const held = (value, low, high) => clamp(value, low, high) || 0;
3
+ export const clampScale = (scale, max) => held(scale, 1, Math.max(max, 1));
3
4
  export const clampPan = (pan, scale, width, height) => {
4
5
  const x = ((scale - 1) * width) / 2;
5
6
  const y = ((scale - 1) * height) / 2;
6
- return { x: clamp(pan.x, -x, x), y: clamp(pan.y, -y, y) };
7
+ return { x: held(pan.x, -x, x), y: held(pan.y, -y, y) };
7
8
  };
8
9
  export const zoomAt = (at, from, to, pan) => ({
9
10
  x: at.x - ((at.x - pan.x) * to) / from,
@@ -14,3 +15,4 @@ export const between = (a, b) => ({
14
15
  x: (a.x + b.x) / 2,
15
16
  y: (a.y + b.y) / 2,
16
17
  });
18
+ export const percentOf = (scale) => Math.round(scale * 100);
@@ -15,6 +15,10 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
15
15
  *
16
16
  * Every series shares one scale, as in the bar chart.
17
17
  *
18
+ * `dashed` names the series drawn as a dashed line, such as a forecast or a
19
+ * target beside the readings: `dashed="Forecast"`. Each takes a
20
+ * `line-dashed` part beside `line`.
21
+ *
18
22
  * `area` fills each series down to the floor on its own, so the fills overlap.
19
23
  * For a total made of parts, use `Area Chart`, which stacks the series so the
20
24
  * top edge is the total and each band is a share.
@@ -47,6 +51,7 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
47
51
  * @csspart label - One reading's name.
48
52
  * @csspart labels - The row of reading names.
49
53
  * @csspart line - The line for one series.
54
+ * @csspart line-dashed - A `line` whose series `dashed` names. Set alongside `line`.
50
55
  * @csspart live - The readout: a polite live region that says the reading the keys moved to. Hide it visually.
51
56
  * @csspart mark - The invisible point the tooltip is anchored to.
52
57
  * @csspart marker - The mark on one reading, under `markers`, and on a reading with a blank on either side. Placed with `inset-inline-start` and `inset-block-start`, and carries its series color as `--chart-series`.
@@ -112,6 +117,15 @@ export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base
112
117
  * here, which is what lets a line take the reading aid.
113
118
  */
114
119
  curve?: Curve;
120
+ /**
121
+ * The series drawn as a dashed line, by their headings, separated by
122
+ * commas: `dashed="Forecast, Target"`.
123
+ *
124
+ * For a series that is not a reading, such as a forecast or a goal, drawn
125
+ * beside the ones that are. Each takes a `line-dashed` part beside `line`,
126
+ * which a stylesheet draws dashed. Absent, every line is solid.
127
+ */
128
+ dashed: string;
115
129
  /**
116
130
  * Which rules are drawn across the plot, if any.
117
131
  *
@@ -276,6 +290,8 @@ export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base
276
290
  * start, as the drawing is mirrored.
277
291
  */
278
292
  protected renderMarkers(series: Series[], x: (value: number) => number, y: (value: number) => number): TemplateResult | typeof nothing;
293
+ /** Whether `dashed` names this series, matched as a heading is. */
294
+ private dashes;
279
295
  /**
280
296
  * One series, its wash first so the line is drawn over its own fill.
281
297
  *
@@ -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 { byRow, CHART_PLOT, customElement, drawTooltip, extent, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, ticks, unbroken, } from '@fusion.dev/helix-core';
7
+ import { byRow, CHART_PLOT, commaList, customElement, drawTooltip, extent, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, ticks, unbroken, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
@@ -12,6 +12,7 @@ let HelixHeadlessLineChart = class HelixHeadlessLineChart extends MixinChartSour
12
12
  constructor() {
13
13
  super(...arguments);
14
14
  this.area = false;
15
+ this.dashed = '';
15
16
  this.markers = false;
16
17
  this.zero = false;
17
18
  }
@@ -166,6 +167,10 @@ let HelixHeadlessLineChart = class HelixHeadlessLineChart extends MixinChartSour
166
167
  style=${`--chart-series: var(--chart-series-${seriesIndex(depth)}); inset-inline-start: ${(x(point.at) / PLOT_WIDTH) * 100}%; inset-block-start: ${(y(point.value) / PLOT_HEIGHT) * 100}%`}
167
168
  ></span>`)}`;
168
169
  }
170
+ dashes(name) {
171
+ const key = seriesKey(name);
172
+ return this.dashed !== '' && commaList(this.dashed).some((one) => seriesKey(one) === key);
173
+ }
169
174
  renderSeries(one, depth, x, y) {
170
175
  if (!this.showsSeries(one.name)) {
171
176
  return html `${nothing}`;
@@ -199,7 +204,7 @@ let HelixHeadlessLineChart = class HelixHeadlessLineChart extends MixinChartSour
199
204
  d=${path}
200
205
  data-series=${index}
201
206
  fill="none"
202
- part="line"
207
+ part="line${this.dashes(one.name) ? ' line-dashed' : ''}"
203
208
  style="--chart-series: var(--chart-series-${index})"
204
209
  />`;
205
210
  }
@@ -235,6 +240,9 @@ __decorate([
235
240
  __decorate([
236
241
  property({ reflect: true })
237
242
  ], HelixHeadlessLineChart.prototype, "curve", void 0);
243
+ __decorate([
244
+ property()
245
+ ], HelixHeadlessLineChart.prototype, "dashed", void 0);
238
246
  __decorate([
239
247
  property()
240
248
  ], HelixHeadlessLineChart.prototype, "gridlines", void 0);
@@ -27,12 +27,14 @@ import { type ListSelectionBehavior, type ListSelectionMode } from '../selection
27
27
  * |---|---|
28
28
  * | Down, Up | Moves to the next or the previous row. Under `replace`, selects it. |
29
29
  * | Home, End | Moves to the first or the last row. Under `replace`, selects it. |
30
+ * | Page Down, Page Up | Moves a box's height of rows, so the row at one edge lands at the other. Under `replace`, selects the row it lands on. |
30
31
  * | A letter or a word | Moves to the next row whose words start with what you typed. |
31
32
  * | Left, Right | Moves between the row and the controls in its `end` slot. |
32
33
  * | Space | Selects the row, or lets it go. |
33
- * | Shift with Down, Up, Home, End or Space | Selects every row from the last one pressed, in `multiple`. |
34
- * | Control or Command with Down, Up, Home or End | Moves without selecting, under `replace`. |
34
+ * | Shift with Down, Up, Home, End, Page Down, Page Up or Space | Selects every row from the last one pressed, in `multiple`. |
35
+ * | Control or Command with Down, Up, Home, End, Page Down or Page Up | Moves without selecting, under `replace`. |
35
36
  * | Control or Command with A | Selects every row, in `multiple`. |
37
+ * | Escape | Lets go of every selected row, asking first, and keeps the key from the dialog or menu around the list. With nothing selected, the key goes on to them. |
36
38
  * | Enter | Acts on the row: follows its link, or sends `helix-action` from it. |
37
39
  *
38
40
  * A press with Shift selects every row from the last one pressed, and a press
@@ -185,6 +187,12 @@ export declare class HelixHeadlessList extends HelixElement {
185
187
  * nothing was pressed yet.
186
188
  */
187
189
  private arrive;
190
+ /**
191
+ * Page Down or Page Up from the row: focus moves a box's height of rows, the
192
+ * list's own where it scrolls and the window's where it does not, and lands
193
+ * the way an arrow does, so `replace` and Shift choose as they do there.
194
+ */
195
+ private page;
188
196
  /** Acts on the row: follows its link, or says it was acted on. */
189
197
  protected act(row: HelixHeadlessListItem): void;
190
198
  /** Left and Right inside a row: the cell, then each control in `end`, and back. */
@@ -4,12 +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 { commaList, customElement, HelixActionEvent, HelixBeforeChangeEvent, HelixElement, hostAria, inlineStep, KeyboardNavigationController, requestChange, rovingStop, typeaheadBy, whenHydrated, } from '@fusion.dev/helix-core';
7
+ import { commaList, customElement, HelixActionEvent, HelixBeforeChangeEvent, HelixElement, hostAria, inlineStep, KeyboardNavigationController, requestChange, rovingStop, selectsAll, typeaheadByWords, whenHydrated, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessListItem } from '../item/list-item.js';
11
11
  import { numbering } from '../numbering.js';
12
- import { choiceValue, rowStop, } from '../selection.js';
12
+ import { choiceValue, pageReach, rowStop, } from '../selection.js';
13
13
  const PRESSABLE = 'a[href], button, input, label, select, textarea, [tabindex]';
14
14
  let HelixHeadlessList = class HelixHeadlessList extends HelixElement {
15
15
  constructor() {
@@ -23,15 +23,19 @@ let HelixHeadlessList = class HelixHeadlessList extends HelixElement {
23
23
  origin: 'focus',
24
24
  reserved: () => ['ArrowLeft', 'ArrowRight'],
25
25
  selects: () => false,
26
- typeahead: typeaheadBy((item) => item.words),
26
+ typeahead: typeaheadByWords(),
27
27
  wrap: false,
28
28
  });
29
29
  this.handleKeyCapture = (event) => {
30
30
  this.moving = { extend: event.shiftKey, toggle: event.ctrlKey || event.metaKey };
31
31
  };
32
32
  this.handleKeydown = (event) => {
33
- this.moving = undefined;
34
33
  const row = this.interactive ? this.rowOf(event) : undefined;
34
+ if (row && !row.disabled && !event.altKey && /^Page(Down|Up)$/.test(event.key)) {
35
+ event.preventDefault();
36
+ this.page(row, event.key === 'PageDown' ? 1 : -1);
37
+ }
38
+ this.moving = undefined;
35
39
  if (!row || row.disabled || event.altKey) {
36
40
  return;
37
41
  }
@@ -43,11 +47,16 @@ let HelixHeadlessList = class HelixHeadlessList extends HelixElement {
43
47
  if (event.composedPath()[0] !== row.cell) {
44
48
  return;
45
49
  }
46
- if (toggle && event.key.toLowerCase() === 'a' && this.selectionMode === 'multiple') {
50
+ if (selectsAll(event) && this.selectionMode === 'multiple') {
47
51
  event.preventDefault();
48
52
  const every = this.reachable.map((one) => this.valueFor(one)).join(', ');
49
53
  this.choose(every === this.value ? null : every);
50
54
  }
55
+ else if (event.key === 'Escape' && this.value !== '' && this.selectionMode !== 'none') {
56
+ event.preventDefault();
57
+ event.stopPropagation();
58
+ this.choose('');
59
+ }
51
60
  else if (event.key === 'Enter' && !toggle) {
52
61
  event.preventDefault();
53
62
  this.act(row);
@@ -188,6 +197,18 @@ let HelixHeadlessList = class HelixHeadlessList extends HelixElement {
188
197
  }
189
198
  this.choose(next);
190
199
  }
200
+ page(row, step) {
201
+ const rows = this.reachable;
202
+ const edges = rows.map((one) => {
203
+ const box = one.getBoundingClientRect();
204
+ return { bottom: box.bottom, top: box.top };
205
+ });
206
+ const room = this.scrollHeight > this.clientHeight + 1 ? this.clientHeight : window.innerHeight;
207
+ const target = rows[pageReach(edges, rows.indexOf(row), room, step)];
208
+ if (target && target !== row) {
209
+ target.focusItem();
210
+ }
211
+ }
191
212
  act(row) {
192
213
  if (row.disabled) {
193
214
  return;
@@ -47,3 +47,20 @@ export declare const choiceValue: ({ anchor, arrived, behavior, extend, mode, pr
47
47
  * and from the cell going back reaches the last control.
48
48
  */
49
49
  export declare const rowStop: (count: number, at: number, step: -1 | 1) => number;
50
+ /** Where a row sits along the list, in pixels, as the page keys measure it. */
51
+ export interface RowEdges {
52
+ /** The row's top edge. */
53
+ top: number;
54
+ /** The row's bottom edge. */
55
+ bottom: number;
56
+ }
57
+ /**
58
+ * Where Page Down (`step` 1) or Page Up (`step` -1) goes from row `at`: the
59
+ * farthest row that fits whole in `room` pixels together with row `at`, so the
60
+ * row at one edge of the box lands at the other, and at least the next row.
61
+ *
62
+ * Measured rather than counted, because a list's rows differ in height. Rows of
63
+ * one height `h` move `floor(room / h) - 1` rows, which is what Data Table and
64
+ * Virtual List move. At either end it stays on row `at`.
65
+ */
66
+ export declare const pageReach: (rows: readonly RowEdges[], at: number, room: number, step: -1 | 1) => number;
@@ -28,3 +28,19 @@ export const rowStop = (count, at, step) => {
28
28
  }
29
29
  return next < 0 ? count - 1 : next;
30
30
  };
31
+ export const pageReach = (rows, at, room, step) => {
32
+ const from = rows[at];
33
+ if (!from) {
34
+ return at;
35
+ }
36
+ let reach = at;
37
+ for (let next = at + step; next >= 0 && next < rows.length; next += step) {
38
+ const row = rows[next];
39
+ const span = step === 1 ? row.bottom - from.top : from.bottom - row.top;
40
+ if (span > room && next !== at + step) {
41
+ break;
42
+ }
43
+ reach = next;
44
+ }
45
+ return reach;
46
+ };
@@ -27,7 +27,9 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
27
27
  * highlighted option, Shift with an arrow adds the option it moves to, Shift
28
28
  * with Home or End adds every option from the highlight to that end, and
29
29
  * Control+A or Command+A chooses every option, or none where all are chosen.
30
- * In a filter field, Space, Home, End and letters edit the text.
30
+ * In a filter field, Space, Home, End and letters edit the text, and with
31
+ * `multiple`, Control with Shift and Home or End adds every option from the
32
+ * highlight to that end.
31
33
  *
32
34
  * An option with `href` is a link: Enter or a press follows it and selects
33
35
  * nothing, and moving onto it with the arrows does not follow it.
@@ -286,7 +288,8 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
286
288
  */
287
289
  get options(): HelixHeadlessOption[];
288
290
  /**
289
- * Walks the tree, and walks *through* a slot when it meets one.
291
+ * The elements of one kind in the list: its options, its groups or its
292
+ * rules. Walks the tree, and walks *through* a slot when it meets one.
290
293
  *
291
294
  * Through a slot because that is how this gets composed. `Select` renders a
292
295
  * listbox in its own shadow root with a slot inside it, so the options are
@@ -294,7 +297,7 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
294
297
  * `querySelectorAll` finds the slot and no options at all, and the whole
295
298
  * component comes up empty.
296
299
  */
297
- private collect;
300
+ private among;
298
301
  /** The groups, found the same way and for the same reason. */
299
302
  private get groups();
300
303
  /** The options a filter has left showing. */
@@ -455,8 +458,9 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
455
458
  private askedAt;
456
459
  /**
457
460
  * Asks for more once the reader is within a screen of the end, React Aria's
458
- * `onLoadMore`: once for each number of options, so a scroll that goes on
459
- * asks again only after the next page arrived, and never while loading.
461
+ * `onLoadMore`, by core's `endInSight`, the rule Virtual List asks too: once
462
+ * for each number of options, so a scroll that goes on asks again only after
463
+ * the next page arrived, and never while loading.
460
464
  */
461
465
  private readonly handleScroll;
462
466
  /** Requests a new filter, and returns whether the request went through. */
@@ -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, countLabel, customElement, DelayController, DisabledCascadeController, HelixFilterEvent, HelixFormElement, HelixLoadMoreEvent, hostAria, MixinCollection, MixinControllerValue, MixinField, rangeAdd, reportValues, requestChange, scrollItemIntoView, Typeahead, typesCharacter, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, countLabel, customElement, DelayController, DisabledCascadeController, endInSight, HelixFilterEvent, HelixFormElement, HelixLoadMoreEvent, hostAria, MixinCollection, MixinControllerValue, MixinField, rangeAdd, reportValues, requestChange, scrollItemIntoView, selectsAll, Typeahead, typesCharacter, } from '@fusion.dev/helix-core';
8
8
  import { magnifyingGlass } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -119,11 +119,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
119
119
  }
120
120
  return;
121
121
  }
122
- if (this.multiple &&
123
- !typing &&
124
- event.key.toLowerCase() === 'a' &&
125
- (event.ctrlKey || event.metaKey) &&
126
- !event.altKey) {
122
+ if (this.multiple && !typing && selectsAll(event)) {
127
123
  event.preventDefault();
128
124
  const reachable = shown.map((option) => option.value);
129
125
  const all = reachable.every((value) => this.values.includes(value));
@@ -139,7 +135,12 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
139
135
  }
140
136
  return;
141
137
  }
142
- if (!(typing ? FIELD_MOVES : MOVES).has(event.key)) {
138
+ const fieldRange = typing &&
139
+ this.multiple &&
140
+ event.shiftKey &&
141
+ (event.ctrlKey || event.metaKey) &&
142
+ (event.key === 'Home' || event.key === 'End');
143
+ if (!fieldRange && !(typing ? FIELD_MOVES : MOVES).has(event.key)) {
143
144
  return;
144
145
  }
145
146
  event.preventDefault();
@@ -170,7 +171,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
170
171
  if (target) {
171
172
  this.arrive(target.value);
172
173
  const ranged = event.key === 'Home' || event.key === 'End';
173
- if (this.multiple && !typing && event.shiftKey && ranged) {
174
+ if (this.multiple && (!typing || fieldRange) && event.shiftKey && ranged) {
174
175
  const taken = rangeAdd(this.values, shown.map((option) => option.value), at, next);
175
176
  if (taken) {
176
177
  this.requestValue(taken.join(','));
@@ -197,13 +198,17 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
197
198
  this.handleScroll = (event) => {
198
199
  const items = event.currentTarget;
199
200
  const count = this.options.length;
200
- if (this.loading || count === this.askedAt || items.clientHeight === 0) {
201
+ if (this.loading ||
202
+ !endInSight({
203
+ askedAt: this.askedAt,
204
+ count,
205
+ remaining: items.scrollHeight - items.scrollTop - items.clientHeight,
206
+ screen: items.clientHeight,
207
+ })) {
201
208
  return;
202
209
  }
203
- if (items.scrollHeight - items.scrollTop - items.clientHeight <= items.clientHeight) {
204
- this.askedAt = count;
205
- this.dispatchEvent(new HelixLoadMoreEvent());
206
- }
210
+ this.askedAt = count;
211
+ this.dispatchEvent(new HelixLoadMoreEvent());
207
212
  };
208
213
  this.handleFilterKeydown = (event) => {
209
214
  if (event.key === 'Enter' || FIELD_MOVES.has(event.key)) {
@@ -293,40 +298,24 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
293
298
  return this.multiple ? 'select-multiple' : 'select-one';
294
299
  }
295
300
  get options() {
296
- return this.collect(this.children);
301
+ return this.among((node) => node instanceof HelixHeadlessOption, this.children);
297
302
  }
298
- collect(nodes) {
303
+ among(is, nodes) {
299
304
  const found = [];
300
305
  for (const node of nodes) {
301
- if (node instanceof HelixHeadlessOption) {
306
+ if (is(node)) {
302
307
  found.push(node);
303
308
  }
304
- else if (node instanceof HTMLSlotElement) {
305
- found.push(...this.collect(node.assignedElements({ flatten: true })));
306
- }
307
309
  else {
308
- found.push(...this.collect(node.children));
310
+ found.push(...this.among(is, node instanceof HTMLSlotElement
311
+ ? node.assignedElements({ flatten: true })
312
+ : node.children));
309
313
  }
310
314
  }
311
315
  return found;
312
316
  }
313
317
  get groups() {
314
- const found = [];
315
- const walk = (nodes) => {
316
- for (const node of nodes) {
317
- if (node instanceof HelixHeadlessOptionGroup) {
318
- found.push(node);
319
- }
320
- if (node instanceof HTMLSlotElement) {
321
- walk(node.assignedElements({ flatten: true }));
322
- }
323
- else {
324
- walk(node.children);
325
- }
326
- }
327
- };
328
- walk(this.children);
329
- return found;
318
+ return this.among((node) => node instanceof HelixHeadlessOptionGroup, this.children);
330
319
  }
331
320
  get shown() {
332
321
  return this.options.filter((option) => !option.hidden);
@@ -398,9 +387,12 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
398
387
  option.selected = chosen.has(option.value);
399
388
  }
400
389
  for (const group of this.groups) {
401
- const inside = this.collect(group.children);
390
+ const inside = this.among((node) => node instanceof HelixHeadlessOption, group.children);
402
391
  group.hidden = inside.length > 0 && inside.every((option) => option.hidden);
403
392
  }
393
+ for (const rule of this.among((node) => node.localName.endsWith('-option-separator'), this.children)) {
394
+ rule.hidden = needle !== '';
395
+ }
404
396
  const shown = this.shown;
405
397
  const rescue = !this.parked && (needle !== '' || this.activeValue !== '');
406
398
  if (rescue && !shown.some((option) => option.value === this.activeValue)) {