@aceshooting/lyra-ui 1.1.0 → 2.0.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 (335) hide show
  1. package/README.md +12 -1
  2. package/custom-elements.json +21460 -8489
  3. package/dist/components/app-rail/app-rail.d.ts +171 -0
  4. package/dist/components/app-rail/app-rail.js +483 -0
  5. package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
  6. package/dist/components/app-rail/app-rail.styles.js +125 -0
  7. package/dist/components/attachment-chip/attachment-chip.d.ts +114 -0
  8. package/dist/components/attachment-chip/attachment-chip.js +312 -0
  9. package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
  10. package/dist/components/attachment-chip/attachment-chip.styles.js +184 -0
  11. package/dist/components/attachment-trigger/attachment-trigger.d.ts +83 -0
  12. package/dist/components/attachment-trigger/attachment-trigger.js +258 -0
  13. package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
  14. package/dist/components/attachment-trigger/attachment-trigger.styles.js +68 -0
  15. package/dist/components/chart/box-plot.js +11 -1
  16. package/dist/components/chart/chart-loader.d.ts +44 -13
  17. package/dist/components/chart/chart-loader.js +65 -18
  18. package/dist/components/chart/chart.d.ts +12 -2
  19. package/dist/components/chart/chart.js +121 -14
  20. package/dist/components/chart/histogram-bin.d.ts +4 -1
  21. package/dist/components/chart/histogram-bin.js +24 -14
  22. package/dist/components/chart/lite-chart.d.ts +63 -0
  23. package/dist/components/chart/lite-chart.js +181 -23
  24. package/dist/components/chart/lite-chart.styles.js +9 -0
  25. package/dist/components/chat-composer/chat-composer.d.ts +92 -0
  26. package/dist/components/chat-composer/chat-composer.js +285 -0
  27. package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
  28. package/dist/components/chat-composer/chat-composer.styles.js +135 -0
  29. package/dist/components/chat-message/chat-message.d.ts +113 -0
  30. package/dist/components/chat-message/chat-message.js +291 -0
  31. package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
  32. package/dist/components/chat-message/chat-message.styles.js +203 -0
  33. package/dist/components/checkbox/checkbox.d.ts +59 -0
  34. package/dist/components/checkbox/checkbox.js +230 -0
  35. package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
  36. package/dist/components/checkbox/checkbox.styles.js +70 -0
  37. package/dist/components/chip/chip-group.d.ts +56 -0
  38. package/dist/components/chip/chip-group.js +124 -0
  39. package/dist/components/chip/chip-group.styles.d.ts +1 -0
  40. package/dist/components/chip/chip-group.styles.js +57 -0
  41. package/dist/components/chip/chip.d.ts +64 -0
  42. package/dist/components/chip/chip.js +115 -0
  43. package/dist/components/chip/chip.styles.d.ts +1 -0
  44. package/dist/components/chip/chip.styles.js +115 -0
  45. package/dist/components/citation-badge/citation-badge.d.ts +113 -0
  46. package/dist/components/citation-badge/citation-badge.js +300 -0
  47. package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
  48. package/dist/components/citation-badge/citation-badge.styles.js +96 -0
  49. package/dist/components/code-block/code-block.d.ts +89 -0
  50. package/dist/components/code-block/code-block.js +317 -0
  51. package/dist/components/code-block/code-block.styles.d.ts +1 -0
  52. package/dist/components/code-block/code-block.styles.js +187 -0
  53. package/dist/components/code-block/code-loader.d.ts +44 -0
  54. package/dist/components/code-block/code-loader.js +79 -0
  55. package/dist/components/combobox/combobox.d.ts +19 -1
  56. package/dist/components/combobox/combobox.js +84 -14
  57. package/dist/components/combobox/option.d.ts +5 -1
  58. package/dist/components/combobox/option.js +35 -0
  59. package/dist/components/context-meter/context-meter.d.ts +53 -0
  60. package/dist/components/context-meter/context-meter.js +150 -0
  61. package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
  62. package/dist/components/context-meter/context-meter.styles.js +120 -0
  63. package/dist/components/conversation-item/conversation-item.d.ts +128 -0
  64. package/dist/components/conversation-item/conversation-item.js +322 -0
  65. package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
  66. package/dist/components/conversation-item/conversation-item.styles.js +155 -0
  67. package/dist/components/date-picker/date-input.d.ts +24 -0
  68. package/dist/components/date-picker/date-input.js +134 -22
  69. package/dist/components/date-picker/date-picker.d.ts +7 -0
  70. package/dist/components/date-picker/date-picker.js +85 -16
  71. package/dist/components/dialog/confirm.d.ts +40 -0
  72. package/dist/components/dialog/confirm.js +79 -0
  73. package/dist/components/dialog/dialog.d.ts +85 -0
  74. package/dist/components/dialog/dialog.js +272 -0
  75. package/dist/components/dialog/dialog.styles.d.ts +1 -0
  76. package/dist/components/dialog/dialog.styles.js +52 -0
  77. package/dist/components/dock-panel/dock-panel.d.ts +134 -0
  78. package/dist/components/dock-panel/dock-panel.js +345 -0
  79. package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
  80. package/dist/components/dock-panel/dock-panel.styles.js +145 -0
  81. package/dist/components/document-preview/document-preview.d.ts +135 -0
  82. package/dist/components/document-preview/document-preview.js +372 -0
  83. package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
  84. package/dist/components/document-preview/document-preview.styles.js +191 -0
  85. package/dist/components/export-button/csv.js +1 -1
  86. package/dist/components/export-button/export-button.js +2 -0
  87. package/dist/components/flag/flag.d.ts +42 -5
  88. package/dist/components/flag/flag.js +70 -3
  89. package/dist/components/gauge/gauge.js +19 -10
  90. package/dist/components/generation-status/generation-status.d.ts +109 -0
  91. package/dist/components/generation-status/generation-status.js +273 -0
  92. package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
  93. package/dist/components/generation-status/generation-status.styles.js +84 -0
  94. package/dist/components/heatmap/calendar-grid.d.ts +4 -1
  95. package/dist/components/heatmap/calendar-grid.js +25 -12
  96. package/dist/components/heatmap/heatmap-scale.d.ts +6 -0
  97. package/dist/components/heatmap/heatmap-scale.js +7 -1
  98. package/dist/components/heatmap/heatmap.d.ts +89 -3
  99. package/dist/components/heatmap/heatmap.js +158 -29
  100. package/dist/components/json-viewer/json-viewer.d.ts +53 -0
  101. package/dist/components/json-viewer/json-viewer.js +283 -0
  102. package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
  103. package/dist/components/json-viewer/json-viewer.styles.js +176 -0
  104. package/dist/components/kbd/kbd.d.ts +77 -0
  105. package/dist/components/kbd/kbd.js +214 -0
  106. package/dist/components/kbd/kbd.styles.d.ts +1 -0
  107. package/dist/components/kbd/kbd.styles.js +52 -0
  108. package/dist/components/live-region/live-region.d.ts +81 -0
  109. package/dist/components/live-region/live-region.js +153 -0
  110. package/dist/components/live-region/live-region.styles.d.ts +1 -0
  111. package/dist/components/live-region/live-region.styles.js +10 -0
  112. package/dist/components/map/map.d.ts +8 -6
  113. package/dist/components/map/map.js +44 -8
  114. package/dist/components/markdown/markdown-loader.d.ts +34 -0
  115. package/dist/components/markdown/markdown-loader.js +43 -0
  116. package/dist/components/markdown/markdown.d.ts +81 -0
  117. package/dist/components/markdown/markdown.js +284 -0
  118. package/dist/components/markdown/markdown.styles.d.ts +1 -0
  119. package/dist/components/markdown/markdown.styles.js +82 -0
  120. package/dist/components/media-card/media-card.d.ts +122 -0
  121. package/dist/components/media-card/media-card.js +295 -0
  122. package/dist/components/media-card/media-card.styles.d.ts +1 -0
  123. package/dist/components/media-card/media-card.styles.js +132 -0
  124. package/dist/components/mention-popover/mention-popover.d.ts +200 -0
  125. package/dist/components/mention-popover/mention-popover.js +475 -0
  126. package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
  127. package/dist/components/mention-popover/mention-popover.styles.js +101 -0
  128. package/dist/components/menu/menu-item.d.ts +62 -0
  129. package/dist/components/menu/menu-item.js +119 -0
  130. package/dist/components/menu/menu-item.styles.d.ts +1 -0
  131. package/dist/components/menu/menu-item.styles.js +67 -0
  132. package/dist/components/menu/menu.d.ts +143 -0
  133. package/dist/components/menu/menu.js +365 -0
  134. package/dist/components/menu/menu.styles.d.ts +1 -0
  135. package/dist/components/menu/menu.styles.js +66 -0
  136. package/dist/components/model-select/model-select.d.ts +125 -0
  137. package/dist/components/model-select/model-select.js +490 -0
  138. package/dist/components/model-select/model-select.styles.d.ts +1 -0
  139. package/dist/components/model-select/model-select.styles.js +189 -0
  140. package/dist/components/model-settings-panel/model-settings-panel.d.ts +69 -0
  141. package/dist/components/model-settings-panel/model-settings-panel.js +145 -0
  142. package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
  143. package/dist/components/model-settings-panel/model-settings-panel.styles.js +82 -0
  144. package/dist/components/playback/playback.d.ts +13 -1
  145. package/dist/components/playback/playback.js +47 -14
  146. package/dist/components/responsive-panel/responsive-panel.d.ts +150 -0
  147. package/dist/components/responsive-panel/responsive-panel.js +365 -0
  148. package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
  149. package/dist/components/responsive-panel/responsive-panel.styles.js +103 -0
  150. package/dist/components/result-card/result-card.d.ts +44 -0
  151. package/dist/components/result-card/result-card.js +83 -0
  152. package/dist/components/result-card/result-card.styles.d.ts +1 -0
  153. package/dist/components/result-card/result-card.styles.js +50 -0
  154. package/dist/components/result-card/result-field.d.ts +36 -0
  155. package/dist/components/result-card/result-field.js +87 -0
  156. package/dist/components/result-card/result-field.styles.d.ts +1 -0
  157. package/dist/components/result-card/result-field.styles.js +32 -0
  158. package/dist/components/select/select.d.ts +45 -1
  159. package/dist/components/select/select.js +114 -17
  160. package/dist/components/select/select.styles.js +26 -2
  161. package/dist/components/slider/slider.d.ts +89 -0
  162. package/dist/components/slider/slider.js +352 -0
  163. package/dist/components/slider/slider.styles.d.ts +1 -0
  164. package/dist/components/slider/slider.styles.js +87 -0
  165. package/dist/components/source-card/source-card.d.ts +92 -0
  166. package/dist/components/source-card/source-card.js +148 -0
  167. package/dist/components/source-card/source-card.styles.d.ts +1 -0
  168. package/dist/components/source-card/source-card.styles.js +69 -0
  169. package/dist/components/source-list/source-list.d.ts +63 -0
  170. package/dist/components/source-list/source-list.js +122 -0
  171. package/dist/components/source-list/source-list.styles.d.ts +1 -0
  172. package/dist/components/source-list/source-list.styles.js +59 -0
  173. package/dist/components/sparkline/sparkline.js +27 -13
  174. package/dist/components/split/split.d.ts +93 -1
  175. package/dist/components/split/split.js +284 -15
  176. package/dist/components/split/split.styles.js +30 -0
  177. package/dist/components/stat/stat.d.ts +22 -1
  178. package/dist/components/stat/stat.js +49 -3
  179. package/dist/components/stat/stat.styles.js +20 -0
  180. package/dist/components/stream-status/stream-status.d.ts +110 -0
  181. package/dist/components/stream-status/stream-status.js +210 -0
  182. package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
  183. package/dist/components/stream-status/stream-status.styles.js +103 -0
  184. package/dist/components/streaming-text/streaming-text.d.ts +98 -0
  185. package/dist/components/streaming-text/streaming-text.js +209 -0
  186. package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
  187. package/dist/components/streaming-text/streaming-text.styles.js +62 -0
  188. package/dist/components/switch/switch.d.ts +58 -0
  189. package/dist/components/switch/switch.js +179 -0
  190. package/dist/components/switch/switch.styles.d.ts +1 -0
  191. package/dist/components/switch/switch.styles.js +75 -0
  192. package/dist/components/table/table.d.ts +73 -4
  193. package/dist/components/table/table.js +169 -20
  194. package/dist/components/table/table.styles.js +6 -1
  195. package/dist/components/tabs/tabs.d.ts +51 -0
  196. package/dist/components/tabs/tabs.js +190 -0
  197. package/dist/components/tabs/tabs.styles.d.ts +1 -0
  198. package/dist/components/tabs/tabs.styles.js +63 -0
  199. package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
  200. package/dist/components/thinking-panel/thinking-panel.js +235 -0
  201. package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
  202. package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
  203. package/dist/components/toast/toast-item.d.ts +1 -0
  204. package/dist/components/toast/toast-item.js +31 -6
  205. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
  206. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
  207. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
  208. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
  209. package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
  210. package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
  211. package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
  212. package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
  213. package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
  214. package/dist/components/tool-param-form/tool-param-form.js +366 -0
  215. package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
  216. package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
  217. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
  218. package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
  219. package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
  220. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
  221. package/dist/components/tool-result-view/registry.d.ts +81 -0
  222. package/dist/components/tool-result-view/registry.js +84 -0
  223. package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
  224. package/dist/components/tool-result-view/tool-result-view.js +138 -0
  225. package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
  226. package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
  227. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
  228. package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
  229. package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
  230. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
  231. package/dist/components/tree/tree-node.d.ts +11 -2
  232. package/dist/components/tree/tree-node.js +16 -6
  233. package/dist/components/tree/tree.d.ts +8 -2
  234. package/dist/components/tree/tree.js +18 -11
  235. package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
  236. package/dist/components/typing-indicator/typing-indicator.js +96 -0
  237. package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
  238. package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
  239. package/dist/components/virtual-list/virtual-list.d.ts +173 -0
  240. package/dist/components/virtual-list/virtual-list.js +461 -0
  241. package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
  242. package/dist/components/virtual-list/virtual-list.styles.js +44 -0
  243. package/dist/components/widget/widget.d.ts +10 -0
  244. package/dist/components/widget/widget.js +24 -1
  245. package/dist/components/widget/widget.styles.js +9 -2
  246. package/dist/components/word-cloud/word-cloud-layout.js +14 -1
  247. package/dist/components/word-cloud/word-cloud.d.ts +6 -0
  248. package/dist/components/word-cloud/word-cloud.js +25 -14
  249. package/dist/components/word-cloud/word-cloud.styles.js +4 -2
  250. package/dist/internal/announcer.d.ts +57 -0
  251. package/dist/internal/announcer.js +71 -0
  252. package/dist/internal/form-associated.d.ts +1 -0
  253. package/dist/internal/form-associated.js +55 -11
  254. package/dist/internal/group-by-recency.d.ts +50 -0
  255. package/dist/internal/group-by-recency.js +80 -0
  256. package/dist/internal/rtl.d.ts +23 -0
  257. package/dist/internal/rtl.js +28 -0
  258. package/dist/internal/scroll-lock.d.ts +10 -5
  259. package/dist/internal/scroll-lock.js +30 -14
  260. package/dist/internal/tokens.styles.js +6 -0
  261. package/dist/lyra.d.ts +130 -31
  262. package/dist/lyra.js +94 -26
  263. package/llms-full.txt +3921 -32
  264. package/llms.txt +16 -1
  265. package/package.json +23 -5
  266. package/dist/components/chart/bar-chart.stories.d.ts +0 -5
  267. package/dist/components/chart/bar-chart.stories.js +0 -20
  268. package/dist/components/chart/box-plot.stories.d.ts +0 -5
  269. package/dist/components/chart/box-plot.stories.js +0 -29
  270. package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
  271. package/dist/components/chart/bubble-chart.stories.js +0 -26
  272. package/dist/components/chart/chart.stories.d.ts +0 -32
  273. package/dist/components/chart/chart.stories.js +0 -141
  274. package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
  275. package/dist/components/chart/doughnut-chart.stories.js +0 -26
  276. package/dist/components/chart/histogram.stories.d.ts +0 -5
  277. package/dist/components/chart/histogram.stories.js +0 -17
  278. package/dist/components/chart/line-chart.stories.d.ts +0 -22
  279. package/dist/components/chart/line-chart.stories.js +0 -99
  280. package/dist/components/chart/lite-chart.stories.d.ts +0 -12
  281. package/dist/components/chart/lite-chart.stories.js +0 -98
  282. package/dist/components/chart/pie-chart.stories.d.ts +0 -5
  283. package/dist/components/chart/pie-chart.stories.js +0 -26
  284. package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
  285. package/dist/components/chart/polar-area-chart.stories.js +0 -20
  286. package/dist/components/chart/radar-chart.stories.d.ts +0 -5
  287. package/dist/components/chart/radar-chart.stories.js +0 -20
  288. package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
  289. package/dist/components/chart/scatter-chart.stories.js +0 -31
  290. package/dist/components/combobox/combobox.stories.d.ts +0 -24
  291. package/dist/components/combobox/combobox.stories.js +0 -102
  292. package/dist/components/date-picker/date-input.stories.d.ts +0 -10
  293. package/dist/components/date-picker/date-input.stories.js +0 -48
  294. package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
  295. package/dist/components/date-picker/date-picker.stories.js +0 -25
  296. package/dist/components/empty/empty.stories.d.ts +0 -7
  297. package/dist/components/empty/empty.stories.js +0 -34
  298. package/dist/components/export-button/export-button.stories.d.ts +0 -7
  299. package/dist/components/export-button/export-button.stories.js +0 -37
  300. package/dist/components/file-input/file-input.stories.d.ts +0 -7
  301. package/dist/components/file-input/file-input.stories.js +0 -19
  302. package/dist/components/flag/flag.stories.d.ts +0 -5
  303. package/dist/components/flag/flag.stories.js +0 -17
  304. package/dist/components/gauge/gauge.stories.d.ts +0 -8
  305. package/dist/components/gauge/gauge.stories.js +0 -19
  306. package/dist/components/graph/graph.stories.d.ts +0 -8
  307. package/dist/components/graph/graph.stories.js +0 -68
  308. package/dist/components/heatmap/heatmap.stories.d.ts +0 -22
  309. package/dist/components/heatmap/heatmap.stories.js +0 -154
  310. package/dist/components/map/map.stories.d.ts +0 -15
  311. package/dist/components/map/map.stories.js +0 -135
  312. package/dist/components/playback/playback.stories.d.ts +0 -7
  313. package/dist/components/playback/playback.stories.js +0 -16
  314. package/dist/components/select/select.stories.d.ts +0 -19
  315. package/dist/components/select/select.stories.js +0 -75
  316. package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
  317. package/dist/components/skeleton/skeleton.stories.js +0 -16
  318. package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
  319. package/dist/components/sparkline/sparkline.stories.js +0 -39
  320. package/dist/components/split/split.stories.d.ts +0 -7
  321. package/dist/components/split/split.stories.js +0 -36
  322. package/dist/components/stat/stat.stories.d.ts +0 -8
  323. package/dist/components/stat/stat.stories.js +0 -64
  324. package/dist/components/table/table.stories.d.ts +0 -12
  325. package/dist/components/table/table.stories.js +0 -67
  326. package/dist/components/time-range/time-range.stories.d.ts +0 -8
  327. package/dist/components/time-range/time-range.stories.js +0 -26
  328. package/dist/components/toast/toast.stories.d.ts +0 -8
  329. package/dist/components/toast/toast.stories.js +0 -72
  330. package/dist/components/tree/tree.stories.d.ts +0 -9
  331. package/dist/components/tree/tree.stories.js +0 -52
  332. package/dist/components/widget/widget.stories.d.ts +0 -7
  333. package/dist/components/widget/widget.stories.js +0 -41
  334. package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
  335. package/dist/components/word-cloud/word-cloud.stories.js +0 -55
