@fusion.dev/helix-headless 0.145.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 (343) hide show
  1. package/custom-elements.json +12286 -4281
  2. package/dist/accordion/item/accordion-item.d.ts +51 -18
  3. package/dist/accordion/item/accordion-item.js +45 -13
  4. package/dist/accordion/root/accordion-root.d.ts +16 -7
  5. package/dist/accordion/root/accordion-root.js +3 -3
  6. package/dist/announcement/root/announcement-root.d.ts +16 -6
  7. package/dist/announcement/root/announcement-root.js +37 -7
  8. package/dist/app-header/root/app-header-root.d.ts +7 -2
  9. package/dist/app-header/root/app-header-root.js +9 -1
  10. package/dist/app-header/sectioning.d.ts +3 -1
  11. package/dist/app-header/sectioning.js +3 -2
  12. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  13. package/dist/area-chart/root/area-chart-root.js +11 -3
  14. package/dist/avatar/group/avatar-group.d.ts +20 -14
  15. package/dist/avatar/group/avatar-group.js +17 -19
  16. package/dist/avatar/root/avatar-root.d.ts +31 -1
  17. package/dist/avatar/root/avatar-root.js +11 -1
  18. package/dist/badge/root/badge-root.d.ts +60 -7
  19. package/dist/badge/root/badge-root.js +89 -6
  20. package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
  21. package/dist/bar-chart/root/bar-chart-root.js +48 -20
  22. package/dist/bar-chart/stack.d.ts +25 -0
  23. package/dist/bar-chart/stack.js +27 -0
  24. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  25. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  26. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +25 -0
  27. package/dist/breadcrumbs/root/breadcrumbs-root.js +51 -4
  28. package/dist/button/root/button-root.d.ts +50 -38
  29. package/dist/button/root/button-root.js +42 -53
  30. package/dist/button/submitter.d.ts +27 -0
  31. package/dist/button/submitter.js +25 -0
  32. package/dist/button-group/root/button-group-root.d.ts +2 -1
  33. package/dist/button-group/root/button-group-root.js +5 -1
  34. package/dist/canvas/node/canvas-node.d.ts +4 -3
  35. package/dist/canvas/node/canvas-node.js +3 -2
  36. package/dist/canvas/root/canvas-root.js +2 -2
  37. package/dist/card/root/card-root.d.ts +36 -4
  38. package/dist/card/root/card-root.js +29 -10
  39. package/dist/carousel/root/carousel-root.d.ts +43 -4
  40. package/dist/carousel/root/carousel-root.js +40 -12
  41. package/dist/carousel/stops.d.ts +20 -0
  42. package/dist/carousel/stops.js +30 -0
  43. package/dist/chat/message/chat-message.js +5 -4
  44. package/dist/chat/naming.d.ts +5 -1
  45. package/dist/chat/naming.js +4 -2
  46. package/dist/chat/root/chat-root.d.ts +40 -4
  47. package/dist/chat/root/chat-root.js +32 -6
  48. package/dist/checkbox/root/checkbox-root.d.ts +20 -0
  49. package/dist/checkbox/root/checkbox-root.js +18 -7
  50. package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
  51. package/dist/checkbox-group/root/checkbox-group-root.js +14 -11
  52. package/dist/checkbox-group/select-all.d.ts +21 -0
  53. package/dist/checkbox-group/select-all.js +7 -0
  54. package/dist/code-block/highlight.d.ts +15 -0
  55. package/dist/code-block/highlight.js +4 -0
  56. package/dist/code-block/root/code-block-root.d.ts +79 -10
  57. package/dist/code-block/root/code-block-root.js +74 -5
  58. package/dist/color-input/root/color-input-root.d.ts +14 -0
  59. package/dist/color-input/root/color-input-root.js +17 -7
  60. package/dist/color-picker/channels.js +1 -2
  61. package/dist/color-picker/root/color-picker-root.d.ts +53 -16
  62. package/dist/color-picker/root/color-picker-root.js +155 -46
  63. package/dist/color-picker/wheel.d.ts +17 -0
  64. package/dist/color-picker/wheel.js +6 -0
  65. package/dist/combobox/root/combobox-root.d.ts +56 -0
  66. package/dist/combobox/root/combobox-root.js +92 -15
  67. package/dist/command/root/command-root.d.ts +19 -2
  68. package/dist/command/root/command-root.js +24 -23
  69. package/dist/copy-button/root/copy-button-root.d.ts +21 -1
  70. package/dist/copy-button/root/copy-button-root.js +16 -4
  71. package/dist/data-table/index.d.ts +1 -1
  72. package/dist/data-table/index.js +1 -1
  73. package/dist/data-table/keys.d.ts +6 -0
  74. package/dist/data-table/keys.js +14 -0
  75. package/dist/data-table/root/data-table-root.d.ts +27 -18
  76. package/dist/data-table/root/data-table-root.js +31 -57
  77. package/dist/data-table/tree.d.ts +16 -1
  78. package/dist/data-table/tree.js +2 -3
  79. package/dist/date-input/root/date-input-root.d.ts +11 -2
  80. package/dist/date-input/root/date-input-root.js +26 -12
  81. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  82. package/dist/date-picker/root/date-picker-root.js +33 -22
  83. package/dist/description-list/item/description-list-item.d.ts +18 -4
  84. package/dist/description-list/item/description-list-item.js +8 -4
  85. package/dist/dialog/description.js +15 -1
  86. package/dist/dialog/root/dialog-root.d.ts +8 -2
  87. package/dist/dialog/root/dialog-root.js +16 -1
  88. package/dist/divider/root/divider-root.d.ts +3 -0
  89. package/dist/divider/root/divider-root.js +2 -1
  90. package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
  91. package/dist/donut-chart/root/donut-chart-root.js +55 -1
  92. package/dist/donut-chart/slice-label.d.ts +35 -0
  93. package/dist/donut-chart/slice-label.js +11 -0
  94. package/dist/drawer/reveal.d.ts +8 -0
  95. package/dist/drawer/reveal.js +1 -0
  96. package/dist/drawer/root/drawer-root.d.ts +63 -2
  97. package/dist/drawer/root/drawer-root.js +75 -4
  98. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  99. package/dist/drilldown/item/drilldown-item.js +40 -5
  100. package/dist/drilldown/root/drilldown-root.d.ts +21 -3
  101. package/dist/drilldown/root/drilldown-root.js +22 -3
  102. package/dist/element-events.generated.js +35 -6
  103. package/dist/empty/root/empty-root.d.ts +34 -2
  104. package/dist/empty/root/empty-root.js +26 -4
  105. package/dist/file-input/root/file-input-root.d.ts +99 -8
  106. package/dist/file-input/root/file-input-root.js +120 -22
  107. package/dist/file-input/screen.d.ts +2 -1
  108. package/dist/file-input/status.d.ts +24 -0
  109. package/dist/file-input/status.js +10 -0
  110. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  111. package/dist/filter-input/chip/filter-chip.js +11 -2
  112. package/dist/filter-input/menu/filter-menu.d.ts +2 -0
  113. package/dist/filter-input/menu/filter-menu.js +12 -4
  114. package/dist/filter-input/root/filter-input-root.d.ts +38 -6
  115. package/dist/filter-input/root/filter-input-root.js +18 -28
  116. package/dist/filter-input/suggest.d.ts +28 -3
  117. package/dist/filter-input/suggest.js +18 -8
  118. package/dist/format-date/options.d.ts +3 -4
  119. package/dist/format-date/options.js +2 -2
  120. package/dist/format-date/root/format-date-root.d.ts +27 -17
  121. package/dist/format-date/root/format-date-root.js +27 -48
  122. package/dist/heatmap/root/buckets.d.ts +21 -0
  123. package/dist/heatmap/root/buckets.js +34 -0
  124. package/dist/heatmap/root/heatmap-root.d.ts +83 -11
  125. package/dist/heatmap/root/heatmap-root.js +104 -12
  126. package/dist/icon/index.d.ts +1 -1
  127. package/dist/icon/index.js +1 -1
  128. package/dist/icon/registry.d.ts +26 -1
  129. package/dist/icon/registry.js +12 -1
  130. package/dist/icon/root/icon-root.d.ts +25 -1
  131. package/dist/icon/root/icon-root.js +44 -12
  132. package/dist/icon/root/index.d.ts +1 -1
  133. package/dist/icon/root/index.js +1 -1
  134. package/dist/icon/settle.d.ts +7 -0
  135. package/dist/icon/settle.js +10 -0
  136. package/dist/icon-button/root/icon-button-root.d.ts +2 -0
  137. package/dist/image/root/image-root.d.ts +14 -1
  138. package/dist/image/root/image-root.js +7 -2
  139. package/dist/input/root/input-root.d.ts +34 -3
  140. package/dist/input/root/input-root.js +17 -4
  141. package/dist/kbd/keys.d.ts +29 -0
  142. package/dist/kbd/keys.js +52 -0
  143. package/dist/kbd/root/kbd-root.d.ts +52 -1
  144. package/dist/kbd/root/kbd-root.js +40 -47
  145. package/dist/legend/hidden-series.d.ts +10 -2
  146. package/dist/legend/hidden-series.js +5 -2
  147. package/dist/legend/root/legend-root.d.ts +77 -12
  148. package/dist/legend/root/legend-root.js +71 -17
  149. package/dist/lightbox/root/lightbox-root.d.ts +98 -10
  150. package/dist/lightbox/root/lightbox-root.js +153 -18
  151. package/dist/lightbox/zoom-controller.d.ts +59 -0
  152. package/dist/lightbox/zoom-controller.js +128 -0
  153. package/dist/lightbox/zoom.d.ts +24 -0
  154. package/dist/lightbox/zoom.js +18 -0
  155. package/dist/line-chart/root/line-chart-root.d.ts +38 -1
  156. package/dist/line-chart/root/line-chart-root.js +31 -3
  157. package/dist/list/item/list-item.d.ts +2 -0
  158. package/dist/list/item/list-item.js +11 -2
  159. package/dist/list/root/list-root.d.ts +70 -13
  160. package/dist/list/root/list-root.js +91 -13
  161. package/dist/list/selection.d.ts +54 -7
  162. package/dist/list/selection.js +32 -7
  163. package/dist/listbox/root/listbox-root.d.ts +44 -4
  164. package/dist/listbox/root/listbox-root.js +72 -34
  165. package/dist/loader/busy.d.ts +10 -0
  166. package/dist/loader/busy.js +11 -0
  167. package/dist/loader/root/loader-root.d.ts +9 -2
  168. package/dist/loader/root/loader-root.js +23 -3
  169. package/dist/megamenu/hand-over.d.ts +75 -0
  170. package/dist/megamenu/hand-over.js +67 -0
  171. package/dist/megamenu/root/megamenu-root.d.ts +80 -15
  172. package/dist/megamenu/root/megamenu-root.js +76 -19
  173. package/dist/menu/item/menu-item.d.ts +51 -2
  174. package/dist/menu/item/menu-item.js +50 -26
  175. package/dist/menu/root/menu-root.d.ts +17 -0
  176. package/dist/menu/root/menu-root.js +2 -2
  177. package/dist/meter/root/meter-root.d.ts +62 -1
  178. package/dist/meter/root/meter-root.js +38 -16
  179. package/dist/navigation/root/navigation-root.d.ts +2 -1
  180. package/dist/navigation/root/navigation-root.js +2 -2
  181. package/dist/notifications/root/notifications-root.d.ts +20 -4
  182. package/dist/notifications/root/notifications-root.js +30 -8
  183. package/dist/number-input/grain.d.ts +28 -0
  184. package/dist/number-input/grain.js +14 -0
  185. package/dist/number-input/root/number-input-root.d.ts +34 -9
  186. package/dist/number-input/root/number-input-root.js +31 -25
  187. package/dist/object-inspector/root/object-inspector-root.d.ts +88 -33
  188. package/dist/object-inspector/root/object-inspector-root.js +134 -92
  189. package/dist/object-inspector/rows.d.ts +18 -0
  190. package/dist/object-inspector/rows.js +33 -0
  191. package/dist/option/index.d.ts +1 -0
  192. package/dist/option/index.js +1 -0
  193. package/dist/option/root/option-root.d.ts +5 -3
  194. package/dist/option/separator/option-separator.d.ts +30 -0
  195. package/dist/option/separator/option-separator.js +21 -0
  196. package/dist/outline/root/outline-root.d.ts +30 -0
  197. package/dist/outline/root/outline-root.js +17 -13
  198. package/dist/pagination/size/pagination-size.d.ts +6 -0
  199. package/dist/pagination/size/pagination-size.js +5 -3
  200. package/dist/pin-input/code.d.ts +37 -0
  201. package/dist/pin-input/code.js +25 -0
  202. package/dist/pin-input/root/pin-input-root.d.ts +116 -115
  203. package/dist/pin-input/root/pin-input-root.js +163 -155
  204. package/dist/popover/hide-outside.d.ts +14 -0
  205. package/dist/popover/hide-outside.js +34 -0
  206. package/dist/popover/root/popover-root.d.ts +64 -8
  207. package/dist/popover/root/popover-root.js +67 -4
  208. package/dist/progress/root/progress-root.d.ts +47 -0
  209. package/dist/progress/root/progress-root.js +34 -16
  210. package/dist/qr-code/export.d.ts +45 -1
  211. package/dist/qr-code/export.js +28 -0
  212. package/dist/qr-code/name.d.ts +11 -0
  213. package/dist/qr-code/name.js +7 -0
  214. package/dist/qr-code/root/qr-code-root.d.ts +36 -11
  215. package/dist/qr-code/root/qr-code-root.js +58 -11
  216. package/dist/quote/marks.d.ts +8 -0
  217. package/dist/quote/marks.js +5 -0
  218. package/dist/quote/root/quote-root.d.ts +14 -2
  219. package/dist/quote/root/quote-root.js +23 -1
  220. package/dist/radio/group/radio-group.d.ts +15 -0
  221. package/dist/radio/root/radio-root.d.ts +6 -2
  222. package/dist/radio/root/radio-root.js +7 -2
  223. package/dist/rating/root/rating-root.d.ts +30 -12
  224. package/dist/rating/root/rating-root.js +27 -27
  225. package/dist/scatter-chart/fit.d.ts +15 -0
  226. package/dist/scatter-chart/fit.js +35 -0
  227. package/dist/scatter-chart/root/scatter-chart-root.d.ts +51 -1
  228. package/dist/scatter-chart/root/scatter-chart-root.js +64 -24
  229. package/dist/scroll-area/root/scroll-area-root.d.ts +22 -51
  230. package/dist/scroll-area/root/scroll-area-root.js +57 -173
  231. package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
  232. package/dist/scroll-area/scrollbar-controller.js +172 -0
  233. package/dist/scroll-area/scrollbar.d.ts +27 -8
  234. package/dist/scroll-area/scrollbar.js +10 -1
  235. package/dist/select/align.d.ts +47 -0
  236. package/dist/select/align.js +7 -0
  237. package/dist/select/root/select-root.d.ts +71 -0
  238. package/dist/select/root/select-root.js +78 -6
  239. package/dist/skeleton/root/skeleton-root.d.ts +3 -0
  240. package/dist/skeleton/root/skeleton-root.js +4 -2
  241. package/dist/skip-link/root/skip-link-root.js +2 -2
  242. package/dist/slider/root/slider-root.d.ts +59 -5
  243. package/dist/slider/root/slider-root.js +45 -3
  244. package/dist/sparkline/readings.d.ts +36 -0
  245. package/dist/sparkline/readings.js +36 -0
  246. package/dist/sparkline/root/sparkline-root.d.ts +121 -7
  247. package/dist/sparkline/root/sparkline-root.js +160 -26
  248. package/dist/speed-dial/item/speed-dial-item.d.ts +1 -1
  249. package/dist/speed-dial/item/speed-dial-item.js +2 -2
  250. package/dist/speed-dial/root/speed-dial-root.d.ts +6 -0
  251. package/dist/speed-dial/root/speed-dial-root.js +11 -3
  252. package/dist/splitter/root/splitter-root.d.ts +10 -12
  253. package/dist/splitter/root/splitter-root.js +211 -204
  254. package/dist/stat/root/stat-root.d.ts +14 -5
  255. package/dist/stat/root/stat-root.js +21 -5
  256. package/dist/steps/reach.d.ts +5 -0
  257. package/dist/steps/reach.js +3 -0
  258. package/dist/steps/root/steps-root.d.ts +56 -3
  259. package/dist/steps/root/steps-root.js +50 -6
  260. package/dist/steps/step/step.d.ts +8 -0
  261. package/dist/steps/step/step.js +8 -3
  262. package/dist/switch/root/switch-root.d.ts +10 -0
  263. package/dist/table/column/table-column.d.ts +6 -10
  264. package/dist/table/column/table-column.js +8 -20
  265. package/dist/table/root/table-root.d.ts +31 -10
  266. package/dist/table/root/table-root.js +14 -18
  267. package/dist/table/row/table-row.d.ts +24 -0
  268. package/dist/table/row/table-row.js +37 -1
  269. package/dist/tabs/panel/tab-panel.d.ts +16 -3
  270. package/dist/tabs/panel/tab-panel.js +34 -5
  271. package/dist/tabs/root/tabs-root.d.ts +17 -3
  272. package/dist/tabs/root/tabs-root.js +1 -1
  273. package/dist/tabs/tab/tab.d.ts +5 -0
  274. package/dist/tabs/tab/tab.js +5 -2
  275. package/dist/tag/group/tag-group.d.ts +44 -5
  276. package/dist/tag/group/tag-group.js +60 -9
  277. package/dist/tag/root/tag-root.js +9 -2
  278. package/dist/textarea/root/textarea-root.d.ts +41 -2
  279. package/dist/textarea/root/textarea-root.js +39 -1
  280. package/dist/time-input/root/time-input-root.d.ts +10 -2
  281. package/dist/time-input/root/time-input-root.js +24 -13
  282. package/dist/time-picker/root/time-picker-root.d.ts +28 -17
  283. package/dist/time-picker/root/time-picker-root.js +18 -29
  284. package/dist/time-picker/wheel-name.d.ts +9 -0
  285. package/dist/time-picker/wheel-name.js +15 -0
  286. package/dist/timeline/group/timeline-group.js +2 -2
  287. package/dist/toast/item/toast-item.d.ts +16 -3
  288. package/dist/toast/item/toast-item.js +22 -6
  289. package/dist/toast/root/toast-root.d.ts +34 -4
  290. package/dist/toast/root/toast-root.js +74 -13
  291. package/dist/toast/words.d.ts +12 -0
  292. package/dist/toast/words.js +25 -0
  293. package/dist/toggle/group/toggle-group.d.ts +52 -7
  294. package/dist/toggle/group/toggle-group.js +58 -44
  295. package/dist/toggle/root/toggle-root.js +2 -4
  296. package/dist/toolbar/root/toolbar-root.d.ts +21 -5
  297. package/dist/toolbar/root/toolbar-root.js +52 -8
  298. package/dist/tooltip/root/tooltip-root.d.ts +20 -3
  299. package/dist/tooltip/root/tooltip-root.js +18 -1
  300. package/dist/tour/back.d.ts +10 -0
  301. package/dist/tour/back.js +8 -0
  302. package/dist/tour/root/tour-root.d.ts +45 -2
  303. package/dist/tour/root/tour-root.js +53 -8
  304. package/dist/tour/stop/tour-stop.d.ts +3 -0
  305. package/dist/tour/stop/tour-stop.js +3 -3
  306. package/dist/tree/item/tree-item.d.ts +7 -3
  307. package/dist/tree/item/tree-item.js +3 -0
  308. package/dist/tree/root/tree-root.d.ts +24 -3
  309. package/dist/tree/root/tree-root.js +32 -13
  310. package/dist/tree/selection.js +2 -15
  311. package/dist/trend/figure.d.ts +16 -0
  312. package/dist/trend/figure.js +8 -0
  313. package/dist/trend/root/trend-root.d.ts +83 -19
  314. package/dist/trend/root/trend-root.js +59 -8
  315. package/dist/video/media-events.d.ts +13 -18
  316. package/dist/video/media-events.js +1 -5
  317. package/dist/video/root/extra-controls.d.ts +12 -0
  318. package/dist/video/root/extra-controls.js +6 -2
  319. package/dist/video/root/video-root.d.ts +23 -6
  320. package/dist/video/root/video-root.js +19 -10
  321. package/dist/video-playlist/menu/video-playlist-menu.d.ts +2 -0
  322. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  323. package/dist/video-playlist/root/video-playlist-root.d.ts +17 -1
  324. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  325. package/dist/virtual-list/fill.d.ts +11 -8
  326. package/dist/virtual-list/fill.js +10 -17
  327. package/dist/virtual-list/keys.d.ts +16 -7
  328. package/dist/virtual-list/keys.js +17 -6
  329. package/dist/virtual-list/plan.d.ts +65 -0
  330. package/dist/virtual-list/plan.js +27 -0
  331. package/dist/virtual-list/root/virtual-list-root.d.ts +136 -13
  332. package/dist/virtual-list/root/virtual-list-root.js +218 -51
  333. package/lint.json +1 -1
  334. package/package.json +8 -8
  335. package/types/jsx.d.ts +7441 -1458
  336. package/types/svelte.d.ts +1 -0
  337. package/types/vue.d.ts +1501 -326
  338. package/dist/chat/relative-clock.d.ts +0 -39
  339. package/dist/chat/relative-clock.js +0 -31
  340. package/dist/data-table/root/order.d.ts +0 -141
  341. package/dist/data-table/root/order.js +0 -76
  342. package/dist/tag/selection.d.ts +0 -10
  343. package/dist/tag/selection.js +0 -12
