@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
@@ -57,3 +57,24 @@ export declare const toneOf: (reading: Reading, thresholds: readonly number[], t
57
57
  * the tail slice aligned.
58
58
  */
59
59
  export declare const fits: (total: number, width: number, minimum: number, gap: number, rows?: number) => number;
60
+ /** What the drawn readings come to: where the busiest stands, and their total. */
61
+ export interface Overview {
62
+ /** The place of the busiest reading, the first where several tie, or -1 with none. */
63
+ busiest: number;
64
+ /** The sum of every reading, blanks counting nothing. */
65
+ total: number;
66
+ }
67
+ /**
68
+ * The busiest reading among `cells` and the total of them all, for the
69
+ * strip's `overview`. A blank cell is passed over.
70
+ */
71
+ export declare const overviewOf: (cells: readonly Reading[]) => Overview;
72
+ /**
73
+ * Where a page of a calendar goes from the reading at `at`: the same day a
74
+ * month on, or a month back, by the dates the readings carry, written
75
+ * `YYYY-MM-DD` as a `<time datetime>` gives them. A day the month lacks,
76
+ * such as the 31st, is the month's last. Where that day has no reading, the
77
+ * nearest one past it in the direction of travel, and the last or the first
78
+ * reading past the end. Undefined where the reading at `at` carries no date.
79
+ */
80
+ export declare const monthPage: (whens: readonly (string | undefined)[], at: number, forward: boolean) => number | undefined;
@@ -1,3 +1,4 @@
1
+ import { extremesOf } from '@fusion.dev/helix-core';
1
2
  export const toneOf = (reading, thresholds, tones) => {
2
3
  if (reading.tone) {
3
4
  return reading.tone;
@@ -24,3 +25,36 @@ export const fits = (total, width, minimum, gap, rows = 1) => {
24
25
  const columns = Math.max(1, Math.floor((width + gap) / (minimum + gap)));
25
26
  return Math.min(total, columns * Math.max(1, rows));
26
27
  };
28
+ export const overviewOf = (cells) => {
29
+ const values = cells.map((cell) => cell.value);
30
+ return {
31
+ busiest: extremesOf(values).high ?? -1,
32
+ total: values.reduce((sum, value) => sum + (value ?? 0), 0),
33
+ };
34
+ };
35
+ const dayOf = (when) => {
36
+ const found = /^(\d{4})-(\d{2})-(\d{2})/.exec(when ?? '');
37
+ return found
38
+ ? Date.UTC(Number(found[1]), Number(found[2]) - 1, Number(found[3])) / 86_400_000
39
+ : undefined;
40
+ };
41
+ export const monthPage = (whens, at, forward) => {
42
+ const from = dayOf(whens[at]);
43
+ if (from === undefined) {
44
+ return undefined;
45
+ }
46
+ const date = new Date(from * 86_400_000);
47
+ const month = date.getUTCMonth() + (forward ? 1 : -1);
48
+ const last = new Date(Date.UTC(date.getUTCFullYear(), month + 1, 0)).getUTCDate();
49
+ const target = Date.UTC(date.getUTCFullYear(), month, Math.min(date.getUTCDate(), last)) / 86_400_000;
50
+ const days = whens.map(dayOf);
51
+ const order = days.map((_, index) => index);
52
+ const past = (forward ? order : order.reverse()).find((index) => {
53
+ const day = days[index];
54
+ return day !== undefined && (forward ? day >= target : day <= target);
55
+ });
56
+ if (past !== undefined) {
57
+ return past;
58
+ }
59
+ return forward ? whens.length - 1 : 0;
60
+ };
@@ -1,6 +1,7 @@
1
1
  import { AriaForwardController, HelixElement } from '@fusion.dev/helix-core';
2
2
  import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../tooltip/index.js';
4
+ import { type Reading } from './buckets.js';
4
5
  declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface);
5
6
  /**
6
7
  * @summary A strip of cells, each colored by its reading, for uptime, contributions, or activity.
@@ -30,7 +31,23 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
30
31
  * down arrows move a cell, the left and right arrows move a column, which is a
31
32
  * week with `rows="7"`, and Home and End go to the first and the last. The
32
33
  * tooltip follows, the `live` part says the cell's row in words, and Escape
33
- * puts the tooltip away.
34
+ * puts the tooltip away. PageUp and PageDown move a month, to the same day,
35
+ * where each row's header carries its date as a `<time datetime>`, and four
36
+ * columns otherwise, which is four weeks with `rows="7"`.
37
+ *
38
+ * **The strip describes itself.** The `overview` part says the busiest
39
+ * reading and the total of the readings drawn, "Busiest: 11 Jun, 12; Total
40
+ * 84", and the strip is described by it, as every chart is by its summary.
41
+ * Hide it visually. The `summary` slot is still yours, for words of your own.
42
+ *
43
+ * **`selection-mode` lets a reader select cells**: `single` one at a time and
44
+ * `multiple` any number. Space, Enter or a press selects the cell the keys or
45
+ * the pointer rest on, or lets it go, and under `multiple` Shift with an arrow,
46
+ * Home, End, PageUp or PageDown takes in every cell from the last one pressed
47
+ * to where the key went. `value` holds the selected days, by the names their
48
+ * rows have in the table, separated by commas. A change asks with
49
+ * `helix-before-change` and tells with `change`, and the `selection` part says
50
+ * how many are selected, in `selection-label`.
34
51
  *
35
52
  * @since 0.1.0
36
53
  * @status stable
@@ -42,15 +59,20 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
42
59
  * @slot start - Below the strip, at the leading edge: usually the oldest date.
43
60
  * @slot summary - Below the strip, in the middle: a summary of the range shown.
44
61
  *
62
+ * @event {Event} change - Fired once a press or a key has changed which cells are selected.
63
+ * @event {HelixBeforeChangeEvent} helix-before-change - Fired before a press or a key changes which cells are selected, with the `value` it would leave. Cancelable.
45
64
  * @event {HelixResizeEvent} helix-resize - Fired when the number of cells drawn changes, with the new number.
46
65
  *
47
66
  * @csspart cell - One reading.
48
67
  * @csspart cell-active - The cell the pointer or the keys rest on. Set alongside `cell`.
68
+ * @csspart cell-selected - A cell that is selected, under `selection-mode`. Set alongside `cell`.
49
69
  * @csspart empty - Shown when the table has no readings. It stays hidden until the table can be read: on the server and during hydration, there is no document to resolve `for` in.
50
70
  * @csspart end - The trailing slot's box.
51
71
  * @csspart footer - The three-slot row under the strip.
52
72
  * @csspart live - The readout: a polite live region that says the cell the keys moved to. Hide it visually.
53
73
  * @csspart mark - The invisible point the tooltip is anchored to.
74
+ * @csspart overview - The busiest reading and the total of the readings drawn, written as a sentence for screen readers, which describes the strip. Rendered once there are cells. Hide it visually.
75
+ * @csspart selection - A polite region that says how many cells are selected after a change, rendered from the first change. Hide it visually.
54
76
  * @csspart start - The leading slot's box.
55
77
  * @csspart strip - The grid of cells, which takes focus to be read by key. It is named by the host's `aria-labelledby` or `aria-label`, then by `label`, then by the caption of the table.
56
78
  * @csspart summary - The middle slot's box.
@@ -87,6 +109,10 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
87
109
  * @cssproperty --heatmap-tone-warning - The color of the `warning` tone.
88
110
  */
89
111
  export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
112
+ /**
113
+ * The word the `overview` puts before the busiest reading.
114
+ */
115
+ busiestLabel: string;
90
116
  /**
91
117
  * Shown when the table has no readings in it.
92
118
  *
@@ -129,6 +155,17 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
129
155
  * days.
130
156
  */
131
157
  rows: number;
158
+ /**
159
+ * What the `selection` part says after the selection changes, with `{count}` for
160
+ * how many cells are selected.
161
+ */
162
+ selectionLabel: string;
163
+ /**
164
+ * How many cells a reader can select: `single` or `multiple`.
165
+ *
166
+ * Absent selects nothing, and a press or Space does what it did.
167
+ */
168
+ selectionMode?: 'multiple' | 'single';
132
169
  /**
133
170
  * Lower bounds for the tone buckets, in the order the tones are given.
134
171
  *
@@ -151,6 +188,18 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
151
188
  * no order in it.
152
189
  */
153
190
  tones: string;
191
+ /**
192
+ * The word the `overview` puts before the total of the readings drawn.
193
+ */
194
+ totalLabel: string;
195
+ /**
196
+ * The selected cells, by the names their rows have in the table, such as
197
+ * `2026-06-11`, separated by commas.
198
+ *
199
+ * Set it to start with cells selected. Written by a press or a key under
200
+ * `selection-mode`.
201
+ */
202
+ value: string;
154
203
  /**
155
204
  * How many cells are actually drawn.
156
205
  *
@@ -197,20 +246,30 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
197
246
  * image a reader lands on. The host is a generic around it.
198
247
  */
199
248
  protected readonly aria: AriaForwardController;
249
+ private observer;
250
+ private announced;
251
+ /** Where a range taken in with Shift starts: the cell last pressed, or none. */
252
+ private anchor;
253
+ /** The region a change of selection is said in, rendered from the first change. */
254
+ private readonly status;
255
+ /** The name each drawn cell stands for in `value`: its row's name in the table. */
256
+ private keysOf;
200
257
  /**
201
- * Hears an `aria-label` written on the element, which no property mirrors.
202
- *
203
- * Lit observes the attributes of its own properties only, and `ariaLabel`
204
- * belongs to the platform.
258
+ * Moves `value` to `next`, asking first: `helix-before-change` carries it,
259
+ * a listener that refuses keeps the selection, and `change` follows, with
260
+ * the count said in the `selection` part.
205
261
  */
206
- static get observedAttributes(): string[];
262
+ private choose;
263
+ /** Selects the drawn cell at `index`, or lets it go, as `selection-mode` says. */
264
+ private press;
207
265
  /**
208
- * Called by the browser when an observed attribute changes. A new
209
- * `aria-label` asks for an update, because it names the strip.
266
+ * Space and Enter select the cell the keys rest on, and Shift with a key
267
+ * that moved them takes in the range from the anchor. After the pointer
268
+ * controller, which has already moved `at` for an arrow.
210
269
  */
211
- attributeChangedCallback(name: string, old: string | null, value: string | null): void;
212
- private observer;
213
- private announced;
270
+ private readonly onKeydown;
271
+ /** A press on a cell selects it, under `selection-mode`. */
272
+ private readonly onClick;
214
273
  /**
215
274
  * Every row in the table, oldest first, as the table is written, with its
216
275
  * reading where the row has one.
@@ -300,6 +359,12 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
300
359
  * the reason `BarChart` gives: SSR calls this one.
301
360
  */
302
361
  protected willUpdate(changed: PropertyValues<this>): void;
362
+ /**
363
+ * Where PageUp or PageDown goes from the drawn cell at `at`: the same day a
364
+ * month on or back, by the dates the rows' headers carry. Undefined where
365
+ * they carry none, and the keys move four columns.
366
+ */
367
+ pageFrom(at: number, forward: boolean): number | undefined;
303
368
  connectedCallback(): void;
304
369
  disconnectedCallback(): void;
305
370
  /**
@@ -314,6 +379,13 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
314
379
  private markEmpty;
315
380
  protected updated(changed: PropertyValues): void;
316
381
  protected render(): TemplateResult;
382
+ /**
383
+ * What the strip shows, in a sentence: the busiest reading drawn, as the
384
+ * table writes it, and the total of them all, in `locale`. The strip is
385
+ * described by it, so a reader landing on the strip hears what it holds
386
+ * before walking it.
387
+ */
388
+ protected renderOverview(cells: Reading[]): TemplateResult;
317
389
  /**
318
390
  * The bubble over the cell a pointer is resting on.
319
391
  *
@@ -4,20 +4,24 @@ 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, byRow, CHART_PLOT, customElement, drawTooltip, HelixElement, HelixResizeEvent, MixinChartSource, markRegion, searchableById, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, byRow, CHART_PLOT, commaList, customElement, drawTooltip, fillTemplate, formatNumbers, HelixBeforeChangeEvent, HelixElement, HelixResizeEvent, MixinChartSource, markRegion, pressedValue, rangeAdd, requestChange, StatusController, searchableById, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
11
- import { fits, toneOf } from './buckets.js';
11
+ import { fits, monthPage, overviewOf, toneOf } from './buckets.js';
12
12
  let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(HelixElement) {
13
13
  constructor() {
14
14
  super(...arguments);
15
+ this.busiestLabel = 'Busiest';
15
16
  this.emptyText = 'No readings';
16
17
  this.gap = 2;
17
18
  this.minCell = 8;
18
19
  this.rows = 1;
20
+ this.selectionLabel = '{count} selected';
19
21
  this.thresholds = '';
20
22
  this.tones = '';
23
+ this.totalLabel = 'Total';
24
+ this.value = '';
21
25
  this.width = undefined;
22
26
  this.metrics = undefined;
23
27
  this.aria = new AriaForwardController(this, {
@@ -25,6 +29,35 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
25
29
  });
26
30
  this.observer = undefined;
27
31
  this.announced = undefined;
32
+ this.anchor = -1;
33
+ this.status = new StatusController(this);
34
+ this.onKeydown = (event) => {
35
+ const at = this.pointer.at;
36
+ if (!this.selectionMode || at < 0 || event.altKey || event.ctrlKey || event.metaKey) {
37
+ return;
38
+ }
39
+ if (event.key === ' ' || event.key === 'Enter') {
40
+ event.preventDefault();
41
+ this.press(at);
42
+ return;
43
+ }
44
+ const moves = /^(Arrow|Page|Home$|End$)/.test(event.key);
45
+ if (!event.shiftKey || !moves || this.selectionMode !== 'multiple') {
46
+ return;
47
+ }
48
+ const keys = this.keysOf(this.drawn);
49
+ const added = rangeAdd(commaList(this.value), keys, this.anchor < 0 ? at : this.anchor, at);
50
+ if (added) {
51
+ this.choose(added.join(', '));
52
+ }
53
+ };
54
+ this.onClick = (event) => {
55
+ const cell = event.target?.closest?.('[part~="cell"]');
56
+ const index = Number(cell?.getAttribute('data-index') ?? -1);
57
+ if (cell && Number.isInteger(index) && index >= 0) {
58
+ this.press(index);
59
+ }
60
+ };
28
61
  this.onPointerOver = (event) => {
29
62
  if (!this.pointer.accepts(event)) {
30
63
  return;
@@ -42,14 +75,34 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
42
75
  get visible() {
43
76
  return Math.min(this.points.length, this.room);
44
77
  }
45
- static get observedAttributes() {
46
- return [...super.observedAttributes, 'aria-label'];
78
+ keysOf(cells) {
79
+ const offset = this.points.length - cells.length;
80
+ const rows = this.sourceRows;
81
+ return cells.map((_, index) => rows[offset + index]?.label ?? '');
82
+ }
83
+ choose(next) {
84
+ if (next === this.value) {
85
+ return;
86
+ }
87
+ requestChange({
88
+ apply: (value) => {
89
+ this.value = value;
90
+ this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
91
+ this.status.say(fillTemplate(this.selectionLabel, { count: commaList(value).length }));
92
+ },
93
+ event: (value) => new HelixBeforeChangeEvent({ value }),
94
+ host: this,
95
+ next,
96
+ });
47
97
  }
48
- attributeChangedCallback(name, old, value) {
49
- super.attributeChangedCallback(name, old, value);
50
- if (name === 'aria-label') {
51
- this.requestUpdate();
98
+ press(index) {
99
+ const mode = this.selectionMode;
100
+ const key = this.keysOf(this.drawn)[index];
101
+ if (!mode || key === undefined || key === '') {
102
+ return;
52
103
  }
104
+ this.anchor = index;
105
+ this.choose(pressedValue(mode, this.value, key));
53
106
  }
54
107
  get points() {
55
108
  const series = this.series[0];
@@ -106,8 +159,13 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
106
159
  super.willUpdate(changed);
107
160
  this.pointer.grid = this.rowCount;
108
161
  }
162
+ pageFrom(at, forward) {
163
+ const offset = this.points.length - this.drawn.length;
164
+ return monthPage(this.sourceRows.slice(offset).map((row) => row.when), at, forward);
165
+ }
109
166
  connectedCallback() {
110
167
  super.connectedCallback();
168
+ this.addEventListener('keydown', this.onKeydown);
111
169
  if (typeof ResizeObserver !== 'undefined') {
112
170
  this.observer = new ResizeObserver((entries) => {
113
171
  const measured = entries[0]?.contentRect.width;
@@ -120,6 +178,7 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
120
178
  }
121
179
  disconnectedCallback() {
122
180
  super.disconnectedCallback();
181
+ this.removeEventListener('keydown', this.onKeydown);
123
182
  this.observer?.disconnect();
124
183
  this.observer = undefined;
125
184
  }
@@ -150,13 +209,17 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
150
209
  const palette = writtenList(this.tones);
151
210
  const cells = this.drawn;
152
211
  const readable = cells.length > 0;
212
+ const keys = this.selectionMode ? this.keysOf(cells) : [];
213
+ const chosen = new Set(this.selectionMode ? commaList(this.value) : []);
153
214
  return html `<div
154
- aria-label=${this.getAttribute?.('aria-label') || this.named || 'Heatmap'}
215
+ aria-describedby=${readable ? 'helix-chart-summary' : nothing}
216
+ aria-label=${this.named || 'Heatmap'}
155
217
  part="strip"
156
218
  id=${CHART_PLOT}
157
219
  role=${readable ? 'application' : 'img'}
158
220
  style="--heatmap-rows: ${this.rowCount}"
159
221
  tabindex=${readable ? '0' : nothing}
222
+ @click=${this.onClick}
160
223
  @pointerover=${this.onPointerOver}
161
224
  @pointerleave=${this.onPointerLeave}
162
225
  >
@@ -165,13 +228,14 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
165
228
  : cells.map((reading, index) => {
166
229
  const tone = toneOf(reading, bounds, palette);
167
230
  const active = this.pointer.at === index;
231
+ const picked = chosen.has(keys[index] ?? '');
168
232
  return tone === undefined
169
233
  ? html `<span
170
- part="cell${active ? ' cell-active' : ''}"
234
+ part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
171
235
  data-index=${index}
172
236
  ></span>`
173
237
  : html `<span
174
- part="cell${active ? ' cell-active' : ''}"
238
+ part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
175
239
  data-index=${index}
176
240
  data-tone=${tone}
177
241
  style="--heatmap-tone: var(--heatmap-tone-${tone})"
@@ -184,7 +248,20 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
184
248
  <span part="start"><slot name="start"></slot></span>
185
249
  <span part="summary"><slot name="summary"></slot></span>
186
250
  <span part="end"><slot name="end"></slot></span>
187
- </div>`;
251
+ </div>${readable ? this.renderOverview(cells) : nothing}${this.status.spoken
252
+ ? html `<span part="selection" role="status">${this.status.said}</span>`
253
+ : nothing}`;
254
+ }
255
+ renderOverview(cells) {
256
+ const rows = this.sourceRows;
257
+ const offset = this.points.length - cells.length;
258
+ const column = this.plottedColumns[0] ?? -1;
259
+ const { busiest, total } = overviewOf(cells);
260
+ const row = rows[offset + busiest];
261
+ const text = row?.cells[column]?.text ?? '';
262
+ const most = row ? `${this.busiestLabel}: ${row.label}${text === '' ? '' : `, ${text}`}` : '';
263
+ const sum = `${this.totalLabel} ${formatNumbers(this.locale)(total)}`;
264
+ return html `<p id="helix-chart-summary" part="overview">${most ? `${most}; ${sum}` : sum}.</p>`;
188
265
  }
189
266
  renderTooltip() {
190
267
  const at = this.pointer.at;
@@ -203,6 +280,9 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
203
280
  }, undefined, this.pointer);
204
281
  }
205
282
  };
283
+ __decorate([
284
+ property({ attribute: 'busiest-label' })
285
+ ], HelixHeadlessHeatmap.prototype, "busiestLabel", void 0);
206
286
  __decorate([
207
287
  property({ attribute: 'empty-text' })
208
288
  ], HelixHeadlessHeatmap.prototype, "emptyText", void 0);
@@ -215,12 +295,24 @@ __decorate([
215
295
  __decorate([
216
296
  property({ type: Number })
217
297
  ], HelixHeadlessHeatmap.prototype, "rows", void 0);
298
+ __decorate([
299
+ property({ attribute: 'selection-label' })
300
+ ], HelixHeadlessHeatmap.prototype, "selectionLabel", void 0);
301
+ __decorate([
302
+ property({ attribute: 'selection-mode' })
303
+ ], HelixHeadlessHeatmap.prototype, "selectionMode", void 0);
218
304
  __decorate([
219
305
  property()
220
306
  ], HelixHeadlessHeatmap.prototype, "thresholds", void 0);
221
307
  __decorate([
222
308
  property()
223
309
  ], HelixHeadlessHeatmap.prototype, "tones", void 0);
310
+ __decorate([
311
+ property({ attribute: 'total-label' })
312
+ ], HelixHeadlessHeatmap.prototype, "totalLabel", void 0);
313
+ __decorate([
314
+ property()
315
+ ], HelixHeadlessHeatmap.prototype, "value", void 0);
224
316
  __decorate([
225
317
  state()
226
318
  ], HelixHeadlessHeatmap.prototype, "width", void 0);
@@ -1,3 +1,3 @@
1
1
  import './set.js';
2
- export { type IconResolver, registerIconLibrary, registerIcons, resolveIcon } from './registry.js';
2
+ export { type IconLibraryOptions, type IconResolver, registerIconLibrary, registerIcons, resolveIcon, } from './registry.js';
3
3
  export * from './root/icon-root.js';
@@ -1,3 +1,3 @@
1
1
  import './set.js';
2
- export { registerIconLibrary, registerIcons, resolveIcon } from './registry.js';
2
+ export { registerIconLibrary, registerIcons, resolveIcon, } from './registry.js';
3
3
  export * from './root/icon-root.js';
@@ -1,6 +1,15 @@
1
1
  import type { HelixIcon } from '@fusion.dev/helix-icons';
2
2
  /** What a library gives for a name: a glyph, a glyph later, or nothing. */
3
3
  export type IconResolver = (name: string) => HelixIcon | Promise<HelixIcon | undefined> | undefined;
4
+ /** How a library's glyphs are drawn, beside the names it resolves. */
5
+ export interface IconLibraryOptions {
6
+ /**
7
+ * Changes each drawn `<svg>` of the library's glyphs, as Web Awesome's
8
+ * `mutator` does: a stroke width, a fill, an attribute the set leaves out.
9
+ * Called after every render that draws one of them.
10
+ */
11
+ mutator?: (svg: SVGSVGElement) => void;
12
+ }
4
13
  /**
5
14
  * Makes glyphs resolvable by name: a record of names to glyphs, such as the
6
15
  * whole set, or a list of glyphs, each under its own `name`.
@@ -15,14 +24,30 @@ export declare const registerIcons: (glyphs: Record<string, HelixIcon> | readonl
15
24
  * The resolver may answer at once or with a promise; an element waiting on a
16
25
  * promise draws the glyph when it arrives and fires `load`. Each written name
17
26
  * is asked once, and a prefix registered again is asked afresh.
27
+ *
28
+ * A sprite sheet is a library whose glyphs point into it, so the sheet is
29
+ * fetched once and cached:
30
+ *
31
+ * ```js
32
+ * registerIconLibrary('sprite', (name) => ({
33
+ * content: `<use href="/icons.svg#${name}"></use>`,
34
+ * name,
35
+ * viewBox: '0 0 24 24',
36
+ * }))
37
+ * ```
38
+ *
39
+ * `mutator` changes each drawn `<svg>` of the library, such as
40
+ * `(svg) => svg.setAttribute('stroke-width', '1.5')`.
18
41
  */
19
- export declare const registerIconLibrary: (prefix: string, resolve: IconResolver) => void;
42
+ export declare const registerIconLibrary: (prefix: string, resolve: IconResolver, options?: IconLibraryOptions) => void;
20
43
  /**
21
44
  * Calls `watcher` whenever a glyph or a library is registered, so an element
22
45
  * drawn before its glyph was registered can look again. Returns the function
23
46
  * that stops it.
24
47
  */
25
48
  export declare const watchIcons: (watcher: () => void) => (() => void);
49
+ /** The mutator of the library a written name asks, or `undefined`. */
50
+ export declare const iconMutator: (name: string) => ((svg: SVGSVGElement) => void) | undefined;
26
51
  /** The registered glyph of this name, or `undefined`. */
27
52
  export declare const registeredIcon: (name: string) => HelixIcon | undefined;
28
53
  /**
@@ -24,9 +24,16 @@ export const registerIcons = (glyphs) => {
24
24
  }
25
25
  changed(held);
26
26
  };
27
- export const registerIconLibrary = (prefix, resolve) => {
27
+ export const registerIconLibrary = (prefix, resolve, options = {}) => {
28
28
  const held = registry();
29
29
  held.libraries.set(prefix, resolve);
30
+ held.mutators ??= new Map();
31
+ if (options.mutator) {
32
+ held.mutators.set(prefix, options.mutator);
33
+ }
34
+ else {
35
+ held.mutators.delete(prefix);
36
+ }
30
37
  for (const name of held.answers.keys()) {
31
38
  if (name.startsWith(`${prefix}:`)) {
32
39
  held.answers.delete(name);
@@ -41,6 +48,10 @@ export const watchIcons = (watcher) => {
41
48
  held.watchers.delete(watcher);
42
49
  };
43
50
  };
51
+ export const iconMutator = (name) => {
52
+ const split = name.indexOf(':');
53
+ return split > 0 ? registry().mutators?.get(name.slice(0, split)) : undefined;
54
+ };
44
55
  export const registeredIcon = (name) => registry().glyphs.get(name);
45
56
  export const resolveIcon = (name) => {
46
57
  const registered = registeredIcon(name);
@@ -28,13 +28,19 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
28
28
  * rendered is drawn then.
29
29
  *
30
30
  * A name with no match renders nothing, and the element fires `error`, so a
31
- * typo in a name can be heard.
31
+ * typo in a name can be heard. It waits until the page has loaded and painted
32
+ * first, so a library a later module registers is not taken for a typo.
32
33
  *
33
34
  * An icon is decorative by default and hidden from assistive technology. Set
34
35
  * `label` only when the icon is the only carrier of meaning, such as in a
35
36
  * button with no text. An `aria-label` on the element works too: the element
36
37
  * is then an image with that name.
37
38
  *
39
+ * **A decorative icon that is all a control holds is reported.** In a
40
+ * development build, a button or a link whose only content is an icon with no
41
+ * `label`, and which has no name of its own, warns `missing-name` once,
42
+ * through the runtime channel every Helix warning uses.
43
+ *
38
44
  * @since 0.1.0
39
45
  * @status stable
40
46
  * @category media
@@ -120,6 +126,24 @@ export declare class HelixHeadlessIcon extends HelixElement {
120
126
  private lookup;
121
127
  protected willUpdate(changed: PropertyValues): void;
122
128
  protected updated(changed: PropertyValues): void;
129
+ /** Hands the drawn `<svg>` to the mutator of the library `name` asks, where it has one. */
130
+ private mutate;
131
+ /**
132
+ * Asks the button or link a decorative icon sits in whether it has a name.
133
+ * Its content is this icon alone where it has none, and the audit says so
134
+ * once, in a development build only. A Helix button reports its own name.
135
+ */
136
+ private auditControl;
137
+ /** The name a report is waiting on the page to settle for, so it is asked once. */
138
+ private pending;
139
+ /**
140
+ * Says a name drew nothing, once the page has had its chance to register
141
+ * it: a library registered by a module further down the page, or by the
142
+ * page's own script after this element, is not a missing glyph. Asked again
143
+ * then, and said only where the name still draws nothing and nothing is
144
+ * still on its way.
145
+ */
146
+ private reportMissing;
123
147
  protected render(): TemplateResult | typeof nothing;
124
148
  }
125
149
  declare global {