@@ -1,25 +1,56 @@
1
1
  import type * as ChartJsModule from 'chart.js';
2
2
  import type ZoomPlugin from 'chartjs-plugin-zoom';
3
3
  /**
4
- * Independently loads the mandatory `chart.js` peer dependency and the
5
- * opt-in `chartjs-plugin-zoom` peer dependency, so a partial install
6
- * (`chart.js` only, which `peerDependenciesMeta` marks as a valid
7
- * combination) degrades to "charts render, zoom is inert" instead of every
8
- * chart breaking. Exported (in addition to `loadChartJs()` below) so both
9
- * failure paths and the real caught error each one logs — are directly
10
- * testable without needing to actually uninstall either package.
4
+ * Independently loads the mandatory `chart.js` peer dependency and, only
5
+ * when `needsZoom` is true, the opt-in `chartjs-plugin-zoom` peer dependency
6
+ * — so a partial install (`chart.js` only, which `peerDependenciesMeta`
7
+ * marks as a valid combination) degrades to "charts render, zoom is inert"
8
+ * instead of every chart breaking. `needsZoom` defaults to `false` because
9
+ * most charts never set `zoom`, and `chartjs-plugin-zoom` has a hard
10
+ * dependency on `hammerjs` no point paying that import cost for every
11
+ * chart on the page. Exported (in addition to `loadChartJs()`/
12
+ * `loadChartJsWithZoom()` below) so both failure paths — and the real caught
13
+ * error each one logs — are directly testable without needing to actually
14
+ * uninstall either package.
11
15
  */
