@fusion.dev/helix-headless 0.145.0 → 0.146.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 (278) hide show
  1. package/custom-elements.json +7373 -3849
  2. package/dist/accordion/item/accordion-item.d.ts +14 -0
  3. package/dist/accordion/item/accordion-item.js +16 -6
  4. package/dist/accordion/root/accordion-root.d.ts +8 -5
  5. package/dist/announcement/root/announcement-root.d.ts +3 -2
  6. package/dist/announcement/root/announcement-root.js +3 -1
  7. package/dist/app-header/root/app-header-root.d.ts +7 -2
  8. package/dist/app-header/root/app-header-root.js +9 -1
  9. package/dist/app-header/sectioning.d.ts +3 -1
  10. package/dist/app-header/sectioning.js +3 -2
  11. package/dist/avatar/group/avatar-group.d.ts +20 -14
  12. package/dist/avatar/group/avatar-group.js +17 -19
  13. package/dist/badge/root/badge-root.d.ts +3 -1
  14. package/dist/badge/root/badge-root.js +2 -2
  15. package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
  16. package/dist/bar-chart/root/bar-chart-root.js +52 -15
  17. package/dist/bar-chart/stack.d.ts +15 -0
  18. package/dist/bar-chart/stack.js +19 -0
  19. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +25 -0
  20. package/dist/breadcrumbs/root/breadcrumbs-root.js +50 -3
  21. package/dist/button/root/button-root.d.ts +26 -0
  22. package/dist/button/root/button-root.js +38 -18
  23. package/dist/button-group/root/button-group-root.d.ts +2 -1
  24. package/dist/button-group/root/button-group-root.js +5 -1
  25. package/dist/canvas/node/canvas-node.d.ts +4 -3
  26. package/dist/canvas/node/canvas-node.js +3 -2
  27. package/dist/canvas/root/canvas-root.js +2 -2
  28. package/dist/card/root/card-root.d.ts +24 -3
  29. package/dist/card/root/card-root.js +20 -8
  30. package/dist/carousel/root/carousel-root.d.ts +43 -4
  31. package/dist/carousel/root/carousel-root.js +40 -12
  32. package/dist/carousel/stops.d.ts +20 -0
  33. package/dist/carousel/stops.js +30 -0
  34. package/dist/chat/message/chat-message.js +5 -4
  35. package/dist/chat/naming.d.ts +5 -1
  36. package/dist/chat/naming.js +4 -2
  37. package/dist/chat/root/chat-root.d.ts +25 -3
  38. package/dist/chat/root/chat-root.js +22 -5
  39. package/dist/checkbox/root/checkbox-root.d.ts +20 -0
  40. package/dist/checkbox/root/checkbox-root.js +14 -1
  41. package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
  42. package/dist/checkbox-group/root/checkbox-group-root.js +12 -10
  43. package/dist/checkbox-group/select-all.d.ts +27 -0
  44. package/dist/checkbox-group/select-all.js +12 -0
  45. package/dist/code-block/highlight.d.ts +15 -0
  46. package/dist/code-block/highlight.js +4 -0
  47. package/dist/code-block/root/code-block-root.d.ts +49 -1
  48. package/dist/code-block/root/code-block-root.js +58 -3
  49. package/dist/color-input/root/color-input-root.d.ts +3 -0
  50. package/dist/color-input/root/color-input-root.js +6 -3
  51. package/dist/color-picker/root/color-picker-root.d.ts +33 -4
  52. package/dist/color-picker/root/color-picker-root.js +80 -21
  53. package/dist/color-picker/swatches.d.ts +12 -0
  54. package/dist/color-picker/swatches.js +26 -0
  55. package/dist/combobox/root/combobox-root.d.ts +43 -0
  56. package/dist/combobox/root/combobox-root.js +79 -15
  57. package/dist/command/root/command-root.d.ts +16 -1
  58. package/dist/command/root/command-root.js +22 -4
  59. package/dist/copy-button/root/copy-button-root.d.ts +5 -0
  60. package/dist/copy-button/root/copy-button-root.js +10 -3
  61. package/dist/data-table/index.d.ts +1 -1
  62. package/dist/data-table/index.js +1 -1
  63. package/dist/data-table/keys.d.ts +24 -0
  64. package/dist/data-table/keys.js +24 -0
  65. package/dist/data-table/root/data-table-root.d.ts +27 -18
  66. package/dist/data-table/root/data-table-root.js +20 -52
  67. package/dist/data-table/tree.d.ts +16 -1
  68. package/dist/data-table/tree.js +2 -3
  69. package/dist/date-input/root/date-input-root.d.ts +3 -1
  70. package/dist/date-input/root/date-input-root.js +18 -7
  71. package/dist/date-picker/root/date-picker-root.js +12 -6
  72. package/dist/dialog/description.js +15 -1
  73. package/dist/dialog/root/dialog-root.d.ts +8 -2
  74. package/dist/dialog/root/dialog-root.js +16 -1
  75. package/dist/divider/root/divider-root.d.ts +3 -0
  76. package/dist/divider/root/divider-root.js +2 -1
  77. package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
  78. package/dist/donut-chart/root/donut-chart-root.js +48 -1
  79. package/dist/drawer/reveal.d.ts +8 -0
  80. package/dist/drawer/reveal.js +1 -0
  81. package/dist/drawer/root/drawer-root.d.ts +59 -0
  82. package/dist/drawer/root/drawer-root.js +63 -4
  83. package/dist/drilldown/item/drilldown-item.js +1 -1
  84. package/dist/drilldown/root/drilldown-root.d.ts +21 -3
  85. package/dist/drilldown/root/drilldown-root.js +21 -3
  86. package/dist/element-events.generated.js +31 -5
  87. package/dist/empty/root/empty-root.d.ts +3 -1
  88. package/dist/empty/root/empty-root.js +3 -2
  89. package/dist/file-input/root/file-input-root.d.ts +69 -6
  90. package/dist/file-input/root/file-input-root.js +82 -19
  91. package/dist/file-input/screen.d.ts +2 -1
  92. package/dist/filter-input/menu/filter-menu.d.ts +2 -0
  93. package/dist/filter-input/menu/filter-menu.js +12 -4
  94. package/dist/filter-input/root/filter-input-root.d.ts +30 -6
  95. package/dist/filter-input/root/filter-input-root.js +1 -1
  96. package/dist/filter-input/suggest.d.ts +28 -3
  97. package/dist/filter-input/suggest.js +18 -8
  98. package/dist/format-date/root/format-date-root.d.ts +9 -16
  99. package/dist/format-date/root/format-date-root.js +10 -27
  100. package/dist/heatmap/root/heatmap-root.d.ts +24 -13
  101. package/dist/heatmap/root/heatmap-root.js +34 -12
  102. package/dist/icon/root/icon-root.d.ts +12 -1
  103. package/dist/icon/root/icon-root.js +21 -10
  104. package/dist/icon/settle.d.ts +7 -0
  105. package/dist/icon/settle.js +10 -0
  106. package/dist/icon-button/root/icon-button-root.d.ts +2 -0
  107. package/dist/image/root/image-root.d.ts +5 -1
  108. package/dist/image/root/image-root.js +1 -1
  109. package/dist/input/root/input-root.d.ts +12 -2
  110. package/dist/input/root/input-root.js +11 -2
  111. package/dist/kbd/root/kbd-root.d.ts +35 -1
  112. package/dist/kbd/root/kbd-root.js +23 -4
  113. package/dist/legend/root/legend-root.d.ts +55 -8
  114. package/dist/legend/root/legend-root.js +55 -13
  115. package/dist/lightbox/root/lightbox-root.d.ts +49 -6
  116. package/dist/lightbox/root/lightbox-root.js +86 -19
  117. package/dist/lightbox/zoom-controller.d.ts +59 -0
  118. package/dist/lightbox/zoom-controller.js +128 -0
  119. package/dist/lightbox/zoom.d.ts +22 -0
  120. package/dist/lightbox/zoom.js +16 -0
  121. package/dist/line-chart/root/line-chart-root.d.ts +22 -1
  122. package/dist/line-chart/root/line-chart-root.js +21 -1
  123. package/dist/list/item/list-item.d.ts +2 -0
  124. package/dist/list/item/list-item.js +11 -2
  125. package/dist/list/root/list-root.d.ts +62 -13
  126. package/dist/list/root/list-root.js +70 -13
  127. package/dist/list/selection.d.ts +37 -7
  128. package/dist/list/selection.js +16 -7
  129. package/dist/listbox/root/listbox-root.d.ts +37 -1
  130. package/dist/listbox/root/listbox-root.js +49 -3
  131. package/dist/loader/busy.d.ts +10 -0
  132. package/dist/loader/busy.js +11 -0
  133. package/dist/loader/root/loader-root.d.ts +9 -2
  134. package/dist/loader/root/loader-root.js +23 -3
  135. package/dist/megamenu/root/megamenu-root.d.ts +51 -10
  136. package/dist/megamenu/root/megamenu-root.js +76 -5
  137. package/dist/menu/item/menu-item.d.ts +51 -2
  138. package/dist/menu/item/menu-item.js +47 -23
  139. package/dist/menu/root/menu-root.d.ts +14 -0
  140. package/dist/menu/root/menu-root.js +2 -2
  141. package/dist/meter/root/meter-root.d.ts +20 -1
  142. package/dist/meter/root/meter-root.js +11 -15
  143. package/dist/navigation/root/navigation-root.d.ts +2 -1
  144. package/dist/navigation/root/navigation-root.js +2 -2
  145. package/dist/notifications/root/notifications-root.d.ts +10 -3
  146. package/dist/notifications/root/notifications-root.js +19 -7
  147. package/dist/number-input/root/number-input-root.d.ts +17 -8
  148. package/dist/number-input/root/number-input-root.js +2 -7
  149. package/dist/object-inspector/root/object-inspector-root.d.ts +75 -27
  150. package/dist/object-inspector/root/object-inspector-root.js +117 -76
  151. package/dist/object-inspector/rows.d.ts +18 -0
  152. package/dist/object-inspector/rows.js +33 -0
  153. package/dist/option/root/option-root.d.ts +5 -3
  154. package/dist/outline/root/outline-root.d.ts +18 -0
  155. package/dist/outline/root/outline-root.js +23 -5
  156. package/dist/pagination/size/pagination-size.d.ts +1 -0
  157. package/dist/pagination/size/pagination-size.js +1 -1
  158. package/dist/pin-input/code.d.ts +10 -0
  159. package/dist/pin-input/code.js +13 -0
  160. package/dist/pin-input/root/pin-input-root.d.ts +25 -1
  161. package/dist/pin-input/root/pin-input-root.js +10 -11
  162. package/dist/popover/hide-outside.d.ts +14 -0
  163. package/dist/popover/hide-outside.js +41 -0
  164. package/dist/popover/root/popover-root.d.ts +57 -7
  165. package/dist/popover/root/popover-root.js +58 -3
  166. package/dist/progress/root/progress-root.d.ts +20 -0
  167. package/dist/progress/root/progress-root.js +15 -15
  168. package/dist/qr-code/export.d.ts +26 -1
  169. package/dist/qr-code/export.js +14 -0
  170. package/dist/qr-code/root/qr-code-root.d.ts +19 -5
  171. package/dist/qr-code/root/qr-code-root.js +58 -9
  172. package/dist/quote/marks.d.ts +8 -0
  173. package/dist/quote/marks.js +5 -0
  174. package/dist/quote/root/quote-root.d.ts +14 -2
  175. package/dist/quote/root/quote-root.js +23 -1
  176. package/dist/radio/root/radio-root.d.ts +6 -2
  177. package/dist/radio/root/radio-root.js +6 -1
  178. package/dist/rating/root/rating-root.d.ts +9 -4
  179. package/dist/rating/root/rating-root.js +31 -7
  180. package/dist/rating/steps.d.ts +7 -0
  181. package/dist/rating/steps.js +9 -0
  182. package/dist/scatter-chart/fit.d.ts +15 -0
  183. package/dist/scatter-chart/fit.js +35 -0
  184. package/dist/scatter-chart/root/scatter-chart-root.d.ts +21 -1
  185. package/dist/scatter-chart/root/scatter-chart-root.js +30 -17
  186. package/dist/scroll-area/root/scroll-area-root.d.ts +22 -51
  187. package/dist/scroll-area/root/scroll-area-root.js +57 -173
  188. package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
  189. package/dist/scroll-area/scrollbar-controller.js +172 -0
  190. package/dist/scroll-area/scrollbar.d.ts +27 -0
  191. package/dist/scroll-area/scrollbar.js +9 -0
  192. package/dist/select/root/select-root.d.ts +15 -0
  193. package/dist/select/root/select-root.js +18 -6
  194. package/dist/skeleton/root/skeleton-root.d.ts +3 -0
  195. package/dist/skeleton/root/skeleton-root.js +4 -2
  196. package/dist/skip-link/root/skip-link-root.js +2 -2
  197. package/dist/slider/root/slider-root.d.ts +41 -4
  198. package/dist/slider/root/slider-root.js +23 -3
  199. package/dist/sparkline/readings.d.ts +18 -0
  200. package/dist/sparkline/readings.js +19 -0
  201. package/dist/sparkline/root/sparkline-root.d.ts +78 -7
  202. package/dist/sparkline/root/sparkline-root.js +118 -23
  203. package/dist/speed-dial/root/speed-dial-root.js +2 -2
  204. package/dist/splitter/root/splitter-root.d.ts +7 -12
  205. package/dist/splitter/root/splitter-root.js +209 -204
  206. package/dist/stat/root/stat-root.d.ts +14 -5
  207. package/dist/stat/root/stat-root.js +21 -5
  208. package/dist/steps/reach.d.ts +5 -0
  209. package/dist/steps/reach.js +3 -0
  210. package/dist/steps/root/steps-root.d.ts +49 -3
  211. package/dist/steps/root/steps-root.js +44 -5
  212. package/dist/steps/step/step.d.ts +7 -0
  213. package/dist/steps/step/step.js +7 -2
  214. package/dist/switch/root/switch-root.d.ts +10 -0
  215. package/dist/table/column/table-column.d.ts +0 -10
  216. package/dist/table/column/table-column.js +3 -19
  217. package/dist/table/root/table-root.d.ts +31 -10
  218. package/dist/table/root/table-root.js +2 -13
  219. package/dist/tabs/panel/tab-panel.d.ts +16 -3
  220. package/dist/tabs/panel/tab-panel.js +34 -5
  221. package/dist/tabs/root/tabs-root.d.ts +12 -0
  222. package/dist/tabs/root/tabs-root.js +1 -1
  223. package/dist/tabs/tab/tab.d.ts +5 -0
  224. package/dist/tabs/tab/tab.js +5 -2
  225. package/dist/tag/group/tag-group.d.ts +31 -4
  226. package/dist/tag/group/tag-group.js +46 -7
  227. package/dist/tag/root/tag-root.js +9 -2
  228. package/dist/textarea/root/textarea-root.d.ts +28 -1
  229. package/dist/textarea/root/textarea-root.js +40 -1
  230. package/dist/time-input/root/time-input-root.d.ts +5 -1
  231. package/dist/time-input/root/time-input-root.js +16 -8
  232. package/dist/time-picker/root/time-picker-root.d.ts +18 -4
  233. package/dist/time-picker/root/time-picker-root.js +7 -10
  234. package/dist/time-picker/wheel-name.d.ts +9 -0
  235. package/dist/time-picker/wheel-name.js +15 -0
  236. package/dist/timeline/group/timeline-group.js +2 -2
  237. package/dist/toast/item/toast-item.d.ts +14 -1
  238. package/dist/toast/item/toast-item.js +22 -5
  239. package/dist/toast/root/toast-root.d.ts +6 -0
  240. package/dist/toast/root/toast-root.js +16 -1
  241. package/dist/toggle/group/toggle-group.d.ts +24 -3
  242. package/dist/toggle/group/toggle-group.js +34 -5
  243. package/dist/tooltip/root/tooltip-root.d.ts +13 -2
  244. package/dist/tooltip/root/tooltip-root.js +9 -1
  245. package/dist/tour/back.d.ts +10 -0
  246. package/dist/tour/back.js +8 -0
  247. package/dist/tour/root/tour-root.d.ts +30 -2
  248. package/dist/tour/root/tour-root.js +34 -8
  249. package/dist/tree/item/tree-item.d.ts +7 -3
  250. package/dist/tree/item/tree-item.js +3 -0
  251. package/dist/tree/root/tree-root.d.ts +24 -3
  252. package/dist/tree/root/tree-root.js +32 -13
  253. package/dist/tree/selection.js +2 -15
  254. package/dist/trend/root/trend-root.d.ts +43 -18
  255. package/dist/trend/root/trend-root.js +31 -6
  256. package/dist/video/media-events.d.ts +13 -18
  257. package/dist/video/media-events.js +1 -5
  258. package/dist/video/root/video-root.d.ts +18 -5
  259. package/dist/video/root/video-root.js +19 -10
  260. package/dist/video-playlist/menu/video-playlist-menu.d.ts +2 -0
  261. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  262. package/dist/video-playlist/root/video-playlist-root.d.ts +7 -1
  263. package/dist/virtual-list/fill.d.ts +0 -8
  264. package/dist/virtual-list/fill.js +1 -17
  265. package/dist/virtual-list/plan.d.ts +49 -0
  266. package/dist/virtual-list/plan.js +16 -0
  267. package/dist/virtual-list/root/virtual-list-root.d.ts +114 -13
  268. package/dist/virtual-list/root/virtual-list-root.js +183 -40
  269. package/lint.json +1 -1
  270. package/package.json +8 -8
  271. package/types/jsx.d.ts +4577 -835
  272. package/types/vue.d.ts +926 -168
  273. package/dist/chat/relative-clock.d.ts +0 -39
  274. package/dist/chat/relative-clock.js +0 -31
  275. package/dist/data-table/root/order.d.ts +0 -141
  276. package/dist/data-table/root/order.js +0 -76
  277. package/dist/tag/selection.d.ts +0 -10
  278. package/dist/tag/selection.js +0 -12
