@fusion.dev/helix-headless 0.0.0-dev

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 (424) hide show
  1. package/LICENSE +21 -0
  2. package/custom-elements.json +75202 -0
  3. package/dist/accordion/index.d.ts +2 -0
  4. package/dist/accordion/index.js +2 -0
  5. package/dist/accordion/item/accordion-item.d.ts +141 -0
  6. package/dist/accordion/item/accordion-item.js +214 -0
  7. package/dist/accordion/root/accordion-root.d.ts +57 -0
  8. package/dist/accordion/root/accordion-root.js +96 -0
  9. package/dist/announcement/index.d.ts +1 -0
  10. package/dist/announcement/index.js +1 -0
  11. package/dist/announcement/root/announcement-root.d.ts +106 -0
  12. package/dist/announcement/root/announcement-root.js +144 -0
  13. package/dist/app-header/index.d.ts +1 -0
  14. package/dist/app-header/index.js +1 -0
  15. package/dist/app-header/root/app-header-root.d.ts +113 -0
  16. package/dist/app-header/root/app-header-root.js +147 -0
  17. package/dist/avatar/group/avatar-group.d.ts +116 -0
  18. package/dist/avatar/group/avatar-group.js +136 -0
  19. package/dist/avatar/index.d.ts +2 -0
  20. package/dist/avatar/index.js +2 -0
  21. package/dist/avatar/root/avatar-root.d.ts +71 -0
  22. package/dist/avatar/root/avatar-root.js +147 -0
  23. package/dist/badge/index.d.ts +1 -0
  24. package/dist/badge/index.js +1 -0
  25. package/dist/badge/root/badge-root.d.ts +61 -0
  26. package/dist/badge/root/badge-root.js +121 -0
  27. package/dist/bar-chart/index.d.ts +1 -0
  28. package/dist/bar-chart/index.js +1 -0
  29. package/dist/bar-chart/root/bar-chart-root.d.ts +266 -0
  30. package/dist/bar-chart/root/bar-chart-root.js +515 -0
  31. package/dist/blockquote/index.d.ts +1 -0
  32. package/dist/blockquote/index.js +1 -0
  33. package/dist/blockquote/root/blockquote-root.d.ts +51 -0
  34. package/dist/blockquote/root/blockquote-root.js +84 -0
  35. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +104 -0
  36. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +137 -0
  37. package/dist/breadcrumbs/index.d.ts +2 -0
  38. package/dist/breadcrumbs/index.js +2 -0
  39. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +77 -0
  40. package/dist/breadcrumbs/root/breadcrumbs-root.js +94 -0
  41. package/dist/button/index.d.ts +1 -0
  42. package/dist/button/index.js +1 -0
  43. package/dist/button/root/button-root.d.ts +251 -0
  44. package/dist/button/root/button-root.js +401 -0
  45. package/dist/button-group/index.d.ts +1 -0
  46. package/dist/button-group/index.js +1 -0
  47. package/dist/button-group/root/button-group-root.d.ts +62 -0
  48. package/dist/button-group/root/button-group-root.js +62 -0
  49. package/dist/callout/index.d.ts +1 -0
  50. package/dist/callout/index.js +1 -0
  51. package/dist/callout/root/callout-root.d.ts +72 -0
  52. package/dist/callout/root/callout-root.js +90 -0
  53. package/dist/card/index.d.ts +1 -0
  54. package/dist/card/index.js +1 -0
  55. package/dist/card/root/card-root.d.ts +54 -0
  56. package/dist/card/root/card-root.js +71 -0
  57. package/dist/carousel/index.d.ts +1 -0
  58. package/dist/carousel/index.js +1 -0
  59. package/dist/carousel/root/carousel-root.d.ts +399 -0
  60. package/dist/carousel/root/carousel-root.js +723 -0
  61. package/dist/chat/composer/chat-composer.d.ts +160 -0
  62. package/dist/chat/composer/chat-composer.js +273 -0
  63. package/dist/chat/index.d.ts +3 -0
  64. package/dist/chat/index.js +3 -0
  65. package/dist/chat/message/chat-message.d.ts +170 -0
  66. package/dist/chat/message/chat-message.js +271 -0
  67. package/dist/chat/root/chat-root.d.ts +216 -0
  68. package/dist/chat/root/chat-root.js +389 -0
  69. package/dist/checkbox/index.d.ts +1 -0
  70. package/dist/checkbox/index.js +1 -0
  71. package/dist/checkbox/root/checkbox-root.d.ts +108 -0
  72. package/dist/checkbox/root/checkbox-root.js +207 -0
  73. package/dist/checkbox-group/index.d.ts +1 -0
  74. package/dist/checkbox-group/index.js +1 -0
  75. package/dist/checkbox-group/root/checkbox-group-root.d.ts +155 -0
  76. package/dist/checkbox-group/root/checkbox-group-root.js +293 -0
  77. package/dist/code/index.d.ts +1 -0
  78. package/dist/code/index.js +1 -0
  79. package/dist/code/root/code-root.d.ts +36 -0
  80. package/dist/code/root/code-root.js +43 -0
  81. package/dist/code-block/index.d.ts +1 -0
  82. package/dist/code-block/index.js +1 -0
  83. package/dist/code-block/root/code-block-root.d.ts +112 -0
  84. package/dist/code-block/root/code-block-root.js +155 -0
  85. package/dist/color-input/index.d.ts +1 -0
  86. package/dist/color-input/index.js +1 -0
  87. package/dist/color-input/root/color-input-root.d.ts +227 -0
  88. package/dist/color-input/root/color-input-root.js +456 -0
  89. package/dist/color-picker/color.d.ts +74 -0
  90. package/dist/color-picker/color.js +238 -0
  91. package/dist/color-picker/index.d.ts +2 -0
  92. package/dist/color-picker/index.js +2 -0
  93. package/dist/color-picker/root/color-picker-root.d.ts +303 -0
  94. package/dist/color-picker/root/color-picker-root.js +576 -0
  95. package/dist/combobox/index.d.ts +1 -0
  96. package/dist/combobox/index.js +1 -0
  97. package/dist/combobox/root/combobox-root.d.ts +396 -0
  98. package/dist/combobox/root/combobox-root.js +808 -0
  99. package/dist/command/index.d.ts +1 -0
  100. package/dist/command/index.js +1 -0
  101. package/dist/command/root/command-root.d.ts +166 -0
  102. package/dist/command/root/command-root.js +353 -0
  103. package/dist/copy-button/index.d.ts +1 -0
  104. package/dist/copy-button/index.js +1 -0
  105. package/dist/copy-button/root/copy-button-root.d.ts +154 -0
  106. package/dist/copy-button/root/copy-button-root.js +225 -0
  107. package/dist/data-table/index.d.ts +2 -0
  108. package/dist/data-table/index.js +2 -0
  109. package/dist/data-table/root/data-table-root.d.ts +392 -0
  110. package/dist/data-table/root/data-table-root.js +729 -0
  111. package/dist/data-table/root/order.d.ts +116 -0
  112. package/dist/data-table/root/order.js +175 -0
  113. package/dist/date-input/index.d.ts +1 -0
  114. package/dist/date-input/index.js +1 -0
  115. package/dist/date-input/root/date-input-root.d.ts +361 -0
  116. package/dist/date-input/root/date-input-root.js +791 -0
  117. package/dist/date-picker/index.d.ts +1 -0
  118. package/dist/date-picker/index.js +1 -0
  119. package/dist/date-picker/root/date-picker-root.d.ts +361 -0
  120. package/dist/date-picker/root/date-picker-root.js +837 -0
  121. package/dist/dialog/index.d.ts +1 -0
  122. package/dist/dialog/index.js +1 -0
  123. package/dist/dialog/root/dialog-root.d.ts +185 -0
  124. package/dist/dialog/root/dialog-root.js +319 -0
  125. package/dist/divider/index.d.ts +1 -0
  126. package/dist/divider/index.js +1 -0
  127. package/dist/divider/root/divider-root.d.ts +68 -0
  128. package/dist/divider/root/divider-root.js +116 -0
  129. package/dist/donut-chart/index.d.ts +1 -0
  130. package/dist/donut-chart/index.js +1 -0
  131. package/dist/donut-chart/root/donut-chart-root.d.ts +209 -0
  132. package/dist/donut-chart/root/donut-chart-root.js +395 -0
  133. package/dist/drawer/index.d.ts +1 -0
  134. package/dist/drawer/index.js +1 -0
  135. package/dist/drawer/root/drawer-root.d.ts +144 -0
  136. package/dist/drawer/root/drawer-root.js +222 -0
  137. package/dist/drilldown/index.d.ts +2 -0
  138. package/dist/drilldown/index.js +2 -0
  139. package/dist/drilldown/item/drilldown-item.d.ts +87 -0
  140. package/dist/drilldown/item/drilldown-item.js +152 -0
  141. package/dist/drilldown/root/drilldown-root.d.ts +115 -0
  142. package/dist/drilldown/root/drilldown-root.js +263 -0
  143. package/dist/element-events.generated.d.ts +1 -0
  144. package/dist/element-events.generated.js +125 -0
  145. package/dist/empty/index.d.ts +1 -0
  146. package/dist/empty/index.js +1 -0
  147. package/dist/empty/root/empty-root.d.ts +47 -0
  148. package/dist/empty/root/empty-root.js +68 -0
  149. package/dist/file-input/index.d.ts +1 -0
  150. package/dist/file-input/index.js +1 -0
  151. package/dist/file-input/root/file-input-root.d.ts +196 -0
  152. package/dist/file-input/root/file-input-root.js +488 -0
  153. package/dist/format-date/index.d.ts +1 -0
  154. package/dist/format-date/index.js +1 -0
  155. package/dist/format-date/root/format-date-root.d.ts +151 -0
  156. package/dist/format-date/root/format-date-root.js +266 -0
  157. package/dist/heatmap/index.d.ts +2 -0
  158. package/dist/heatmap/index.js +2 -0
  159. package/dist/heatmap/root/buckets.d.ts +62 -0
  160. package/dist/heatmap/root/buckets.js +85 -0
  161. package/dist/heatmap/root/heatmap-root.d.ts +330 -0
  162. package/dist/heatmap/root/heatmap-root.js +526 -0
  163. package/dist/icon/index.d.ts +1 -0
  164. package/dist/icon/index.js +1 -0
  165. package/dist/icon/root/icon-root.d.ts +53 -0
  166. package/dist/icon/root/icon-root.js +93 -0
  167. package/dist/icon-button/index.d.ts +1 -0
  168. package/dist/icon-button/index.js +1 -0
  169. package/dist/icon-button/root/icon-button-root.d.ts +40 -0
  170. package/dist/icon-button/root/icon-button-root.js +46 -0
  171. package/dist/image/index.d.ts +1 -0
  172. package/dist/image/index.js +1 -0
  173. package/dist/image/root/image-root.d.ts +213 -0
  174. package/dist/image/root/image-root.js +335 -0
  175. package/dist/index.d.ts +99 -0
  176. package/dist/index.js +99 -0
  177. package/dist/input/index.d.ts +1 -0
  178. package/dist/input/index.js +1 -0
  179. package/dist/input/root/input-root.d.ts +281 -0
  180. package/dist/input/root/input-root.js +472 -0
  181. package/dist/kbd/index.d.ts +1 -0
  182. package/dist/kbd/index.js +1 -0
  183. package/dist/kbd/root/kbd-root.d.ts +98 -0
  184. package/dist/kbd/root/kbd-root.js +230 -0
  185. package/dist/legend/index.d.ts +1 -0
  186. package/dist/legend/index.js +1 -0
  187. package/dist/legend/root/legend-root.d.ts +83 -0
  188. package/dist/legend/root/legend-root.js +125 -0
  189. package/dist/line-chart/index.d.ts +1 -0
  190. package/dist/line-chart/index.js +1 -0
  191. package/dist/line-chart/root/line-chart-root.d.ts +238 -0
  192. package/dist/line-chart/root/line-chart-root.js +365 -0
  193. package/dist/list/index.d.ts +2 -0
  194. package/dist/list/index.js +2 -0
  195. package/dist/list/item/list-item.d.ts +95 -0
  196. package/dist/list/item/list-item.js +148 -0
  197. package/dist/list/root/list-root.d.ts +72 -0
  198. package/dist/list/root/list-root.js +61 -0
  199. package/dist/listbox/index.d.ts +1 -0
  200. package/dist/listbox/index.js +1 -0
  201. package/dist/listbox/root/listbox-root.d.ts +357 -0
  202. package/dist/listbox/root/listbox-root.js +890 -0
  203. package/dist/loader/index.d.ts +1 -0
  204. package/dist/loader/index.js +1 -0
  205. package/dist/loader/root/loader-root.d.ts +52 -0
  206. package/dist/loader/root/loader-root.js +74 -0
  207. package/dist/megamenu/index.d.ts +1 -0
  208. package/dist/megamenu/index.js +1 -0
  209. package/dist/megamenu/root/megamenu-root.d.ts +254 -0
  210. package/dist/megamenu/root/megamenu-root.js +526 -0
  211. package/dist/menu/index.d.ts +2 -0
  212. package/dist/menu/index.js +2 -0
  213. package/dist/menu/item/menu-item.d.ts +231 -0
  214. package/dist/menu/item/menu-item.js +405 -0
  215. package/dist/menu/root/menu-root.d.ts +158 -0
  216. package/dist/menu/root/menu-root.js +422 -0
  217. package/dist/meter/index.d.ts +1 -0
  218. package/dist/meter/index.js +1 -0
  219. package/dist/meter/root/meter-root.d.ts +234 -0
  220. package/dist/meter/root/meter-root.js +316 -0
  221. package/dist/navigation/index.d.ts +1 -0
  222. package/dist/navigation/index.js +1 -0
  223. package/dist/navigation/root/navigation-root.d.ts +55 -0
  224. package/dist/navigation/root/navigation-root.js +130 -0
  225. package/dist/notifications/index.d.ts +2 -0
  226. package/dist/notifications/index.js +2 -0
  227. package/dist/notifications/notification/notification.d.ts +181 -0
  228. package/dist/notifications/notification/notification.js +305 -0
  229. package/dist/notifications/root/notifications-root.d.ts +168 -0
  230. package/dist/notifications/root/notifications-root.js +291 -0
  231. package/dist/number-input/index.d.ts +1 -0
  232. package/dist/number-input/index.js +1 -0
  233. package/dist/number-input/root/number-input-root.d.ts +282 -0
  234. package/dist/number-input/root/number-input-root.js +592 -0
  235. package/dist/option/group/option-group.d.ts +38 -0
  236. package/dist/option/group/option-group.js +58 -0
  237. package/dist/option/index.d.ts +2 -0
  238. package/dist/option/index.js +2 -0
  239. package/dist/option/root/option-root.d.ts +194 -0
  240. package/dist/option/root/option-root.js +247 -0
  241. package/dist/outline/index.d.ts +1 -0
  242. package/dist/outline/index.js +1 -0
  243. package/dist/outline/root/outline-root.d.ts +237 -0
  244. package/dist/outline/root/outline-root.js +519 -0
  245. package/dist/pagination/index.d.ts +3 -0
  246. package/dist/pagination/index.js +3 -0
  247. package/dist/pagination/root/pagination-root.d.ts +390 -0
  248. package/dist/pagination/root/pagination-root.js +695 -0
  249. package/dist/pagination/size/pagination-size.d.ts +127 -0
  250. package/dist/pagination/size/pagination-size.js +232 -0
  251. package/dist/pagination/summary/pagination-summary.d.ts +90 -0
  252. package/dist/pagination/summary/pagination-summary.js +145 -0
  253. package/dist/pin-input/index.d.ts +1 -0
  254. package/dist/pin-input/index.js +1 -0
  255. package/dist/pin-input/root/pin-input-root.d.ts +292 -0
  256. package/dist/pin-input/root/pin-input-root.js +553 -0
  257. package/dist/popover/index.d.ts +1 -0
  258. package/dist/popover/index.js +1 -0
  259. package/dist/popover/root/popover-root.d.ts +108 -0
  260. package/dist/popover/root/popover-root.js +231 -0
  261. package/dist/progress/index.d.ts +1 -0
  262. package/dist/progress/index.js +1 -0
  263. package/dist/progress/root/progress-root.d.ts +125 -0
  264. package/dist/progress/root/progress-root.js +212 -0
  265. package/dist/qr-code/index.d.ts +1 -0
  266. package/dist/qr-code/index.js +1 -0
  267. package/dist/qr-code/root/qr-code-root.d.ts +93 -0
  268. package/dist/qr-code/root/qr-code-root.js +233 -0
  269. package/dist/quote/index.d.ts +1 -0
  270. package/dist/quote/index.js +1 -0
  271. package/dist/quote/root/quote-root.d.ts +36 -0
  272. package/dist/quote/root/quote-root.js +50 -0
  273. package/dist/radio/group/radio-group.d.ts +84 -0
  274. package/dist/radio/group/radio-group.js +170 -0
  275. package/dist/radio/index.d.ts +2 -0
  276. package/dist/radio/index.js +2 -0
  277. package/dist/radio/root/radio-root.d.ts +102 -0
  278. package/dist/radio/root/radio-root.js +151 -0
  279. package/dist/rating/index.d.ts +1 -0
  280. package/dist/rating/index.js +1 -0
  281. package/dist/rating/root/rating-root.d.ts +242 -0
  282. package/dist/rating/root/rating-root.js +484 -0
  283. package/dist/scroll-area/index.d.ts +1 -0
  284. package/dist/scroll-area/index.js +1 -0
  285. package/dist/scroll-area/root/scroll-area-root.d.ts +96 -0
  286. package/dist/scroll-area/root/scroll-area-root.js +215 -0
  287. package/dist/select/index.d.ts +1 -0
  288. package/dist/select/index.js +1 -0
  289. package/dist/select/root/select-root.d.ts +170 -0
  290. package/dist/select/root/select-root.js +419 -0
  291. package/dist/skeleton/index.d.ts +1 -0
  292. package/dist/skeleton/index.js +1 -0
  293. package/dist/skeleton/root/skeleton-root.d.ts +31 -0
  294. package/dist/skeleton/root/skeleton-root.js +42 -0
  295. package/dist/slider/index.d.ts +1 -0
  296. package/dist/slider/index.js +1 -0
  297. package/dist/slider/root/slider-root.d.ts +235 -0
  298. package/dist/slider/root/slider-root.js +488 -0
  299. package/dist/sparkline/index.d.ts +1 -0
  300. package/dist/sparkline/index.js +1 -0
  301. package/dist/sparkline/root/sparkline-root.d.ts +147 -0
  302. package/dist/sparkline/root/sparkline-root.js +278 -0
  303. package/dist/speed-dial/index.d.ts +2 -0
  304. package/dist/speed-dial/index.js +2 -0
  305. package/dist/speed-dial/item/speed-dial-item.d.ts +71 -0
  306. package/dist/speed-dial/item/speed-dial-item.js +113 -0
  307. package/dist/speed-dial/root/speed-dial-root.d.ts +122 -0
  308. package/dist/speed-dial/root/speed-dial-root.js +251 -0
  309. package/dist/splitter/index.d.ts +1 -0
  310. package/dist/splitter/index.js +1 -0
  311. package/dist/splitter/root/splitter-root.d.ts +491 -0
  312. package/dist/splitter/root/splitter-root.js +1030 -0
  313. package/dist/stat/group/stat-group.d.ts +30 -0
  314. package/dist/stat/group/stat-group.js +47 -0
  315. package/dist/stat/index.d.ts +2 -0
  316. package/dist/stat/index.js +2 -0
  317. package/dist/stat/root/stat-root.d.ts +62 -0
  318. package/dist/stat/root/stat-root.js +51 -0
  319. package/dist/steps/index.d.ts +2 -0
  320. package/dist/steps/index.js +2 -0
  321. package/dist/steps/root/steps-root.d.ts +110 -0
  322. package/dist/steps/root/steps-root.js +130 -0
  323. package/dist/steps/step/step.d.ts +139 -0
  324. package/dist/steps/step/step.js +215 -0
  325. package/dist/switch/index.d.ts +1 -0
  326. package/dist/switch/index.js +1 -0
  327. package/dist/switch/root/switch-root.d.ts +74 -0
  328. package/dist/switch/root/switch-root.js +153 -0
  329. package/dist/table/cell/table-cell.d.ts +34 -0
  330. package/dist/table/cell/table-cell.js +52 -0
  331. package/dist/table/column/table-column.d.ts +165 -0
  332. package/dist/table/column/table-column.js +222 -0
  333. package/dist/table/index.d.ts +4 -0
  334. package/dist/table/index.js +4 -0
  335. package/dist/table/root/table-root.d.ts +98 -0
  336. package/dist/table/root/table-root.js +161 -0
  337. package/dist/table/row/table-row.d.ts +27 -0
  338. package/dist/table/row/table-row.js +42 -0
  339. package/dist/tabs/index.d.ts +3 -0
  340. package/dist/tabs/index.js +3 -0
  341. package/dist/tabs/panel/tab-panel.d.ts +67 -0
  342. package/dist/tabs/panel/tab-panel.js +98 -0
  343. package/dist/tabs/root/tabs-root.d.ts +123 -0
  344. package/dist/tabs/root/tabs-root.js +217 -0
  345. package/dist/tabs/tab/tab.d.ts +64 -0
  346. package/dist/tabs/tab/tab.js +116 -0
  347. package/dist/tag/index.d.ts +1 -0
  348. package/dist/tag/index.js +1 -0
  349. package/dist/tag/root/tag-root.d.ts +41 -0
  350. package/dist/tag/root/tag-root.js +67 -0
  351. package/dist/textarea/index.d.ts +1 -0
  352. package/dist/textarea/index.js +1 -0
  353. package/dist/textarea/root/textarea-root.d.ts +215 -0
  354. package/dist/textarea/root/textarea-root.js +380 -0
  355. package/dist/time-input/index.d.ts +1 -0
  356. package/dist/time-input/index.js +1 -0
  357. package/dist/time-input/root/time-input-root.d.ts +322 -0
  358. package/dist/time-input/root/time-input-root.js +661 -0
  359. package/dist/time-picker/index.d.ts +1 -0
  360. package/dist/time-picker/index.js +1 -0
  361. package/dist/time-picker/root/time-picker-root.d.ts +380 -0
  362. package/dist/time-picker/root/time-picker-root.js +669 -0
  363. package/dist/timeline/index.d.ts +2 -0
  364. package/dist/timeline/index.js +2 -0
  365. package/dist/timeline/item/timeline-item.d.ts +81 -0
  366. package/dist/timeline/item/timeline-item.js +128 -0
  367. package/dist/timeline/root/timeline-root.d.ts +52 -0
  368. package/dist/timeline/root/timeline-root.js +72 -0
  369. package/dist/toast/index.d.ts +2 -0
  370. package/dist/toast/index.js +2 -0
  371. package/dist/toast/item/toast-item.d.ts +183 -0
  372. package/dist/toast/item/toast-item.js +345 -0
  373. package/dist/toast/root/toast-root.d.ts +72 -0
  374. package/dist/toast/root/toast-root.js +87 -0
  375. package/dist/toggle/group/toggle-group.d.ts +140 -0
  376. package/dist/toggle/group/toggle-group.js +278 -0
  377. package/dist/toggle/index.d.ts +2 -0
  378. package/dist/toggle/index.js +2 -0
  379. package/dist/toggle/root/toggle-root.d.ts +99 -0
  380. package/dist/toggle/root/toggle-root.js +146 -0
  381. package/dist/toolbar/index.d.ts +1 -0
  382. package/dist/toolbar/index.js +1 -0
  383. package/dist/toolbar/root/toolbar-root.d.ts +105 -0
  384. package/dist/toolbar/root/toolbar-root.js +260 -0
  385. package/dist/tooltip/index.d.ts +1 -0
  386. package/dist/tooltip/index.js +1 -0
  387. package/dist/tooltip/root/tooltip-root.d.ts +388 -0
  388. package/dist/tooltip/root/tooltip-root.js +707 -0
  389. package/dist/tour/index.d.ts +2 -0
  390. package/dist/tour/index.js +2 -0
  391. package/dist/tour/root/tour-root.d.ts +161 -0
  392. package/dist/tour/root/tour-root.js +351 -0
  393. package/dist/tour/stop/tour-stop.d.ts +131 -0
  394. package/dist/tour/stop/tour-stop.js +249 -0
  395. package/dist/tree/index.d.ts +2 -0
  396. package/dist/tree/index.js +2 -0
  397. package/dist/tree/item/tree-item.d.ts +208 -0
  398. package/dist/tree/item/tree-item.js +381 -0
  399. package/dist/tree/root/tree-root.d.ts +88 -0
  400. package/dist/tree/root/tree-root.js +287 -0
  401. package/dist/trend/index.d.ts +1 -0
  402. package/dist/trend/index.js +1 -0
  403. package/dist/trend/root/trend-root.d.ts +67 -0
  404. package/dist/trend/root/trend-root.js +106 -0
  405. package/dist/video/index.d.ts +1 -0
  406. package/dist/video/index.js +1 -0
  407. package/dist/video/root/video-root.d.ts +711 -0
  408. package/dist/video/root/video-root.js +1821 -0
  409. package/dist/video-playlist/index.d.ts +3 -0
  410. package/dist/video-playlist/index.js +3 -0
  411. package/dist/video-playlist/item/video-playlist-item.d.ts +101 -0
  412. package/dist/video-playlist/item/video-playlist-item.js +162 -0
  413. package/dist/video-playlist/menu/video-playlist-menu.d.ts +121 -0
  414. package/dist/video-playlist/menu/video-playlist-menu.js +237 -0
  415. package/dist/video-playlist/root/video-playlist-root.d.ts +108 -0
  416. package/dist/video-playlist/root/video-playlist-root.js +218 -0
  417. package/dist/virtual-list/index.d.ts +1 -0
  418. package/dist/virtual-list/index.js +1 -0
  419. package/dist/virtual-list/root/virtual-list-root.d.ts +121 -0
  420. package/dist/virtual-list/root/virtual-list-root.js +221 -0
  421. package/jsx.d.ts +49767 -0
  422. package/package.json +77 -0
  423. package/svelte.d.ts +129 -0
  424. package/vue.d.ts +9242 -0
