@fusion.dev/helix-headless 0.146.0 → 0.148.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 (288) hide show
  1. package/custom-elements.json +9287 -1084
  2. package/dist/accordion/item/accordion-item.d.ts +43 -21
  3. package/dist/accordion/item/accordion-item.js +37 -11
  4. package/dist/accordion/root/accordion-root.d.ts +8 -2
  5. package/dist/accordion/root/accordion-root.js +6 -8
  6. package/dist/announcement/root/announcement-root.d.ts +66 -6
  7. package/dist/announcement/root/announcement-root.js +144 -13
  8. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  9. package/dist/area-chart/root/area-chart-root.js +14 -7
  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/count.d.ts +14 -0
  13. package/dist/badge/count.js +20 -0
  14. package/dist/badge/root/badge-root.d.ts +57 -6
  15. package/dist/badge/root/badge-root.js +77 -8
  16. package/dist/bar-chart/root/bar-chart-root.js +11 -20
  17. package/dist/bar-chart/stack.d.ts +10 -0
  18. package/dist/bar-chart/stack.js +8 -0
  19. package/dist/blockquote/root/blockquote-root.d.ts +66 -8
  20. package/dist/blockquote/root/blockquote-root.js +85 -8
  21. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  22. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  23. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -2
  24. package/dist/button/root/button-root.d.ts +24 -38
  25. package/dist/button/root/button-root.js +6 -37
  26. package/dist/button/submitter.d.ts +27 -0
  27. package/dist/button/submitter.js +25 -0
  28. package/dist/button-group/arrows.d.ts +14 -0
  29. package/dist/button-group/arrows.js +10 -0
  30. package/dist/button-group/root/button-group-root.d.ts +141 -3
  31. package/dist/button-group/root/button-group-root.js +142 -2
  32. package/dist/canvas/edge-pan.d.ts +61 -0
  33. package/dist/canvas/edge-pan.js +63 -0
  34. package/dist/canvas/node/canvas-node.d.ts +12 -2
  35. package/dist/canvas/node/canvas-node.js +6 -2
  36. package/dist/canvas/root/canvas-document.d.ts +10 -0
  37. package/dist/canvas/root/canvas-document.js +1 -0
  38. package/dist/canvas/root/canvas-root.d.ts +107 -5
  39. package/dist/canvas/root/canvas-root.js +140 -36
  40. package/dist/canvas/snap.d.ts +42 -0
  41. package/dist/canvas/snap.js +32 -0
  42. package/dist/card/root/card-root.d.ts +12 -1
  43. package/dist/card/root/card-root.js +11 -4
  44. package/dist/carousel/root/carousel-root.d.ts +4 -3
  45. package/dist/carousel/root/carousel-root.js +2 -6
  46. package/dist/chat/root/chat-root.d.ts +28 -4
  47. package/dist/chat/root/chat-root.js +12 -7
  48. package/dist/checkbox/root/checkbox-root.js +5 -7
  49. package/dist/checkbox-group/root/checkbox-group-root.d.ts +13 -0
  50. package/dist/checkbox-group/root/checkbox-group-root.js +16 -7
  51. package/dist/checkbox-group/select-all.d.ts +9 -4
  52. package/dist/checkbox-group/select-all.js +1 -1
  53. package/dist/code-block/root/code-block-root.d.ts +41 -18
  54. package/dist/code-block/root/code-block-root.js +21 -6
  55. package/dist/color-input/root/color-input-root.d.ts +11 -0
  56. package/dist/color-input/root/color-input-root.js +12 -5
  57. package/dist/color-picker/channels.js +1 -2
  58. package/dist/color-picker/root/color-picker-root.d.ts +32 -24
  59. package/dist/color-picker/root/color-picker-root.js +99 -55
  60. package/dist/color-picker/wheel.d.ts +28 -0
  61. package/dist/color-picker/wheel.js +15 -0
  62. package/dist/combobox/root/combobox-root.d.ts +83 -11
  63. package/dist/combobox/root/combobox-root.js +81 -18
  64. package/dist/command/root/command-root.d.ts +7 -4
  65. package/dist/command/root/command-root.js +2 -19
  66. package/dist/copy-button/root/copy-button-root.d.ts +16 -1
  67. package/dist/copy-button/root/copy-button-root.js +7 -2
  68. package/dist/data-table/keys.d.ts +0 -18
  69. package/dist/data-table/keys.js +1 -11
  70. package/dist/data-table/root/data-table-root.js +29 -50
  71. package/dist/date-input/root/date-input-root.d.ts +8 -1
  72. package/dist/date-input/root/date-input-root.js +8 -5
  73. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  74. package/dist/date-picker/root/date-picker-root.js +21 -16
  75. package/dist/description-list/item/description-list-item.d.ts +22 -4
  76. package/dist/description-list/item/description-list-item.js +14 -4
  77. package/dist/divider/root/divider-root.d.ts +45 -16
  78. package/dist/divider/root/divider-root.js +28 -4
  79. package/dist/donut-chart/root/donut-chart-root.js +13 -6
  80. package/dist/donut-chart/slice-label.d.ts +35 -0
  81. package/dist/donut-chart/slice-label.js +11 -0
  82. package/dist/drawer/root/drawer-root.d.ts +4 -2
  83. package/dist/drawer/root/drawer-root.js +15 -3
  84. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  85. package/dist/drilldown/item/drilldown-item.js +38 -4
  86. package/dist/drilldown/root/drilldown-root.js +3 -2
  87. package/dist/element-events.generated.js +5 -1
  88. package/dist/empty/root/empty-root.d.ts +46 -2
  89. package/dist/empty/root/empty-root.js +50 -5
  90. package/dist/file-input/root/file-input-root.d.ts +34 -6
  91. package/dist/file-input/root/file-input-root.js +55 -21
  92. package/dist/file-input/status.d.ts +24 -0
  93. package/dist/file-input/status.js +11 -0
  94. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  95. package/dist/filter-input/chip/filter-chip.js +10 -2
  96. package/dist/filter-input/root/filter-input-root.d.ts +8 -0
  97. package/dist/filter-input/root/filter-input-root.js +17 -27
  98. package/dist/format-date/fields.d.ts +20 -0
  99. package/dist/format-date/fields.js +14 -0
  100. package/dist/format-date/options.d.ts +5 -5
  101. package/dist/format-date/options.js +10 -39
  102. package/dist/format-date/root/format-date-root.d.ts +53 -4
  103. package/dist/format-date/root/format-date-root.js +38 -50
  104. package/dist/graph/root/graph-root.d.ts +23 -5
  105. package/dist/graph/root/graph-root.js +17 -20
  106. package/dist/heatmap/root/buckets.d.ts +21 -0
  107. package/dist/heatmap/root/buckets.js +34 -0
  108. package/dist/heatmap/root/heatmap-root.d.ts +76 -3
  109. package/dist/heatmap/root/heatmap-root.js +105 -20
  110. package/dist/icon/index.d.ts +1 -1
  111. package/dist/icon/index.js +1 -1
  112. package/dist/icon/registry.d.ts +26 -1
  113. package/dist/icon/registry.js +12 -1
  114. package/dist/icon/root/icon-root.d.ts +13 -0
  115. package/dist/icon/root/icon-root.js +23 -2
  116. package/dist/icon/root/index.d.ts +1 -1
  117. package/dist/icon/root/index.js +1 -1
  118. package/dist/image/root/image-root.d.ts +20 -0
  119. package/dist/image/root/image-root.js +6 -1
  120. package/dist/input/root/input-root.d.ts +23 -2
  121. package/dist/input/root/input-root.js +12 -8
  122. package/dist/kbd/root/kbd-root.d.ts +20 -3
  123. package/dist/kbd/root/kbd-root.js +23 -50
  124. package/dist/legend/hidden-series.d.ts +10 -2
  125. package/dist/legend/hidden-series.js +5 -2
  126. package/dist/legend/root/legend-root.d.ts +25 -7
  127. package/dist/legend/root/legend-root.js +25 -26
  128. package/dist/lightbox/root/lightbox-root.d.ts +50 -5
  129. package/dist/lightbox/root/lightbox-root.js +76 -8
  130. package/dist/lightbox/zoom.d.ts +2 -0
  131. package/dist/lightbox/zoom.js +5 -3
  132. package/dist/line-chart/root/line-chart-root.d.ts +16 -0
  133. package/dist/line-chart/root/line-chart-root.js +10 -2
  134. package/dist/list/root/list-root.d.ts +10 -2
  135. package/dist/list/root/list-root.js +31 -24
  136. package/dist/list/selection.d.ts +17 -0
  137. package/dist/list/selection.js +16 -0
  138. package/dist/listbox/root/listbox-root.d.ts +125 -26
  139. package/dist/listbox/root/listbox-root.js +156 -166
  140. package/dist/map/marker/map-marker.d.ts +2 -1
  141. package/dist/map/root/map-root.d.ts +40 -2
  142. package/dist/map/root/map-root.js +64 -23
  143. package/dist/megamenu/hand-over.d.ts +75 -0
  144. package/dist/megamenu/hand-over.js +67 -0
  145. package/dist/megamenu/root/megamenu-root.d.ts +38 -14
  146. package/dist/megamenu/root/megamenu-root.js +29 -50
  147. package/dist/menu/item/menu-item.js +4 -4
  148. package/dist/menu/root/menu-root.d.ts +5 -2
  149. package/dist/meter/root/meter-root.d.ts +42 -0
  150. package/dist/meter/root/meter-root.js +33 -7
  151. package/dist/navigation/root/navigation-root.js +5 -8
  152. package/dist/notifications/group/notification-group.d.ts +8 -1
  153. package/dist/notifications/group/notification-group.js +12 -6
  154. package/dist/notifications/notification/notification.d.ts +12 -1
  155. package/dist/notifications/notification/notification.js +3 -1
  156. package/dist/notifications/root/notifications-root.d.ts +48 -3
  157. package/dist/notifications/root/notifications-root.js +36 -6
  158. package/dist/number-input/grain.d.ts +28 -0
  159. package/dist/number-input/grain.js +14 -0
  160. package/dist/number-input/root/number-input-root.d.ts +17 -1
  161. package/dist/number-input/root/number-input-root.js +29 -18
  162. package/dist/object-inspector/root/object-inspector-root.d.ts +17 -6
  163. package/dist/object-inspector/root/object-inspector-root.js +31 -20
  164. package/dist/option/index.d.ts +1 -0
  165. package/dist/option/index.js +1 -0
  166. package/dist/option/separator/option-separator.d.ts +30 -0
  167. package/dist/option/separator/option-separator.js +21 -0
  168. package/dist/outline/root/outline-root.d.ts +12 -0
  169. package/dist/outline/root/outline-root.js +5 -19
  170. package/dist/pagination/size/pagination-size.d.ts +6 -0
  171. package/dist/pagination/size/pagination-size.js +5 -3
  172. package/dist/pin-input/code.d.ts +51 -1
  173. package/dist/pin-input/code.js +37 -0
  174. package/dist/pin-input/root/pin-input-root.d.ts +98 -121
  175. package/dist/pin-input/root/pin-input-root.js +151 -156
  176. package/dist/popover/hide-outside.js +2 -9
  177. package/dist/popover/root/popover-root.d.ts +7 -1
  178. package/dist/popover/root/popover-root.js +9 -1
  179. package/dist/progress/root/progress-root.d.ts +27 -0
  180. package/dist/progress/root/progress-root.js +25 -7
  181. package/dist/qr-code/export.d.ts +19 -0
  182. package/dist/qr-code/export.js +14 -0
  183. package/dist/qr-code/name.d.ts +11 -0
  184. package/dist/qr-code/name.js +7 -0
  185. package/dist/qr-code/root/qr-code-root.d.ts +31 -9
  186. package/dist/qr-code/root/qr-code-root.js +29 -17
  187. package/dist/radio/group/radio-group.d.ts +15 -0
  188. package/dist/radio/root/radio-root.js +2 -2
  189. package/dist/rating/root/rating-root.d.ts +22 -9
  190. package/dist/rating/root/rating-root.js +15 -39
  191. package/dist/scatter-chart/measures.d.ts +22 -0
  192. package/dist/scatter-chart/measures.js +11 -0
  193. package/dist/scatter-chart/root/scatter-chart-root.d.ts +26 -0
  194. package/dist/scatter-chart/root/scatter-chart-root.js +21 -7
  195. package/dist/scroll-area/scrollbar.d.ts +0 -8
  196. package/dist/scroll-area/scrollbar.js +1 -1
  197. package/dist/select/align.d.ts +47 -0
  198. package/dist/select/align.js +7 -0
  199. package/dist/select/root/select-root.d.ts +115 -11
  200. package/dist/select/root/select-root.js +104 -20
  201. package/dist/slider/root/slider-root.d.ts +18 -1
  202. package/dist/slider/root/slider-root.js +22 -0
  203. package/dist/sparkline/readings.d.ts +30 -12
  204. package/dist/sparkline/readings.js +32 -15
  205. package/dist/sparkline/root/sparkline-root.d.ts +46 -3
  206. package/dist/sparkline/root/sparkline-root.js +60 -21
  207. package/dist/speed-dial/group/speed-dial-group.d.ts +48 -0
  208. package/dist/speed-dial/group/speed-dial-group.js +43 -0
  209. package/dist/speed-dial/index.d.ts +1 -0
  210. package/dist/speed-dial/index.js +1 -0
  211. package/dist/speed-dial/item/speed-dial-item.d.ts +27 -2
  212. package/dist/speed-dial/item/speed-dial-item.js +43 -5
  213. package/dist/speed-dial/long-press.d.ts +50 -0
  214. package/dist/speed-dial/long-press.js +48 -0
  215. package/dist/speed-dial/root/speed-dial-root.d.ts +86 -1
  216. package/dist/speed-dial/root/speed-dial-root.js +123 -6
  217. package/dist/splitter/root/splitter-root.d.ts +9 -2
  218. package/dist/splitter/root/splitter-root.js +18 -3
  219. package/dist/steps/root/steps-root.d.ts +8 -1
  220. package/dist/steps/root/steps-root.js +6 -1
  221. package/dist/steps/step/step.d.ts +1 -0
  222. package/dist/steps/step/step.js +1 -1
  223. package/dist/table/cell/table-cell.d.ts +8 -1
  224. package/dist/table/cell/table-cell.js +6 -2
  225. package/dist/table/column/table-column.d.ts +6 -0
  226. package/dist/table/column/table-column.js +5 -1
  227. package/dist/table/grid-controller.d.ts +117 -0
  228. package/dist/table/grid-controller.js +293 -0
  229. package/dist/table/root/table-root.d.ts +47 -3
  230. package/dist/table/root/table-root.js +93 -29
  231. package/dist/table/row/table-row.d.ts +36 -0
  232. package/dist/table/row/table-row.js +52 -2
  233. package/dist/tabs/root/tabs-root.d.ts +5 -3
  234. package/dist/tag/group/tag-group.d.ts +16 -4
  235. package/dist/tag/group/tag-group.js +23 -20
  236. package/dist/textarea/root/textarea-root.d.ts +14 -2
  237. package/dist/textarea/root/textarea-root.js +6 -7
  238. package/dist/time-input/root/time-input-root.d.ts +5 -1
  239. package/dist/time-input/root/time-input-root.js +10 -7
  240. package/dist/time-picker/root/time-picker-root.d.ts +12 -14
  241. package/dist/time-picker/root/time-picker-root.js +18 -23
  242. package/dist/toast/item/toast-item.d.ts +9 -6
  243. package/dist/toast/item/toast-item.js +1 -1
  244. package/dist/toast/root/toast-root.d.ts +40 -4
  245. package/dist/toast/root/toast-root.js +68 -16
  246. package/dist/toast/words.d.ts +12 -0
  247. package/dist/toast/words.js +25 -0
  248. package/dist/toggle/group/toggle-group.d.ts +29 -5
  249. package/dist/toggle/group/toggle-group.js +26 -43
  250. package/dist/toggle/root/toggle-root.js +2 -4
  251. package/dist/toolbar/root/toolbar-root.d.ts +52 -5
  252. package/dist/toolbar/root/toolbar-root.js +88 -13
  253. package/dist/tooltip/root/tooltip-root.d.ts +7 -1
  254. package/dist/tooltip/root/tooltip-root.js +10 -2
  255. package/dist/tour/root/tour-root.d.ts +15 -0
  256. package/dist/tour/root/tour-root.js +20 -1
  257. package/dist/tour/stop/tour-stop.d.ts +3 -0
  258. package/dist/tour/stop/tour-stop.js +3 -3
  259. package/dist/tree/root/tree-root.js +2 -2
  260. package/dist/trend/figure.d.ts +16 -0
  261. package/dist/trend/figure.js +8 -0
  262. package/dist/trend/root/trend-root.d.ts +41 -2
  263. package/dist/trend/root/trend-root.js +29 -3
  264. package/dist/video/root/extra-controls.d.ts +12 -0
  265. package/dist/video/root/extra-controls.js +6 -2
  266. package/dist/video/root/video-root.d.ts +16 -1
  267. package/dist/video/root/video-root.js +15 -1
  268. package/dist/video-playlist/root/video-playlist-root.d.ts +11 -1
  269. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  270. package/dist/virtual-list/fill.d.ts +11 -0
  271. package/dist/virtual-list/fill.js +9 -0
  272. package/dist/virtual-list/keys.d.ts +16 -7
  273. package/dist/virtual-list/keys.js +17 -6
  274. package/dist/virtual-list/plan.d.ts +17 -1
  275. package/dist/virtual-list/plan.js +13 -2
  276. package/dist/virtual-list/root/virtual-list-root.d.ts +25 -3
  277. package/dist/virtual-list/root/virtual-list-root.js +53 -41
  278. package/lint.json +1 -1
  279. package/package.json +5 -5
  280. package/types/jsx.d.ts +4934 -954
  281. package/types/svelte.d.ts +2 -0
  282. package/types/vue.d.ts +946 -253
  283. package/dist/color-picker/swatches.d.ts +0 -12
  284. package/dist/color-picker/swatches.js +0 -26
  285. package/dist/rating/steps.d.ts +0 -7
  286. package/dist/rating/steps.js +0 -9
  287. package/dist/tooltip/focus-target.d.ts +0 -6
  288. package/dist/tooltip/focus-target.js +0 -9
