stream-charts 1.2.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 (463) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +809 -1022
  3. package/changes.md +40 -0
  4. package/dist/app/charts/Chart.d.ts +35 -139
  5. package/dist/app/charts/Chart.d.ts.map +1 -0
  6. package/dist/app/charts/Chart.js +74 -65
  7. package/dist/app/charts/Chart.js.map +1 -1
  8. package/dist/app/charts/axes/AxesState.d.ts +26 -18
  9. package/dist/app/charts/axes/AxesState.d.ts.map +1 -0
  10. package/dist/app/charts/axes/AxesState.js +26 -24
  11. package/dist/app/charts/axes/AxesState.js.map +1 -1
  12. package/dist/app/charts/axes/AxisInterval.d.ts +44 -26
  13. package/dist/app/charts/axes/AxisInterval.d.ts.map +1 -0
  14. package/dist/app/charts/axes/AxisInterval.js +48 -29
  15. package/dist/app/charts/axes/AxisInterval.js.map +1 -1
  16. package/dist/app/charts/axes/BaseAxisRange.d.ts +46 -22
  17. package/dist/app/charts/axes/BaseAxisRange.d.ts.map +1 -0
  18. package/dist/app/charts/axes/BaseAxisRange.js +17 -12
  19. package/dist/app/charts/axes/BaseAxisRange.js.map +1 -1
  20. package/dist/app/charts/axes/ContinuousAxis.d.ts +47 -6
  21. package/dist/app/charts/axes/ContinuousAxis.d.ts.map +1 -0
  22. package/dist/app/charts/axes/ContinuousAxis.js +160 -27
  23. package/dist/app/charts/axes/ContinuousAxis.js.map +1 -1
  24. package/dist/app/charts/axes/ContinuousAxisRange.d.ts +29 -10
  25. package/dist/app/charts/axes/ContinuousAxisRange.d.ts.map +1 -0
  26. package/dist/app/charts/axes/ContinuousAxisRange.js +29 -11
  27. package/dist/app/charts/axes/ContinuousAxisRange.js.map +1 -1
  28. package/dist/app/charts/axes/EmptyAxis.d.ts +5 -2
  29. package/dist/app/charts/axes/EmptyAxis.d.ts.map +1 -0
  30. package/dist/app/charts/axes/EmptyAxis.js +30 -9
  31. package/dist/app/charts/axes/EmptyAxis.js.map +1 -1
  32. package/dist/app/charts/axes/OrdinalAxis.d.ts +5 -3
  33. package/dist/app/charts/axes/OrdinalAxis.d.ts.map +1 -0
  34. package/dist/app/charts/axes/OrdinalAxis.js +39 -16
  35. package/dist/app/charts/axes/OrdinalAxis.js.map +1 -1
  36. package/dist/app/charts/axes/OrdinalAxisRange.d.ts +76 -13
  37. package/dist/app/charts/axes/OrdinalAxisRange.d.ts.map +1 -0
  38. package/dist/app/charts/axes/OrdinalAxisRange.js +95 -16
  39. package/dist/app/charts/axes/OrdinalAxisRange.js.map +1 -1
  40. package/dist/app/charts/axes/autoScale.d.ts +111 -0
  41. package/dist/app/charts/axes/autoScale.d.ts.map +1 -0
  42. package/dist/app/charts/axes/autoScale.js +188 -0
  43. package/dist/app/charts/axes/autoScale.js.map +1 -0
  44. package/dist/app/charts/axes/axes.d.ts +273 -150
  45. package/dist/app/charts/axes/axes.d.ts.map +1 -0
  46. package/dist/app/charts/axes/axes.js +808 -583
  47. package/dist/app/charts/axes/axes.js.map +1 -1
  48. package/dist/app/charts/d3types.d.ts +40 -11
  49. package/dist/app/charts/d3types.d.ts.map +1 -0
  50. package/dist/app/charts/d3types.js +3 -0
  51. package/dist/app/charts/d3types.js.map +1 -1
  52. package/dist/app/charts/datasources/StreamingDataSource.d.ts +149 -0
  53. package/dist/app/charts/datasources/StreamingDataSource.d.ts.map +1 -0
  54. package/dist/app/charts/datasources/StreamingDataSource.js +110 -0
  55. package/dist/app/charts/datasources/StreamingDataSource.js.map +1 -0
  56. package/dist/app/charts/datasources/iteratesDataSource.d.ts +73 -0
  57. package/dist/app/charts/datasources/iteratesDataSource.d.ts.map +1 -0
  58. package/dist/app/charts/datasources/iteratesDataSource.js +87 -0
  59. package/dist/app/charts/datasources/iteratesDataSource.js.map +1 -0
  60. package/dist/app/charts/datasources/ordinalDataSource.d.ts +75 -0
  61. package/dist/app/charts/datasources/ordinalDataSource.d.ts.map +1 -0
  62. package/dist/app/charts/datasources/ordinalDataSource.js +135 -0
  63. package/dist/app/charts/datasources/ordinalDataSource.js.map +1 -0
  64. package/dist/app/charts/datasources/outlierDataSource.d.ts +53 -0
  65. package/dist/app/charts/datasources/outlierDataSource.d.ts.map +1 -0
  66. package/dist/app/charts/datasources/outlierDataSource.js +57 -0
  67. package/dist/app/charts/datasources/outlierDataSource.js.map +1 -0
  68. package/dist/app/charts/datasources/timeSeriesDataSource.d.ts +77 -0
  69. package/dist/app/charts/datasources/timeSeriesDataSource.d.ts.map +1 -0
  70. package/dist/app/charts/datasources/timeSeriesDataSource.js +85 -0
  71. package/dist/app/charts/datasources/timeSeriesDataSource.js.map +1 -0
  72. package/dist/app/charts/filters/regexFilter.d.ts +31 -4
  73. package/dist/app/charts/filters/regexFilter.d.ts.map +1 -0
  74. package/dist/app/charts/filters/regexFilter.js +38 -4
  75. package/dist/app/charts/filters/regexFilter.js.map +1 -1
  76. package/dist/app/charts/hooks/AxesProvider.d.ts +21 -0
  77. package/dist/app/charts/hooks/AxesProvider.d.ts.map +1 -0
  78. package/dist/app/charts/hooks/AxesProvider.js +213 -0
  79. package/dist/app/charts/hooks/AxesProvider.js.map +1 -0
  80. package/dist/app/charts/hooks/CanvasSurfaceProvider.d.ts +25 -0
  81. package/dist/app/charts/hooks/CanvasSurfaceProvider.d.ts.map +1 -0
  82. package/dist/app/charts/hooks/CanvasSurfaceProvider.js +76 -0
  83. package/dist/app/charts/hooks/CanvasSurfaceProvider.js.map +1 -0
  84. package/dist/app/charts/hooks/ChartProvider.d.ts +40 -0
  85. package/dist/app/charts/hooks/ChartProvider.d.ts.map +1 -0
  86. package/dist/app/charts/hooks/ChartProvider.js +33 -0
  87. package/dist/app/charts/hooks/ChartProvider.js.map +1 -0
  88. package/dist/app/charts/hooks/InteractionProvider.d.ts +23 -0
  89. package/dist/app/charts/hooks/InteractionProvider.d.ts.map +1 -0
  90. package/dist/app/charts/hooks/InteractionProvider.js +60 -0
  91. package/dist/app/charts/hooks/InteractionProvider.js.map +1 -0
  92. package/dist/app/charts/hooks/PlotDimensionsProvider.d.ts +16 -0
  93. package/dist/app/charts/hooks/PlotDimensionsProvider.d.ts.map +1 -0
  94. package/dist/app/charts/hooks/PlotDimensionsProvider.js +76 -0
  95. package/dist/app/charts/hooks/PlotDimensionsProvider.js.map +1 -0
  96. package/dist/app/charts/hooks/defaultAxesValues.d.ts +11 -0
  97. package/dist/app/charts/hooks/defaultAxesValues.d.ts.map +1 -0
  98. package/dist/app/charts/hooks/defaultAxesValues.js +36 -0
  99. package/dist/app/charts/hooks/defaultAxesValues.js.map +1 -0
  100. package/dist/app/charts/hooks/defaultMouseValues.d.ts +9 -0
  101. package/dist/app/charts/hooks/defaultMouseValues.d.ts.map +1 -0
  102. package/dist/app/charts/hooks/defaultMouseValues.js +23 -0
  103. package/dist/app/charts/hooks/defaultMouseValues.js.map +1 -0
  104. package/dist/app/charts/hooks/defaultPlotDimensions.d.ts +13 -0
  105. package/dist/app/charts/hooks/defaultPlotDimensions.d.ts.map +1 -0
  106. package/dist/app/charts/hooks/defaultPlotDimensions.js +23 -0
  107. package/dist/app/charts/hooks/defaultPlotDimensions.js.map +1 -0
  108. package/dist/app/charts/hooks/defaultTooltipValues.d.ts +9 -0
  109. package/dist/app/charts/hooks/defaultTooltipValues.d.ts.map +1 -0
  110. package/dist/app/charts/hooks/defaultTooltipValues.js +21 -0
  111. package/dist/app/charts/hooks/defaultTooltipValues.js.map +1 -0
  112. package/dist/app/charts/hooks/defaultUseChartValues.d.ts +14 -0
  113. package/dist/app/charts/hooks/defaultUseChartValues.d.ts.map +1 -0
  114. package/dist/app/charts/hooks/defaultUseChartValues.js +30 -0
  115. package/dist/app/charts/hooks/defaultUseChartValues.js.map +1 -0
  116. package/dist/app/charts/hooks/useAxes.d.ts +49 -36
  117. package/dist/app/charts/hooks/useAxes.d.ts.map +1 -0
  118. package/dist/app/charts/hooks/useAxes.js +16 -176
  119. package/dist/app/charts/hooks/useAxes.js.map +1 -1
  120. package/dist/app/charts/hooks/useCanvasSurface.d.ts +25 -0
  121. package/dist/app/charts/hooks/useCanvasSurface.d.ts.map +1 -0
  122. package/dist/app/charts/hooks/useCanvasSurface.js +18 -0
  123. package/dist/app/charts/hooks/useCanvasSurface.js.map +1 -0
  124. package/dist/app/charts/hooks/useChart.d.ts +30 -44
  125. package/dist/app/charts/hooks/useChart.d.ts.map +1 -0
  126. package/dist/app/charts/hooks/useChart.js +26 -48
  127. package/dist/app/charts/hooks/useChart.js.map +1 -1
  128. package/dist/app/charts/hooks/useDataSource.d.ts +68 -0
  129. package/dist/app/charts/hooks/useDataSource.d.ts.map +1 -0
  130. package/dist/app/charts/hooks/useDataSource.js +63 -0
  131. package/dist/app/charts/hooks/useDataSource.js.map +1 -0
  132. package/dist/app/charts/hooks/useHoveredSeries.d.ts +25 -0
  133. package/dist/app/charts/hooks/useHoveredSeries.d.ts.map +1 -0
  134. package/dist/app/charts/hooks/useHoveredSeries.js +15 -0
  135. package/dist/app/charts/hooks/useHoveredSeries.js.map +1 -0
  136. package/dist/app/charts/hooks/useMouse.d.ts +46 -22
  137. package/dist/app/charts/hooks/useMouse.d.ts.map +1 -0
  138. package/dist/app/charts/hooks/useMouse.js +12 -56
  139. package/dist/app/charts/hooks/useMouse.js.map +1 -1
  140. package/dist/app/charts/hooks/usePlotDimensions.d.ts +25 -13
  141. package/dist/app/charts/hooks/usePlotDimensions.d.ts.map +1 -0
  142. package/dist/app/charts/hooks/usePlotDimensions.js +6 -62
  143. package/dist/app/charts/hooks/usePlotDimensions.js.map +1 -1
  144. package/dist/app/charts/hooks/useTooltip.d.ts +24 -26
  145. package/dist/app/charts/hooks/useTooltip.d.ts.map +1 -0
  146. package/dist/app/charts/hooks/useTooltip.js +12 -42
  147. package/dist/app/charts/hooks/useTooltip.js.map +1 -1
  148. package/dist/app/charts/index.d.ts +66 -0
  149. package/dist/app/charts/index.d.ts.map +1 -0
  150. package/dist/app/charts/index.js +87 -0
  151. package/dist/app/charts/index.js.map +1 -0
  152. package/dist/app/charts/legends/Legend.d.ts +33 -12
  153. package/dist/app/charts/legends/Legend.d.ts.map +1 -0
  154. package/dist/app/charts/legends/Legend.js +138 -379
  155. package/dist/app/charts/legends/Legend.js.map +1 -1
  156. package/dist/app/charts/legends/constants.d.ts +19 -0
  157. package/dist/app/charts/legends/constants.d.ts.map +1 -0
  158. package/dist/app/charts/legends/constants.js +34 -0
  159. package/dist/app/charts/legends/constants.js.map +1 -0
  160. package/dist/app/charts/observables/ChartData.d.ts +1 -0
  161. package/dist/app/charts/observables/ChartData.d.ts.map +1 -0
  162. package/dist/app/charts/observables/iterates.d.ts +13 -7
  163. package/dist/app/charts/observables/iterates.d.ts.map +1 -0
  164. package/dist/app/charts/observables/iterates.js +42 -9
  165. package/dist/app/charts/observables/iterates.js.map +1 -1
  166. package/dist/app/charts/observables/ordinals.d.ts +70 -12
  167. package/dist/app/charts/observables/ordinals.d.ts.map +1 -0
  168. package/dist/app/charts/observables/ordinals.js +127 -48
  169. package/dist/app/charts/observables/ordinals.js.map +1 -1
  170. package/dist/app/charts/observables/outliers.d.ts +18 -0
  171. package/dist/app/charts/observables/outliers.d.ts.map +1 -0
  172. package/dist/app/charts/observables/outliers.js +2 -0
  173. package/dist/app/charts/observables/outliers.js.map +1 -0
  174. package/dist/app/charts/plots/BarPlot.d.ts +87 -13
  175. package/dist/app/charts/plots/BarPlot.d.ts.map +1 -0
  176. package/dist/app/charts/plots/BarPlot.js +603 -356
  177. package/dist/app/charts/plots/BarPlot.js.map +1 -1
  178. package/dist/app/charts/plots/OutlierPlot.d.ts +123 -0
  179. package/dist/app/charts/plots/OutlierPlot.d.ts.map +1 -0
  180. package/dist/app/charts/plots/OutlierPlot.js +430 -0
  181. package/dist/app/charts/plots/OutlierPlot.js.map +1 -0
  182. package/dist/app/charts/plots/PoincarePlot.d.ts +92 -14
  183. package/dist/app/charts/plots/PoincarePlot.d.ts.map +1 -0
  184. package/dist/app/charts/plots/PoincarePlot.js +523 -364
  185. package/dist/app/charts/plots/PoincarePlot.js.map +1 -1
  186. package/dist/app/charts/plots/RasterPlot.d.ts +31 -10
  187. package/dist/app/charts/plots/RasterPlot.d.ts.map +1 -0
  188. package/dist/app/charts/plots/RasterPlot.js +198 -306
  189. package/dist/app/charts/plots/RasterPlot.js.map +1 -1
  190. package/dist/app/charts/plots/ScatterPlot.d.ts +77 -21
  191. package/dist/app/charts/plots/ScatterPlot.d.ts.map +1 -0
  192. package/dist/app/charts/plots/ScatterPlot.js +359 -302
  193. package/dist/app/charts/plots/ScatterPlot.js.map +1 -1
  194. package/dist/app/charts/plots/autoScaleYAxes.d.ts +43 -0
  195. package/dist/app/charts/plots/autoScaleYAxes.d.ts.map +1 -0
  196. package/dist/app/charts/plots/autoScaleYAxes.js +71 -0
  197. package/dist/app/charts/plots/autoScaleYAxes.js.map +1 -0
  198. package/dist/app/charts/plots/colorizedLine.d.ts +61 -0
  199. package/dist/app/charts/plots/colorizedLine.d.ts.map +1 -0
  200. package/dist/app/charts/plots/colorizedLine.js +157 -0
  201. package/dist/app/charts/plots/colorizedLine.js.map +1 -0
  202. package/dist/app/charts/plots/constants.d.ts +7 -0
  203. package/dist/app/charts/plots/constants.d.ts.map +1 -0
  204. package/dist/app/charts/plots/constants.js +11 -0
  205. package/dist/app/charts/plots/constants.js.map +1 -0
  206. package/dist/app/charts/plots/hitTesting.d.ts +88 -0
  207. package/dist/app/charts/plots/hitTesting.d.ts.map +1 -0
  208. package/dist/app/charts/plots/hitTesting.js +136 -0
  209. package/dist/app/charts/plots/hitTesting.js.map +1 -0
  210. package/dist/app/charts/plots/plot.d.ts +52 -16
  211. package/dist/app/charts/plots/plot.d.ts.map +1 -0
  212. package/dist/app/charts/plots/plot.js +108 -35
  213. package/dist/app/charts/plots/plot.js.map +1 -1
  214. package/dist/app/charts/plots/useCanvasZoomPan.d.ts +119 -0
  215. package/dist/app/charts/plots/useCanvasZoomPan.d.ts.map +1 -0
  216. package/dist/app/charts/plots/useCanvasZoomPan.js +113 -0
  217. package/dist/app/charts/plots/useCanvasZoomPan.js.map +1 -0
  218. package/dist/app/charts/plots/useStreamingTimeAxes.d.ts +103 -0
  219. package/dist/app/charts/plots/useStreamingTimeAxes.d.ts.map +1 -0
  220. package/dist/app/charts/plots/useStreamingTimeAxes.js +257 -0
  221. package/dist/app/charts/plots/useStreamingTimeAxes.js.map +1 -0
  222. package/dist/app/charts/series/baseSeries.d.ts +60 -8
  223. package/dist/app/charts/series/baseSeries.d.ts.map +1 -0
  224. package/dist/app/charts/series/baseSeries.js +53 -8
  225. package/dist/app/charts/series/baseSeries.js.map +1 -1
  226. package/dist/app/charts/series/iterateSeries.d.ts +9 -8
  227. package/dist/app/charts/series/iterateSeries.d.ts.map +1 -0
  228. package/dist/app/charts/series/iterateSeries.js +7 -7
  229. package/dist/app/charts/series/iterateSeries.js.map +1 -1
  230. package/dist/app/charts/series/ordinalSeries.d.ts +27 -13
  231. package/dist/app/charts/series/ordinalSeries.d.ts.map +1 -0
  232. package/dist/app/charts/series/ordinalSeries.js +29 -15
  233. package/dist/app/charts/series/ordinalSeries.js.map +1 -1
  234. package/dist/app/charts/series/outlierSeries.d.ts +142 -0
  235. package/dist/app/charts/series/outlierSeries.d.ts.map +1 -0
  236. package/dist/app/charts/series/outlierSeries.js +82 -0
  237. package/dist/app/charts/series/outlierSeries.js.map +1 -0
  238. package/dist/app/charts/series/timeSeries.d.ts +13 -9
  239. package/dist/app/charts/series/timeSeries.d.ts.map +1 -0
  240. package/dist/app/charts/series/timeSeries.js +6 -3
  241. package/dist/app/charts/series/timeSeries.js.map +1 -1
  242. package/dist/app/charts/series/timeSeriesChartData.d.ts +18 -10
  243. package/dist/app/charts/series/timeSeriesChartData.d.ts.map +1 -0
  244. package/dist/app/charts/series/timeSeriesChartData.js +28 -14
  245. package/dist/app/charts/series/timeSeriesChartData.js.map +1 -1
  246. package/dist/app/charts/styling/barPlotStyle.d.ts +46 -2
  247. package/dist/app/charts/styling/barPlotStyle.d.ts.map +1 -0
  248. package/dist/app/charts/styling/barPlotStyle.js +37 -3
  249. package/dist/app/charts/styling/barPlotStyle.js.map +1 -1
  250. package/dist/app/charts/styling/canvasStyle.d.ts +53 -0
  251. package/dist/app/charts/styling/canvasStyle.d.ts.map +1 -0
  252. package/dist/app/charts/styling/canvasStyle.js +71 -0
  253. package/dist/app/charts/styling/canvasStyle.js.map +1 -0
  254. package/dist/app/charts/styling/margins.d.ts +21 -2
  255. package/dist/app/charts/styling/margins.d.ts.map +1 -0
  256. package/dist/app/charts/styling/margins.js +25 -4
  257. package/dist/app/charts/styling/margins.js.map +1 -1
  258. package/dist/app/charts/styling/svgStyle.d.ts +10 -56
  259. package/dist/app/charts/styling/svgStyle.d.ts.map +1 -0
  260. package/dist/app/charts/styling/svgStyle.js +3 -78
  261. package/dist/app/charts/styling/svgStyle.js.map +1 -1
  262. package/dist/app/charts/subscriptions/viewDrivers.d.ts +124 -0
  263. package/dist/app/charts/subscriptions/viewDrivers.d.ts.map +1 -0
  264. package/dist/app/charts/subscriptions/viewDrivers.js +338 -0
  265. package/dist/app/charts/subscriptions/viewDrivers.js.map +1 -0
  266. package/dist/app/charts/tooltips/BarPlotTooltipContent.d.ts +12 -78
  267. package/dist/app/charts/tooltips/BarPlotTooltipContent.d.ts.map +1 -0
  268. package/dist/app/charts/tooltips/BarPlotTooltipContent.js +65 -257
  269. package/dist/app/charts/tooltips/BarPlotTooltipContent.js.map +1 -1
  270. package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.d.ts +9 -0
  271. package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.d.ts.map +1 -0
  272. package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.js +16 -0
  273. package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.js.map +1 -0
  274. package/dist/app/charts/tooltips/OutlierPlotTooltipContent.d.ts +30 -0
  275. package/dist/app/charts/tooltips/OutlierPlotTooltipContent.d.ts.map +1 -0
  276. package/dist/app/charts/tooltips/OutlierPlotTooltipContent.js +26 -0
  277. package/dist/app/charts/tooltips/OutlierPlotTooltipContent.js.map +1 -0
  278. package/dist/app/charts/tooltips/PoincarePlotTooltipContent.d.ts +22 -72
  279. package/dist/app/charts/tooltips/PoincarePlotTooltipContent.d.ts.map +1 -0
  280. package/dist/app/charts/tooltips/PoincarePlotTooltipContent.js +104 -171
  281. package/dist/app/charts/tooltips/PoincarePlotTooltipContent.js.map +1 -1
  282. package/dist/app/charts/tooltips/RasterPlotTooltipContent.d.ts +32 -8
  283. package/dist/app/charts/tooltips/RasterPlotTooltipContent.d.ts.map +1 -0
  284. package/dist/app/charts/tooltips/RasterPlotTooltipContent.js +62 -115
  285. package/dist/app/charts/tooltips/RasterPlotTooltipContent.js.map +1 -1
  286. package/dist/app/charts/tooltips/ScatterPlotTooltipContent.d.ts +39 -12
  287. package/dist/app/charts/tooltips/ScatterPlotTooltipContent.d.ts.map +1 -0
  288. package/dist/app/charts/tooltips/ScatterPlotTooltipContent.js +162 -134
  289. package/dist/app/charts/tooltips/ScatterPlotTooltipContent.js.map +1 -1
  290. package/dist/app/charts/tooltips/Tooltip.d.ts +27 -32
  291. package/dist/app/charts/tooltips/Tooltip.d.ts.map +1 -0
  292. package/dist/app/charts/tooltips/Tooltip.js +37 -81
  293. package/dist/app/charts/tooltips/Tooltip.js.map +1 -1
  294. package/dist/app/charts/tooltips/TooltipContent.d.ts +81 -0
  295. package/dist/app/charts/tooltips/TooltipContent.d.ts.map +1 -0
  296. package/dist/app/charts/tooltips/TooltipContent.js +99 -0
  297. package/dist/app/charts/tooltips/TooltipContent.js.map +1 -0
  298. package/dist/app/charts/tooltips/tooltipUtils.d.ts +186 -36
  299. package/dist/app/charts/tooltips/tooltipUtils.d.ts.map +1 -0
  300. package/dist/app/charts/tooltips/tooltipUtils.js +277 -48
  301. package/dist/app/charts/tooltips/tooltipUtils.js.map +1 -1
  302. package/dist/app/charts/tooltips/useOutlierTooltip.d.ts +59 -0
  303. package/dist/app/charts/tooltips/useOutlierTooltip.d.ts.map +1 -0
  304. package/dist/app/charts/tooltips/useOutlierTooltip.js +16 -0
  305. package/dist/app/charts/tooltips/useOutlierTooltip.js.map +1 -0
  306. package/dist/app/charts/tooltips/useTooltipPlacement.d.ts +51 -0
  307. package/dist/app/charts/tooltips/useTooltipPlacement.d.ts.map +1 -0
  308. package/dist/app/charts/tooltips/useTooltipPlacement.js +52 -0
  309. package/dist/app/charts/tooltips/useTooltipPlacement.js.map +1 -0
  310. package/dist/app/charts/tooltips/useTooltipStyle.d.ts +11 -0
  311. package/dist/app/charts/tooltips/useTooltipStyle.d.ts.map +1 -0
  312. package/dist/app/charts/tooltips/useTooltipStyle.js +14 -0
  313. package/dist/app/charts/tooltips/useTooltipStyle.js.map +1 -0
  314. package/dist/app/charts/trackers/Tracker.d.ts +22 -6
  315. package/dist/app/charts/trackers/Tracker.d.ts.map +1 -0
  316. package/dist/app/charts/trackers/Tracker.js +36 -22
  317. package/dist/app/charts/trackers/Tracker.js.map +1 -1
  318. package/dist/app/charts/trackers/trackerUtils.d.ts +40 -18
  319. package/dist/app/charts/trackers/trackerUtils.d.ts.map +1 -0
  320. package/dist/app/charts/trackers/trackerUtils.js +213 -300
  321. package/dist/app/charts/trackers/trackerUtils.js.map +1 -1
  322. package/dist/app/charts/utils.d.ts +72 -33
  323. package/dist/app/charts/utils.d.ts.map +1 -0
  324. package/dist/app/charts/utils.js +87 -42
  325. package/dist/app/charts/utils.js.map +1 -1
  326. package/dist/index.d.ts +2 -45
  327. package/dist/index.d.ts.map +1 -0
  328. package/dist/index.js +2 -72
  329. package/dist/index.js.map +1 -1
  330. package/package.json +87 -46
  331. package/src/app/charts/Chart.tsx +132 -168
  332. package/src/app/charts/axes/AxesState.ts +27 -29
  333. package/src/app/charts/axes/AxisInterval.ts +57 -32
  334. package/src/app/charts/axes/BaseAxisRange.ts +47 -27
  335. package/src/app/charts/axes/ContinuousAxis.tsx +235 -39
  336. package/src/app/charts/axes/ContinuousAxisRange.ts +29 -11
  337. package/src/app/charts/axes/EmptyAxis.tsx +52 -17
  338. package/src/app/charts/axes/OrdinalAxis.tsx +54 -28
  339. package/src/app/charts/axes/OrdinalAxisRange.ts +96 -16
  340. package/src/app/charts/axes/autoScale.ts +244 -0
  341. package/src/app/charts/axes/axes.ts +954 -735
  342. package/src/app/charts/d3types.ts +42 -13
  343. package/src/app/charts/datasources/StreamingDataSource.ts +214 -0
  344. package/src/app/charts/datasources/iteratesDataSource.ts +122 -0
  345. package/src/app/charts/datasources/ordinalDataSource.ts +205 -0
  346. package/src/app/charts/datasources/outlierDataSource.ts +90 -0
  347. package/src/app/charts/datasources/timeSeriesDataSource.ts +133 -0
  348. package/src/app/charts/filters/regexFilter.ts +45 -5
  349. package/src/app/charts/hooks/AxesProvider.tsx +301 -0
  350. package/src/app/charts/hooks/CanvasSurfaceProvider.tsx +117 -0
  351. package/src/app/charts/hooks/ChartProvider.tsx +79 -0
  352. package/src/app/charts/hooks/InteractionProvider.tsx +88 -0
  353. package/src/app/charts/hooks/PlotDimensionsProvider.tsx +109 -0
  354. package/src/app/charts/hooks/defaultAxesValues.ts +40 -0
  355. package/src/app/charts/hooks/defaultMouseValues.ts +24 -0
  356. package/src/app/charts/hooks/defaultPlotDimensions.ts +27 -0
  357. package/src/app/charts/hooks/defaultTooltipValues.tsx +23 -0
  358. package/src/app/charts/hooks/defaultUseChartValues.tsx +36 -0
  359. package/src/app/charts/hooks/useAxes.tsx +60 -228
  360. package/src/app/charts/hooks/useCanvasSurface.tsx +35 -0
  361. package/src/app/charts/hooks/useChart.tsx +47 -106
  362. package/src/app/charts/hooks/useDataSource.tsx +106 -0
  363. package/src/app/charts/hooks/useHoveredSeries.tsx +33 -0
  364. package/src/app/charts/hooks/useMouse.tsx +58 -82
  365. package/src/app/charts/hooks/usePlotDimensions.tsx +25 -89
  366. package/src/app/charts/hooks/useTooltip.tsx +32 -63
  367. package/src/app/charts/index.ts +213 -0
  368. package/src/app/charts/legends/Legend.tsx +198 -479
  369. package/src/app/charts/legends/constants.ts +37 -0
  370. package/src/app/charts/observables/iterates.ts +32 -12
  371. package/src/app/charts/observables/ordinals.ts +141 -67
  372. package/src/app/charts/observables/outliers.ts +20 -0
  373. package/src/app/charts/plots/BarPlot.tsx +784 -666
  374. package/src/app/charts/plots/OutlierPlot.tsx +685 -0
  375. package/src/app/charts/plots/PoincarePlot.tsx +707 -489
  376. package/src/app/charts/plots/RasterPlot.tsx +253 -445
  377. package/src/app/charts/plots/ScatterPlot.tsx +480 -463
  378. package/src/app/charts/plots/autoScaleYAxes.ts +102 -0
  379. package/src/app/charts/plots/colorizedLine.ts +185 -0
  380. package/src/app/charts/plots/constants.ts +16 -0
  381. package/src/app/charts/plots/hitTesting.ts +198 -0
  382. package/src/app/charts/plots/plot.ts +138 -44
  383. package/src/app/charts/plots/useCanvasZoomPan.ts +240 -0
  384. package/src/app/charts/plots/useStreamingTimeAxes.ts +384 -0
  385. package/src/app/charts/series/baseSeries.ts +81 -14
  386. package/src/app/charts/series/iterateSeries.ts +8 -8
  387. package/src/app/charts/series/ordinalSeries.ts +35 -20
  388. package/src/app/charts/series/outlierSeries.ts +188 -0
  389. package/src/app/charts/series/timeSeries.ts +14 -12
  390. package/src/app/charts/series/timeSeriesChartData.ts +31 -17
  391. package/src/app/charts/styling/barPlotStyle.ts +48 -5
  392. package/src/app/charts/styling/canvasStyle.ts +86 -0
  393. package/src/app/charts/styling/margins.ts +28 -4
  394. package/src/app/charts/styling/svgStyle.ts +10 -91
  395. package/src/app/charts/subscriptions/viewDrivers.ts +447 -0
  396. package/src/app/charts/tooltips/BarPlotTooltipContent.tsx +156 -380
  397. package/src/app/charts/tooltips/DefaultOutlierTooltipContent.tsx +40 -0
  398. package/src/app/charts/tooltips/OutlierPlotTooltipContent.tsx +67 -0
  399. package/src/app/charts/tooltips/PoincarePlotTooltipContent.tsx +229 -292
  400. package/src/app/charts/tooltips/RasterPlotTooltipContent.tsx +164 -217
  401. package/src/app/charts/tooltips/ScatterPlotTooltipContent.tsx +373 -236
  402. package/src/app/charts/tooltips/Tooltip.tsx +58 -124
  403. package/src/app/charts/tooltips/TooltipContent.tsx +177 -0
  404. package/src/app/charts/tooltips/tooltipUtils.ts +359 -61
  405. package/src/app/charts/tooltips/useOutlierTooltip.tsx +68 -0
  406. package/src/app/charts/tooltips/useTooltipPlacement.ts +86 -0
  407. package/src/app/charts/tooltips/useTooltipStyle.ts +15 -0
  408. package/src/app/charts/trackers/Tracker.tsx +68 -33
  409. package/src/app/charts/trackers/trackerUtils.ts +263 -376
  410. package/src/app/charts/utils.ts +90 -48
  411. package/src/index.ts +2 -322
  412. package/dist/app/charts/axes/AxesState.test.d.ts +0 -1
  413. package/dist/app/charts/axes/AxesState.test.js +0 -30
  414. package/dist/app/charts/axes/AxesState.test.js.map +0 -1
  415. package/dist/app/charts/axes/AxisInterval.test.d.ts +0 -1
  416. package/dist/app/charts/axes/AxisInterval.test.js +0 -120
  417. package/dist/app/charts/axes/AxisInterval.test.js.map +0 -1
  418. package/dist/app/charts/axes/ContinuousAxisRange.test.d.ts +0 -1
  419. package/dist/app/charts/axes/ContinuousAxisRange.test.js +0 -47
  420. package/dist/app/charts/axes/ContinuousAxisRange.test.js.map +0 -1
  421. package/dist/app/charts/axes/OrdinalAxisRange.test.d.ts +0 -1
  422. package/dist/app/charts/axes/OrdinalAxisRange.test.js +0 -48
  423. package/dist/app/charts/axes/OrdinalAxisRange.test.js.map +0 -1
  424. package/dist/app/charts/filters/regexFilter.test.d.ts +0 -1
  425. package/dist/app/charts/filters/regexFilter.test.js +0 -11
  426. package/dist/app/charts/filters/regexFilter.test.js.map +0 -1
  427. package/dist/app/charts/hooks/useDataObservable.d.ts +0 -72
  428. package/dist/app/charts/hooks/useDataObservable.js +0 -74
  429. package/dist/app/charts/hooks/useDataObservable.js.map +0 -1
  430. package/dist/app/charts/hooks/useInitialData.d.ts +0 -36
  431. package/dist/app/charts/hooks/useInitialData.js +0 -28
  432. package/dist/app/charts/hooks/useInitialData.js.map +0 -1
  433. package/dist/app/charts/observables/iterates.test.d.ts +0 -1
  434. package/dist/app/charts/observables/iterates.test.js +0 -180
  435. package/dist/app/charts/observables/iterates.test.js.map +0 -1
  436. package/dist/app/charts/observables/ordinals.test.d.ts +0 -1
  437. package/dist/app/charts/observables/ordinals.test.js +0 -102
  438. package/dist/app/charts/observables/ordinals.test.js.map +0 -1
  439. package/dist/app/charts/series/datumSeries.test.d.ts +0 -1
  440. package/dist/app/charts/series/datumSeries.test.js +0 -50
  441. package/dist/app/charts/series/datumSeries.test.js.map +0 -1
  442. package/dist/app/charts/styling/margins.test.d.ts +0 -1
  443. package/dist/app/charts/styling/margins.test.js +0 -19
  444. package/dist/app/charts/styling/margins.test.js.map +0 -1
  445. package/dist/app/charts/subscriptions/subscription.test.d.ts +0 -1
  446. package/dist/app/charts/subscriptions/subscription.test.js +0 -395
  447. package/dist/app/charts/subscriptions/subscription.test.js.map +0 -1
  448. package/dist/app/charts/subscriptions/subscriptions.d.ts +0 -100
  449. package/dist/app/charts/subscriptions/subscriptions.js +0 -449
  450. package/dist/app/charts/subscriptions/subscriptions.js.map +0 -1
  451. package/src/app/charts/axes/AxesState.test.ts +0 -34
  452. package/src/app/charts/axes/AxisInterval.test.ts +0 -143
  453. package/src/app/charts/axes/ContinuousAxisRange.test.ts +0 -55
  454. package/src/app/charts/axes/OrdinalAxisRange.test.ts +0 -55
  455. package/src/app/charts/filters/regexFilter.test.ts +0 -13
  456. package/src/app/charts/hooks/useDataObservable.tsx +0 -170
  457. package/src/app/charts/hooks/useInitialData.tsx +0 -64
  458. package/src/app/charts/observables/iterates.test.ts +0 -201
  459. package/src/app/charts/observables/ordinals.test.ts +0 -135
  460. package/src/app/charts/series/datumSeries.test.ts +0 -60
  461. package/src/app/charts/styling/margins.test.ts +0 -22
  462. package/src/app/charts/subscriptions/subscription.test.ts +0 -499
  463. package/src/app/charts/subscriptions/subscriptions.ts +0 -635