12
16
  export declare function loadChartAndZoom(importChart?: () => Promise<typeof ChartJsModule>, importZoom?: () => Promise<{
13
17
  default: typeof ZoomPlugin;
14
- }>): Promise<{
18
+ }>, needsZoom?: boolean): Promise<{
15
19
  mod: typeof ChartJsModule;
16
20
  zoomPlugin: typeof ZoomPlugin | undefined;
17
21
  } | null>;
18
22
  /**
19
- * Lazily loads the optional peer dependency `chart.js` (+ `chartjs-plugin-zoom`,
20
- * loaded independently see `loadChartAndZoom()`) once per page, registering
21
- * only the tree-shaken subset this library uses. Resolves to `null` if
22
- * chart.js isn't installed; still resolves the real module (with zoom simply
23
- * unregistered) if only `chartjs-plugin-zoom` is missing.
23
+ * Lazily loads the optional peer dependency `chart.js` once per page,
24
+ * registering only the tree-shaken subset this library uses.
25
+ * `chartjs-plugin-zoom` is *not* loaded here see `loadChartJsWithZoom()`
26
+ * below, which any chart that actually sets `zoom` should call instead.
27
+ * Resolves to `null` if chart.js isn't installed.
24
28
  */
25
29
  export declare function loadChartJs(): Promise<typeof ChartJsModule | null>;