@@ -4,59 +4,15 @@ 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, centerInView, columnStep, countLabel, customElement, DelayController, DisabledCascadeController, drawAutofill, endInSight, fillTemplate, HelixFilterEvent, HelixFormElement, HelixLoadMoreEvent, hostAria, inlineStep, MixinCollection, MixinControllerValue, MixinField, markMatches, OptionWindowController, onApple, pageRows, rangeAdd, reportValues, requestChange, scrollItemIntoView, selectsAll, Typeahead, toggleEvery, 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';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
- import { HelixHeadlessOptionGroup } from '../../option/group/option-group.js';
13
12
  import { HelixHeadlessOption } from '../../option/root/option-root.js';
14
- const MOVES = new Set(['ArrowDown', 'ArrowUp', 'End', 'Home', 'PageDown', 'PageUp']);
15
13
  const FIELD_MOVES = new Set(['ArrowDown', 'ArrowUp', 'PageDown', 'PageUp']);
14
+ const MOVES = new Set([...FIELD_MOVES, 'End', 'Home']);
16
15
  const PAGE = 10;
17
- const matched = new Map();
18
- const HIGHLIGHT = 'helix-filter-match';
19
- const paintable = () => typeof CSS !== 'undefined' && 'highlights' in CSS && typeof Highlight !== 'undefined';
20
- const paintMatches = () => {
21
- if (!paintable()) {
22
- return;
23
- }
24
- const all = [...matched.values()].flat();
25
- if (all.length === 0) {
26
- CSS.highlights.delete(HIGHLIGHT);
27
- return;
28
- }
29
- CSS.highlights.set(HIGHLIGHT, new Highlight(...all));
30
- };
31
- const rangeFor = (root, needle) => {
32
- const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
33
- const nodes = [];
34
- let whole = '';
35
- for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
36
- nodes.push(node);
37
- whole += node.nodeValue ?? '';
38
- }
39
- const at = whole.toLowerCase().indexOf(needle);
40
- if (at < 0) {
41
- return [];
42
- }
43
- const range = document.createRange();
44
- let seen = 0;
45
- let started = false;
46
- for (const node of nodes) {
47
- const length = node.nodeValue?.length ?? 0;
48
- if (!started && at < seen + length) {
49
- range.setStart(node, at - seen);
50
- started = true;
51
- }
52
- if (started && at + needle.length <= seen + length) {
53
- range.setEnd(node, at + needle.length - seen);
54
- return [range];
55
- }
56
- seen += length;
57
- }
58
- return [];
59
- };
60
16
  let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(MixinControllerValue(MixinField(HelixFormElement))) {
61
17
  constructor() {
62
18
  super(...arguments);
@@ -70,13 +26,20 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
70
26
  this.filterable = false;
71
27
  this.filterDelay = 0;
72
28
  this.filterLabel = 'Filter';
29
+ this.items = undefined;
30
+ this.data = new OptionWindowController(this, {
31
+ current: () => this.currentValue,
32
+ items: () => this.items,
33
+ });
73
34
  this.loadError = '';
74
35
  this.manual = false;
75
36
  this.multiple = false;
76
37
  this.resultLabel = '{count} result';
77
38
  this.resultsLabel = '{count} results';
39
+ this.selectedLabel = '{name}, selected';
78
40
  this.activeValue = '';
79
41
  this.parked = false;
42
+ this.anchor = '';
80
43
  this.typeahead = new Typeahead();
81
44
  this.seen = new Set();
82
45
  this.forwarded = false;
@@ -85,6 +48,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
85
48
  this.cascade = new DisabledCascadeController(this, {
86
49
  items: () => this.options,
87
50
  });
51
+ this.chosenAgain = undefined;
88
52
  this.handleSelection = (event) => {
89
53
  if (event.detail.href !== undefined) {
90
54
  return;
@@ -102,11 +66,17 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
102
66
  return;
103
67
  }
104
68
  const typing = this.forwarded || event.composedPath()[0] === this.partNamed('search-field');
69
+ if (this.multiple && event.key === ' ' && event.shiftKey && !typing) {
70
+ event.preventDefault();
71
+ this.extend(this.activeValue);
72
+ return;
73
+ }
105
74
  if (event.key === 'Enter' || (event.key === ' ' && !typing && !this.typeahead.typing())) {
106
75
  const link = shown.find((option) => option.value === this.activeValue && Boolean(option.href));
107
76
  if (link !== undefined) {
108
77
  event.preventDefault();
109
78
  if (event.key === 'Enter') {
79
+ ;
110
80
  link.select(event);
111
81
  }
112
82
  }
@@ -119,16 +89,9 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
119
89
  }
120
90
  return;
121
91
  }
122
- if (this.multiple &&
123
- !typing &&
124
- event.key.toLowerCase() === 'a' &&
125
- (event.ctrlKey || event.metaKey) &&
126
- !event.altKey) {
92
+ if (this.multiple && !typing && selectsAll(event)) {
127
93
  event.preventDefault();
128
- const reachable = shown.map((option) => option.value);
129
- const all = reachable.every((value) => this.values.includes(value));
130
- const kept = this.values.filter((value) => !reachable.includes(value));
131
- this.requestValue((all ? kept : [...kept, ...reachable]).join(','));
94
+ this.requestValue(toggleEvery(this.values, shown.map((option) => option.value)).join(','));
132
95
  return;
133
96
  }
134
97
  if (!typing && typesCharacter(event)) {
@@ -139,7 +102,14 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
139
102
  }
140
103
  return;
141
104
  }
142
- if (!(typing ? FIELD_MOVES : MOVES).has(event.key)) {
105
+ const fieldRange = typing &&
106
+ this.multiple &&
107
+ event.shiftKey &&
108
+ (event.ctrlKey || event.metaKey) &&
109
+ (event.key === 'Home' || event.key === 'End');
110
+ const grid = this.layout === 'grid' && !this.data.records;
111
+ const along = grid && !typing ? inlineStep(this, event.key) : 0;
112
+ if (!fieldRange && !along && !(typing ? FIELD_MOVES : MOVES).has(event.key)) {
143
113
  return;
144
114
  }
145
115
  event.preventDefault();
@@ -148,7 +118,13 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
148
118
  const at = shown.findIndex((option) => option.value === from);
149
119
  const last = shown.length - 1;
150
120
  let next = at;
151
- if (event.key === 'ArrowDown') {
121
+ if (along) {
122
+ next = Math.min(Math.max(at + along, 0), last);
123
+ }
124
+ else if (grid && at >= 0 && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
125
+ next = columnStep(shown.map((option) => option.getBoundingClientRect().left), at, event.key === 'ArrowDown' ? 1 : -1);
126
+ }
127
+ else if (event.key === 'ArrowDown') {
152
128
  next = at < 0 ? 0 : (at + 1) % shown.length;
153
129
  }
154
130
  else if (event.key === 'ArrowUp') {
@@ -170,7 +146,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
170
146
  if (target) {
171
147
  this.arrive(target.value);
172
148
  const ranged = event.key === 'Home' || event.key === 'End';
173
- if (this.multiple && !typing && event.shiftKey && ranged) {
149
+ if (this.multiple && (!typing || fieldRange) && event.shiftKey && ranged) {
174
150
  const taken = rangeAdd(this.values, shown.map((option) => option.value), at, next);
175
151
  if (taken) {
176
152
  this.requestValue(taken.join(','));
@@ -196,14 +172,18 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
196
172
  this.askedAt = -1;
197
173
  this.handleScroll = (event) => {
198
174
  const items = event.currentTarget;
199
- const count = this.options.length;
200
- if (this.loading || count === this.askedAt || items.clientHeight === 0) {
175
+ const count = this.entries.length;
176
+ if (this.loading ||
177
+ !endInSight({
178
+ askedAt: this.askedAt,
179
+ count,
180
+ remaining: items.scrollHeight - items.scrollTop - items.clientHeight,
181
+ screen: items.clientHeight,
182
+ })) {
201
183
  return;
202
184
  }
203
- if (items.scrollHeight - items.scrollTop - items.clientHeight <= items.clientHeight) {
204
- this.askedAt = count;
205
- this.dispatchEvent(new HelixLoadMoreEvent());
206
- }
185
+ this.askedAt = count;
186
+ this.dispatchEvent(new HelixLoadMoreEvent());
207
187
  };
208
188
  this.handleFilterKeydown = (event) => {
209
189
  if (event.key === 'Enter' || FIELD_MOVES.has(event.key)) {
@@ -236,43 +216,52 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
236
216
  this.requestUpdate('filter', previous);
237
217
  }
238
218
  get selectedIndex() {
239
- return this.options.findIndex((option) => option.selected);
219
+ return this.entries.findIndex((option) => option.selected);
240
220
  }
241
221
  set selectedIndex(at) {
242
- const option = this.options[at];
222
+ const option = this.entries[at];
243
223
  this.value = option?.value ?? '';
244
224
  }
245
225
  highlight(where) {
246
226
  const shown = this.shown.filter((option) => !option.disabled);
247
227
  this.typeahead.reset();
248
228
  this.parked = where === 'none';
249
- if (where === 'none') {
250
- this.activeValue = '';
251
- }
252
- else if (where === 'first') {
253
- this.activeValue = shown[0]?.value ?? '';
254
- }
255
- else if (where === 'last') {
256
- this.activeValue = shown.at(-1)?.value ?? '';
257
- }
258
- else {
259
- const chosen = shown.find((option) => this.values.includes(option.value));
260
- this.activeValue = (chosen ?? shown[0])?.value ?? '';
261
- }
229
+ const chosen = where === 'selected' ? shown.find((option) => this.values.includes(option.value)) : undefined;
230
+ this.activeValue =
231
+ where === 'none'
232
+ ? ''
233
+ : ((where === 'last' ? shown.at(-1) : (chosen ?? shown[0]))?.value ?? '');
262
234
  if (this.childrenKnown) {
263
235
  this.place();
264
236
  }
265
237
  }
266
238
  tell(changed) {
267
- if (!changed.has('filter') || !this.hasUpdated) {
239
+ if (!this.hasUpdated) {
240
+ return;
241
+ }
242
+ if (!changed.has('filter')) {
243
+ this.tellActive(changed);
268
244
  return;
269
245
  }
270
246
  const count = this.shown.length;
271
- if (this.filter.trim() === '' || count === 0) {
272
- this.announced = '';
247
+ this.announced =
248
+ this.filter.trim() && count
249
+ ? countLabel(count, this.resultLabel, this.resultsLabel, this.lang)
250
+ : '';
251
+ }
252
+ tellActive(changed) {
253
+ if (!changed.has('activeValue') ||
254
+ !this.filterable ||
255
+ !onApple() ||
256
+ this.renderRoot.activeElement?.getAttribute('part') !== 'search-field') {
273
257
  return;
274
258
  }
275
- this.announced = countLabel(count, this.resultLabel, this.resultsLabel, this.lang);
259
+ const active = this.entries.find((option) => option.value === this.activeValue);
260
+ if (active) {
261
+ this.announced = active.selected
262
+ ? fillTemplate(this.selectedLabel, { name: active.text })
263
+ : active.text;
264
+ }
276
265
  }
277
266
  typeaheadTo(key) {
278
267
  const shown = this.shown.filter((option) => !option.disabled);
@@ -293,49 +282,42 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
293
282
  return this.multiple ? 'select-multiple' : 'select-one';
294
283
  }
295
284
  get options() {
296
- return this.collect(this.children);
285
+ return this.among((node) => node instanceof HelixHeadlessOption, this.children);
297
286
  }
298
- collect(nodes) {
287
+ among(is, nodes) {
299
288
  const found = [];
300
289
  for (const node of nodes) {
301
- if (node instanceof HelixHeadlessOption) {
290
+ if (is(node)) {
302
291
  found.push(node);
303
292
  }
304
- else if (node instanceof HTMLSlotElement) {
305
- found.push(...this.collect(node.assignedElements({ flatten: true })));
306
- }
307
293
  else {
308
- found.push(...this.collect(node.children));
294
+ found.push(...this.among(is, node instanceof HTMLSlotElement
295
+ ? node.assignedElements({ flatten: true })
296
+ : node.children));
309
297
  }
310
298
  }
311
299
  return found;
312
300
  }
313
301
  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;
302
+ return this.among((node) => node.localName.endsWith('-option-group'), this.children);
303
+ }
304
+ get entries() {
305
+ return this.data.records ?? this.options;
306
+ }
307
+ get elements() {
308
+ return this.data.records ? this.data.drawn : this.options;
330
309
  }
331
310
  get shown() {
332
- return this.options.filter((option) => !option.hidden);
311
+ return this.entries.filter((option) => !option.hidden);
333
312
  }
334
313
  get values() {
335
314
  return this.value === '' ? [] : this.value.split(',');
336
315
  }
337
316
  choose(value) {
338
317
  const chosen = this.values;
318
+ if (!chosen.includes(value)) {
319
+ this.anchor = value;
320
+ }
339
321
  const next = this.multiple
340
322
  ? chosen.includes(value)
341
323
  ? chosen.filter((each) => each !== value)
@@ -343,23 +325,31 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
343
325
  : [value];
344
326
  const joined = next.join(',');
345
327
  if (joined === this.value) {
328
+ this.chosenAgain?.();
346
329
  return;
347
330
  }
348
331
  this.requestValue(joined);
349
332
  }
333
+ extend(to) {
334
+ const reach = this.shown.filter((option) => !option.disabled).map((option) => option.value);
335
+ const taken = rangeAdd(this.values, reach, reach.indexOf(this.anchor), reach.indexOf(to));
336
+ if (taken) {
337
+ this.requestValue(taken.join(','));
338
+ }
339
+ }
350
340
  arrive(value) {
351
341
  this.parked = false;
352
342
  this.activeValue = value;
353
- const link = this.options.some((option) => option.value === value && Boolean(option.href));
343
+ const link = this.entries.some((option) => option.value === value && Boolean(option.href));
354
344
  if (!this.multiple && !this.manual && !this.forwarded && !link) {
355
345
  this.choose(value);
356
346
  }
357
347
  }
358
348
  pageSize() {
359
349
  const items = this.partNamed('items');
360
- const row = this.shown[0]?.getBoundingClientRect().height ?? 0;
350
+ const row = this.elements.find((option) => !option.hidden)?.getBoundingClientRect().height ?? 0;
361
351
  const height = items?.clientHeight ?? 0;
362
- return row > 0 && height > 0 ? Math.max(1, Math.floor(height / row) - 1) : PAGE;
352
+ return row > 0 && height > 0 ? pageRows(height, row) : PAGE;
363
353
  }
364
354
  navigate(event) {
365
355
  this.forwarded = true;
@@ -371,55 +361,40 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
371
361
  }
372
362
  }
373
363
  reveal() {
374
- const items = this.partNamed('items');
375
- const chosen = this.options.find((option) => option.selected && !option.hidden);
376
- if (!items || !chosen) {
364
+ if (this.data.records) {
365
+ this.data.center(this.values[0] ?? '');
377
366
  return;
378
367
  }
379
- for (let pass = 0; pass < 2; pass += 1) {
380
- const box = items.getBoundingClientRect();
381
- const row = chosen.getBoundingClientRect();
382
- if (box.height === 0 || row.height === 0) {
383
- return;
384
- }
385
- const off = row.top + row.height / 2 - (box.top + box.height / 2);
386
- if (Math.abs(off) < 1) {
387
- return;
388
- }
389
- items.scrollTop += off;
368
+ const items = this.partNamed('items');
369
+ const chosen = this.options.find((option) => option.selected && !option.hidden);
370
+ if (items && chosen) {
371
+ centerInView(items, chosen);
390
372
  }
391
373
  }
392
374
  place() {
393
375
  this.forgetRemoved();
394
376
  const needle = this.filter.trim().toLowerCase();
395
377
  const chosen = new Set(this.values);
396
- for (const option of this.options) {
378
+ for (const option of this.entries) {
397
379
  option.hidden = needle !== '' && !option.text.toLowerCase().includes(needle);
398
380
  option.selected = chosen.has(option.value);
399
381
  }
400
382
  for (const group of this.groups) {
401
- const inside = this.collect(group.children);
383
+ const inside = this.among((node) => node instanceof HelixHeadlessOption, group.children);
402
384
  group.hidden = inside.length > 0 && inside.every((option) => option.hidden);
403
385
  }
386
+ for (const rule of this.among((node) => node.localName.endsWith('-option-separator'), this.children)) {
387
+ rule.hidden = needle !== '';
388
+ }
404
389
  const shown = this.shown;
405
390
  const rescue = !this.parked && (needle !== '' || this.activeValue !== '');
406
391
  if (rescue && !shown.some((option) => option.value === this.activeValue)) {
407
392
  this.activeValue = shown.find((option) => !option.disabled)?.value ?? '';
408
393
  }
409
- for (const option of this.options) {
394
+ for (const option of this.entries) {
410
395
  option.active = option.value === this.activeValue;
411
396
  }
412
- if (needle === '' || !paintable()) {
413
- matched.delete(this);
414
- }
415
- else {
416
- matched.set(this, shown.flatMap((option) => rangeFor(option, needle)));
417
- }
418
- paintMatches();
419
- const anyShown = shown.length > 0;
420
- if (anyShown !== this.hasMatches) {
421
- this.hasMatches = anyShown;
422
- }
397
+ this.hasMatches = shown.length > 0;
423
398
  this.counted = true;
424
399
  }
425
400
  get owned() {
@@ -430,15 +405,19 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
430
405
  if (this.owned) {
431
406
  return;
432
407
  }
433
- const present = new Set(this.options.map((option) => option.value));
408
+ const present = new Set(this.entries.map((option) => option.value));
434
409
  const gone = this.values.filter((value) => this.seen.has(value) && !present.has(value));
435
410
  this.seen = present;
436
411
  if (gone.length > 0) {
437
412
  this.value = this.values.filter((value) => !gone.includes(value)).join(',');
438
413
  }
439
414
  }
440
- followActive() {
441
- const active = this.shown.find((option) => option.value === this.activeValue);
415
+ followActive(changed) {
416
+ if (this.data.records &&
417
+ !this.data.follow(changed.has('activeValue') || changed.has('filter'))) {
418
+ return;
419
+ }
420
+ const active = this.elements.find((option) => !option.hidden && option.value === this.activeValue);
442
421
  if (active) {
443
422
  const items = this.partNamed('items');
444
423
  scrollItemIntoView(active, { padding: 4, ...(items ? { scroller: items } : {}) });
@@ -462,8 +441,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
462
441
  super.disconnectedCallback();
463
442
  this.removeEventListener('helix-select', this.handleSelection);
464
443
  this.removeEventListener('keydown', this.handleKeydown);
465
- matched.delete(this);
466
- paintMatches();
444
+ markMatches(this, [], '');
467
445
  }
468
446
  willUpdate(changed) {
469
447
  super.willUpdate(changed);
@@ -484,7 +462,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
484
462
  }
485
463
  this.toggleState('empty', !this.showsOptions);
486
464
  this.toggleState('loading', this.loading);
487
- this.toggleState('error', this.loadError !== '');
465
+ this.toggleState('error', this.failed);
488
466
  this.toggleState('no-results', this.noResults);
489
467
  }
490
468
  seedDeclared() {
@@ -506,14 +484,15 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
506
484
  if (!list || !('ariaActiveDescendantElement' in list)) {
507
485
  return;
508
486
  }
509
- const active = this.options.find((option) => option.value === this.currentValue);
487
+ const active = this.elements.find((option) => option.value === this.currentValue);
510
488
  list.ariaActiveDescendantElement = active ?? null;
511
489
  }
512
490
  updated(changed) {
513
491
  super.updated(changed);
514
- this.followActive();
492
+ this.followActive(changed);
515
493
  this.sync();
516
494
  this.linkActive();
495
+ markMatches(this, this.elements.filter((option) => !option.hidden), this.filter.trim());
517
496
  }
518
497
  takeFilter(control) {
519
498
  this.filtering.cancel();
@@ -555,12 +534,12 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
555
534
  get showsOptions() {
556
535
  return this.counted && !this.loading
557
536
  ? this.filter === ''
558
- ? this.options.length > 0
537
+ ? this.entries.length > 0
559
538
  : this.hasMatches
560
539
  : true;
561
540
  }
562
541
  renderStatus(hasContent) {
563
- if (this.loadError !== '') {
542
+ if (this.failed) {
564
543
  return html `<p part="error"><slot name="error">${this.loadError}</slot></p>`;
565
544
  }
566
545
  if (!this.loading && hasContent) {
@@ -579,17 +558,17 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
579
558
  return (this.counted &&
580
559
  !this.loading &&
581
560
  this.filter !== '' &&
582
- this.options.length > 0 &&
561
+ this.entries.length > 0 &&
583
562
  !this.hasMatches);
584
563
  }
564
+ get failed() {
565
+ return Boolean(this.loadError);
566
+ }
585
567
  renderLive(hasContent) {
586
- if (this.loadError !== '') {
587
- return html `<span part="live" role="status">${this.loadError}</span>`;
588
- }
589
- if (this.noResults && this.noResultsText !== undefined) {
590
- return html `<span part="live" role="status">${this.noResultsText}</span>`;
591
- }
592
- return super.renderLive(hasContent);
568
+ const words = this.loadError || (this.noResults ? this.noResultsText : undefined);
569
+ return words === undefined
570
+ ? super.renderLive(hasContent)
571
+ : html `<span part="live" role="status">${words}</span>`;
593
572
  }
594
573
  render() {
595
574
  const shown = this.showsOptions;
@@ -602,7 +581,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
602
581
  <input
603
582
  part="search-field"
604
583
  type="text"
605
- autocomplete=${this.autocomplete || nothing}
584
+ autocomplete="off"
606
585
  spellcheck="false"
607
586
  .value=${this.filter}
608
587
  placeholder=${this.filterLabel}
@@ -625,14 +604,22 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
625
604
  aria-labelledby=${this.labeled ? this.labelId : nothing}
626
605
  aria-multiselectable=${this.multiple ? 'true' : nothing}
627
606
  aria-required=${this.required ? 'true' : nothing}
628
- ${''}
629
607
  tabindex=${this.filterable ? nothing : this.isDisabled ? -1 : 0}
630
608
  @scroll=${this.handleScroll}
631
609
  >
632
- <slot @slotchange=${() => this.place()}></slot>
610
+ ${this.data.render() ?? html `<slot @slotchange=${() => this.requestUpdate()}></slot>`}
633
611
  ${this.renderStatus(shown)}
634
612
  </div>
635
- ${this.renderLive(shown)}${this.renderHint()}${this.renderFeedback()}`;
613
+ ${this.renderLive(shown)}${this.renderHint()}${this.renderFeedback()}${this.autocomplete === 'off'
614
+ ? nothing
615
+ : drawAutofill({
616
+ autocomplete: this.autocomplete,
617
+ disabled: this.isDisabled,
618
+ multiple: this.multiple,
619
+ onFill: (value) => this.requestValue(value),
620
+ options: this.childrenKnown ? this.entries : [],
621
+ value: this.value,
622
+ })}`;
636
623
  }
637
624
  };
638
625
  __decorate([
@@ -656,6 +643,12 @@ __decorate([
656
643
  __decorate([
657
644
  property({ attribute: 'filter-label' })
658
645
  ], HelixHeadlessListbox.prototype, "filterLabel", void 0);
646
+ __decorate([
647
+ property({ attribute: false })
648
+ ], HelixHeadlessListbox.prototype, "items", void 0);
649
+ __decorate([
650
+ property({ reflect: true })
651
+ ], HelixHeadlessListbox.prototype, "layout", void 0);
659
652
  __decorate([
660
653
  property({ attribute: 'load-error' })
661
654
  ], HelixHeadlessListbox.prototype, "loadError", void 0);
@@ -675,14 +668,11 @@ __decorate([
675
668
  property({ attribute: 'results-label' })
676
669
  ], HelixHeadlessListbox.prototype, "resultsLabel", void 0);
677
670
  __decorate([
678
- state()
679
- ], HelixHeadlessListbox.prototype, "activeValue", void 0);
671
+ property({ attribute: 'selected-label' })
672
+ ], HelixHeadlessListbox.prototype, "selectedLabel", void 0);
680
673
  __decorate([
681
674
  state()
682
- ], HelixHeadlessListbox.prototype, "hasMatches", void 0);
683
- __decorate([
684
- state()
685
- ], HelixHeadlessListbox.prototype, "counted", void 0);
675
+ ], HelixHeadlessListbox.prototype, "activeValue", void 0);
686
676
  HelixHeadlessListbox = __decorate([
687
677
  customElement('helix-headless-listbox')
688
678
  ], HelixHeadlessListbox);
@@ -12,7 +12,8 @@ import { type PropertyValues, type TemplateResult } from 'lit';
12
12
  * `name`, or its `label` if it has no name.
13
13
  *
14
14
  * All the markers in a map share one tab stop. The arrow keys move between
15
- * them in the order they are written.
15
+ * them in the order they are written. Taking away the marker that has focus
16
+ * moves focus to the next one, or to the one before where it was the last.
16
17
  *
17
18
  * @since 0.105.0
18
19
  * @status experimental