@@ -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.
@@ -44,18 +46,22 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
44
46
  *
45
47
  * @slot - The options, and any groups of them.
46
48
  * @slot empty - What the list shows when it has no options to show, for content richer than `empty-text`.
49
+ * @slot error - What the list shows while `load-error` is set, for content richer than its text.
47
50
  * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
48
51
  * @slot filter-icon - The icon in the filter field. Defaults to a magnifying glass.
49
52
  * @slot hint - The hint, for content richer than plain text.
50
53
  * @slot label - The label, for content richer than plain text.
51
54
  * @slot loading - What the list shows while its options load, for content richer than `loading-text`.
55
+ * @slot no-results - What the list shows when a filter hides every option, for content richer than `no-results-text`.
52
56
  *
53
57
  * @event {Event} change - Fired after the value changes.
54
58
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired when the value is about to change, and cancelable.
55
59
  * @event {HelixFilterEvent} helix-filter - Fired before the list is filtered, and cancelable.
60
+ * @event {HelixLoadMoreEvent} helix-load-more - Fired once the list is scrolled within a screen of its end, once for each number of options, and not while `loading` is set. Add the next page of options and set `loading` while you fetch it.
56
61
  * @event {Event} input - Fired after the value changes.
57
62
  *
58
- * @csspart empty - The text shown when the filter hides every option.
63
+ * @csspart empty - The text shown when the list has no options to show, or the filter hides every one. `:state(no-results)` is set for the second.
64
+ * @csspart error - The text shown while `load-error` is set.
59
65
  * @csspart feedback - The message that says what is wrong with the value.