30
+ /**
31
+ * Loads `chart.js` (reusing the cached load above) plus `chartjs-plugin-zoom`,
32
+ * on first actual demand — most charts never set `zoom`, and the plugin has
33
+ * a hard dependency on `hammerjs`. Registers the plugin at most once across
34
+ * the page. Call this instead of `loadChartJs()` from any chart that has
35
+ * `zoom` set (at connect time, or later once `zoom` turns on).
36
+ *
37
+ * The whole operation (chart.js core + the zoom plugin import + its
38
+ * registration) is memoized behind a single `zoomLoad` promise, assigned
39
+ * synchronously before any `await` — mirroring `loadChartJs()`'s own
40
+ * `chartJs` memoization above. A plain boolean "already registered" guard
41
+ * checked before an `await` and only set after would leave a check-then-act
42
+ * race across that `await` boundary: two callers racing to turn `zoom` on
43
+ * close together (e.g. two `<lyra-chart zoom>` elements connecting around
44
+ * the same time) could both pass the check before either sets the flag,
45
+ * each independently re-importing the plugin and calling
46
+ * `mod.Chart.register()`. A single promise assigned up front closes that
47
+ * window — the second caller synchronously observes `zoomLoad` already set
48
+ * and awaits the same in-flight load instead of starting its own.
49
+ *
50
+ * `importZoom` defaults to the real dynamic import; it's a parameter purely
51
+ * so tests can instrument/count the underlying import without needing to
52
+ * actually uninstall the package.
53
+ */
54
+ export declare function loadChartJsWithZoom(importZoom?: () => Promise<{
55
+ default: typeof ZoomPlugin;
56
+ }>): Promise<typeof ChartJsModule | null>;
@@ -1,15 +1,20 @@
1
1
  let chartJs;
2
+ let zoomLoad;
2
3
  let registered = false;
3
4
  /**
4
- * Independently loads the mandatory `chart.js` peer dependency and the
5
- * opt-in `chartjs-plugin-zoom` peer dependency, so a partial install
6
- * (`chart.js` only, which `peerDependenciesMeta` marks as a valid
7
- * combination) degrades to "charts render, zoom is inert" instead of every
8
- * chart breaking. Exported (in addition to `loadChartJs()` below) so both
9
- * failure paths and the real caught error each one logs — are directly
10
- * testable without needing to actually uninstall either package.
5
+ * Independently loads the mandatory `chart.js` peer dependency and, only
6
+ * when `needsZoom` is true, the opt-in `chartjs-plugin-zoom` peer dependency
7
+ * — so a partial install (`chart.js` only, which `peerDependenciesMeta`
8
+ * marks as a valid combination) degrades to "charts render, zoom is inert"
9
+ * instead of every chart breaking. `needsZoom` defaults to `false` because
10
+ * most charts never set `zoom`, and `chartjs-plugin-zoom` has a hard
11
+ * dependency on `hammerjs` no point paying that import cost for every
12
+ * chart on the page. Exported (in addition to `loadChartJs()`/
13
+ * `loadChartJsWithZoom()` below) so both failure paths — and the real caught
14
+ * error each one logs — are directly testable without needing to actually
15
+ * uninstall either package.
11
16
  */