@@ -1,21 +1,17 @@
1
- import {BaseAxis, defaultLineStyle, SeriesLineStyle, SeriesStyle} from "../axes/axes";
1
+ import {type BaseAxis, type SeriesStyle} from "../axes/axes";
2
+ import {useHoveredSeries} from "../hooks/useHoveredSeries";
3
+ import {useInitialData} from "../hooks/useDataSource";
2
4
  import {BaseAxisRange} from "../axes/BaseAxisRange";
3
5
  import {usePlotDimensions} from "../hooks/usePlotDimensions";
4
6
  import {useChart} from "../hooks/useChart";
5
- import {useInitialData} from "../hooks/useInitialData";
6
- import React, {useCallback, useEffect, useMemo, useRef, useState} from "react";
7
+ import React, {useCallback, useEffect, useMemo, useRef} from "react";
7
8
  import {createPortal} from "react-dom";
8
9
  import * as d3 from "d3";
10
+ import {defaultLegendStyle, LegendLocation} from "./constants";
9
11
 
10
- export enum LegendLocation {
11
- // noinspection JSUnusedGlobalSymbols
12
- TOP_LEFT = "top-left",
13
- TOP_RIGHT = "top-right",
14
- BOTTOM_LEFT = "bottom-left",
15
- BOTTOM_RIGHT = "bottom-right",
16
- EXTERNAL_CONTAINER = "external-container"
17
- }
18
-
12
+ /**
13
+ * The style of the legend's box and entries
14
+ */
19
15
  export interface LegendStyle {
20
16
  /** Font size for the legend labels */
21
17
  fontSize: number
@@ -45,30 +41,15 @@ export interface LegendStyle {
45
41
  swatchHeight: number
46
42
  /** Gap between the swatch and the label text */
47
43
  swatchLabelGap: number
48
- /** Maximum height of the legend before it starts scrolling (in pixels). If not provided, defaults to the plot height. */
44
+ /** Maximum height of the legend before it starts scrolling (in pixels). If not provided, defaults to a little less than the plot height. */
49
45
  maxHeight?: number
50
46
  /** Duration of the visibility transition in milliseconds */
51
47
  transitionDuration: number
52
48
  }
53
49
 
54
- export const defaultLegendStyle: LegendStyle = {
55
- fontSize: 12,
56
- fontFamily: "sans-serif",
57
- fontColor: "#d2933f",
58
- backgroundColor: "#202020",
59
- backgroundOpacity: 0.85,
60
- borderColor: "#d2933f",
61
- borderWidth: 1,
62
- borderOpacity: 0.7,
63
- borderRadius: 4,
64
- padding: 8,
65
- rowGap: 6,
66
- swatchWidth: 16,
67
- swatchHeight: 3,
68
- swatchLabelGap: 6,
69
- transitionDuration: 350,
70
- }
71
-
50
+ /**
51
+ * The properties of the {@link Legend}
52
+ */
72
53
  export interface Props {
73
54
  /** Whether the legend is visible */
74
55
  visible: boolean
@@ -88,20 +69,39 @@ export interface Props {
88
69
  offset?: { x: number; y: number }
89
70
  /**
90
71
  * When provided, the legend renders as an HTML element portal into this
91
- * external container instead of inside the chart SVG. Position the container
92
- * however you like — the legend fills it.
72
+ * external container instead of as an overlay on the chart canvas. Position the
73
+ * container however you like — the legend fills it.
93
74
  */
94
75
  container?: React.RefObject<HTMLElement | null>
95
76
  }
96
77
 
97
- const LEGEND_CONTAINER_ID_PREFIX = "stream-charts-legend"
98
-
99
78
  /**
100
79
  * A legend component that can be placed inside any `<Chart>` alongside any Plot.
101
80
  * It automatically reads the series names from the initial data and their colors
102
81
  * from the `seriesStyles` map (falling back to the chart's base `color`).
103
82
  * The legend respects the active `seriesFilter`, showing only the matching series.
104
83
  *
84
+ * Renders as an HTML overlay (a React portal) rather than as SVG elements -- either into the
85
+ * `container` prop, when supplied, or (the default) into the chart's own canvas overlay wrapper
86
+ * (the `position: relative` `<div>` that {@link Chart} wraps the `<canvas>` in), absolutely
87
+ * positioned against the plot area using `location`/`offset`. This replaces the old version's two
88
+ * separate rendering paths (an SVG-internal legend with a hand-rolled scrollbar/clip-path/wheel
89
+ * handler, and an HTML-portal legend for the external-container case) with one: the SVG path's
90
+ * custom scrolling machinery existed only because SVG has no native scrolling or text
91
+ * auto-sizing -- an HTML `<div>` with `overflowY: 'auto'` gets both for free, which is exactly
92
+ * what the already-existing external-container path relied on.
93
+ *
94
+ * Hovering over an entry highlights its series (through the chart's shared hovered-series state),
95
+ * and hovering over a series in the plot highlights its entry.
96
+ * @param props The properties of the legend
97
+ * @return The legend (rendered into a portal), or `null` when it isn't visible, there are no
98
+ * series to show, or there's nowhere (yet) to render it into
99
+ * @template D The type of the series' datum
100
+ * @template S The type of the series style
101
+ * @template TM The type of the tooltip metadata
102
+ * @template AR The type of the axis range
103
+ * @template A The type of the axis
104
+ *
105
105
  * @example
106
106
  * ```tsx
107
107
  * <Chart ...>
@@ -123,20 +123,15 @@ export function Legend<D, S extends SeriesStyle, TM, AR extends BaseAxisRange, A
123
123
  container: externalContainer
124
124
  } = props
125
125
 
126
- const {chartId, container, color, seriesStyles, seriesFilter, mouse, hoveredSeriesRef} = useChart<D, S, TM, AR, A>()
126
+ const {
127
+ canvas,
128
+ color,
129
+ seriesStyles,
130
+ seriesFilter,
131
+ } = useChart<D, S, TM, AR, A>()
132
+ const {hoveredSeriesName, setHoveredSeriesName} = useHoveredSeries()
127
133
  const {margin, plotDimensions} = usePlotDimensions()
128
- const {initialData} = useInitialData<any, D>()
129
-
130
- // the mouse over series name interferes with scrolling, so we keep track of the
131
- // scrolling state so the series-name mouse-over can be disabled during scrolling
132
- const wheelTimeoutRef = useRef<NodeJS.Timeout>(undefined)
133
- const isWheelingRef = useRef(false)
134
-
135
- const scrollYRef = useRef<number>(0)
136
-
137
- // keep track of whether the mouse is in the legend so that we can restore the legend
138
- // opacity when the mouse leaves and is not hovering over a series
139
- const [mouseInLegend, setMouseInLegend] = useState<boolean>(false)
134
+ const {initialData} = useInitialData<D>()
140
135
 
141
136
  const legendStyle = useMemo<LegendStyle>(
142
137
  () => ({
@@ -147,53 +142,20 @@ export function Legend<D, S extends SeriesStyle, TM, AR extends BaseAxisRange, A
147
142
  [style, plotDimensions.height]
148
143
  )
149
144
 
150
- // Refs don't trigger re-renders when populated, so track readiness in state
151
- const [externalContainerReady, setExternalContainerReady] = useState(false)
152
- useEffect(() => {
153
- setExternalContainerReady(!!externalContainer?.current)
154
- }, [externalContainer])
155
-
156
- // Track the currently hovered series name so legend entries can be highlighted
157
- const [hoveredSeriesName, setHoveredSeriesName] = useState<string | null>(null)
158
- useEffect(() => {
159
- const handlerId = `legend-${chartId}`
160
- mouse.registerMouseOverHandler(handlerId, seriesName => setHoveredSeriesName(seriesName))
161
- mouse.registerMouseLeaveHandler(handlerId, () => setHoveredSeriesName(null))
162
- return () => {
163
- mouse.unregisterMouseOverHandler(handlerId)
164
- mouse.unregisterMouseLeaveHandler(handlerId)
165
- }
166
- }, [chartId, mouse])
167
-
168
- // Keep a ref so D3 closures in the SVG legend always read current styles
169
- const seriesStylesRef = useRef<Map<string, S>>(seriesStyles)
170
- seriesStylesRef.current = seriesStyles
171
-
172
- const highlightSeriesInPlot = useCallback<(name: string) => void>(name => {
173
- hoveredSeriesRef.current = name
174
- if (!container) return
175
- const {
176
- highlightColor,
177
- highlightWidth
178
- } = (seriesStylesRef.current.get(name) as SeriesLineStyle | undefined) || defaultLineStyle()
179
- d3.select(container)
180
- .selectAll<SVGPathElement, unknown>(`path[data-series-name="${name}"], line[data-series-name="${name}"]`)
181
- .attr('stroke', highlightColor)
182
- .attr('stroke-width', highlightWidth)
183
- }, [container, hoveredSeriesRef])
145
+ const highlightSeriesInPlot = useCallback<(name: string) => void>(
146
+ name => {
147
+ setHoveredSeriesName(name)
148
+ if (!canvas) return
149
+ },
150
+ [canvas, setHoveredSeriesName]
151
+ )
184
152
 
185
- const restoreSeriesInPlot = useCallback<(name: string) => void>(name => {
186
- hoveredSeriesRef.current = null
187
- if (!container) return
188
- const {
189
- color,
190
- lineWidth
191
- } = (seriesStylesRef.current.get(name) as SeriesLineStyle | undefined) || defaultLineStyle()
192
- d3.select(container)
193
- .selectAll<SVGPathElement, unknown>(`path[data-series-name="${name}"], line[data-series-name="${name}"]`)
194
- .attr('stroke', color)
195
- .attr('stroke-width', lineWidth)
196
- }, [container, hoveredSeriesRef])
153
+ const restoreSeriesInPlot = useCallback<(name: string) => void>(
154
+ () => {
155
+ if (!canvas) return
156
+ },
157
+ [canvas]
158
+ )
197
159
 
198
160
  // Derive the filtered list of series names
199
161
  const visibleSeriesNames = useMemo<Array<string>>(
@@ -201,400 +163,157 @@ export function Legend<D, S extends SeriesStyle, TM, AR extends BaseAxisRange, A
201
163
  [initialData, seriesFilter]
202
164
  )
203
165
 
166
+ // A `container` prop that never resolves to a mounted element is a caller-attributable
167
+ // misconfiguration (unlike a not-yet-attached `canvas`, which is normal/transient on first
168
+ // mount) -- surface it once, rather than silently rendering nothing forever with no clue why.
169
+ // Gated on `visible && visibleSeriesNames.length > 0` -- i.e. only when we'd actually attempt
170
+ // to render -- since a container legitimately un-mounted alongside `visible: false` (a common
171
+ // pattern: hide the legend and its container together) is not a misconfiguration and shouldn't
172
+ // warn. Depending on `canvas` too (not just `externalContainer`, whose identity is otherwise
173
+ // stable across renders) gives this a few extra chances to reassess as the chart's own canvas
174
+ // surface settles, before concluding the ref is genuinely never going to attach.
175
+ const hasWarnedMissingContainerRef = useRef(false)
204
176
  useEffect(
205
177
  () => {
206
- if (!container) return
207
-
208
- const legendId = `${LEGEND_CONTAINER_ID_PREFIX}-${chartId}`
209
- const svg = d3.select<SVGSVGElement, unknown>(container)
210
- const {transitionDuration} = legendStyle
211
-
212
- if (externalContainer) {
213
- svg.select(`#${legendId}`).remove()
214
- return
178
+ if (
179
+ visible &&
180
+ visibleSeriesNames.length > 0 &&
181
+ externalContainer &&
182
+ externalContainer.current === null &&
183
+ !hasWarnedMissingContainerRef.current
184
+ ) {
185
+ console.warn(
186
+ 'Legend: the "container" prop was provided, but its .current is null, so the legend has ' +
187
+ 'nothing to render into and will render nothing. Make sure the ref is attached to an ' +
188
+ 'element that actually mounts.'
189
+ )
190
+ hasWarnedMissingContainerRef.current = true
215
191
  }
216
-
217
- if (!visible || visibleSeriesNames.length === 0) {
218
- svg.select(`#${legendId}`).remove()
219
- return
220
- }
221
-
222
- // Remove any existing legend (before we potentially redraw) or just reuse?
223
- // Usually, redrawing is safer if we want to ensure everything is in the right place.
224
- // But if we're transitioning from visible: false to true, the old one might be gone.
225
- // Let's just remove without transition for redrawing, except when explicitly making it invisible.
226
- svg.select(`#${legendId}`).remove()
227
-
228
- const {
229
- fontSize,
230
- fontFamily,
231
- fontColor,
232
- backgroundColor,
233
- backgroundOpacity,
234
- borderColor,
235
- borderWidth,
236
- borderOpacity,
237
- borderRadius,
238
- padding,
239
- rowGap,
240
- swatchWidth,
241
- swatchHeight,
242
- swatchLabelGap,
243
- maxHeight,
244
- } = legendStyle
245
-
246
- const rowHeight = Math.max(swatchHeight, fontSize)
247
- const totalRows = visibleSeriesNames.length
248
- const contentHeight = totalRows * rowHeight + (totalRows - 1) * rowGap
249
- const totalContentHeight = contentHeight + 2 * padding
250
- const boxHeight = maxHeight !== undefined ? Math.min(totalContentHeight, maxHeight) : totalContentHeight
251
-
252
- // Use a temporary hidden SVG text element to measure max label width
253
- const tempText = svg
254
- .append<SVGTextElement>("text")
255
- .style("font-size", `${fontSize}px`)
256
- .style("font-family", fontFamily)
257
- .style("visibility", "hidden")
258
-
259
- let maxLabelWidth = 0
260
- visibleSeriesNames.forEach(name => {
261
- tempText.text(name)
262
- const w = tempText.node()?.getBBox().width ?? name.length * (fontSize * 0.6)
263
- if (w > maxLabelWidth) maxLabelWidth = w
264
- })
265
- tempText.remove()
266
-
267
- const boxWidth = padding + swatchWidth + swatchLabelGap + maxLabelWidth + padding
268
-
269
- // Determine the (x, y) position of the legend box within the SVG (plot area coordinates)
270
- const plotLeft = margin.left
271
- const plotTop = margin.top
272
- const plotRight = margin.left + plotDimensions.width
273
- const plotBottom = margin.top + plotDimensions.height
274
-
275
- let boxX: number
276
- let boxY: number
277
-
278
- switch (location) {
279
- case LegendLocation.TOP_LEFT:
280
- boxX = plotLeft + offset.x
281
- boxY = plotTop + offset.y
282
- break
283
- case LegendLocation.TOP_RIGHT:
284
- boxX = plotRight - boxWidth - offset.x
285
- boxY = plotTop + offset.y
286
- break
287
- case LegendLocation.BOTTOM_LEFT:
288
- boxX = plotLeft + offset.x
289
- boxY = plotBottom - boxHeight - offset.y
290
- break
291
- case LegendLocation.BOTTOM_RIGHT:
292
- default:
293
- boxX = plotRight - boxWidth - offset.x
294
- boxY = plotBottom - boxHeight - offset.y
295
- break
296
- }
297
-
298
- // Create the legend container group
299
- const legendG = svg
300
- .append<SVGGElement>("g")
301
- .attr("id", legendId)
302
- .attr("transform", `translate(${boxX}, ${boxY})`)
303
- .style("opacity", 0)
304
- .on("mouseover", () => {
305
- setMouseInLegend(true)
306
- })
307
- .on("mouseleave", () => {
308
- setMouseInLegend(false)
309
- })
310
-
311
- // Apply transition for shimmering effect
312
- legendG
313
- .style("transition", `opacity ${transitionDuration}ms ease-in-out`)
314
- .style("opacity", 1)
315
-
316
- // Background box
317
- legendG
318
- .append("rect")
319
- .attr("x", 0)
320
- .attr("y", 0)
321
- .attr("width", boxWidth)
322
- .attr("height", boxHeight)
323
- .attr("rx", borderRadius)
324
- .attr("fill", backgroundColor)
325
- .attr("fill-opacity", backgroundOpacity)
326
- .attr("stroke", borderColor)
327
- .attr("stroke-width", borderWidth)
328
- .attr("stroke-opacity", borderOpacity)
329
-
330
- const innerG = legendG
331
- .append("g")
332
- .attr("class", "legend-content")
333
- .attr("transform", `translate(0, ${-scrollYRef.current})`)
334
-
335
- // If we have a max height and the content is taller, we'd need a scrollbar.
336
- // In SVG, we can use a clipPath and handle scroll events manually, or use foreignObject.
337
- // foreignObject is generally better for this if we want real scrollbars.
338
- const isScrolling = maxHeight !== undefined && totalContentHeight > maxHeight
339
-
340
- if (isScrolling) {
341
- const clipId = `legend-clip-${chartId}`
342
- // Remove existing clipPath for this legend to avoid duplicates
343
- svg.select(`#${clipId}`).remove()
344
- //
345
- svg.append("defs")
346
- .append("clipPath")
347
- .attr("id", clipId)
348
- .attr("clipPathUnits", "userSpaceOnUse")
349
- .append("rect")
350
- .attr("x", 0)
351
- .attr("y", 0)
352
- .attr("width", boxWidth)
353
- .attr("height", boxHeight)
354
-
355
- legendG.attr("clip-path", `url(#${clipId})`)
356
-
357
- // Simple scroll handling via mouse wheel
358
- legendG.on("wheel", (event: WheelEvent) => {
359
- event.preventDefault()
360
- // scrollBarVisibleRef.current = true
361
- isWheelingRef.current = true
362
- const maxScroll = totalContentHeight - boxHeight
363
- scrollYRef.current = Math.max(0, Math.min(maxScroll, scrollYRef.current + event.deltaY))
364
- innerG.attr("transform", `translate(0, ${-scrollYRef.current})`)
365
-
366
- // Clear the existing timer if the user is still wheeling
367
- clearTimeout(wheelTimeoutRef.current);
368
-
369
- // Set a new timer to fire after 100-200ms of inactivity
370
- wheelTimeoutRef.current = setTimeout(() => {
371
- isWheelingRef.current = false
372
- // scrollBarVisibleRef.current = false
373
- console.log(`Wheel movement has ended. ${isWheelingRef.current ? "Still wheeling." : "No longer wheeling."}`);
374
- }, 150);
375
- }, {passive: false})
376
-
377
- // Visual scrollbar (optional but good for visibility)
378
- const scrollbarWidth = 4
379
- const scrollbarHeight = (boxHeight / totalContentHeight) * boxHeight - padding
380
- const scrollbar = legendG.append("rect")
381
- .attr("class", "legend-scrollbar")
382
- .attr("x", boxWidth - scrollbarWidth - 2)
383
- .attr("y", calculateScrollbarY(totalContentHeight, boxHeight, scrollYRef.current, scrollbarHeight, padding))
384
- .attr("width", scrollbarWidth)
385
- .attr("height", scrollbarHeight)
386
- .attr("rx", scrollbarWidth / 2)
387
- .style("fill-opacity", mouseInLegend ? 0.25 : 0)
388
- .attr("fill", fontColor)
389
-
390
- legendG.on("wheel.scrollbar", (_: WheelEvent) => {
391
- scrollbar.attr("y", calculateScrollbarY(totalContentHeight, boxHeight, scrollYRef.current, scrollbarHeight, padding))
392
- })
393
- }
394
-
395
- // Legend rows
396
- visibleSeriesNames.forEach((name, i) => {
397
- const seriesColor = seriesStyles.get(name)?.color ?? color
398
- const rowY = padding + i * (rowHeight + rowGap)
399
- const swatchMidY = rowY + rowHeight / 2
400
-
401
- const rowG = innerG
402
- .append("g")
403
- .attr("class", "legend-row")
404
- .attr("data-series-name", name)
405
- .style("cursor", "default")
406
-
407
- // Color swatch — a short horizontal line to mimic series appearance
408
- rowG
409
- .append("line")
410
- .attr("x1", padding)
411
- .attr("y1", swatchMidY)
412
- .attr("x2", padding + swatchWidth)
413
- .attr("y2", swatchMidY)
414
- .attr("stroke", seriesColor)
415
- .attr("stroke-width", swatchHeight)
416
- .attr("stroke-linecap", "round")
417
-
418
- // Series name label
419
- rowG
420
- .append("text")
421
- .attr("x", padding + swatchWidth + swatchLabelGap)
422
- .attr("y", swatchMidY)
423
- .attr("dominant-baseline", "middle")
424
- .attr("data-series-name", name)
425
- .style("font-size", `${fontSize}px`)
426
- .style("font-family", fontFamily)
427
- .style("fill", fontColor)
428
- .text(name)
429
-
430
- // keep this at the end
431
- rowG
432
- .append("rect")
433
- .attr("x", 0)
434
- .attr("width", boxWidth)
435
- .attr("y", padding + i * (rowHeight + rowGap) - rowGap)
436
- .attr("height", rowHeight + rowGap)
437
- .style("fill", backgroundColor)
438
- .style("fill-opacity", 0)
439
- .on("mouseover", () => {
440
- if (isWheelingRef.current) return
441
- setHoveredSeriesName(prevName => {
442
- // restore any previous names in case the events
443
- // get out of order, this prevents multiple series
444
- // being highlighted when the mouse moves quickly
445
- if (prevName && prevName !== name) {
446
- restoreSeriesInPlot(prevName)
447
- }
448
- return name
449
- })
450
- highlightSeriesInPlot(name)
451
- })
452
- .on("mouseleave", () => {
453
- setHoveredSeriesName(null)
454
- restoreSeriesInPlot(name)
455
- })
456
-
457
- })
458
192
  },
459
- [
460
- visible, container, externalContainer, chartId, visibleSeriesNames,
461
- legendStyle, location, offset, margin, plotDimensions, color,
462
- seriesStyles, highlightSeriesInPlot, restoreSeriesInPlot, mouseInLegend
463
- ]
193
+ [externalContainer, canvas, visible, visibleSeriesNames.length]
464
194
  )
465
195
 
466
- // Update SVG row opacity when the hovered series changes
467
- useEffect(
468
- () => {
469
- if (!container || externalContainer) return
470
- const legendG = d3.select(container).select(`#${LEGEND_CONTAINER_ID_PREFIX}-${chartId}`)
471
- if (!legendG.empty()) {
472
- const FADE_BACK_OPACITY = 0.35
473
- legendG.selectAll<SVGGElement, unknown>("g.legend-row")
474
- .attr("transition", `opacity ${350}ms ease-in-out`)
475
- .style("opacity", function (): number {
476
- const seriesName = d3.select(this).attr("data-series-name")
477
- if (mouseInLegend) {
478
- return hoveredSeriesName !== null && seriesName === hoveredSeriesName ? 1 : FADE_BACK_OPACITY
479
- } else if (hoveredSeriesName !== null && seriesName !== hoveredSeriesName) {
480
- return FADE_BACK_OPACITY
481
- } else {
482
- return 1
483
- }
484
- })
485
- legendG.selectAll<SVGTextElement, unknown>("text[data-series-name]")
486
- .style("font-weight", function (): string {
487
- const seriesName: string = d3.select(this).attr("data-series-name")
488
- return hoveredSeriesName !== null && seriesName === hoveredSeriesName ? "bold" : "normal"
489
- })
490
- }
491
- },
492
- [hoveredSeriesName, container, externalContainer, chartId, mouseInLegend]
493
- )
196
+ if (!visible || visibleSeriesNames.length === 0) return null
494
197
 
495
- // HTML portal legend — rendered outside the SVG into an external container
496
- if (externalContainerReady && externalContainer?.current && visibleSeriesNames.length > 0) {
497
- const {
498
- fontSize,
499
- fontFamily,
500
- fontColor,
501
- backgroundColor,
502
- backgroundOpacity,
503
- borderColor,
504
- borderWidth,
505
- borderOpacity,
506
- borderRadius,
507
- padding,
508
- rowGap,
509
- swatchWidth,
510
- swatchHeight,
511
- swatchLabelGap,
512
- maxHeight,
513
- transitionDuration,
514
- } = legendStyle
198
+ // the portal target: the caller-supplied external container, or (by default) the chart's own
199
+ // canvas overlay wrapper -- the `position: relative` div Chart.tsx wraps the <canvas> in
200
+ const portalTarget = externalContainer?.current ?? canvas?.parentElement ?? null
201
+ if (portalTarget === null) return null
515
202
 
516
- const bg = d3.color(backgroundColor) as d3.RGBColor | undefined
517
- const bgWithOpacity = bg
518
- ? `rgba(${bg.r},${bg.g},${bg.b},${backgroundOpacity})`
519
- : backgroundColor
520
- const bd = d3.color(borderColor) as d3.RGBColor | undefined
521
- const bdWithOpacity = bd
522
- ? `rgba(${bd.r},${bd.g},${bd.b},${borderOpacity})`
523
- : borderColor
524
-
525
- const boxStyle: React.CSSProperties = {
526
- display: "inline-flex",
527
- flexDirection: "column",
528
- backgroundColor: bgWithOpacity,
529
- border: `${borderWidth}px solid ${bdWithOpacity}`,
530
- borderRadius,
531
- padding,
532
- maxHeight,
533
- overflowY: "auto",
534
- fontFamily,
535
- fontSize,
536
- color: fontColor,
537
- boxSizing: "border-box",
538
- opacity: visible ? 1 : 0,
539
- transition: visible ? `opacity ${transitionDuration}ms ease-in-out` : "none",
540
- pointerEvents: visible ? "auto" : "none",
203
+ const {
204
+ fontSize,
205
+ fontFamily,
206
+ fontColor,
207
+ backgroundColor,
208
+ backgroundOpacity,
209
+ borderColor,
210
+ borderWidth,
211
+ borderOpacity,
212
+ borderRadius,
213
+ padding,
214
+ rowGap,
215
+ swatchWidth,
216
+ swatchHeight,
217
+ swatchLabelGap,
218
+ maxHeight,
219
+ transitionDuration,
220
+ } = legendStyle
221
+
222
+ const bg = d3.color(backgroundColor) as d3.RGBColor | undefined
223
+ const bgWithOpacity = bg
224
+ ? `rgba(${bg.r},${bg.g},${bg.b},${backgroundOpacity})`
225
+ : backgroundColor
226
+ const bd = d3.color(borderColor) as d3.RGBColor | undefined
227
+ const bdWithOpacity = bd
228
+ ? `rgba(${bd.r},${bd.g},${bd.b},${borderOpacity})`
229
+ : borderColor
230
+
231
+ // when rendering into our own overlay (no external container), anchor the box to the
232
+ // corresponding corner of the *plot area* (inside the margins), matching the old SVG
233
+ // version's positioning intent -- but via CSS `top`/`right`/`bottom`/`left` instead of a
234
+ // manually-computed pixel (x, y), since we don't need to know the box's own width/height
235
+ // ahead of time the way the old version's manual layout math did
236
+ const anchorStyle: React.CSSProperties = externalContainer ? {} : (() => {
237
+ switch (location) {
238
+ case LegendLocation.TOP_LEFT:
239
+ return {position: 'absolute', top: margin.top + offset.y, left: margin.left + offset.x}
240
+ case LegendLocation.TOP_RIGHT:
241
+ return {position: 'absolute', top: margin.top + offset.y, right: margin.right + offset.x}
242
+ case LegendLocation.BOTTOM_LEFT:
243
+ return {position: 'absolute', bottom: margin.bottom + offset.y, left: margin.left + offset.x}
244
+ case LegendLocation.BOTTOM_RIGHT:
245
+ case LegendLocation.EXTERNAL_CONTAINER:
246
+ default:
247
+ return {position: 'absolute', bottom: margin.bottom + offset.y, right: margin.right + offset.x}
541
248
  }
542
-
543
- const anyHovered = hoveredSeriesName !== null
544
- return createPortal(
545
- <div style={boxStyle}>
546
- {visibleSeriesNames.map(name => {
547
- const seriesColor = seriesStyles.get(name)?.color ?? color
548
- const isHovered = name === hoveredSeriesName
549
- const rowStyle: React.CSSProperties = {
550
- display: "flex",
551
- alignItems: "center",
552
- gap: swatchLabelGap,
553
- opacity: anyHovered && !isHovered ? 0.35 : 1,
554
- fontWeight: isHovered ? "bold" : "normal",
555
- transition: "opacity 0.15s, font-weight 0s",
556
- height: rowGap + fontSize,
557
- }
558
- return (
559
- <div
560
- key={name}
561
- style={{...rowStyle, cursor: "default"}}
562
- onMouseEnter={() => {
563
- setHoveredSeriesName(name)
564
- highlightSeriesInPlot(name)
565
- }}
566
- onMouseLeave={() => {
567
- setHoveredSeriesName(null)
568
- restoreSeriesInPlot(name)
569
- }}
570
- >
571
- <span style={{
572
- display: "inline-block",
573
- width: swatchWidth,
574
- height: swatchHeight,
575
- backgroundColor: seriesColor,
576
- borderRadius: swatchHeight / 2,
577
- flexShrink: 0,
578
- }}/>
579
- <span style={{
580
- height: rowGap + fontSize,
581
- alignItems: "center",
582
- display: "inline-flex"
583
- }}>{name}</span>
584
- </div>
585
- )
586
- })}
587
- </div>,
588
- externalContainer.current
589
- )
249
+ })()
250
+
251
+ const boxStyle: React.CSSProperties = {
252
+ ...anchorStyle,
253
+ display: "inline-flex",
254
+ flexDirection: "column",
255
+ backgroundColor: bgWithOpacity,
256
+ border: `${borderWidth}px solid ${bdWithOpacity}`,
257
+ borderRadius,
258
+ padding,
259
+ maxHeight,
260
+ overflowY: "auto",
261
+ fontFamily,
262
+ fontSize,
263
+ color: fontColor,
264
+ boxSizing: "border-box",
265
+ opacity: visible ? 1 : 0,
266
+ transition: visible ? `opacity ${transitionDuration}ms ease-in-out` : "none",
267
+ pointerEvents: visible ? "auto" : "none",
268
+ whiteSpace: "nowrap",
269
+ zIndex: 10,
590
270
  }
591
271
 
592
- return null
272
+ const anyHovered = hoveredSeriesName !== null
273
+ return createPortal(
274
+ <div style={boxStyle}>
275
+ {visibleSeriesNames.map(name => {
276
+ const seriesColor = seriesStyles.get(name)?.color ?? color
277
+ const isHovered = name === hoveredSeriesName
278
+ const rowStyle: React.CSSProperties = {
279
+ display: "flex",
280
+ alignItems: "center",
281
+ gap: swatchLabelGap,
282
+ opacity: anyHovered && !isHovered ? 0.35 : 1,
283
+ fontWeight: isHovered ? "bold" : "normal",
284
+ transition: "opacity 0.15s, font-weight 0s",
285
+ height: rowGap + fontSize,
286
+ }
287
+ return (
288
+ <div
289
+ key={name}
290
+ style={{...rowStyle, cursor: "default"}}
291
+ onMouseEnter={() => {
292
+ setHoveredSeriesName(name)
293
+ highlightSeriesInPlot(name)
294
+ }}
295
+ onMouseLeave={() => {
296
+ setHoveredSeriesName(null)
297
+ restoreSeriesInPlot(name)
298
+ }}
299
+ >
300
+ <span style={{
301
+ display: "inline-block",
302
+ width: swatchWidth,
303
+ height: swatchHeight,
304
+ backgroundColor: seriesColor,
305
+ borderRadius: swatchHeight / 2,
306
+ flexShrink: 0,
307
+ }}/>
308
+ <span style={{
309
+ height: rowGap + fontSize,
310
+ alignItems: "center",
311
+ display: "inline-flex"
312
+ }}>{name}</span>
313
+ </div>
314
+ )
315
+ })}
316
+ </div>,
317
+ portalTarget
318
+ )
593
319
  }
594
-
595
- function calculateScrollbarY(totalContentHeight: number, boxHeight: number, scrollY: number, scrollbarHeight: number, padding: number): number {
596
- const maxScroll = totalContentHeight - boxHeight
597
- const scrollPercent = scrollY / maxScroll
598
- const scrollbarMaxY = boxHeight - scrollbarHeight - 4 - padding
599
- return Math.max(padding, 2 + scrollPercent * scrollbarMaxY)
600
- }