60
66
  * @csspart filter-icon - The icon beside the filter field.
61
67
  * @csspart filter-mark - The default icon beside the filter field, shown when none is slotted.
@@ -68,8 +74,10 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
68
74
  * @csspart search-field - The filter field.
69
75
  *
70
76
  * @cssstate empty - The list has no options to show: none were given, or the filter hid every one.
77
+ * @cssstate error - While `load-error` is set.
71
78
  * @cssstate invalid - The control has an error to show, from the browser's validation or from your `feedback`.
72
79
  * @cssstate loading - While `loading` is set.
80
+ * @cssstate no-results - A filter hid every option of a list that has some.
73
81
  */
74
82
  export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
75
83
  protected readonly aria: AriaForwardController;
@@ -130,6 +138,14 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
130
138
  * Placeholder text for the filter field.
131
139
  */
132
140
  filterLabel: string;
141
+ /**
142
+ * What the list says when its options could not be loaded, such as after a
143
+ * request to a server fails. Empty is no error.
144
+ *
145
+ * Set, it shows under any options there are, in place of `empty-text` and
146
+ * `loading-text`, and a screen reader hears it. Clear it when you ask again.
147
+ */
148
+ loadError: string;
133
149
  /**
134
150
  * Stops the arrow keys from choosing each option they move to. Enter, Space
135
151
  * or a click chooses the highlighted option.
@@ -143,6 +159,13 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
143
159
  * Whether more than one option can be chosen.
144
160
  */