@@ -0,0 +1,890 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { AriaForwardController, customElement, DisabledCascadeController, HelixFilterEvent, HelixFormElement, hostAria, MixinCollection, MixinControllerValue, MixinField, requestChange, scrollItemIntoView, } from '@fusion.dev/helix-core';
8
+ import { magnifyingGlass } from '@fusion.dev/helix-icons';
9
+ import { html, nothing } from 'lit';
10
+ import { property, state } from 'lit/decorators.js';
11
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
+ import { HelixHeadlessOptionGroup } from '../../option/group/option-group.js';
13
+ import { HelixHeadlessOption } from '../../option/root/option-root.js';
14
+ /** The keys that move within a list, which is every key that moves at all. */
15
+ const MOVES = new Set(['ArrowDown', 'ArrowUp', 'End', 'Home']);
16
+ /**
17
+ * The ranges each filtering list has matched, so the union can be rebuilt when
18
+ * any one of them changes.
19
+ *
20
+ * `CSS.highlights` is one registry for the whole document, keyed by name, so a
21
+ * second list writing under the same name would erase the first one's marks.
22
+ * Keyed by element and rebuilt from every entry, two filtered lists on a page
23
+ * both stay marked. A `Map` rather than a `WeakMap` because the union has to be
24
+ * walked and a `WeakMap` cannot be, which is why a list takes itself out on
25
+ * disconnect.
26
+ */
27
+ const matched = new Map();
28
+ /** What the marks are called in `CSS.highlights`, and in `preview.css`. */
29
+ const HIGHLIGHT = 'helix-filter-match';
30
+ /*
31
+ * Marks what the filter matched, without touching what it matched it in.
32
+ *
33
+ * The words are the consumer's own light DOM, slotted through as many as two
34
+ * shadow roots, and wrapping the matched part in a `<mark>` would mean this
35
+ * element rewriting their content: markup they wrote would come back changed.
36
+ * The Custom Highlight API paints a range instead. Nothing is inserted and
37
+ * nothing is removed, so an option holding a link or an icon is marked as
38
+ * readily as one holding a bare word.
39
+ *
40
+ * The rule that paints it lives in `preview.css` rather than in a component's
41
+ * styles, which is not a choice either: a highlight is styled from the tree its
42
+ * text lives in, and that text lives in the consumer's document.
43
+ */
44
+ /**
45
+ * Whether this environment can paint a highlight at all.
46
+ *
47
+ * Asked before the ranges are built rather than after. Walking every shown
48
+ * option's text nodes to hand the result to an API that is not there is work
49
+ * with no output, and it is not free: a server, a test environment and an older
50
+ * browser all take that path, and the DOM implementations they use are not the
51
+ * one this was measured against.
52
+ */
53
+ const paintable = () => typeof CSS !== 'undefined' && 'highlights' in CSS && typeof Highlight !== 'undefined';
54
+ const paintMatches = () => {
55
+ if (!paintable()) {
56
+ return;
57
+ }
58
+ const all = [...matched.values()].flat();
59
+ if (all.length === 0) {
60
+ CSS.highlights.delete(HIGHLIGHT);
61
+ return;
62
+ }
63
+ CSS.highlights.set(HIGHLIGHT, new Highlight(...all));
64
+ };
65
+ /**
66
+ * Where a needle sits inside an element, as a range over its text nodes.
67
+ *
68
+ * Walked rather than measured off `textContent`, because the words may be split
69
+ * across several nodes and wrapped in elements of the consumer's, and a match
70
+ * can begin in one node and end in another. The offsets are mapped back onto
71
+ * the nodes they came from, so the range covers the real text either way.
72
+ */
73
+ const rangeFor = (root, needle) => {
74
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
75
+ const nodes = [];
76
+ let whole = '';
77
+ for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
78
+ nodes.push(node);
79
+ whole += node.nodeValue ?? '';
80
+ }
81
+ const at = whole.toLowerCase().indexOf(needle);
82
+ if (at < 0) {
83
+ return [];
84
+ }
85
+ const range = document.createRange();
86
+ let seen = 0;
87
+ let started = false;
88
+ for (const node of nodes) {
89
+ const length = node.nodeValue?.length ?? 0;
90
+ if (!started && at < seen + length) {
91
+ range.setStart(node, at - seen);
92
+ started = true;
93
+ }
94
+ if (started && at + needle.length <= seen + length) {
95
+ range.setEnd(node, at + needle.length - seen);
96
+ return [range];
97
+ }
98
+ seen += length;
99
+ }
100
+ return [];
101
+ };
102
+ /**
103
+ * @summary A list of options, one or several of which are the answer.
104
+ *
105
+ * @description
106
+ * **The listbox is the control and the options are its vocabulary.** A Select
107
+ * is this with a trigger in front of it, a Combobox is this with a text input,
108
+ * and a command palette is this again: they differ in what opens the list and
109
+ * what filters it, not in what the list is. Building it as its own family is
110
+ * what stops each of those reimplementing the keyboard and the filtering.
111
+ *
112
+ * **`value` is comma separated when `multiple` is set**, the way
113
+ * `Checkbox.Group`'s is. It stays a string because that is what a form submits
114
+ * and what `MixinControllerValue` holds; an array property would be a second
115
+ * shape for the same fact, and the two would disagree the first time one was
116
+ * set without the other.
117
+ *
118
+ * **Chosen and active are two different things.** Chosen is the answer; active
119
+ * is where the keyboard is. In a single-select list they move together, which
120
+ * is the radio group pattern and is what a reader expects from arrow keys. With
121
+ * `multiple` they come apart: the arrows move without choosing and Space
122
+ * chooses, because a reader passing over four options on the way to the fifth
123
+ * did not mean to pick all five.
124
+ *
125
+ * **This does not use `KeyboardNavigationController`, and the reason given here
126
+ * was the wrong one.** It said the controller moves and chooses in one act by
127
+ * design, so half of this component needed that and half the opposite. The
128
+ * controller does not work that way: it takes `origin: 'focus' | 'selection'`,
129
+ * and `focus` exists precisely so a reader can arrow without choosing, which is
130
+ * what `Tabs` uses in `manual` mode. `focusItem()` and `select()` are separate
131
+ * members of `NavigableItem`. The incompatibility named above is one the
132
+ * controller had already solved.
133
+ *
134
+ * The real one is where focus lives. `NavigableItem.focusItem()` moves DOM
135
+ * focus to the item, because the controller's model is a roving tab stop. A
136
+ * filterable list cannot do that: focus has to stay in the field or typing
137
+ * stops, so what moves is a virtual cursor and `aria-activedescendant` is what
138
+ * says where it is. Those two disagree about the one thing neither can
139
+ * abstract, and no flag reconciles them.
140
+ *
141
+ * Worth keeping the distinction sharp, because the old wording invites the
142
+ * wrong fix: someone reading it would add `origin` to the controller, find it
143
+ * already there, and conclude this component could adopt it.
144
+ *
145
+ * **Filtering hides rather than removes.** An option the filter has hidden is
146
+ * still the consumer's element in the consumer's order, so a filter that clears
147
+ * puts the list back exactly as it was with nothing re-rendered. What counts as
148
+ * a match is a plain case-insensitive substring, and a consumer wanting more
149
+ * hides options itself: `filter` is a convenience over the same `hidden` the
150
+ * component uses.
151
+ *
152
+ * @since 0.1.0
153
+ * @status experimental
154
+ * @category forms
155
+ * @aliases Options, Select list
156
+ * @documentation https://helix.fusion.dev/components/listbox
157
+ *
158
+ * @slot - The options, and any groups of them.
159
+ * @slot hint - Richer than a string.
160
+ * @slot label - Richer than a string.
161
+ *
162
+ * @event {Event} change - Announced once the value has moved.
163
+ * @event {HelixBeforeChangeEvent} helix-before-change - Fired when the answer changes, and cancelable.
164
+ * @event {HelixFilterEvent} helix-filter - Fired before the list narrows, and cancelable.
165
+ * @event {Event} input - Announced once the value has moved.
166
+ *
167
+ * @csspart empty - Shown when a filter has hidden everything.
168
+ * @csspart feedback - What is wrong with it.
169
+ * @csspart filter-icon - The mark beside that field.
170
+ * @csspart filter-mark - The glyph beside the filter field, where none is slotted.
171
+ * @csspart hint - Help under the list.
172
+ * @csspart items - The list itself.
173
+ * @csspart label - The question, over the list.
174
+ * @csspart loading - Shown while the options are still on their way.
175
+ * @csspart search - The region holding the field that narrows the list.
176
+ * @csspart search-field - The field itself.
177
+ */
178
+ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(MixinControllerValue(MixinField(HelixFormElement))) {
179
+ constructor() {
180
+ super(...arguments);
181
+ /* A consumer's `aria-describedby` names ids in their own tree and the control
182
+ is in this shadow root, so the reference has to be carried down as an
183
+ element rather than copied as an id. See `AriaForwardController`: measured
184
+ in Chrome's own tree, the host held the description and the control the
185
+ reader is actually on was exposed without it. */
186
+ this.aria = new AriaForwardController(this, {
187
+ target: () => this.renderRoot?.querySelector('[part="items"]') ?? null,
188
+ });
189
+ /**
190
+ * What the browser may autofill in the text field, as a platform token.
191
+ *
192
+ * **Defaults to `off`, which is not the platform's default and is the
193
+ * point.** The field filters a list rather than collecting a value, so a
194
+ * browser offering to fill it with a saved address is competing for the same
195
+ * keystrokes as the listbox. A consumer who wants autofill can still ask.
196
+ */
197
+ this.autocomplete = 'off';
198
+ /**
199
+ * What to start narrowed to, where nothing declared it.
200
+ */
201
+ this.defaultFilter = '';
202
+ /** What the list is narrowed to, whoever set it. */
203
+ this.filterState = undefined;
204
+ /**
205
+ * Whether the list renders a field for narrowing itself.
206
+ *
207
+ * The field lives here rather than in whatever frames the list, because the
208
+ * filtering already does: `filter`, the empty state and the keyboard are all
209
+ * this component's. A select that drew its own search box above a listbox
210
+ * would be writing the same field again, and so would a combobox, and so
211
+ * would a command palette, which is the fault the three-family split exists
212
+ * to prevent.
213
+ */
214
+ this.filterable = false;
215
+ /**
216
+ * Placeholder for the field that narrows the list.
217
+ */
218
+ this.filterLabel = 'Filter';
219
+ /**
220
+ * Whether more than one option can be the answer.
221
+ */
222
+ this.multiple = false;
223
+ /**
224
+ * What is shown when a filter has hidden every option.
225
+ *
226
+ * From `MixinCollection`, which every collection in the library now shares,
227
+ * and re-declared here only to keep this list's own wording: "No matches" is
228
+ * about a filter, and the mixin's "Nothing to show" is about a list that was
229
+ * never given anything.
230
+ */
231
+ this.emptyText = 'No matches';
232
+ /**
233
+ * Which option the keyboard is on, which is not what is chosen.
234
+ */
235
+ this.activeValue = '';
236
+ /**
237
+ * Whether the key being handled was forwarded rather than typed into this.
238
+ *
239
+ * Plain state rather than reactive: it is set and cleared inside one
240
+ * synchronous call, so nothing renders while it is true and nothing outside
241
+ * can observe it.
242
+ */
243
+ this.forwarded = false;
244
+ /**
245
+ * Whether anything survived the filter, kept as state rather than counted at
246
+ * render time.
247
+ *
248
+ * Counting during render reads the hiding from the *previous* pass, because
249
+ * hiding happens in `updated` against options that are the consumer's
250
+ * elements rather than this component's markup. So a filter matching nothing
251
+ * rendered the full list, hid every option, and left an empty box with no
252
+ * message in it: the count was right one pass too late. Written to state
253
+ * instead, which schedules the one further render that shows it.
254
+ */
255
+ this.hasMatches = true;
256
+ /**
257
+ * Whether the options have been looked at yet.
258
+ *
259
+ * Emptiness is a fact about light DOM children, and the server has none to
260
+ * count: without this the list shipped "Nothing to show" over markup that was
261
+ * about to have options in it, which is the one thing an empty state must
262
+ * never do. `place` runs from `updated`, which the server never reaches, so
263
+ * this is false exactly when the answer is unknown.
264
+ */
265
+ this.counted = false;
266
+ /* Stated once, and the group never thinks about it again. See the
267
+ controller: the cascade was four hand calls from four different
268
+ methods, so which paths reached it differed by component and a fifth
269
+ group would have got none at all. */
270
+ this.cascade = new DisabledCascadeController(this, {
271
+ items: () => this.options,
272
+ });
273
+ this.handleSelection = (event) => {
274
+ event.stopPropagation();
275
+ this.activeValue = event.detail.value;
276
+ this.choose(event.detail.value);
277
+ };
278
+ /**
279
+ * Moves the keyboard, and chooses with it only where that is the pattern.
280
+ *
281
+ * Space and Enter are how a multi-select list chooses, and they are also how
282
+ * a single-select one confirms what the arrows already did, so they act
283
+ * either way rather than being guarded on `multiple`.
284
+ */
285
+ this.handleKeydown = (event) => {
286
+ const shown = this.shown.filter((option) => !option.disabled);
287
+ if (shown.length === 0) {
288
+ return;
289
+ }
290
+ if (event.key === ' ' || event.key === 'Enter') {
291
+ event.preventDefault();
292
+ if (this.activeValue !== '') {
293
+ this.choose(this.activeValue);
294
+ }
295
+ return;
296
+ }
297
+ /* The list's own movement keys, plus Enter, which is choosing rather than
298
+ moving and is still the list's: a reader who has narrowed to one option
299
+ expects Enter to take it without leaving the field first. */
300
+ if (!MOVES.has(event.key) && event.key !== 'Enter') {
301
+ return;
302
+ }
303
+ event.preventDefault();
304
+ const from = this.currentValue;
305
+ const at = shown.findIndex((option) => option.value === from);
306
+ let next = at;
307
+ if (event.key === 'ArrowDown') {
308
+ next = at < 0 ? 0 : (at + 1) % shown.length;
309
+ }
310
+ else if (event.key === 'ArrowUp') {
311
+ next = at < 0 ? shown.length - 1 : (at - 1 + shown.length) % shown.length;
312
+ }
313
+ else if (event.key === 'Home') {
314
+ next = 0;
315
+ }
316
+ else {
317
+ next = shown.length - 1;
318
+ }
319
+ const target = shown[next];
320
+ if (!target) {
321
+ return;
322
+ }
323
+ this.activeValue = target.value;
324
+ /* Chosen on the way past only where moving is choosing. With `multiple` a
325
+ reader passing over four options on the way to the fifth did not mean to
326
+ pick all five.
327
+
328
+ Nor where the keys were forwarded. A list with the focus on it selects as
329
+ it moves, which is the pattern for a listbox; a combobox's arrows move
330
+ the highlight and Enter takes it, which is the pattern for a combobox,
331
+ and the two differ by exactly which element the reader is in. Measured
332
+ before this: the first Down in a combobox committed the second option
333
+ and shut the panel, one keystroke into the interaction. */
334
+ if (!this.multiple && !this.forwarded) {
335
+ this.choose(target.value);
336
+ }
337
+ };
338
+ /**
339
+ * Narrows the list as the reader types, announcing it first.
340
+ *
341
+ * `helix-filter` and not `helix-change`, because nothing about the value
342
+ * changes: the options are hidden rather than removed, and telling a consumer
343
+ * their answer moved when it did not is worse than telling them nothing. It
344
+ * told them nothing before this.
345
+ */
346
+ this.handleFilter = (event) => {
347
+ // Stopped for the reason `Input.handleInput` gives: the inner control's own
348
+ // `input` is composed and would reach a consumer beside this element's.
349
+ event.stopPropagation();
350
+ const control = event.target;
351
+ this.requestFilter(control.value);
352
+ if (control.value !== this.filter) {
353
+ control.value = this.filter;
354
+ }
355
+ };
356
+ /**
357
+ * Sends the keys that belong to the list to the list.
358
+ *
359
+ * A reader filtering is still choosing: they type two letters and press Down
360
+ * to reach what is left, and the field would otherwise swallow it because the
361
+ * arrows move a caret. Typing keys stay with the field, which is what makes
362
+ * this different from forwarding everything.
363
+ *
364
+ * Escape clears rather than closing. What frames the list may close on Escape
365
+ * too, so this stops the event when there is something to clear: one press
366
+ * empties the filter, a second reaches whatever is above and closes it. A
367
+ * filter that closed the panel would lose the reader's place for a keystroke
368
+ * they meant as an undo.
369
+ */
370
+ this.handleFilterKeydown = (event) => {
371
+ if (event.key === 'Escape' && this.filter !== '') {
372
+ /* Stopped and prevented whether or not the clearing stands. The reader
373
+ pressed Escape at a filtered list, so the key was for this list either
374
+ way, and letting it through to close the surface around it would be a
375
+ second thing happening from one press. */
376
+ event.stopPropagation();
377
+ event.preventDefault();
378
+ this.requestFilter('');
379
+ return;
380
+ }
381
+ if (!MOVES.has(event.key)) {
382
+ return;
383
+ }
384
+ /* **Forwarded, because the reader is in the field.** `navigate` says the
385
+ rule this has to follow: a listbox and a combobox differ by which element
386
+ the focus is in, not by a mode, and a reader typing into a filter is in
387
+ the second case whether or not the field belongs to this element. So the
388
+ arrows move the highlight and Enter takes it, which is what Enter already
389
+ does by bubbling to the host handler.
390
+
391
+ **What was here instead moved focus to `[part="items"]` and re-dispatched
392
+ the key.** Both halves of that were wrong, and measured on a bare
393
+ `<helix-listbox filterable>` one Down produced: two `helix-change` events,
394
+ `a` then `b`, and a highlight on the *second* option. Focus on the list
395
+ meant moving chose, so a single press committed a value the reader was
396
+ arrowing past; and the original press went on bubbling to the same host
397
+ handler the re-dispatch had already reached, so every key was handled
398
+ twice and the list advanced two rows for one press. In `Command` that
399
+ reads as a palette that navigates away the moment a reader presses Down.
400
+
401
+ `stopPropagation` is what closes the second half: the field handled the
402
+ key, so it must not arrive at the host as well. The move keys are the
403
+ only ones taken, so Enter, Tab and every character still pass. */
404
+ event.stopPropagation();
405
+ this.navigate(event);
406
+ };
407
+ }
408
+ /**
409
+ * What the list is narrowed to, if anything.
410
+ *
411
+ * Filtering hides rather than removes, so nothing about the value changes and
412
+ * the list is still the consumer's own elements in their own order. Reading
413
+ * gives what is being filtered on; writing declares it theirs, and
414
+ * `default-filter` seeds one the list owns.
415
+ *
416
+ *
417
+ */
418
+ get filter() {
419
+ return this.filterState ?? this.defaultFilter;
420
+ }
421
+ set filter(next) {
422
+ const previous = this.filter;
423
+ this.filterState = next;
424
+ this.requestUpdate('filter', previous);
425
+ }
426
+ /**
427
+ * Which option is chosen, as an index, or `-1` where none is.
428
+ *
429
+ * `<select>`'s own pair, and this control is what stands in for one. The
430
+ * options are the consumer's own light-DOM children, so an index is a
431
+ * position among the elements they wrote rather than into anything this
432
+ * renders, which is what makes it the same number `<select>` would give.
433
+ *
434
+ * With `multiple` it is the first chosen one, which is also what the platform
435
+ * answers: a single index cannot describe a set, and `value` is the property
436
+ * that can.
437
+ */
438
+ get selectedIndex() {
439
+ return this.options.findIndex((option) => option.selected);
440
+ }
441
+ set selectedIndex(at) {
442
+ const option = this.options[at];
443
+ this.value = option?.value ?? '';
444
+ }
445
+ /**
446
+ * Which kind of chooser this is, in the platform's two words.
447
+ *
448
+ * Derived rather than declared, because `multiple` already decides it and a
449
+ * second property could disagree.
450
+ */
451
+ get type() {
452
+ return this.multiple ? 'select-multiple' : 'select-one';
453
+ }
454
+ /**
455
+ * Every option, in the order a reader meets them.
456
+ *
457
+ * Descendants rather than children, because a group puts a level between the
458
+ * list and its options and the keyboard has to move through a grouped list
459
+ * exactly as it moves through a flat one.
460
+ */
461
+ get options() {
462
+ return this.collect(this.children);
463
+ }
464
+ /**
465
+ * Walks the tree, and walks *through* a slot when it meets one.
466
+ *
467
+ * Descendants rather than children, because a group puts a level between the
468
+ * list and its options and the keyboard has to move through a grouped list
469
+ * exactly as it moves through a flat one.
470
+ *
471
+ * Through a slot because that is how this gets composed. `Select` renders a
472
+ * listbox in its own shadow root with a slot inside it, so the options are
473
+ * the *select's* light DOM and the listbox's only child is the slot: a plain
474
+ * `querySelectorAll` finds the slot and no options at all, and the whole
475
+ * component comes up empty. Found the first time the two were put together,
476
+ * which is the argument for building the pieces separately rather than
477
+ * discovering it inside a finished Select.
478
+ */
479
+ collect(nodes) {
480
+ const found = [];
481
+ for (const node of nodes) {
482
+ if (node instanceof HelixHeadlessOption) {
483
+ found.push(node);
484
+ }
485
+ else if (node instanceof HTMLSlotElement) {
486
+ found.push(...this.collect(node.assignedElements({ flatten: true })));
487
+ }
488
+ else {
489
+ found.push(...this.collect(node.children));
490
+ }
491
+ }
492
+ return found;
493
+ }
494
+ /** The groups, found the same way and for the same reason. */
495
+ get groups() {
496
+ const found = [];
497
+ const walk = (nodes) => {
498
+ for (const node of nodes) {
499
+ if (node instanceof HelixHeadlessOptionGroup) {
500
+ found.push(node);
501
+ }
502
+ if (node instanceof HTMLSlotElement) {
503
+ walk(node.assignedElements({ flatten: true }));
504
+ }
505
+ else {
506
+ walk(node.children);
507
+ }
508
+ }
509
+ };
510
+ walk(this.children);
511
+ return found;
512
+ }
513
+ /** The options a filter has left showing. */
514
+ get shown() {
515
+ return this.options.filter((option) => !option.hidden);
516
+ }
517
+ /** The chosen values, which is a list whether or not `multiple` is set. */
518
+ get values() {
519
+ return this.value === '' ? [] : this.value.split(',');
520
+ }
521
+ /**
522
+ * Applies a choice, announcing first so a listener can refuse it.
523
+ *
524
+ * With `multiple` a choice is a toggle, because the same press has to be able
525
+ * to take an answer back; without it a choice replaces whatever was there.
526
+ */
527
+ choose(value) {
528
+ const chosen = this.values;
529
+ const next = this.multiple
530
+ ? chosen.includes(value)
531
+ ? chosen.filter((each) => each !== value)
532
+ : [...chosen, value]
533
+ : [value];
534
+ const joined = next.join(',');
535
+ if (joined === this.value) {
536
+ return;
537
+ }
538
+ /* Through the mixin rather than hand-rolled. It did the same two steps in
539
+ the same order, and doing them here meant the control could not be
540
+ owned: writing `value` is how a consumer says it is theirs, so a listbox
541
+ writing its own would take ownership away from them on every change. */
542
+ this.requestValue(joined);
543
+ }
544
+ /**
545
+ * Runs the list's own key handling for a caller whose focus is elsewhere.
546
+ *
547
+ * A combobox keeps focus in its text field, because that is the whole of what
548
+ * a combobox is, and still has to move this list with the arrows. Without
549
+ * this it would have to reimplement the moving, the wrapping, the skipping of
550
+ * disabled options and the seeding from the current value, and the second
551
+ * copy would drift from this one the first time either changed.
552
+ *
553
+ * Public and taking the event rather than a direction, so a caller forwards
554
+ * what it received and this decides what the key means. `preventDefault` is
555
+ * called in here on the keys it acts on, so a caller can hand over every
556
+ * keydown and let the list ignore the ones that are not its business.
557
+ *
558
+ * The entry point is also what says whether moving chooses, which is why the
559
+ * flag is set here rather than exposed as a property. The difference between
560
+ * a listbox and a combobox is not a mode a consumer picks: it is which
561
+ * element the reader's focus is in, and that is exactly what calling this
562
+ * rather than being typed into means. Enter and Space still choose, because
563
+ * those are the caller asking for the answer rather than for a move.
564
+ */
565
+ navigate(event) {
566
+ this.forwarded = true;
567
+ try {
568
+ this.handleKeydown(event);
569
+ }
570
+ finally {
571
+ this.forwarded = false;
572
+ }
573
+ }
574
+ /**
575
+ * Brings the chosen option to rest in the middle of the list.
576
+ *
577
+ * A method rather than something this does on its own, because a listbox
578
+ * opening on its answer usually wants it merely *visible* and the browser
579
+ * already manages that: centering is what a caller wants when the list is being
580
+ * read as a dial rather than scanned as a list, which is what Time Picker's wheels
581
+ * does with three of these.
582
+ *
583
+ * A delta from the two boxes rather than `offsetTop`. An option is a slotted
584
+ * light DOM child, so its `offsetParent` is whatever is positioned above it,
585
+ * which is not this list and is not guaranteed to be anything: measuring both
586
+ * rects and moving by the difference needs no such assumption.
587
+ */
588
+ reveal() {
589
+ const items = this.renderRoot?.querySelector('[part="items"]');
590
+ const chosen = this.options.find((option) => option.selected && !option.hidden);
591
+ if (!items || !chosen) {
592
+ return;
593
+ }
594
+ /* Twice, and the second pass is not superstition. The first delta is
595
+ computed against whatever layout is current when this is called, and
596
+ inside a panel that has just been revealed it is a frame behind: measured,
597
+ one call landed a whole row short, 672 where 704 was wanted, and calling
598
+ it again from a settled layout put it exactly on center. Reading a rect
599
+ forces layout, so the second pass sees the result of the first and
600
+ corrects the remainder. It stops as soon as there is nothing left to
601
+ correct, so the usual case is one pass and a cheap check. */
602
+ for (let pass = 0; pass < 2; pass += 1) {
603
+ const box = items.getBoundingClientRect();
604
+ const row = chosen.getBoundingClientRect();
605
+ if (box.height === 0 || row.height === 0) {
606
+ return;
607
+ }
608
+ const off = row.top + row.height / 2 - (box.top + box.height / 2);
609
+ if (Math.abs(off) < 1) {
610
+ return;
611
+ }
612
+ items.scrollTop += off;
613
+ }
614
+ }
615
+ /** Hides what the filter excludes, and writes the rest onto the options. */
616
+ place() {
617
+ const needle = this.filter.trim().toLowerCase();
618
+ const chosen = new Set(this.values);
619
+ for (const option of this.options) {
620
+ option.hidden = needle !== '' && !option.text.toLowerCase().includes(needle);
621
+ option.selected = chosen.has(option.value);
622
+ }
623
+ /* A group whose options the filter has all hidden goes with them. CSS
624
+ cannot do this: a heading and its options are siblings under the group,
625
+ so the rule would be `:host(:has(...))`, which is dropped at parse time.
626
+ The listbox can, because it is the thing that did the hiding and already
627
+ knows which. Left showing, the picture was a heading over nothing, which
628
+ reads as a group whose contents failed to render. */
629
+ for (const group of this.groups) {
630
+ const inside = this.collect(group.children);
631
+ group.hidden = inside.length > 0 && inside.every((option) => option.hidden);
632
+ }
633
+ /* The keyboard cannot sit on something the filter has hidden, so it moves
634
+ to the first thing still showing rather than nowhere: a list whose active
635
+ option has just been filtered away is a list the next arrow press would
636
+ start from the top of anyway.
637
+
638
+ But only where there is a filter to have hidden it, or a highlight worth
639
+ rescuing. Clearing one must not *invent* a highlight out of nothing:
640
+ measured, a combobox clears its filter as it closes, and that alone
641
+ parked the highlight on the first option while the panel was shut, so
642
+ the reader's first Down arrived at the second. */
643
+ const shown = this.shown;
644
+ const rescue = needle !== '' || this.activeValue !== '';
645
+ if (rescue && !shown.some((option) => option.value === this.activeValue)) {
646
+ this.activeValue = shown.find((option) => !option.disabled)?.value ?? '';
647
+ }
648
+ for (const option of this.options) {
649
+ option.active = option.value === this.activeValue;
650
+ }
651
+ /* And the marks, from the same needle that did the hiding: the two answers
652
+ have to come from one place or a list can show a match it does not mark. */
653
+ if (needle === '' || !paintable()) {
654
+ matched.delete(this);
655
+ }
656
+ else {
657
+ matched.set(this, shown.flatMap((option) => rangeFor(option, needle)));
658
+ }
659
+ paintMatches();
660
+ const anyShown = shown.length > 0;
661
+ if (anyShown !== this.hasMatches) {
662
+ this.hasMatches = anyShown;
663
+ }
664
+ this.counted = true;
665
+ const active = shown.find((option) => option.value === this.activeValue);
666
+ if (active) {
667
+ const items = this.renderRoot.querySelector('[part="items"]');
668
+ scrollItemIntoView(active, { padding: 4, ...(items ? { scroller: items } : {}) });
669
+ }
670
+ }
671
+ /** Reports the answer and the verdict on it. */
672
+ sync() {
673
+ this.internals.setFormValue(this.value === '' ? null : this.value);
674
+ this.reportRequired(this.value === '', 'Choose one to continue.', 'items');
675
+ }
676
+ connectedCallback() {
677
+ super.connectedCallback();
678
+ this.addEventListener('helix-select', this.handleSelection);
679
+ this.addEventListener('keydown', this.handleKeydown);
680
+ }
681
+ disconnectedCallback() {
682
+ super.disconnectedCallback();
683
+ this.removeEventListener('helix-select', this.handleSelection);
684
+ this.removeEventListener('keydown', this.handleKeydown);
685
+ /* Its marks go with it. The registry is the document's and outlives this
686
+ element, so a list removed while filtered would leave ranges over text
687
+ that is no longer on the page. */
688
+ matched.delete(this);
689
+ paintMatches();
690
+ }
691
+ willUpdate(changed) {
692
+ super.willUpdate(changed);
693
+ hostAria(this.internals, { label: this.label, role: 'listbox' });
694
+ this.internals.ariaMultiSelectable = this.multiple ? 'true' : null;
695
+ }
696
+ /*
697
+ * Points the focused list at the option the keyboard is on.
698
+ *
699
+ * **The gap this closes.** Focus stays on `[part="items"]` while a highlight
700
+ * moves between options, which is the half of the ARIA listbox pattern we
701
+ * had. The other half is saying *which* option, and nothing did: a screen
702
+ * reader heard the list and its selection and never heard the item being
703
+ * moved over. Neither `aria-activedescendant` nor a roving `tabindex` was
704
+ * wired, and the pattern requires one of the two.
705
+ *
706
+ * **An element reference and not an id, because an IDREF cannot do this.**
707
+ * The options are the consumer's light DOM and the element that takes focus
708
+ * is in this shadow root, so `aria-activedescendant="whatever"` resolves
709
+ * against the shadow tree, finds nothing, and is dropped in silence. Element
710
+ * references are the platform's answer to exactly that, and they resolve
711
+ * outward: a shadow-tree element may point at a light-DOM one, which is the
712
+ * direction this needs.
713
+ *
714
+ * Feature-tested rather than assumed, for the reason `hostAria` gives:
715
+ * assigning to a property an engine does not implement is a no-op rather than
716
+ * an error, so the guard is what makes the absence visible to anyone reading
717
+ * this. Where it is missing the list still works and still announces its
718
+ * selection; what is lost is the running commentary as the keys move.
719
+ */
720
+ /**
721
+ * Where the reader is, and failing that where the answer is.
722
+ *
723
+ * `activeValue` starts empty, so the first arrow press in a list that already
724
+ * has a chosen option started from nowhere: Down went to the first option,
725
+ * which is a move backwards from anything below it, and Up went to the last.
726
+ * Seeding from the value means the first press moves from what the reader can
727
+ * see is selected.
728
+ *
729
+ * **The keyboard knew this and the announcement did not.** `linkActive` asked
730
+ * for `activeValue` alone, so a list that had been given a value and not yet
731
+ * arrowed through pointed `aria-activedescendant` at nothing: a screen reader
732
+ * was told the list and its selection, and nothing about where the cursor
733
+ * was, until a key moved it. Both read this now, so the thing announced and
734
+ * the thing the arrows move from cannot disagree.
735
+ */
736
+ get currentValue() {
737
+ return this.activeValue || (this.value.split(',')[0] ?? '');
738
+ }
739
+ linkActive() {
740
+ /* Whichever element the reader is actually in. `aria-activedescendant` is
741
+ read from the focused element, so pointing the list at the active option
742
+ says nothing while focus is in the filter, which is where it now stays.
743
+ A filterable list announces from its field and a plain one from its
744
+ items, for the same reason the arrows are forwarded in one and not the
745
+ other. */
746
+ const list = this.renderRoot.querySelector(this.filterable ? '[part="search-field"]' : '[part="items"]');
747
+ if (!list || !('ariaActiveDescendantElement' in list)) {
748
+ return;
749
+ }
750
+ const active = this.options.find((option) => option.value === this.currentValue);
751
+ list.ariaActiveDescendantElement = active ?? null;
752
+ }
753
+ updated(changed) {
754
+ super.updated(changed);
755
+ this.place();
756
+ this.sync();
757
+ this.linkHost();
758
+ this.linkActive();
759
+ }
760
+ /** Requests a narrowing, and reports whether the request stood. */
761
+ requestFilter(next) {
762
+ if (next === this.filter) {
763
+ return true;
764
+ }
765
+ return requestChange({
766
+ apply: (value) => {
767
+ const previous = this.filter;
768
+ this.filterState = value;
769
+ this.requestUpdate('filter', previous);
770
+ },
771
+ event: (value) => new HelixFilterEvent({ value }),
772
+ host: this,
773
+ next,
774
+ });
775
+ }
776
+ /**
777
+ * Focusing the host focuses the list inside it.
778
+ *
779
+ * The focusable node is `[part="items"]`, a div in this shadow root, and a
780
+ * caller outside has no way to reach it: `select` opened its panel and called
781
+ * `.focus()` on this element, which is not focusable, so focus stayed on the
782
+ * trigger and the arrow keys never reached the options at all. Delegation is
783
+ * the platform answering that, and it keeps `document.activeElement` on the
784
+ * host, which is what a caller reasons about.
785
+ */
786
+ static { this.shadowRootOptions = {
787
+ ...HelixFormElement.shadowRootOptions,
788
+ delegatesFocus: true,
789
+ }; }
790
+ /**
791
+ * The mark in the filter's field, behind a seam.
792
+ *
793
+ * Slot fallback, so a consumer replaces it by slotting; and a method, so the
794
+ * tier above replaces it by overriding. Those answer different people: the
795
+ * slot is for the page and the method is for whoever is building the styled
796
+ * layer, and a fallback with only the first is one a tier can restyle and
797
+ * cannot change.
798
+ */
799
+ renderFilterIcon() {
800
+ return html `<slot name="filter-icon">
801
+ <svg part="filter-mark" fill="currentColor" viewBox=${magnifyingGlass.viewBox}>
802
+ ${unsafeSVG(magnifyingGlass.content)}
803
+ </svg>
804
+ </slot>`;
805
+ }
806
+ render() {
807
+ /* One tab stop for the whole list, which is what a listbox is in the tab
808
+ order: the arrows move within it. `tabindex` on the wrapper rather than
809
+ the host so the host stays as the consumer wrote it. */
810
+ return html `${this.renderGroupLabel()}
811
+ ${this.filterable
812
+ ? html `<div part="search">
813
+ <span part="filter-icon" aria-hidden="true">
814
+ ${this.renderFilterIcon()}
815
+ </span>
816
+ <input
817
+ part="search-field"
818
+ type="text"
819
+ autocomplete=${this.autocomplete || nothing}
820
+ spellcheck="false"
821
+ .value=${this.filter}
822
+ placeholder=${this.filterLabel}
823
+ aria-label=${this.filterLabel}
824
+ aria-controls="items"
825
+ ?disabled=${this.isDisabled}
826
+ @input=${this.handleFilter}
827
+ @keydown=${this.handleFilterKeydown}
828
+ />
829
+ </div>`
830
+ : ''}
831
+ <div
832
+ part="items"
833
+ id="items"
834
+ aria-busy=${this.loading ? 'true' : nothing}
835
+ ${
836
+ /* `isDisabled`, matching the filter input above it. The two lines
837
+ disagreed, so a listbox in a disabled fieldset kept its tab stop
838
+ and took arrow keys while its own filter was correctly out. */
839
+ ''}
840
+ tabindex=${this.isDisabled ? -1 : 0}
841
+ >
842
+ <slot @slotchange=${() => this.place()}></slot>
843
+ ${
844
+ /* Loading and empty come from the shared mixin; what is local is what
845
+ counts as content. A list with a filter that matched nothing is
846
+ empty, and one with no filter and no options is empty too, but a
847
+ list still waiting on its options is neither. */
848
+ this.renderStatus(this.counted && !this.loading
849
+ ? this.filter === ''
850
+ ? this.options.length > 0
851
+ : this.hasMatches
852
+ : true)}
853
+ </div>
854
+ ${this.renderHint()}${this.renderFeedback()}`;
855
+ }
856
+ };
857
+ __decorate([
858
+ property()
859
+ ], HelixHeadlessListbox.prototype, "autocomplete", void 0);
860
+ __decorate([
861
+ property()
862
+ ], HelixHeadlessListbox.prototype, "filter", null);
863
+ __decorate([
864
+ property({ attribute: 'default-filter' })
865
+ ], HelixHeadlessListbox.prototype, "defaultFilter", void 0);
866
+ __decorate([
867
+ property({ attribute: 'filterable', reflect: true, type: Boolean })
868
+ ], HelixHeadlessListbox.prototype, "filterable", void 0);
869
+ __decorate([
870
+ property({ attribute: 'filter-label' })
871
+ ], HelixHeadlessListbox.prototype, "filterLabel", void 0);
872
+ __decorate([
873
+ property({ reflect: true, type: Boolean })
874
+ ], HelixHeadlessListbox.prototype, "multiple", void 0);
875
+ __decorate([
876
+ property({ attribute: 'empty-text' })
877
+ ], HelixHeadlessListbox.prototype, "emptyText", void 0);
878
+ __decorate([
879
+ state()
880
+ ], HelixHeadlessListbox.prototype, "activeValue", void 0);
881
+ __decorate([
882
+ state()
883
+ ], HelixHeadlessListbox.prototype, "hasMatches", void 0);
884
+ __decorate([
885
+ state()
886
+ ], HelixHeadlessListbox.prototype, "counted", void 0);
887
+ HelixHeadlessListbox = __decorate([
888
+ customElement('helix-headless-listbox')
889
+ ], HelixHeadlessListbox);
890
+ export { HelixHeadlessListbox };