@@ -46,21 +46,48 @@ export interface VirtualListRange {
46
46
  *
47
47
  * Rows are your markup in the light DOM, so your stylesheet styles them.
48
48
  *
49
- * **Keyboard.** The list is one tab stop. Up and Down move between items, Home
50
- * and End go to the first and the last, and Page Up and Page Down move by a
51
- * screen. An item that is not drawn is scrolled into view and focused, so a
52
- * keyboard reaches the whole set. Tab moves on into the focused row's own links
53
- * and buttons. When the row holding the tab stop scrolls away, the stop moves
54
- * to the first row on screen.
49
+ * **Keyboard.** The list is one tab stop.
50
+ *
51
+ * | Key | What it does |
52
+ * |---|---|
53
+ * | Down, Up | Moves to the next or the previous item. |
54
+ * | Home, End | Moves to the first or the last item. |
55
+ * | Page Down, Page Up | Moves by the number of items on screen. |
56
+ * | A letter or a word | Moves to the next item whose text starts with what you typed. |
57
+ * | Tab | Moves into the focused row's own links and buttons, then out of the list. |
58
+ *
59
+ * An item that is not drawn is scrolled into view and focused, so a keyboard
60
+ * reaches the whole set. When the row holding the tab stop scrolls away, the
61
+ * stop moves to the first row on screen. Typing matches each item's
62
+ * `text-field`, which is the first `data-field` of the row template by default,
63
+ * or the item itself when it is a string or a number.
55
64
  *
56
65
  * Each row says its place in the whole set with `aria-posinset` and
57
66
  * `aria-setsize`, so a screen reader says "4,000 of 10,000". The row holding
58
67
  * focus stays drawn when it scrolls out.
59
68
  *
69
+ * **Selecting.** `selection-mode` makes the list a list box a reader chooses
70
+ * from: `single` selects one item and `multiple` any number. Space or a press
71
+ * selects an item, Shift with Space, Up, Down, Home, End or a press takes in
72
+ * the items from the last one chosen, and Control or Command with A selects
73
+ * them all, in `multiple`. `value` holds the chosen items' keys, separated by
74
+ * commas, in the order of the set: `key-field` names each item's key, and a
75
+ * string or a number item is its own key. A change asks with
76
+ * `helix-before-change` and tells with `change`. Each row is an `option`
77
+ * marked `aria-selected`, so a row's own links and buttons are not reached
78
+ * there: keep those to a list without `selection-mode`.
79
+ *
60
80
  * **Loading more.** `helix-range-change` fires as the items on screen change,
61
81
  * and `range` holds them. `helix-load-more` fires once a reader is within one
62
82
  * screen of the end, once for each length of the set: add to `items` and it can
63
- * fire again. `scrollToIndex` takes a reader to an item.
83
+ * fire again. While you fetch, set `loading`: the list is busy, and a row after
84
+ * the last item says "Loading more" (`loading-label`, or your own content in
85
+ * the `loading` slot). `scrollToIndex` takes a reader to an item.
86
+ *
87
+ * **Row height.** Rows are placed by `estimate` until they are drawn. Then the
88
+ * list reads the drawn rows' height once and places every row by it, so a
89
+ * wrong estimate still reaches the last item. Set `measured` for rows of
90
+ * different heights.
64
91
  *
65
92
  * The list follows its source. A new `items` array, an edit to the script's
66
93
  * text, or a script replaced under the same id draws the new rows. A row that
@@ -76,12 +103,16 @@ export interface VirtualListRange {
76
103
  * @documentation https://fusion.dev/helix/components/virtual-list
77
104
  *
78
105
  * @slot - The `<template>` that defines one row. The list writes the rows it renders beside it, and this slot shows them between the two spacers.
106
+ * @slot loading - Content for the row after the last item while `loading` is set. Falls back to `loading-label`.
79
107
  *
108
+ * @event {Event} change - Fired once a press or a key has changed which items are selected, in a list with `selection-mode`.
109
+ * @event {HelixBeforeChangeEvent} helix-before-change - Fired before a press or a key changes which items are selected, with the `value` it would leave. Cancelable.
80
110
  * @event {HelixLoadMoreEvent} helix-load-more - Fired when a reader comes within one screen of the last item, once for each length of the set.
81
111
  * @event {HelixRangeChangeEvent} helix-range-change - Fired when the items on screen change, with the new `first` and `last`, which `range` also holds.
82
112
  *
83
113
  * @csspart after - The space that stands in for the rows below the rendered ones.
84
114
  * @csspart before - The space that stands in for the rows above the rendered ones.
115
+ * @csspart loading - The row after the last item while `loading` is set.
85
116
  * @csspart viewport - The scrolling box. On this tier, it needs only a block size.
86
117
  */
87
118
  export declare class HelixHeadlessVirtualList extends HelixElement {
@@ -105,6 +136,12 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
105
136
  * Left unset, the list reads the script that `for` names.
106
137
  */
107
138
  items: readonly unknown[] | undefined;
139
+ /**
140
+ * The field of each item that is its key in `value`, as a key or a path of
141
+ * keys. Empty reads a string or a number item as its own key, and any
142
+ * other item by its place in the set.
143
+ */
144
+ keyField: string;
108
145
  /**
109
146
  * The list's name, read by screen readers, because only some of its rows
110
147
  * are rendered at a time.
@@ -114,11 +151,22 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
114
151
  */
115
152
  label: string;
116
153
  /**
117
- * Places rows by their measured height. Without it, every row is placed by
118
- * `estimate`.
154
+ * Marks the list as busy fetching more items, and draws a row after the last
155
+ * item that says so.
156
+ *
157
+ * Set it when `helix-load-more` starts a fetch, and clear it when the items
158
+ * arrive. The list does not ask for more while it is set.
159
+ */
160
+ loading: boolean;
161
+ /** The text of the row drawn while `loading` is set, where the `loading` slot is empty. */
162
+ loadingLabel: string;
163
+ /**
164
+ * Places each row by its own measured height. Without it, rows are placed
165
+ * by one height: `estimate` until rows are drawn, then the drawn rows' height,
166
+ * read once.
119
167
  *
120
168
  * **Off by default, because the straight line is better when it holds.** One
121
- * estimate makes offset and index convert by division, which can never
169
+ * height makes offset and index convert by division, which can never
122
170
  * disagree with itself; measuring is what a set of genuinely different
123
171
  * heights needs and costs a read of each row as it renders. A list of
124
172
  * one-line rows should leave this alone.
@@ -133,6 +181,24 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
133
181
  * and place in the set. Where it is unset, the row `<template>` is used.
134
182
  */
135
183
  renderItem: VirtualListRenderer | undefined;
184
+ /**
185
+ * Makes the list a list box a reader chooses from: `single` selects one
186
+ * item, and `multiple` any number. Unset is a list a reader reads.
187
+ */
188
+ selectionMode?: 'multiple' | 'single';
189
+ /**
190
+ * The field of each item that typing matches, as a key or a path of keys
191
+ * (`author.name`).
192
+ *
193
+ * Empty by default, which matches the first `data-field` of the row
194
+ * template, or the item itself where it is a string or a number.
195
+ */
196
+ textField: string;
197
+ /**
198
+ * The keys of the chosen items, separated by commas, in a list with
199
+ * `selection-mode`. Set it to start with items chosen.
200
+ */
201
+ value: string;
136
202
  /**
137
203
  * The parsed script.
138
204
  *
@@ -158,8 +224,10 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
158
224
  private released;
159
225
  /** The range last reported, so `helix-range-change` fires only on a change. */
160
226
  private said;
161
- /** The length of the set when `helix-load-more` last fired. */
162
- private askedAt;
227
+ /** The letters typed at the list, which move to the item they start. */
228
+ private readonly typeahead;
229
+ /** The key of the item a Shift range runs from: the last one chosen. */
230
+ private anchor;
163
231
  /**
164
232
  * Watches the source and the tree around it.
165
233
  *
@@ -235,6 +303,11 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
235
303
  * has not been laid out yet asks for nothing.
236
304
  */
237
305
  private report;
306
+ /**
307
+ * The text typing matches for each item: `text-field`, the template's first
308
+ * `data-field`, or a string or number item itself.
309
+ */
310
+ private texts;
238
311
  /** The item a row element drew, for an element that is one of the rows. */
239
312
  private indexOf;
240
313
  /** The row that holds the tab stop for item `index`, where it is drawn. */
@@ -246,6 +319,28 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
246
319
  * is that control's, so a field in a row keeps its arrows.
247
320
  */
248
321
  private readonly handleKeydown;
322
+ /** The key of item `at` in `value`. See `keyField`. */
323
+ private keyAt;
324
+ /** Every item's key, in the order of the set. */
325
+ private keys;
326
+ /** The keys chosen now. */
327
+ private get chosen();
328
+ /**
329
+ * Selects item `at`, or lets it go, asking first. With `extend`, in
330
+ * `multiple`, the items from the anchor to it are taken in, as Data Table
331
+ * and List take in a range. Otherwise the item is where the next range runs
332
+ * from.
333
+ */
334
+ private press;
335
+ /** Keys in the order of the set, a key it no longer holds kept after them. */
336
+ private inOrder;
337
+ /** Asks for a new `value`, and says so once it has moved. */
338
+ private choose;
339
+ /**
340
+ * A press on a row selects it, in a list with `selection-mode`, except on a
341
+ * control of its own. Shift takes in a range.
342
+ */
343
+ private readonly handleClick;
249
344
  /**
250
345
  * Focuses the row for item `index`, scrolling it into view and drawing it
251
346
  * first when it is not drawn.
@@ -266,7 +361,13 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
266
361
  private focusing;
267
362
  /** The item drawn outside the window to keep focus, or to take it, if there is one. */
268
363
  private keptIndex;
269
- /** Writes a row's role and its place in the set. */
364
+ /**
365
+ * Writes a row's role, and whether it is chosen in a list that selects:
366
+ * `option` with `aria-selected` there, and `listitem` otherwise. Every pass,
367
+ * since a choice changes no row's content.
368
+ */
369
+ private mark;
370
+ /** Writes a row's place in the set. */
270
371
  private stamp;
271
372
  /** Fills a drawn row from its item, by `renderItem` or by the template's holes. */
272
373
  private fill;
@@ -4,24 +4,30 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { AriaForwardController, customElement, elementById, HelixElement, HelixLoadMoreEvent, HelixRangeChangeEvent, searchableById, VirtualizeController, } from '@fusion.dev/helix-core';
8
- import { html, render } from 'lit';
7
+ import { AriaForwardController, commaList, customElement, elementById, HelixBeforeChangeEvent, HelixElement, HelixLoadMoreEvent, HelixRangeChangeEvent, rangeAdd, requestChange, searchableById, Typeahead, toggleValue, typesCharacter, VirtualizeController, valueAt, } from '@fusion.dev/helix-core';
8
+ import { html, nothing, render } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { holdControls, releaseControls } from '../controls.js';
11
- import { fillHoles } from '../fill.js';
11
+ import { fillHoles, textOf } from '../fill.js';
12
12
  import { stepIndex } from '../keys.js';
13
+ import { drawPlan, stopAfter } from '../plan.js';
13
14
  let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixElement {
14
15
  constructor() {
15
16
  super(...arguments);
16
17
  this.estimate = 32;
17
18
  this.for = '';
19
+ this.keyField = '';
18
20
  this.label = '';
21
+ this.loading = false;
22
+ this.loadingLabel = 'Loading more';
19
23
  this.measured = false;
24
+ this.textField = '';
25
+ this.value = '';
20
26
  this.rows = [];
21
27
  this.source = null;
22
28
  this.active = 0;
23
29
  this.said = { first: 0, last: 0 };
24
- this.askedAt = -1;
30
+ this.typeahead = new Typeahead();
25
31
  this.aria = new AriaForwardController(this, {
26
32
  label: true,
27
33
  target: () => this.partNamed('viewport'),
@@ -34,17 +40,38 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
34
40
  rows: () => this.drawn(),
35
41
  });
36
42
  this.handleKeydown = (event) => {
37
- if (event.defaultPrevented ||
38
- event.altKey ||
39
- event.ctrlKey ||
40
- event.metaKey ||
41
- event.shiftKey) {
43
+ if (event.defaultPrevented) {
42
44
  return;
43
45
  }
44
46
  const at = this.indexOf(event.target);
45
47
  if (at === undefined) {
46
48
  return;
47
49
  }
50
+ if (typesCharacter(event) && (event.key !== ' ' || this.typeahead.typing())) {
51
+ const found = this.typeahead.match(event.key, this.texts(), at);
52
+ event.preventDefault();
53
+ if (found >= 0 && found !== at) {
54
+ void this.focusIndex(found);
55
+ }
56
+ return;
57
+ }
58
+ const toggle = event.ctrlKey || event.metaKey;
59
+ if (this.selectionMode && !event.altKey) {
60
+ if (toggle && event.key.toLowerCase() === 'a' && this.selectionMode === 'multiple') {
61
+ event.preventDefault();
62
+ this.choose(this.keys().join(','));
63
+ return;
64
+ }
65
+ if (event.key === ' ' && !toggle) {
66
+ event.preventDefault();
67
+ this.press(at, event.shiftKey);
68
+ return;
69
+ }
70
+ }
71
+ const extend = event.shiftKey && this.selectionMode === 'multiple';
72
+ if (event.altKey || toggle || (event.shiftKey && !extend)) {
73
+ return;
74
+ }
48
75
  const { first, last } = this.virtualize.visible();
49
76
  const next = stepIndex(event.key, at, this.data.length, last - first - 1);
50
77
  if (next === undefined) {
@@ -52,6 +79,27 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
52
79
  }
53
80
  event.preventDefault();
54
81
  void this.focusIndex(next);
82
+ if (extend) {
83
+ this.anchor ??= this.keyAt(at);
84
+ this.press(next, true);
85
+ }
86
+ };
87
+ this.handleClick = (event) => {
88
+ if (!this.selectionMode) {
89
+ return;
90
+ }
91
+ const path = event.composedPath();
92
+ const row = path.find((node) => node instanceof HTMLElement && node.parentElement === this);
93
+ const at = this.indexOf(row ?? null);
94
+ if (at === undefined) {
95
+ return;
96
+ }
97
+ const inner = path.slice(0, path.indexOf(row));
98
+ if (inner.some((node) => node instanceof HTMLElement &&
99
+ node.matches('a[href], button, input, select, textarea, label'))) {
100
+ return;
101
+ }
102
+ this.press(at, event.shiftKey && this.selectionMode === 'multiple');
55
103
  };
56
104
  this.handleFocusOut = () => {
57
105
  this.requestUpdate();
@@ -98,6 +146,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
98
146
  connectedCallback() {
99
147
  super.connectedCallback();
100
148
  this.addEventListener('keydown', this.handleKeydown);
149
+ this.addEventListener('click', this.handleClick);
101
150
  this.addEventListener('focusin', this.handleFocusIn);
102
151
  this.addEventListener('focusout', this.handleFocusOut);
103
152
  if (typeof MutationObserver === 'undefined') {
@@ -113,6 +162,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
113
162
  disconnectedCallback() {
114
163
  super.disconnectedCallback();
115
164
  this.removeEventListener('keydown', this.handleKeydown);
165
+ this.removeEventListener('click', this.handleClick);
116
166
  this.removeEventListener('focusin', this.handleFocusIn);
117
167
  this.removeEventListener('focusout', this.handleFocusOut);
118
168
  this.watcher?.disconnect();
@@ -160,17 +210,20 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
160
210
  this.said = { first, last };
161
211
  this.dispatchEvent(new HelixRangeChangeEvent({ first, last }));
162
212
  }
163
- const box = this.partNamed('viewport');
164
- const count = this.data.length;
165
- const screen = box?.clientHeight ?? 0;
166
- if (count > 0 &&
167
- screen > 0 &&
168
- this.askedAt !== count &&
169
- this.virtualize.remaining() <= screen) {
170
- this.askedAt = count;
213
+ if (!this.loading && this.virtualize.endInSight()) {
171
214
  this.dispatchEvent(new HelixLoadMoreEvent());
172
215
  }
173
216
  }
217
+ texts() {
218
+ const field = this.textField ||
219
+ this.querySelector(':scope > template')
220
+ ?.content.querySelector('[data-field]')
221
+ ?.getAttribute('data-field') ||
222
+ '';
223
+ return this.data.map((item) => field === '' || item === null || typeof item !== 'object'
224
+ ? textOf(item)
225
+ : textOf(valueAt(item, field)));
226
+ }
174
227
  indexOf(node) {
175
228
  if (!(node instanceof HTMLElement) || node.parentElement !== this) {
176
229
  return undefined;
@@ -181,6 +234,62 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
181
234
  rowFor(index) {
182
235
  return this.querySelector(`:scope > [data-index="${index}"]`);
183
236
  }
237
+ keyAt(at) {
238
+ const item = this.data[at];
239
+ if (this.keyField !== '' && item !== null && typeof item === 'object') {
240
+ return textOf(valueAt(item, this.keyField));
241
+ }
242
+ return typeof item === 'string' || typeof item === 'number' ? String(item) : String(at);
243
+ }
244
+ keys() {
245
+ return this.data.map((_, at) => this.keyAt(at));
246
+ }
247
+ get chosen() {
248
+ return new Set(commaList(this.value));
249
+ }
250
+ press(at, extend = false) {
251
+ const mode = this.selectionMode;
252
+ const key = this.keyAt(at);
253
+ if (!mode || at < 0 || at >= this.data.length) {
254
+ return;
255
+ }
256
+ const now = commaList(this.value);
257
+ if (extend && mode === 'multiple') {
258
+ const keys = this.keys();
259
+ const from = this.anchor === undefined ? -1 : keys.indexOf(this.anchor);
260
+ const range = rangeAdd(now, keys, from, at);
261
+ if (range) {
262
+ this.choose(this.inOrder(range));
263
+ }
264
+ return;
265
+ }
266
+ this.anchor = key;
267
+ if (mode === 'single') {
268
+ this.choose(now.length === 1 && now[0] === key ? '' : key);
269
+ return;
270
+ }
271
+ this.choose(this.inOrder(toggleValue(now, key)));
272
+ }
273
+ inOrder(keys) {
274
+ const wanted = new Set(keys);
275
+ const ordered = this.keys().filter((key) => wanted.has(key));
276
+ const placed = new Set(ordered);
277
+ return [...ordered, ...keys.filter((key) => !placed.has(key))].join(',');
278
+ }
279
+ choose(next) {
280
+ if (next === this.value) {
281
+ return;
282
+ }
283
+ requestChange({
284
+ apply: (value) => {
285
+ this.value = value;
286
+ this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
287
+ },
288
+ event: (value) => new HelixBeforeChangeEvent({ value }),
289
+ host: this,
290
+ next,
291
+ });
292
+ }
184
293
  async focusIndex(index) {
185
294
  this.active = index;
186
295
  this.focusing = index;
@@ -211,8 +320,16 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
211
320
  ? index
212
321
  : undefined;
213
322
  }
323
+ mark(element, at, chosen) {
324
+ element.setAttribute('role', this.selectionMode ? 'option' : 'listitem');
325
+ if (this.selectionMode) {
326
+ element.setAttribute('aria-selected', String(chosen.has(this.keyAt(at))));
327
+ }
328
+ else {
329
+ element.removeAttribute('aria-selected');
330
+ }
331
+ }
214
332
  stamp(element, at) {
215
- element.setAttribute('role', 'listitem');
216
333
  element.setAttribute('aria-posinset', String(at + 1));
217
334
  element.setAttribute('aria-setsize', String(this.data.length));
218
335
  element.setAttribute('data-index', String(at));
@@ -247,24 +364,19 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
247
364
  }
248
365
  const count = this.data.length;
249
366
  const { first, last } = this.virtualize.window;
250
- const kept = this.keptIndex();
251
- const wanted = [];
252
- for (let at = first; at < Math.min(last, count); at += 1) {
253
- wanted.push(at);
254
- }
255
- if (kept !== undefined) {
256
- wanted.push(kept);
257
- wanted.sort((a, b) => a - b);
258
- }
259
- const keep = new Set(wanted);
260
- const redraw = (this.filledBy === undefined) !== (this.renderItem === undefined);
367
+ const children = [...this.children].filter((child) => child !== template && child instanceof HTMLElement && !child.hasAttribute('slot'));
368
+ const plan = drawPlan({
369
+ count,
370
+ drawn: children.map((child) => Number(child.dataset.index)),
371
+ first,
372
+ kept: this.keptIndex(),
373
+ last,
374
+ redraw: (this.filledBy === undefined) !== (this.renderItem === undefined),
375
+ });
261
376
  const drawn = new Map();
262
- for (const child of [...this.children]) {
263
- if (child === template) {
264
- continue;
265
- }
377
+ for (const child of children) {
266
378
  const index = Number(child.dataset.index);
267
- if (redraw || !Number.isInteger(index) || !keep.has(index)) {
379
+ if (plan.remove.has(index)) {
268
380
  child.remove();
269
381
  continue;
270
382
  }
@@ -275,7 +387,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
275
387
  this.filledBy = this.renderItem;
276
388
  const fresh = new Set();
277
389
  let anchor = drawn.values().next().value?.[0] ?? null;
278
- for (const at of wanted) {
390
+ for (const at of plan.wanted) {
279
391
  const group = drawn.get(at);
280
392
  if (group) {
281
393
  if (refill) {
@@ -290,11 +402,17 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
290
402
  fresh.add(at);
291
403
  this.insertBefore(fragment, anchor);
292
404
  }
293
- if (!keep.has(this.active)) {
294
- const onScreen = this.virtualize.visible().first;
295
- this.active = Math.max(Math.min(onScreen, Math.min(last, count) - 1), first, 0);
296
- }
405
+ this.active = stopAfter(this.active, plan.wanted, {
406
+ count,
407
+ first,
408
+ last,
409
+ onScreen: this.virtualize.visible().first,
410
+ });
411
+ const chosen = this.chosen;
297
412
  for (const [at, group] of drawn) {
413
+ for (const element of group) {
414
+ this.mark(element, at, chosen);
415
+ }
298
416
  const stop = at === this.active;
299
417
  group[0]?.setAttribute('tabindex', stop ? '0' : '-1');
300
418
  if (stop && (fresh.has(at) || this.released !== at)) {
@@ -311,7 +429,9 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
311
429
  return html `<div
312
430
  data-scroller
313
431
  part="viewport"
314
- role="list"
432
+ role=${this.selectionMode ? 'listbox' : 'list'}
433
+ aria-multiselectable=${this.selectionMode === 'multiple' ? 'true' : nothing}
434
+ aria-busy=${this.loading ? 'true' : nothing}
315
435
  aria-label=${this.label}
316
436
  @scroll=${() => {
317
437
  this.virtualize.measure();
@@ -321,6 +441,11 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
321
441
  <div part="before" style="block-size: ${before}px"></div>
322
442
  <slot></slot>
323
443
  <div part="after" style="block-size: ${after}px"></div>
444
+ ${this.loading
445
+ ? html `<div part="loading" role=${this.selectionMode ? 'option' : 'listitem'}>
446
+ <slot name="loading">${this.loadingLabel}</slot>
447
+ </div>`
448
+ : nothing}
324
449
  </div>`;
325
450
  }
326
451
  };
@@ -333,15 +458,33 @@ __decorate([
333
458
  __decorate([
334
459
  property({ attribute: false })
335
460
  ], HelixHeadlessVirtualList.prototype, "items", void 0);
461
+ __decorate([
462
+ property({ attribute: 'key-field' })
463
+ ], HelixHeadlessVirtualList.prototype, "keyField", void 0);
336
464
  __decorate([
337
465
  property()
338
466
  ], HelixHeadlessVirtualList.prototype, "label", void 0);
467
+ __decorate([
468
+ property({ type: Boolean })
469
+ ], HelixHeadlessVirtualList.prototype, "loading", void 0);
470
+ __decorate([
471
+ property({ attribute: 'loading-label' })
472
+ ], HelixHeadlessVirtualList.prototype, "loadingLabel", void 0);
339
473
  __decorate([
340
474
  property({ type: Boolean })
341
475
  ], HelixHeadlessVirtualList.prototype, "measured", void 0);
342
476
  __decorate([
343
477
  property({ attribute: false })
344
478
  ], HelixHeadlessVirtualList.prototype, "renderItem", void 0);
479
+ __decorate([
480
+ property({ attribute: 'selection-mode' })
481
+ ], HelixHeadlessVirtualList.prototype, "selectionMode", void 0);
482
+ __decorate([
483
+ property({ attribute: 'text-field' })
484
+ ], HelixHeadlessVirtualList.prototype, "textField", void 0);
485
+ __decorate([
486
+ property()
487
+ ], HelixHeadlessVirtualList.prototype, "value", void 0);
345
488
  __decorate([
346
489
  state()
347
490
  ], HelixHeadlessVirtualList.prototype, "rows", void 0);