145
161
  multiple: boolean;
162
+ /**
163
+ * The text shown when a filter hides every option of a list that has some.
164
+ *
165
+ * Absent, the list says `empty-text` in both cases. Set it to tell "nothing
166
+ * matches what you typed" from "there is nothing here".
167
+ */
168
+ noResultsText?: string;
146
169
  /**
147
170
  * What a screen reader hears when a filter leaves one option. `{count}` is
148
171
  * the count.
@@ -265,7 +288,8 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
265
288
  */
266
289
  get options(): HelixHeadlessOption[];
267
290
  /**
268
- * 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.
269
293
  *
270
294
  * Through a slot because that is how this gets composed. `Select` renders a
271
295
  * listbox in its own shadow root with a slot inside it, so the options are
@@ -273,7 +297,7 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
273
297
  * `querySelectorAll` finds the slot and no options at all, and the whole
274
298
  * component comes up empty.
275
299
  */
276
- private collect;
300
+ private among;
277
301
  /** The groups, found the same way and for the same reason. */
278
302
  private get groups();
279
303
  /** The options a filter has left showing. */
@@ -430,6 +454,15 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
430
454
  private takeFilter;
431
455
  /** Filters at once on a filter still waiting out `filterDelay`. */
432
456
  private flushFilter;
457
+ /** The number of options the list last asked for more at. */
458
+ private askedAt;
459
+ /**
460
+ * Asks for more once the reader is within a screen of the end, React Aria's
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.
464
+ */
465
+ private readonly handleScroll;
433
466
  /** Requests a new filter, and returns whether the request went through. */