12
- export async function loadChartAndZoom(importChart = () => import('chart.js'), importZoom = () => import('chartjs-plugin-zoom')) {
17
+ export async function loadChartAndZoom(importChart = () => import('chart.js'), importZoom = () => import('chartjs-plugin-zoom'), needsZoom = false) {
13
18
  let mod;
14
19
  try {
15
20
  mod = await importChart();
@@ -18,6 +23,8 @@ export async function loadChartAndZoom(importChart = () => import('chart.js'), i
18
23
  console.warn('<lyra-chart> needs the optional peer dependency `chart.js` — install it with `pnpm add chart.js`:', err);
19
24
  return null;
20
25
  }
26
+ if (!needsZoom)
27
+ return { mod, zoomPlugin: undefined };
21
28
  let zoomPlugin;
22
29
  try {
23
30
  zoomPlugin = (await importZoom()).default;
@@ -29,25 +36,65 @@ export async function loadChartAndZoom(importChart = () => import('chart.js'), i
29
36
  }
30
37
  return { mod, zoomPlugin };
31
38
  }
39
+ function registerCore(mod) {
40
+ if (registered)
41
+ return;
42
+ mod.Chart.register(mod.LineController, mod.BarController, mod.ScatterController, mod.DoughnutController, mod.PieController, mod.RadarController, mod.PolarAreaController, mod.BubbleController, mod.LineElement, mod.PointElement, mod.BarElement, mod.ArcElement, mod.LinearScale, mod.CategoryScale, mod.RadialLinearScale, mod.Filler, mod.Tooltip, mod.Legend);
43
+ registered = true;
44
+ }
32
45
  /**
33
- * Lazily loads the optional peer dependency `chart.js` (+ `chartjs-plugin-zoom`,
34
- * loaded independently see `loadChartAndZoom()`) once per page, registering
35
- * only the tree-shaken subset this library uses. Resolves to `null` if
36
- * chart.js isn't installed; still resolves the real module (with zoom simply
37
- * unregistered) if only `chartjs-plugin-zoom` is missing.
46
+ * Lazily loads the optional peer dependency `chart.js` once per page,
47
+ * registering only the tree-shaken subset this library uses.
48
+ * `chartjs-plugin-zoom` is *not* loaded here see `loadChartJsWithZoom()`
49
+ * below, which any chart that actually sets `zoom` should call instead.
50
+ * Resolves to `null` if chart.js isn't installed.
38
51
  */
39
52
  export function loadChartJs() {
40
53
  if (!chartJs) {
41
54
  chartJs = loadChartAndZoom().then((result) => {
42
55
  if (!result)
43
56
  return null;
44
- const { mod, zoomPlugin } = result;
45
- if (!registered) {
46
- mod.Chart.register(mod.LineController, mod.BarController, mod.ScatterController, mod.DoughnutController, mod.PieController, mod.RadarController, mod.PolarAreaController, mod.BubbleController, mod.LineElement, mod.PointElement, mod.BarElement, mod.ArcElement, mod.LinearScale, mod.CategoryScale, mod.RadialLinearScale, mod.Filler, mod.Tooltip, mod.Legend, ...(zoomPlugin ? [zoomPlugin] : []));
47
- registered = true;
57
+ registerCore(result.mod);
58
+ return result.mod;
59
+ });
60
+ }
61
+ return chartJs;
62
+ }
63
+ /**
64
+ * Loads `chart.js` (reusing the cached load above) plus `chartjs-plugin-zoom`,
65
+ * on first actual demand — most charts never set `zoom`, and the plugin has
66
+ * a hard dependency on `hammerjs`. Registers the plugin at most once across
67
+ * the page. Call this instead of `loadChartJs()` from any chart that has
68
+ * `zoom` set (at connect time, or later once `zoom` turns on).
69
+ *
70
+ * The whole operation (chart.js core + the zoom plugin import + its
71
+ * registration) is memoized behind a single `zoomLoad` promise, assigned
72
+ * synchronously before any `await` — mirroring `loadChartJs()`'s own
73
+ * `chartJs` memoization above. A plain boolean "already registered" guard
74
+ * checked before an `await` and only set after would leave a check-then-act
75
+ * race across that `await` boundary: two callers racing to turn `zoom` on
76
+ * close together (e.g. two `<lyra-chart zoom>` elements connecting around
77
+ * the same time) could both pass the check before either sets the flag,
78
+ * each independently re-importing the plugin and calling
79
+ * `mod.Chart.register()`. A single promise assigned up front closes that
80
+ * window — the second caller synchronously observes `zoomLoad` already set
81
+ * and awaits the same in-flight load instead of starting its own.
82
+ *
83
+ * `importZoom` defaults to the real dynamic import; it's a parameter purely
84
+ * so tests can instrument/count the underlying import without needing to
85
+ * actually uninstall the package.
86
+ */
87
+ export function loadChartJsWithZoom(importZoom = () => import('chartjs-plugin-zoom')) {
88
+ if (!zoomLoad) {
89
+ zoomLoad = loadChartJs().then(async (mod) => {
90
+ if (!mod)
91
+ return null;
92
+ const result = await loadChartAndZoom(() => Promise.resolve(mod), importZoom, true);
93
+ if (result?.zoomPlugin) {
94
+ mod.Chart.register(result.zoomPlugin);
48
95
  }
49
96
  return mod;
50
97
  });
51
98
  }
52
- return chartJs;
99
+ return zoomLoad;
53
100
  }
@@ -1,5 +1,4 @@
1
1
  import { type TemplateResult, type PropertyValues } from 'lit';
2
- import type { ChartConfiguration } from 'chart.js';
3
2
  import { LyraElement } from '../../internal/lyra-element.js';
4
3
  import '../skeleton/skeleton.js';
5
4
  export interface Series {
@@ -73,10 +72,13 @@ export declare class LyraChart extends LyraElement {
73
72
  * without discarding sibling keys the generated config set), for consumers
74
73
  * who need full Chart.js control beyond the simplified `Series` shape.
75
74
  */
76
- config?: Partial<ChartConfiguration>;
75
+ config?: Partial<import('chart.js').ChartConfiguration>;
77
76
  /** True until the lazy-loaded `chart.js` peer dependency has settled (success or failure). */
78
77
  private loading;
79
78
  private zoomed;
79
+ private visible;
80
+ private intersectionObserver?;
81
+ private lastSignature;
80
82
  private canvasEl?;
81
83
  private chart?;
82
84
  private chartJsModule?;
@@ -95,6 +97,14 @@ export declare class LyraChart extends LyraElement {
95
97
  * is called fresh from `buildConfig()` on every draw rather than cached.
96
98
  */
97
99
  private themeColors;
100
+ /**
101
+ * A content-affecting-properties fingerprint used by `updated()` to skip a
102
+ * redundant `draw()` when neither visibility nor any of these properties
103
+ * actually changed since the last draw (e.g. an unrelated property/state
104
+ * update, or `requestUpdate()` with nothing changed). Mirrors
105
+ * `lite-chart.ts`'s `computeSignature()`.
106
+ */
107
+ private computeSignature;
98
108
  /**
99
109
  * Builds `options.scales` for the effective chart type: no scale at all for
100
110
  * pie/doughnut (a proportional-area chart has no axis), the single radial
@@ -8,7 +8,7 @@ import { html, nothing } from 'lit';
8
8
  import { property, query, state } from 'lit/decorators.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { defineElement } from '../../internal/prefix.js';
11
- import { loadChartJs } from './chart-loader.js';
11
+ import { loadChartJs, loadChartJsWithZoom } from './chart-loader.js';
12
12
  import { styles } from './chart.styles.js';
13
13
  import '../skeleton/skeleton.js';
14
14
  /**
@@ -99,6 +99,8 @@ export class LyraChart extends LyraElement {
99
99
  /** True until the lazy-loaded `chart.js` peer dependency has settled (success or failure). */
100
100
  this.loading = true;
101
101
  this.zoomed = false;
102
+ this.visible = true;
103
+ this.lastSignature = '';
102
104
  // `chartjs-plugin-zoom`'s own `resetZoom()` synchronously re-invokes the
103
105
  // `onZoomComplete` callback below as part of its reset, which would emit a
104
106
  // stale `{zoomed: true}` right before `resetZoom()` emits the real
@@ -109,20 +111,46 @@ export class LyraChart extends LyraElement {
109
111
  static { this.styles = [LyraElement.styles, styles]; }
110
112
  connectedCallback() {
111
113
  super.connectedCallback();
112
- void loadChartJs().then((mod) => {
114
+ const load = this.zoom ? loadChartJsWithZoom() : loadChartJs();
115
+ void load.then((mod) => {
116
+ if (!this.isConnected)
117
+ return;
113
118
  this.loading = false;
114
119
  if (!mod)
115
120
  return;
116
121
  this.chartJsModule = mod;
117
122
  this.draw();
118
123
  });
124
+ if (typeof IntersectionObserver !== 'undefined') {
125
+ this.intersectionObserver = new IntersectionObserver((entries) => {
126
+ const wasVisible = this.visible;
127
+ this.visible = entries[0]?.isIntersecting ?? true;
128
+ if (this.visible && !wasVisible)
129
+ this.draw();
130
+ });
131
+ this.intersectionObserver.observe(this);
132
+ }
119
133
  }
120
134
  disconnectedCallback() {
121
135
  super.disconnectedCallback();
122
136
  this.chart?.destroy();
123
137
  this.chart = undefined;
138
+ this.intersectionObserver?.disconnect();
124
139
  }
125
140
  updated(changed) {
141
+ // Disconnected between the property change that scheduled this update
142
+ // and Lit's (microtask-deferred) processing of it — e.g. a property
143
+ // changes and the element is removed in the same synchronous tick, so
144
+ // `disconnectedCallback()` (which already destroyed `this.chart`) runs
145
+ // *before* this method does. Nothing below should run in that case:
146
+ // this method's own unconditional `draw()` call further down would
147
+ // otherwise construct a brand-new `Chart` bound to the now-detached
148
+ // canvas — `draw()`'s own guard (`!chartJsModule || !canvasEl`) doesn't
149
+ // catch this, since both persist on a disconnected-but-intact element.
150
+ // If the component is reconnected later, `connectedCallback()` re-kicks
151
+ // its own load/draw sequence, so nothing is lost by bailing out here.
152
+ if (!this.isConnected)
153
+ return;
126
154
  if (this.loading)
127
155
  this.setAttribute('aria-busy', 'true');
128
156
  else
@@ -135,16 +163,59 @@ export class LyraChart extends LyraElement {
135
163
  if (changed.has('height')) {
136
164
  this.style.setProperty('--lyra-chart-height', this.height);
137
165
  }
166
+ // While `chart.js` is still loading, `draw()` would no-op anyway (no
167
+ // `chartJsModule`/`canvasEl` yet) — bail before touching `lastSignature`
168
+ // so that phantom "no-op" update doesn't get cached as the baseline and
169
+ // silently swallow the real first draw once loading finishes with no
170
+ // other property having changed in the meantime.
171
+ if (this.loading)
172
+ return;
173
+ // `zoom` can also turn on after connect (it was false at
174
+ // `connectedCallback()` time, so only the core `loadChartJs()` load was
175
+ // kicked off) — load the zoom plugin on demand now and redraw once it's
176
+ // registered. Mirrors the same `isConnected` guard `connectedCallback()`
177
+ // uses: this method already bailed out above if *already* disconnected,
178
+ // but the element can *also* disconnect during the gap while this
179
+ // dynamic import is in flight — without this guard, `draw()` would
180
+ // construct a new, leaked `Chart` bound to the now-detached canvas once
181
+ // the import resolves.
182
+ if (changed.has('zoom') && this.zoom) {
183
+ void loadChartJsWithZoom().then(() => {
184
+ if (!this.isConnected)
185
+ return;
186
+ this.draw();
187
+ });
188
+ }
138
189
  const onlyZoomChanged = changed.size === 1 && changed.has('zoomed');
139
- if (!onlyZoomChanged)
140
- this.draw();
190
+ if (onlyZoomChanged)
191
+ return;
192
+ if (!this.visible)
193
+ return; // becoming visible again triggers its own draw() via the observer above
194
+ const signature = this.computeSignature();
195
+ if (signature === this.lastSignature)
196
+ return;
197
+ this.lastSignature = signature;
198
+ this.draw();
141
199
  }
142
200
  seriesToDataset(s) {
143
201
  const colors = Array.isArray(s.color) ? s.color : s.color ? [s.color] : undefined;
144
202
  return {
145
203
  label: s.label,
146
204
  data: s.points ?? s.data ?? [],
147
- type: s.type ?? this.type,
205
+ // Leave unset rather than defaulting to `this.type`: Chart.js already
206
+ // falls back to the chart-level (effective) type for any dataset that
207
+ // doesn't set its own `type`, and forcing `this.type` here unconditionally
208
+ // used to be harmless (it normally matched the effective type anyway) —
209
+ // but it actively breaks a `config.type` override to a different chart
210
+ // family (e.g. attribute `type="line"` + `config.type: 'radar'`): every
211
+ // dataset would carry an explicit `type: 'line'` under a chart whose
212
+ // scales are built for `radar` (a single radial `r` scale, no `x`/`y`),
213
+ // which Chart.js can't reconcile — it hangs the page trying to lay out
214
+ // a cartesian-scale controller against a radial-only scale set. Only
215
+ // `s.type` (an explicit per-series mixed-type override, e.g. a line
216
+ // series over a bar chart of the *same* effective family) is passed
217
+ // through.
218
+ type: s.type,
148
219
  fill: s.fill ?? this.area,
149
220
  borderWidth: s.width ?? 2,
150
221
  borderDash: s.dash ? [4, 4] : undefined,
@@ -153,7 +224,6 @@ export class LyraChart extends LyraElement {
153
224
  pointBackgroundColor: s.pointColors,
154
225
  pointRadius: s.pointRadius,
155
226
  yAxisID: s.axis === 'y2' ? 'y2' : 'y',
156
- tooltip: s.noTooltip ? { enabled: false } : undefined,
157
227
  };
158
228
  }
159
229
  /**
@@ -174,6 +244,29 @@ export class LyraChart extends LyraElement {
174
244
  tooltipText: cs.getPropertyValue('--lyra-chart-tooltip-text').trim() || FALLBACK_TOOLTIP_TEXT,
175
245
  };
176
246
  }
247
+ /**
248
+ * A content-affecting-properties fingerprint used by `updated()` to skip a
249
+ * redundant `draw()` when neither visibility nor any of these properties
250
+ * actually changed since the last draw (e.g. an unrelated property/state
251
+ * update, or `requestUpdate()` with nothing changed). Mirrors
252
+ * `lite-chart.ts`'s `computeSignature()`.
253
+ */
254
+ computeSignature() {
255
+ return JSON.stringify([
256
+ this.type,
257
+ this.labels,
258
+ this.datasets,
259
+ this.legend,
260
+ this.area,
261
+ this.xLabel,
262
+ this.yLabel,
263
+ this.y2Label,
264
+ this.beginAtZero,
265
+ this.horizontal,
266
+ this.stacked,
267
+ this.config,
268
+ ]);
269
+ }
177
270
  /**
178
271
  * Builds `options.scales` for the effective chart type: no scale at all for
179
272
  * pie/doughnut (a proportional-area chart has no axis), the single radial
@@ -186,10 +279,10 @@ export class LyraChart extends LyraElement {
186
279
  * scale's `ticks.color`/`grid.color`/axis `title.color` so grid lines and
187
280
  * labels retheme instead of sitting at Chart.js's own hardcoded defaults.
188
281
  */
189
- buildScales(theme) {
190
- if (this.type === 'pie' || this.type === 'doughnut')
282
+ buildScales(effectiveType, theme) {
283
+ if (effectiveType === 'pie' || effectiveType === 'doughnut')
191
284
  return {};
192
- if (this.type === 'radar' || this.type === 'polarArea') {
285
+ if (effectiveType === 'radar' || effectiveType === 'polarArea') {
193
286
  return {
194
287
  r: {
195
288
  beginAtZero: this.beginAtZero,
@@ -204,10 +297,10 @@ export class LyraChart extends LyraElement {
204
297
  // `stacked` only applies to bar/line-family charts sharing a categorical
205
298
  // axis, per the design spec — scatter/bubble's linear x scale and the
206
299
  // radial r scale above are out of scope.
207
- const stacked = this.stacked && (this.type === 'bar' || this.type === 'line');
300
+ const stacked = this.stacked && (effectiveType === 'bar' || effectiveType === 'line');
208
301
  return {
209
302
  x: {
210
- type: this.type === 'scatter' || this.type === 'bubble' ? 'linear' : 'category',
303
+ type: effectiveType === 'scatter' || effectiveType === 'bubble' ? 'linear' : 'category',
211
304
  title: { display: !!this.xLabel, text: this.xLabel, color: theme.tick },
212
305
  ticks: { color: theme.tick },
213
306
  grid: { color: theme.grid },
@@ -261,8 +354,15 @@ export class LyraChart extends LyraElement {
261
354
  }
262
355
  buildConfig() {
263
356
  const theme = this.themeColors();
357
+ // Resolve the effective type up front: `config.type` (if set) overrides
358
+ // the attribute `type` post-merge, so scales/interaction must be built
359
+ // for *that* type, not `this.type` — otherwise a config.type override
360
+ // (e.g. line -> radar) ships with the wrong axis shape (categorical x/y
361
+ // instead of a radial r scale).
362
+ const effectiveType = this.config?.type ??
363
+ this.type;
264
364
  const generated = {
265
- type: this.type,
365
+ type: effectiveType,
266
366
  data: {
267
367
  labels: this.labels,
268
368
  datasets: this.datasets.map((s) => this.seriesToDataset(s)),
@@ -280,7 +380,7 @@ export class LyraChart extends LyraElement {
280
380
  // canvas-internal animation loop, so `prefersReducedMotion()` is
281
381
  // checked here instead and fed into `options.animation`.
282
382
  animation: prefersReducedMotion() ? false : undefined,
283
- interaction: { intersect: false, mode: this.type === 'scatter' ? 'nearest' : 'index' },
383
+ interaction: { intersect: false, mode: effectiveType === 'scatter' ? 'nearest' : 'index' },
284
384
  onClick: (event, _elements, chart) => this.handlePointClick(event, chart),
285
385
  plugins: {
286
386
  legend: { display: this.legend, labels: { color: theme.legend } },
@@ -288,6 +388,10 @@ export class LyraChart extends LyraElement {
288
388
  backgroundColor: theme.tooltipBg,
289
389
  titleColor: theme.tooltipText,
290
390
  bodyColor: theme.tooltipText,
391
+ // Chart.js's tooltip plugin has no per-dataset `tooltip.enabled`
392
+ // — `Series.noTooltip` is implemented here instead, via the one
393
+ // mechanism the core tooltip plugin actually reads.
394
+ filter: (item) => !this.datasets[item.datasetIndex]?.noTooltip,
291
395
  },
292
396
  zoom: this.zoom
293
397
  ? {
@@ -308,7 +412,7 @@ export class LyraChart extends LyraElement {
308
412
  }
309
413
  : undefined,
310
414
  },
311
- scales: this.buildScales(theme),
415
+ scales: this.buildScales(effectiveType, theme),
312
416
  },
313
417
  };
314
418
  if (!this.config)
@@ -428,6 +532,9 @@ __decorate([
428
532
  __decorate([
429
533
  state()
430
534
  ], LyraChart.prototype, "zoomed", void 0);
535
+ __decorate([
536
+ state()
537
+ ], LyraChart.prototype, "visible", void 0);
431
538
  __decorate([
432
539
  query('canvas')
433
540
  ], LyraChart.prototype, "canvasEl", void 0);
@@ -2,5 +2,8 @@ export interface HistogramBucket {
2
2
  label: string;
3
3
  count: number;
4
4
  }
5
- /** Splits `values` into `binCount` equal-width buckets and counts membership. */
5
+ /** Splits `values` into `binCount` equal-width buckets and counts membership.
6
+ * Non-finite `binCount` (or <= 0) yields no buckets; a fractional `binCount`
7
+ * is floored; non-finite samples in `values` are dropped rather than
8
+ * corrupting bucket-index math. */
6
9
  export declare function binValues(values: number[], binCount: number): HistogramBucket[];
@@ -1,29 +1,39 @@
1
- /** Splits `values` into `binCount` equal-width buckets and counts membership. */
1
+ /** Splits `values` into `binCount` equal-width buckets and counts membership.
2
+ * Non-finite `binCount` (or <= 0) yields no buckets; a fractional `binCount`
3
+ * is floored; non-finite samples in `values` are dropped rather than
4
+ * corrupting bucket-index math. */
2
5
  export function binValues(values, binCount) {
3
- if (values.length === 0 || binCount <= 0)
6
+ const finite = values.filter((v) => Number.isFinite(v));
7
+ if (finite.length === 0 || !Number.isFinite(binCount))
4
8
  return [];
5
- // Spreading `values` as call arguments (`Math.min(...values)`) throws
6
- // `RangeError: Maximum call stack size exceeded` once `values` is large
7
- // enough to overflow the engine's argument-list limit — reduce manually
8
- // instead so an arbitrarily large sample never crashes.
9
- let lo = values[0];
10
- let hi = values[0];
11
- for (const v of values) {
9
+ const bins = Math.floor(binCount);
10
+ if (bins <= 0)
11
+ return [];
12
+ let lo = finite[0];
13
+ let hi = finite[0];
14
+ for (const v of finite) {
12
15
  if (v < lo)
13
16
  lo = v;
14
17
  if (v > hi)
15
18
  hi = v;
16
19
  }
20
+ // Constant data (lo === hi) previously fell into the `v === hi` branch
21
+ // below, which places every value in the *last* bucket alone — misleading,
22
+ // since every other bucket reads 0 and the label implies a spread that
23
+ // isn't real. Spread it into the *first* bucket instead so a single-value
24
+ // dataset reads as "N items, one bucket populated" starting from the data's
25
+ // own value, not its synthetic +1 upper edge.
26
+ const constant = hi === lo;
17
27
  const span = hi - lo || 1;
18
- const width = span / binCount;
19
- const buckets = Array.from({ length: binCount }, (_, i) => {
28
+ const width = span / bins;
29
+ const buckets = Array.from({ length: bins }, (_, i) => {
20
30
  const bLo = lo + i * width;
21
31
  const bHi = lo + (i + 1) * width;
22
32
  return { label: `${bLo.toFixed(1)}–${bHi.toFixed(1)}`, count: 0 };
23
33
  });
24
- for (const v of values) {
25
- const index = v === hi ? binCount - 1 : Math.floor((v - lo) / width);
26
- buckets[Math.min(binCount - 1, Math.max(0, index))].count++;
34
+ for (const v of finite) {
35
+ const index = constant ? 0 : v === hi ? bins - 1 : Math.floor((v - lo) / width);
36
+ buckets[Math.min(bins - 1, Math.max(0, index))].count++;
27
37
  }
28
38
  return buckets;
29
39
  }
@@ -7,6 +7,13 @@ export interface LiteSeries {
7
7
  color?: string;
8
8
  }
9
9
  export type LyraLiteChartType = 'bar' | 'line';
10
+ /**
11
+ * `'fit'` (default) squeezes the whole plot into the measured host width,
12
+ * exactly as this component always behaved. `'scroll'` gives every bar a
13
+ * fixed `barWidth` instead and lets the plot's content width exceed the
14
+ * host's, making the host horizontally scrollable.
15
+ */
16
+ export type LyraLiteChartLayout = 'fit' | 'scroll';
10
17
  /**
11
18
  * `<lyra-lite-chart>` — a dependency-free bar/line chart, plain SVG/DOM
12
19
  * rendering with zero peer dependencies (unlike `lyra-chart`, which wraps
@@ -22,6 +29,17 @@ export type LyraLiteChartType = 'bar' | 'line';
22
29
  * `getComputedStyle()`-based re-theming step is needed the way `chart.ts`
23
30
  * needs one for its canvas.
24
31
  *
32
+ * By default (`layout="fit"`) the plot always squeezes to the measured host
33
+ * width. Three independent, opt-in escape hatches for dense/aligned data:
34
+ * `layout="scroll"` (+ `barWidth`) gives every bar a fixed pixel width and
35
+ * lets the plot overflow the host horizontally (scrollable) instead of
36
+ * squeezing; `maxLabels` decimates which x-axis text labels render (bars
37
+ * always still render) once there are more categories than that; and
38
+ * `barX` lets a consumer hand in its own per-category x-coordinate function
39
+ * — e.g. to pixel-align this chart's bars with a sibling `lyra-heatmap`'s
40
+ * calendar columns — overriding the internal slot math for both bars and
41
+ * their labels. All three are additive and no-ops when left unset.
42
+ *
25
43
  * @customElement lyra-lite-chart
26
44
  * @event lyra-point-click - Fired when a bar/point is activated (click, or
27
45
  * Enter/Space while focused). `detail: { datasetIndex: number, index:
@@ -50,22 +68,67 @@ export declare class LyraLiteChart extends LyraElement {
50
68
  beginAtZero: boolean;
51
69
  /** Stacks each category's bars into one segmented bar. Ignored for `type="line"`. */
52
70
  stacked: boolean;
71
+ /** Formats a y-axis tick value for display (e.g. `(v) => \`$${v.toFixed(2)}\``). Falls back to the
72
+ * built-in nice-number formatter when unset. */
73
+ tickFormat?: (value: number) => string;
74
+ /** `'fit'` (default) squeezes the plot into the measured host width, unchanged from before this
75
+ * property existed. `'scroll'` gives every bar a fixed `barWidth` instead, letting the plot's
76
+ * content width exceed the host's — the host becomes horizontally scrollable
77
+ * (`overflow-x: auto`) so every bar stays exactly `barWidth` wide regardless of category count.
78
+ * Reflects to the `layout` attribute (e.g. for `:host([layout='scroll'])` host styling). */
79
+ layout: LyraLiteChartLayout;
80
+ /** Fixed per-category bar width in px, used only when `layout="scroll"`. Ignored (as before this
81
+ * property existed) in `layout="fit"`, the default. */
82
+ barWidth: number;
83
+ /** Caps how many x-axis category labels render text once `this.labels.length` exceeds it,
84
+ * decimating roughly evenly while always keeping the first and last label. Bars themselves
85
+ * always render regardless — only the axis text is decimated. Unset (the default) renders every
86
+ * label, unchanged from before this property existed. Works in either `layout` mode. */
87
+ maxLabels?: number;
88
+ /** Overrides the x-origin `renderBars()`/the category labels would otherwise compute internally
89
+ * for a given category index, for `type="bar"` only (bars and their axis labels stay
90
+ * consistent with each other either way). Lets a consumer pixel-align this chart's bars with,
91
+ * e.g., a sibling `lyra-heatmap`'s calendar columns by handing both components the same
92
+ * coordinate function. Unset (the default) uses the existing internal per-category slot math,
93
+ * unchanged from before this property existed. */
94
+ barX?: (index: number) => number;
53
95
  private plotWidth;
54
96
  private plotHeight;
97
+ private visible;
55
98
  private svgEl?;
56
99
  private resizeObserver?;
100
+ private intersectionObserver?;
101
+ private lastSignature;
102
+ private lastResult?;
57
103
  connectedCallback(): void;
58
104
  disconnectedCallback(): void;
59
105
  protected firstUpdated(): void;
60
106
  protected updated(changed: PropertyValues): void;
61
107
  private colorFor;
62
108
  private domain;
109
+ private computeSignature;
63
110
  private emitPoint;
64
111
  private onPointKeyDown;
65
112
  private renderGrid;
113
+ /**
114
+ * `slot` is the per-category width to lay bars out against — either the
115
+ * measured-width-derived `plotW / n` (`layout="fit"`) or the fixed
116
+ * `barWidth` (`layout="scroll"`), computed once by the caller
117
+ * (`renderChart()`) and handed to both this method and the category-label
118
+ * x-position calc so the two can never drift apart from each other.
119
+ */
66
120
  private renderBars;
67
121
  private renderLines;
68
122
  render(): TemplateResult;
123
+ /**
124
+ * Step size for `maxLabels` decimation: 1 (show every label, today's
125
+ * unchanged default) when `maxLabels` is unset or already >= the label
126
+ * count, otherwise the ceil-divide spacing that lands close to
127
+ * `maxLabels` shown labels while always keeping index 0 and the last
128
+ * index (enforced by the caller, not here).
129
+ */
130
+ private labelStep;
131
+ private renderChart;
69
132
  }
70
133
  declare global {
71
134
  interface HTMLElementTagNameMap {