434
467
  requestFilter(next: string): boolean;
435
468
  /**
@@ -485,6 +518,13 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
485
518
  * the live region still says it.
486
519
  */
487
520
  protected renderStatus(hasContent: boolean): TemplateResult | typeof nothing;
521
+ /**
522
+ * Whether a filter hid every option of a list that has some, which is a
523
+ * different thing to say from a list that has none.
524
+ */
525
+ private get noResults();
526
+ /** The error, or what a filter that hid everything says, before the shared words. */
527
+ protected renderLive(hasContent: boolean): TemplateResult;
488
528
  protected render(): TemplateResult;
489
529
  }
490
530
  declare global {
@@ -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, 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';
@@ -70,6 +70,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
70
70
  this.filterable = false;
71
71
  this.filterDelay = 0;
72
72
  this.filterLabel = 'Filter';
73
+ this.loadError = '';
73
74
  this.manual = false;
74
75
  this.multiple = false;
75
76
  this.resultLabel = '{count} result';
@@ -118,11 +119,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
118
119
  }
119
120
  return;
120
121
  }
121
- if (this.multiple &&
122
- !typing &&
123
- event.key.toLowerCase() === 'a' &&
124
- (event.ctrlKey || event.metaKey) &&
125
- !event.altKey) {
122
+ if (this.multiple && !typing && selectsAll(event)) {
126
123
  event.preventDefault();
127
124
  const reachable = shown.map((option) => option.value);
128
125
  const all = reachable.every((value) => this.values.includes(value));
@@ -138,7 +135,12 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
138
135
  }
139
136
  return;
140
137
  }
141
- 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)) {
142
144
  return;
143
145
  }
144
146
  event.preventDefault();
@@ -169,7 +171,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
169
171
  if (target) {
170
172
  this.arrive(target.value);
171
173
  const ranged = event.key === 'Home' || event.key === 'End';
172
- if (this.multiple && !typing && event.shiftKey && ranged) {
174
+ if (this.multiple && (!typing || fieldRange) && event.shiftKey && ranged) {
173
175
  const taken = rangeAdd(this.values, shown.map((option) => option.value), at, next);
174
176
  if (taken) {
175
177
  this.requestValue(taken.join(','));
@@ -192,6 +194,22 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
192
194
  this.takeFilter(control);
193
195
  };
194
196
  this.filtering = new DelayController(this, { renders: false });
197
+ this.askedAt = -1;
198
+ this.handleScroll = (event) => {
199
+ const items = event.currentTarget;
200
+ const count = this.options.length;
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
+ })) {
208
+ return;
209
+ }
210
+ this.askedAt = count;
211
+ this.dispatchEvent(new HelixLoadMoreEvent());
212
+ };
195
213
  this.handleFilterKeydown = (event) => {
196
214
  if (event.key === 'Enter' || FIELD_MOVES.has(event.key)) {
197
215
  this.flushFilter();
@@ -280,40 +298,24 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
280
298
  return this.multiple ? 'select-multiple' : 'select-one';
281
299
  }
282
300
  get options() {
283
- return this.collect(this.children);
301
+ return this.among((node) => node instanceof HelixHeadlessOption, this.children);
284
302
  }
285
- collect(nodes) {
303
+ among(is, nodes) {
286
304
  const found = [];
287
305
  for (const node of nodes) {
288
- if (node instanceof HelixHeadlessOption) {
306
+ if (is(node)) {
289
307
  found.push(node);
290
308
  }
291
- else if (node instanceof HTMLSlotElement) {
292
- found.push(...this.collect(node.assignedElements({ flatten: true })));
293
- }
294
309
  else {
295
- found.push(...this.collect(node.children));
310
+ found.push(...this.among(is, node instanceof HTMLSlotElement
311
+ ? node.assignedElements({ flatten: true })
312
+ : node.children));
296
313
  }
297
314
  }
298
315
  return found;
299
316
  }
300
317
  get groups() {
301
- const found = [];
302
- const walk = (nodes) => {
303
- for (const node of nodes) {
304
- if (node instanceof HelixHeadlessOptionGroup) {
305
- found.push(node);
306
- }
307
- if (node instanceof HTMLSlotElement) {
308
- walk(node.assignedElements({ flatten: true }));
309
- }
310
- else {
311
- walk(node.children);
312
- }
313
- }
314
- };
315
- walk(this.children);
316
- return found;
318
+ return this.among((node) => node instanceof HelixHeadlessOptionGroup, this.children);
317
319
  }
318
320
  get shown() {
319
321
  return this.options.filter((option) => !option.hidden);
@@ -385,9 +387,12 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
385
387
  option.selected = chosen.has(option.value);
386
388
  }
387
389
  for (const group of this.groups) {
388
- const inside = this.collect(group.children);
390
+ const inside = this.among((node) => node instanceof HelixHeadlessOption, group.children);
389
391
  group.hidden = inside.length > 0 && inside.every((option) => option.hidden);
390
392
  }
393
+ for (const rule of this.among((node) => node.localName.endsWith('-option-separator'), this.children)) {
394
+ rule.hidden = needle !== '';
395
+ }
391
396
  const shown = this.shown;
392
397
  const rescue = !this.parked && (needle !== '' || this.activeValue !== '');
393
398
  if (rescue && !shown.some((option) => option.value === this.activeValue)) {
@@ -471,6 +476,8 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
471
476
  }
472
477
  this.toggleState('empty', !this.showsOptions);
473
478
  this.toggleState('loading', this.loading);
479
+ this.toggleState('error', this.loadError !== '');
480
+ this.toggleState('no-results', this.noResults);
474
481
  }
475
482
  seedDeclared() {
476
483
  if (this.owned || this.defaultValue !== '' || this.hasAttribute('default-value')) {
@@ -545,13 +552,37 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
545
552
  : true;
546
553
  }
547
554
  renderStatus(hasContent) {
555
+ if (this.loadError !== '') {
556
+ return html `<p part="error"><slot name="error">${this.loadError}</slot></p>`;
557
+ }
548
558
  if (!this.loading && hasContent) {
549
559
  return nothing;
550
560
  }
551
- return this.loading
552
- ? html `<p part="loading"><slot name="loading">${this.loadingText}</slot></p>`
561
+ if (this.loading) {
562
+ return html `<p part="loading"><slot name="loading">${this.loadingText}</slot></p>`;
563
+ }
564
+ const given = this.noResultsText !== undefined ||
565
+ this.querySelector?.(':scope > [slot="no-results"]') != null;
566
+ return this.noResults && given
567
+ ? html `<p part="empty"><slot name="no-results">${this.noResultsText ?? this.emptyText}</slot></p>`
553
568
  : html `<p part="empty"><slot name="empty">${this.emptyText}</slot></p>`;
554
569
  }
570
+ get noResults() {
571
+ return (this.counted &&
572
+ !this.loading &&
573
+ this.filter !== '' &&
574
+ this.options.length > 0 &&
575
+ !this.hasMatches);
576
+ }
577
+ renderLive(hasContent) {
578
+ if (this.loadError !== '') {
579
+ return html `<span part="live" role="status">${this.loadError}</span>`;
580
+ }
581
+ if (this.noResults && this.noResultsText !== undefined) {
582
+ return html `<span part="live" role="status">${this.noResultsText}</span>`;
583
+ }
584
+ return super.renderLive(hasContent);
585
+ }
555
586
  render() {
556
587
  const shown = this.showsOptions;
557
588
  return html `${this.renderGroupLabel()}
@@ -588,6 +619,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
588
619
  aria-required=${this.required ? 'true' : nothing}
589
620
  ${''}
590
621
  tabindex=${this.filterable ? nothing : this.isDisabled ? -1 : 0}
622
+ @scroll=${this.handleScroll}
591
623
  >
592
624
  <slot @slotchange=${() => this.place()}></slot>
593
625
  ${this.renderStatus(shown)}
@@ -616,12 +648,18 @@ __decorate([
616
648
  __decorate([
617
649
  property({ attribute: 'filter-label' })
618
650
  ], HelixHeadlessListbox.prototype, "filterLabel", void 0);
651
+ __decorate([
652
+ property({ attribute: 'load-error' })
653
+ ], HelixHeadlessListbox.prototype, "loadError", void 0);
619
654
  __decorate([
620
655
  property({ type: Boolean })
621
656
  ], HelixHeadlessListbox.prototype, "manual", void 0);
622
657
  __decorate([
623
658
  property({ reflect: true, type: Boolean })
624
659
  ], HelixHeadlessListbox.prototype, "multiple", void 0);
660
+ __decorate([
661
+ property({ attribute: 'no-results-text' })
662
+ ], HelixHeadlessListbox.prototype, "noResultsText", void 0);
625
663
  __decorate([
626
664
  property({ attribute: 'result-label' })
627
665
  ], HelixHeadlessListbox.prototype, "resultLabel", void 0);
@@ -15,3 +15,13 @@
15
15
  export declare const claimBusy: (region: Element) => void;
16
16
  /** Takes a claim back, and the attribute with the last one. */
17
17
  export declare const releaseBusy: (region: Element) => void;
18
+ /**
19
+ * Calls `found` with the region `find` answers once it is in `root`, and
20
+ * returns the call that stops waiting.
21
+ *
22
+ * For a loader whose region is not on the page yet: a framework often draws
23
+ * the loader before the list it stands for, and a region looked up once, a
24
+ * frame after connecting, was never marked. Watches for children and ids being
25
+ * written, and stops at the first answer.
26
+ */
27
+ export declare const awaitRegion: (root: Node, find: () => Element | null, found: (region: Element) => void) => (() => void);
@@ -22,3 +22,14 @@ export const releaseBusy = (region) => {
22
22
  CLAIMS.set(region, held - 1);
23
23
  }
24
24
  };
25
+ export const awaitRegion = (root, find, found) => {
26
+ const observer = new MutationObserver(() => {
27
+ const region = find();
28
+ if (region) {
29
+ observer.disconnect();
30
+ found(region);
31
+ }
32
+ });
33
+ observer.observe(root, { attributeFilter: ['id'], childList: true, subtree: true });
34
+ return () => observer.disconnect();
35
+ };
@@ -22,11 +22,12 @@ import { type PropertyValues, type TemplateResult } from 'lit';
22
22
  * **`for` names the region being loaded**, and the loader sets
23
23
  * `aria-busy="true"` on it for as long as the loader is on the page, so a
24
24
  * screen reader waits for the region rather than reading it half filled. Two
25
- * loaders in one region hold it busy until the last one leaves.
25
+ * loaders in one region hold it busy until the last one leaves. A region drawn
26
+ * after the loader is marked when it arrives.
26
27
  *
27
28
  * **`delay` keeps a quick load from flashing a loader.** The loader stays
28
29
  * hidden, and silent, for that many milliseconds, and appears only if the work
29
- * is still going.
30
+ * is still going. While it waits it carries `:state(waiting)`.
30
31
  *
31
32
  * @since 0.1.0
32
33
  * @status stable
@@ -40,6 +41,8 @@ import { type PropertyValues, type TemplateResult } from 'lit';
40
41
  * @csspart mark - The region that holds the slotted content at the center.
41
42
  * @csspart svg - The rotating circle pair.
42
43
  * @csspart track - The full circle behind the indicator.
44
+ *
45
+ * @cssstate waiting - The loader is waiting out its `delay`, hidden and silent.
43
46
  */
44
47
  export declare class HelixHeadlessLoader extends HelixElement {
45
48
  /**
@@ -67,6 +70,10 @@ export declare class HelixHeadlessLoader extends HelixElement {
67
70
  private readonly due;
68
71
  /** The region this loader marked busy, so it is the one released. */
69
72
  private region;
73
+ /** Stops waiting for a region `for` names that is not on the page yet. */
74
+ private stopAwaiting;
75
+ /** Whether `for` has named an element while the loader was on the page. */
76
+ private found;
70
77
  /** Whether the loader is still waiting out its `delay`. */
71
78
  private get waiting();
72
79
  connectedCallback(): void;
@@ -4,10 +4,10 @@ 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 { customElement, DelayController, elementById, HelixElement, hostAria, } from '@fusion.dev/helix-core';
7
+ import { customElement, DelayController, elementById, HelixElement, hostAria, reportRuntime, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
- import { claimBusy, releaseBusy } from '../busy.js';
10
+ import { awaitRegion, claimBusy, releaseBusy } from '../busy.js';
11
11
  let HelixHeadlessLoader = class HelixHeadlessLoader extends HelixElement {
12
12
  constructor() {
13
13
  super(...arguments);
@@ -16,6 +16,7 @@ let HelixHeadlessLoader = class HelixHeadlessLoader extends HelixElement {
16
16
  this.label = 'Loading';
17
17
  this.due = new DelayController(this);
18
18
  this.region = null;
19
+ this.found = false;
19
20
  }
20
21
  get waiting() {
21
22
  return this.delay > 0 && !this.due.elapsed;
@@ -29,10 +30,26 @@ let HelixHeadlessLoader = class HelixHeadlessLoader extends HelixElement {
29
30
  }
30
31
  disconnectedCallback() {
31
32
  super.disconnectedCallback();
33
+ if (this.stopAwaiting && !this.found) {
34
+ reportRuntime({
35
+ kind: 'missing-target',
36
+ message: `<${this.localName} for="${this.for}"> named no element in its tree while it was on ` +
37
+ 'the page, so no region was marked busy. Give the region that id.',
38
+ tag: this.localName,
39
+ });
40
+ }
41
+ this.stopAwaiting?.();
42
+ this.stopAwaiting = undefined;
32
43
  this.release();
33
44
  }
34
45
  mark() {
35
- const region = this.isConnected && this.for !== '' ? elementById(this, this.for) : null;
46
+ this.stopAwaiting?.();
47
+ this.stopAwaiting = undefined;
48
+ const named = this.isConnected && this.for !== '';
49
+ const region = named ? elementById(this, this.for) : null;
50
+ if (named && !region) {
51
+ this.stopAwaiting = awaitRegion(this.getRootNode(), () => elementById(this, this.for), () => this.mark());
52
+ }
36
53
  if (region === this.region) {
37
54
  return;
38
55
  }
@@ -40,6 +57,7 @@ let HelixHeadlessLoader = class HelixHeadlessLoader extends HelixElement {
40
57
  if (region) {
41
58
  claimBusy(region);
42
59
  this.region = region;
60
+ this.found = true;
43
61
  }
44
62
  }
45
63
  release() {
@@ -51,6 +69,7 @@ let HelixHeadlessLoader = class HelixHeadlessLoader extends HelixElement {
51
69
  updated(changed) {
52
70
  super.updated(changed);
53
71
  if (changed.has('for') && changed.get('for') !== undefined) {
72
+ this.found = false;
54
73
  this.mark();
55
74
  }
56
75
  }
@@ -59,6 +78,7 @@ let HelixHeadlessLoader = class HelixHeadlessLoader extends HelixElement {
59
78
  hostAria(this.internals, { label: this.label, role: 'progressbar' });
60
79
  this.internals.ariaBusy = 'true';
61
80
  this.internals.ariaHidden = this.waiting ? 'true' : null;
81
+ this.toggleState('waiting', this.waiting);
62
82
  }
63
83
  render() {
64
84
  return html `<svg part="svg" viewBox="0 0 24 24" aria-hidden="true" ?hidden=${this.waiting}>
@@ -0,0 +1,75 @@
1
+ /** Which way a panel moved when another took its place along the bar. */
2
+ export type Motion = 'from-end' | 'from-start' | 'to-end' | 'to-start';
3
+ /** What a hand-over asks of a megamenu. */
4
+ export interface Handed extends Node {
5
+ /** Asks the panel to open or close, and returns whether it did. */
6
+ requestOpen(open: boolean): boolean;
7
+ }
8
+ /**
9
+ * The bar's one open panel, handed from one megamenu to the next.
10
+ *
11
+ * A page has one panel open at a time: a nav bar whose Products panel is still
12
+ * down while Solutions opens is a nav bar with two panels over the page. Module
13
+ * scope rather than a coordinating parent, because there is nothing to
14
+ * coordinate through: megamenus sit in whatever container the consumer used,
15
+ * which is usually `Navigation` and is not required to be. `Video` holds the
16
+ * media session the same way and for the same reason.
17
+ *
18
+ * When one panel opens in place of another, each is told which way the reader
19
+ * moved, as Radix's `data-motion` does: toward the end of the bar where the
20
+ * one opening comes after the one closing in the document. The one opening
21
+ * enters from the side the reader moved toward, and the one closing leaves
22
+ * toward the side they came from. Only the latest move is ever described.
23
+ */
24
+ export declare class HandOver<T extends Handed> {
25
+ private readonly motion;
26
+ /**
27
+ * Every megamenu on the page, so a press on another one's trigger counts as
28
+ * moving along the bar. Closing on that press would close this panel before
29
+ * the other opens, and the hand-over would have no direction.
30
+ */
31
+ readonly connected: Set<T>;
32
+ /** The panel showing now, so opening one closes the last. */
33
+ showing: T | undefined;
34
+ /**
35
+ * The megamenu a pointer is pressing, from the press until the click that
36
+ * ends it. A press moves focus before the click opens anything, and the open
37
+ * panel must not close on that focus move, or the hand-over has no direction.
38
+ */
39
+ pressed: T | undefined;
40
+ /** The panels closing because another opened in their place. */
41
+ private readonly handed;
42
+ /** @param motion Writes a megamenu's direction, and clears it for `undefined`. */
43
+ constructor(motion: (megamenu: T, to: Motion | undefined) => void);
44
+ /** Counts a megamenu as part of the bar. */
45
+ add(megamenu: T): void;
46
+ /** Stops counting a megamenu that left the page. */
47
+ remove(megamenu: T): void;
48
+ /**
49
+ * Forgets the press a task after it ends, so the click it makes still finds
50
+ * it. One function for every press, so a listener added again is the same
51
+ * listener and never a second one.
52
+ */
53
+ private readonly release;
54
+ /**
55
+ * The megamenus that share this one's parent, in document order: the bar it
56
+ * is one trigger of.
57
+ */
58
+ alongside(megamenu: T): T[];
59
+ /** Notes a press on a megamenu, until the press ends. */
60
+ press(megamenu: T): void;
61
+ /**
62
+ * Opens one panel in place of the one showing, and returns that one, if
63
+ * there was one. The other closes through `requestOpen`, so a consumer
64
+ * holding it open is asked rather than overruled, and a refusal clears its
65
+ * direction again.
66
+ */
67
+ open(opening: T): T | undefined;
68
+ /**
69
+ * Notes a panel closing. One that closed on its own moved nowhere, and
70
+ * neither did any other, so every direction clears.
71
+ */
72
+ close(closing: T): void;
73
+ /** Clears the direction of every megamenu but the ones moving. */
74
+ private settle;
75
+ }
@@ -0,0 +1,67 @@
1
+ export class HandOver {
2
+ constructor(motion) {
3
+ this.motion = motion;
4
+ this.connected = new Set();
5
+ this.handed = new Set();
6
+ this.release = () => {
7
+ setTimeout(() => {
8
+ this.pressed = undefined;
9
+ });
10
+ };
11
+ }
12
+ add(megamenu) {
13
+ this.connected.add(megamenu);
14
+ }
15
+ remove(megamenu) {
16
+ this.connected.delete(megamenu);
17
+ this.handed.delete(megamenu);
18
+ if (this.showing === megamenu) {
19
+ this.showing = undefined;
20
+ }
21
+ if (this.pressed === megamenu) {
22
+ this.pressed = undefined;
23
+ }
24
+ }
25
+ alongside(megamenu) {
26
+ return [...this.connected]
27
+ .filter((other) => other.parentNode === megamenu.parentNode)
28
+ .sort((a, b) => (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1));
29
+ }
30
+ press(megamenu) {
31
+ this.pressed = megamenu;
32
+ window.addEventListener('pointerup', this.release, { capture: true, once: true });
33
+ window.addEventListener('pointercancel', this.release, { capture: true, once: true });
34
+ }
35
+ open(opening) {
36
+ const previous = this.showing !== opening ? this.showing : undefined;
37
+ const forward = previous !== undefined &&
38
+ (previous.compareDocumentPosition(opening) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0;
39
+ this.motion(opening, previous ? (forward ? 'from-end' : 'from-start') : undefined);
40
+ this.settle(opening, previous);
41
+ if (previous) {
42
+ this.motion(previous, forward ? 'to-start' : 'to-end');
43
+ this.handed.add(previous);
44
+ if (!previous.requestOpen(false)) {
45
+ this.handed.delete(previous);
46
+ this.motion(previous, undefined);
47
+ }
48
+ }
49
+ this.showing = opening;
50
+ return previous;
51
+ }
52
+ close(closing) {
53
+ if (this.showing === closing) {
54
+ this.showing = undefined;
55
+ }
56
+ if (!this.handed.delete(closing)) {
57
+ this.settle();
58
+ }
59
+ }
60
+ settle(...moving) {
61
+ for (const megamenu of this.connected) {
62
+ if (!moving.includes(megamenu)) {
63
+ this.motion(megamenu, undefined);
64
+ }
65
+ }
66
+ }
67
+ }