stream-charts 1.2.1 → 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 +36 -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
@@ -0,0 +1,685 @@
1
+ import {useCallback, useEffect, useRef} from 'react'
2
+ import {useStreamingTimeAxes} from "./useStreamingTimeAxes";
3
+ import {useHoveredSeries} from "../hooks/useHoveredSeries";
4
+ import * as d3 from "d3"
5
+
6
+ import {useChart} from "../hooks/useChart"
7
+ import {useDataSource, useDataSourceRunning, useInitialData} from "../hooks/useDataSource";
8
+ import {usePlotDimensions} from "../hooks/usePlotDimensions"
9
+ import {type AxesAssignment, clipToArea} from "./plot"
10
+ import type {CanvasContext} from "../d3types"
11
+ import {seriesAt, canvasLocalPoint, type SeriesGeometry} from "./hitTesting"
12
+ import {noop} from "../utils"
13
+ import {ContinuousAxisRange} from "../axes/ContinuousAxisRange"
14
+ import {extentWith, visibleExtent} from "../axes/autoScale"
15
+ import {growAutoScaledYAxes, useAutoScaleRestart} from "./autoScaleYAxes"
16
+ import {
17
+ type BaseAxis,
18
+ AxisType,
19
+ checkAxisType,
20
+ type ContinuousNumericAxis,
21
+ defaultLineStyle,
22
+ type SeriesLineStyle
23
+ } from "../axes/axes"
24
+ import {outlierAxisTimes, TimeWindowBehavior} from "../subscriptions/viewDrivers";
25
+ import type {OutlierChartData} from "../observables/outliers"
26
+ import type {OutlierDatum, OutlierSeries} from "../series/outlierSeries"
27
+ import {FastShiftArray} from "fast-shift-array";
28
+
29
+ type OutlierDatumColor<M extends readonly number[]> = { datum: OutlierDatum<M>, color: string }
30
+
31
+ export interface Props {
32
+ /**
33
+ * Holds the mapping between a series and the axis it uses (is assigned). The
34
+ * map's key holds the series name, and the value is an {@link AxesAssignment}
35
+ * object holding the ID of the assigned x-axis and y-axis.
36
+ */
37
+ axisAssignments?: Map<string, AxesAssignment>
38
+ /**
39
+ * The line interpolation curve factory. See the d3 documentation for curves at
40
+ * {@link https://github.com/d3/d3-shape#curves} for information on available interpolations.
41
+ */
42
+ interpolation?: d3.CurveFactory
43
+ /**
44
+ * Whether to enable panning. Defaults to false.
45
+ */
46
+ panEnabled?: boolean
47
+ /**
48
+ * Whether to enable zooming. Defaults to false.
49
+ */
50
+ zoomEnabled?: boolean
51
+ /**
52
+ * Whether to require the shift or ctrl key to be held down when zooming. Defaults to true.
53
+ */
54
+ zoomKeyModifiersRequired?: boolean
55
+ /**
56
+ * Behavior for time windowing (see {@link TimeWindowBehavior}). Defaults to `SCROLL`. Ignored
57
+ * when `withCadenceOf` is set.
58
+ */
59
+ timeWindowBehavior?: TimeWindowBehavior
60
+ /**
61
+ * When set, uses a cadence with the specified refresh period (in milliseconds). For plots
62
+ * where the updates are slow (> 100 ms) using a cadence of 10 to 25 ms smooths out the
63
+ * updates and makes the plot updates look cleaner. When updates are around 25 ms or less,
64
+ * then setting the cadence period too small will result in poor update performance. Generally
65
+ * at high update speeds, the cadence is unnecessary. Finally, using cadence, sets the max time
66
+ * to the current time -- this is what causes the x-axis to keep scrolling once the data
67
+ * reaches the right-hand edge, even if new data hasn't arrived yet.
68
+ */
69
+ withCadenceOf?: number
70
+ /**
71
+ * Base fill-opacity for the outermost (widest) outlier band. Inner (narrower / more
72
+ * confident) bands are rendered with progressively higher opacity, capped at 1. Defaults to 0.15.
73
+ */
74
+ bandOpacity?: number
75
+ /**
76
+ * Optional per-band opacity step added for each more-confident (lower-index) band. Defaults to 0.12.
77
+ */
78
+ bandOpacityStep?: number
79
+ /**
80
+ * Radius (px) of the circle marker drawn at each datum on the central line, while the data
81
+ * source is paused. When omitted, no markers are drawn.
82
+ */
83
+ markerRadius?: number
84
+ /**
85
+ * Colors for the outlier marker drawn on any point that falls outside one or more bounds.
86
+ * The array is indexed by bound (i.e. by measure), so `outlierMarkerColors[i]` is used when
87
+ * the point is outside `bounds[i]`. When a point is outside multiple bounds, the color
88
+ * associated with the largest exceeded bound wins. When omitted, outlier markers fall back
89
+ * to red.
90
+ */
91
+ outlierMarkerColors?: ReadonlyArray<string>
92
+ /**
93
+ * When true, hovering over a series also highlights the x- and y-axes it is plotted
94
+ * against (in the series' own highlight color/width). Defaults to `false`.
95
+ */
96
+ highlightAxesOnMouseOver?: boolean
97
+ /**
98
+ * Called whenever this plot (re)creates its zoom behavior, handing
99
+ * the caller a `resetZoom` function that programmatically clears d3-zoom's own accumulated
100
+ * scale/pan state back to identity. See {@link ScatterPlot}'s identical prop for the full
101
+ * explanation of why this is needed. Only meaningful (called) while `zoomEnabled` is true.
102
+ */
103
+ onZoomReset?: (resetZoom: () => void) => void
104
+ }
105
+
106
+ /**
107
+ * Metadata carried through the tooltip system when the user hovers over an outlier band.
108
+ */
109
+ export interface OutlierBandTooltipMetadata<M extends readonly number[] = readonly number[]> {
110
+ /**
111
+ * The outlier datum
112
+ */
113
+ datum?: OutlierDatum<M>
114
+ /**
115
+ * The measure (confidence level) associated with the hovered band.
116
+ */
117
+ upperMeasure?: number
118
+ /**
119
+ * The measure (confidence level) associated with the band below the hovered band.
120
+ */
121
+ lowerMeasure?: number
122
+ /**
123
+ * The index of the hovered band (0 = tightest / most confident).
124
+ */
125
+ bandIndex: number
126
+ /**
127
+ * Number of visible points whose y-value falls within the band bounds.
128
+ */
129
+ pointsInBand: number
130
+ }
131
+
132
+ /**
133
+ * A band's fill region, captured at draw time for `isPointInPath`-based hover hit-testing.
134
+ * Canvas has no per-shape events, and a filled, possibly-curved area isn't well modeled by
135
+ * `hitTesting.ts`'s line/segment/rect primitives, so bands use `ctx.isPointInPath` directly
136
+ * instead -- this is what canvas is built for.
137
+ */
138
+ interface BandHitRegion<M extends readonly number[]> {
139
+ path: Path2D
140
+ seriesName: string
141
+ bandIndex: number
142
+ upperMeasure: number | undefined
143
+ lowerMeasure: number | undefined
144
+ plotData: FastShiftArray<OutlierDatum<M>>
145
+ }
146
+
147
+ /**
148
+ * Renders a streaming outlier plot. Each series consists of {@link OutlierDatum} points, where every
149
+ * datum carries an (x, y) value plus a set of (lower, upper) bounds — one per measure. The plot
150
+ * draws the central y-line for the series and a translucent band per measure, filled between
151
+ * the corresponding lower and upper bounds. The x-axis scrolls as new points arrive past the
152
+ * end of the visible window, in the same fashion as {@link ScatterPlot}.
153
+ *
154
+ * Internally, this no longer creates/updates SVG `<path>`/`<circle>` elements. Instead, it
155
+ * registers a single draw function with the chart's {@link CanvasContext} that redraws every
156
+ * series' bands, line, and markers from scratch each time the canvas repaints. Band hover uses
157
+ * `ctx.isPointInPath` against the band's actual filled path (captured each draw); outlier-marker
158
+ * hover uses the shared point-hit-testing in `hitTesting.ts`, same as {@link ScatterPlot}'s markers.
159
+ * When the data source is paused, hovering over a band shows the tooltip (when there's one); a
160
+ * y-axis with `autoScale` set grows to fit the data on screen (bands included).
161
+ * @param props The properties associated with the outlier plot
162
+ * @return `null` (the plot draws onto the chart's canvas)
163
+ * @template M The type of the measures (a tuple of numbers)
164
+ */
165
+ export function OutlierPlot<M extends readonly number[] = readonly number[]>(props: Props): null {
166
+ const {
167
+ chartId,
168
+ canvasContext,
169
+ axes,
170
+ seriesStyles,
171
+ seriesFilter,
172
+ mouse,
173
+ } = useChart<OutlierDatum<M>, SeriesLineStyle, OutlierBandTooltipMetadata<M>, ContinuousAxisRange, ContinuousNumericAxis>()
174
+ const {hoveredSeriesName} = useHoveredSeries()
175
+
176
+ const {mouseOverHandlerFor, mouseLeaveHandlerFor} = mouse
177
+
178
+ const {initialData} = useInitialData<OutlierDatum<M>>()
179
+
180
+ const {
181
+ xAxesState,
182
+ yAxesState,
183
+ updateAxisRanges = noop,
184
+ axisRangeFor,
185
+ } = axes
186
+
187
+ const {plotDimensions, margin} = usePlotDimensions()
188
+
189
+ // the application-owned source of the data, and whether it's running (streaming)
190
+ const dataSource = useDataSource<OutlierChartData<M>, OutlierDatum<M>, OutlierSeries<M>>()
191
+ const running = useDataSourceRunning(dataSource)
192
+
193
+ const {
194
+ axisAssignments = new Map<string, AxesAssignment>(),
195
+ interpolation = d3.curveLinear,
196
+ panEnabled = false,
197
+ zoomEnabled = false,
198
+ zoomKeyModifiersRequired = true,
199
+ timeWindowBehavior = TimeWindowBehavior.SCROLL,
200
+ withCadenceOf,
201
+ bandOpacity = 0.15,
202
+ bandOpacityStep = 0.12,
203
+ markerRadius,
204
+ outlierMarkerColors,
205
+ highlightAxesOnMouseOver = false,
206
+ onZoomReset = noop,
207
+ } = props
208
+
209
+
210
+ // seriesRef is the single source of truth for what to render: the data source's own (live)
211
+ // series map, which grows as series first appear in the stream. the draw function iterates this
212
+ // directly (rather than a parallel dataRef array) so dynamically-arriving series get rendered.
213
+ const seriesRef = useRef<ReadonlyMap<string, OutlierSeries<M>>>(
214
+ dataSource?.series ?? new Map(initialData.map(series => [series.name, series as OutlierSeries<M>]))
215
+ )
216
+
217
+ // captured band fill-regions and outlier-marker geometry, in canvas coordinates, used for
218
+ // hit-testing mouse hover on `mousemove` (see the effect below that wires up the listener)
219
+ const bandsRef = useRef<Array<BandHitRegion<M>>>([])
220
+ const outlierGeometryRef = useRef<Map<string, SeriesGeometry>>(new Map())
221
+ const outlierDatumsRef = useRef<Map<string, Array<OutlierDatumColor<M>>>>(new Map())
222
+ // what's currently hovered: a band (series + band index) or an outlier point (series + point
223
+ // index) -- mutually exclusive, so we know when to fire a "leave" before an "over"
224
+ const lastHoveredRef = useRef<{kind: 'band', seriesName: string, bandIndex: number} | {kind: 'outlier', seriesName: string, index: number} | undefined>(undefined)
225
+
226
+ /**
227
+ * (Re-)registers this plot's draw function with the canvas context and requests a redraw.
228
+ * Replaces the old version, which directly mutated SVG `<path>`/`<circle>` elements bound via
229
+ * d3's enter/update/exit join. Canvas has no persistent elements to join against, so the draw
230
+ * function just redraws every band/line/marker from current data/scale state each time it's
231
+ * invoked.
232
+ *
233
+ * Pan/zoom behavior setup lives in a separate effect (see below), not here -- this function
234
+ * runs on every data tick (each `windowingTime` interval), and recreating/reattaching a
235
+ * `d3.drag()`/`d3.zoom()` behavior that often is pure overhead unrelated to drawing the new
236
+ * data.
237
+ * @param cc The canvas context to register the draw function with
238
+ */
239
+ const updatePlot = useCallback(
240
+ (cc: CanvasContext) => {
241
+ const draw = (context: CanvasContext) => {
242
+ const {context2D} = context
243
+
244
+ context2D.save()
245
+ clipToArea(context, plotDimensions, {x: margin.left, y: margin.top})
246
+ context2D.translate(margin.left, margin.top)
247
+
248
+ const newBands: Array<BandHitRegion<M>> = []
249
+ const newOutlierGeometry = new Map<string, SeriesGeometry>()
250
+ const newOutlierDatums = new Map<string, Array<OutlierDatumColor<M>>>()
251
+
252
+ seriesRef.current.forEach(series => {
253
+ const [xAxis, yAxis] = axesFor(
254
+ series.name,
255
+ axisAssignments,
256
+ id => xAxesState.axisFor(id).getOrUndefined(),
257
+ id => yAxesState.axisFor(id).getOrUndefined()
258
+ )
259
+ if (xAxis === undefined || yAxis === undefined) return
260
+
261
+ const style = seriesStyles.get(series.name) ?? defaultLineStyle()
262
+ const plotData = series.name.match(seriesFilter) ? series.data : FastShiftArray.empty<OutlierDatum<M>>()
263
+ const numBands = plotData.length > 0 ? plotData[0].bounds.length : 0
264
+
265
+ // render the widest (highest-index) band first so the narrower, more-confident
266
+ // bands stack on top with darker opacity
267
+ for (let bandIndex = numBands - 1; bandIndex >= 0; bandIndex--) {
268
+ const opacity = Math.min(1, bandOpacity + (numBands - 1 - bandIndex) * bandOpacityStep)
269
+ const areaGen = d3.area<OutlierDatum<M>>()
270
+ .x(d => xAxis.scale(d.datum.x) || 0)
271
+ .y0(d => yAxis.scale(d.bounds[bandIndex].lower) || 0)
272
+ .y1(d => yAxis.scale(d.bounds[bandIndex].upper) || 0)
273
+ .curve(interpolation)
274
+ const upperMeasure = series.measures[bandIndex]
275
+ const lowerMeasure = bandIndex > 0 ? series.measures[bandIndex - 1] : undefined
276
+
277
+ const path = new Path2D(areaGen(Array.from(plotData)) ?? "")
278
+ context2D.fillStyle = style.color
279
+ context2D.globalAlpha = opacity
280
+ context2D.fill(path)
281
+ context2D.globalAlpha = 1
282
+
283
+ // pushed in draw order (widest first, narrowest/topmost last); hit-testing
284
+ // iterates this backwards so the topmost band wins, matching visual stacking
285
+ newBands.push({path, seriesName: series.name, bandIndex, upperMeasure, lowerMeasure, plotData})
286
+ }
287
+
288
+ // central line for the series y-value
289
+ const isHovered = hoveredSeriesName === series.name
290
+ const stroke = isHovered ? style.highlightColor : style.color
291
+ const strokeWidth = isHovered ? style.highlightWidth : style.lineWidth
292
+ const lineGen = d3.line<OutlierDatum<M>>()
293
+ .x(d => xAxis.scale(d.datum.x) || 0)
294
+ .y(d => yAxis.scale(d.datum.y) || 0)
295
+ .curve(interpolation)
296
+
297
+ context2D.strokeStyle = stroke
298
+ context2D.lineWidth = strokeWidth
299
+ context2D.stroke(new Path2D(lineGen(Array.from(plotData)) ?? ""))
300
+
301
+ // for the markers, we split the data into two categories: regular and outlier
302
+ const {regular, outlier} = categorizePoints(plotData, outlierMarkerColors)
303
+
304
+ // point markers (one circle per datum) -- decorative only, no hover/tooltip
305
+ if (markerRadius != null && markerRadius >= 0 && !running) {
306
+ context2D.fillStyle = stroke
307
+ regular.forEach(d => {
308
+ const x = xAxis.scale(d.datum.x) || 0
309
+ const y = yAxis.scale(d.datum.y) || 0
310
+ context2D.beginPath()
311
+ context2D.arc(x, y, markerRadius, 0, 2 * Math.PI)
312
+ context2D.fill()
313
+ })
314
+ }
315
+
316
+ // outlier markers -- these DO get hover/tooltip, so we record their screen
317
+ // positions (and the underlying datum, for the tooltip) for hit-testing
318
+ const outlierPoints: Array<[number, number]> = []
319
+ outlier.forEach(o => {
320
+ const x = xAxis.scale(o.datum.datum.x) || 0
321
+ const y = yAxis.scale(o.datum.datum.y) || 0
322
+ context2D.fillStyle = o.color
323
+ context2D.beginPath()
324
+ context2D.arc(x, y, 4, 0, 2 * Math.PI)
325
+ context2D.fill()
326
+ outlierPoints.push([x + margin.left, y + margin.top])
327
+ })
328
+ newOutlierGeometry.set(`${series.name}::outlier`, {
329
+ points: outlierPoints,
330
+ hitRadius: 6
331
+ })
332
+ newOutlierDatums.set(series.name, outlier)
333
+ })
334
+
335
+ bandsRef.current = newBands
336
+ outlierGeometryRef.current = newOutlierGeometry
337
+ outlierDatumsRef.current = newOutlierDatums
338
+
339
+ context2D.restore()
340
+ }
341
+
342
+ /**
343
+ * Grows the auto-scaling y-axes (see `ContinuousAxis`'s `autoScale` prop) to fit the
344
+ * data that's on screen: for each shown series (those matching the filter), the values
345
+ * and the bands' lower and upper bounds of its points within the x-axis' current
346
+ * window, along with where their lines cross the window's edges (see `visibleExtent`).
347
+ * Since an axis only ever grows, only the visible data needs checking each frame.
348
+ * Registered to run before the axes draw (they're at z-index 0), so the axes and the
349
+ * series are drawn with the grown domain in the same frame.
350
+ */
351
+ const autoScaleYAxes = (): void => {
352
+ const extents = new Map<string, [min: number, max: number]>()
353
+ seriesRef.current.forEach(series => {
354
+ if (!series.name.match(seriesFilter)) return
355
+ const [xAxis, yAxis] = axesFor(
356
+ series.name,
357
+ axisAssignments,
358
+ id => xAxesState.axisFor(id).getOrUndefined(),
359
+ id => yAxesState.axisFor(id).getOrUndefined()
360
+ )
361
+ if (xAxis === undefined || yAxis?.autoScale === undefined) return
362
+
363
+ const [start, end] = xAxis.scale.domain()
364
+ // (the series' line and its bands' edges are always drawn as lines)
365
+ const seriesExtent = visibleExtent(
366
+ series.data, [start, end], yAxis.scale, true,
367
+ point => point.datum.x,
368
+ point => [point.datum.y, ...point.bounds.flatMap(({lower, upper}) => [lower, upper])]
369
+ )
370
+ if (seriesExtent === undefined) return
371
+ const extent = extents.get(yAxis.axisId)
372
+ extents.set(
373
+ yAxis.axisId,
374
+ extent === undefined ? seriesExtent : extentWith(extentWith(extent, seriesExtent[0]), seriesExtent[1])
375
+ )
376
+ })
377
+ growAutoScaledYAxes(extents, {yAxes: yAxesState.axes, axisRangeFor, updateAxisRanges})
378
+ }
379
+
380
+ cc.register(`outlier-plot-autoscale-${chartId}`, autoScaleYAxes, -10)
381
+ cc.register(`outlier-plot-${chartId}`, draw, 10)
382
+ cc.requestRedraw()
383
+ },
384
+ // note: my WebStorm claims that bandOpacityStep is not a dependency, but it is and the
385
+ // linter flags is as missing when it is removed
386
+ [
387
+ chartId, plotDimensions, axisAssignments, xAxesState, yAxesState, seriesStyles, seriesFilter,
388
+ interpolation, bandOpacity, bandOpacityStep, markerRadius, outlierMarkerColors, hoveredSeriesName, running,
389
+ axisRangeFor, updateAxisRanges, margin.left, margin.top
390
+ ]
391
+ )
392
+
393
+ // new data, new scale: restarts the auto-scaling y-axes when the data source is replaced
394
+ useAutoScaleRestart(dataSource, {yAxes: yAxesState.axes, axisRangeFor, updateAxisRanges})
395
+
396
+ // the time axes: their ranges, the view driver, panning and zooming, highlighting, and
397
+ // reporting the axes' intervals -- see `useStreamingTimeAxes`
398
+ useStreamingTimeAxes<OutlierChartData<M>, OutlierDatum<M>>({
399
+ canvasContext,
400
+ dataSource,
401
+ running,
402
+ initialData,
403
+ timeOf: datum => datum.datum.x,
404
+ axisTimesFor: outlierAxisTimes<M>(axisAssignments, xAxesState),
405
+ axisAssignments,
406
+ updatePlot,
407
+ onDataReset: () => {
408
+ seriesRef.current = dataSource?.series ?? new Map(initialData.map(series => [series.name, series as OutlierSeries<M>]))
409
+ },
410
+ panEnabled,
411
+ zoomEnabled,
412
+ zoomKeyModifiersRequired,
413
+ onZoomReset,
414
+ withCadenceOf,
415
+ timeWindowBehavior,
416
+ highlightAxesOnMouseOver,
417
+ })
418
+
419
+ // wires up a single mousemove/mouseleave listener on the shared canvas to replace the old
420
+ // per-element SVG mouseover/mouseleave handlers on the band paths and outlier-marker circles.
421
+ // Bands are hit-tested via `ctx.isPointInPath` against their captured fill path (checked
422
+ // topmost/narrowest-first, matching visual stacking); outlier markers use the shared
423
+ // point-hit-testing in `hitTesting.ts`. Outlier markers are checked first since they're drawn
424
+ // on top of the bands.
425
+ useEffect(
426
+ () => {
427
+ if (!canvasContext) return
428
+
429
+ const canvas = canvasContext.canvas
430
+ const {context2D, dpr} = canvasContext
431
+
432
+ const handleMove = (event: MouseEvent) => {
433
+ const [x, y] = canvasLocalPoint(event, canvas)
434
+
435
+ // check outlier markers first (drawn on top of the bands)
436
+ const outlierHit = seriesAt(x, y, outlierGeometryRef.current)
437
+ // The band's `Path2D` is built from raw (pre-margin) local coordinates -- the same
438
+ // space `x - margin.left, y - margin.top` (the "local" point) is in -- but
439
+ // `ctx.isPointInPath` only reported correct hits once that local point was ALSO
440
+ // scaled by `dpr`. (An earlier version of this instead dropped the margin
441
+ // subtraction and scaled the raw canvas-relative point by `dpr` -- that's wrong:
442
+ // verified against the actual rendered band, rigorously, by isolating a single
443
+ // band layer's on-screen pixels via `getImageData` and testing candidate query
444
+ // points against the real `Path2D` object captured from a live hover. Dropping the
445
+ // margin subtraction was close enough to pass a single, less careful spot-check,
446
+ // but consistently mislocated the hit region by `margin.left`/`margin.top`, which
447
+ // is what produced "hover ~margin-worth of pixels to the side of the band" in
448
+ // practice.) The exact reason `dpr` needs to be reapplied here, on top of a
449
+ // transform that already includes it, isn't fully pinned down -- this is the
450
+ // empirically-verified formula, not a re-derivation from the Canvas 2D spec.
451
+ const localX = (x - margin.left) * dpr
452
+ const localY = (y - margin.top) * dpr
453
+ const current = outlierHit !== undefined ?
454
+ {kind: 'outlier' as const, seriesName: outlierHit.name.replace(/::outlier$/, ''), index: outlierHit.index} :
455
+ findHoveredBand(bandsRef.current, context2D, localX, localY)
456
+
457
+ const previous = lastHoveredRef.current
458
+ const sameAsBefore = previous !== undefined && current !== undefined &&
459
+ previous.kind === current.kind && previous.seriesName === current.seriesName &&
460
+ ((previous.kind === 'band' && current.kind === 'band' && previous.bandIndex === current.bandIndex) ||
461
+ (previous.kind === 'outlier' && current.kind === 'outlier' && previous.index === current.index))
462
+
463
+ if (sameAsBefore) return
464
+
465
+ if (previous !== undefined) {
466
+ mouseLeaveHandlerFor(`tooltip-${chartId}`)?.(previous.seriesName)
467
+ }
468
+
469
+ if (current !== undefined) {
470
+ if (current.kind === 'band') {
471
+ const band = bandsRef.current.find(b => b.seriesName === current.seriesName && b.bandIndex === current.bandIndex)
472
+ if (band !== undefined && band.upperMeasure != null) {
473
+ const pointsInBand = calcPointsInBand(band.plotData, band.bandIndex)
474
+ mouseOverHandlerFor(`tooltip-${chartId}`)?.(
475
+ current.seriesName,
476
+ current.bandIndex,
477
+ {
478
+ series: band.plotData,
479
+ metadata: {
480
+ upperMeasure: band.upperMeasure,
481
+ lowerMeasure: band.lowerMeasure,
482
+ bandIndex: current.bandIndex,
483
+ pointsInBand,
484
+ }
485
+ },
486
+ [x, y]
487
+ )
488
+ }
489
+ } else {
490
+ const series = seriesRef.current.get(current.seriesName)
491
+ const outlierDatum = outlierDatumsRef.current.get(current.seriesName)?.[current.index]
492
+ if (series !== undefined && outlierDatum !== undefined) {
493
+ const plotData = series.name.match(seriesFilter) ? series.data : FastShiftArray.empty<OutlierDatum<M>>()
494
+ const datum = outlierDatum.datum
495
+ const bandIndex = largestExceededBoundIndex(datum) + 1
496
+ const upperMeasure = bandIndex < series.measures.length ? series.measures[bandIndex] : undefined
497
+ const lowerMeasure = bandIndex > 0 ? series.measures[bandIndex - 1] : undefined
498
+ const pointsInBand = calcPointsInBand(plotData, bandIndex)
499
+ mouseOverHandlerFor(`tooltip-${chartId}`)?.(
500
+ current.seriesName,
501
+ datum.datum.x,
502
+ {
503
+ series: series.data,
504
+ metadata: {datum, upperMeasure, lowerMeasure, bandIndex, pointsInBand}
505
+ },
506
+ [x, y]
507
+ )
508
+ }
509
+ }
510
+ }
511
+
512
+ lastHoveredRef.current = current
513
+ }
514
+
515
+ const handleLeaveCanvas = () => {
516
+ const previous = lastHoveredRef.current
517
+ if (previous !== undefined) {
518
+ mouseLeaveHandlerFor(`tooltip-${chartId}`)?.(previous.seriesName)
519
+ lastHoveredRef.current = undefined
520
+ }
521
+ }
522
+
523
+ canvas.addEventListener('mousemove', handleMove)
524
+ canvas.addEventListener('mouseleave', handleLeaveCanvas)
525
+ return () => {
526
+ canvas.removeEventListener('mousemove', handleMove)
527
+ canvas.removeEventListener('mouseleave', handleLeaveCanvas)
528
+ }
529
+ },
530
+ [canvasContext, chartId, margin, seriesFilter, mouseOverHandlerFor, mouseLeaveHandlerFor]
531
+ )
532
+
533
+ // this plot needs a data source (see the `Chart`'s `dataSource` prop)
534
+ useEffect(() => {
535
+ if (dataSource === undefined) {
536
+ console.error("OutlierPlot requires a data source; set the enclosing Chart's `dataSource` prop")
537
+ }
538
+ }, [dataSource])
539
+
540
+ // unregister this plot's draw function on unmount
541
+ useEffect(
542
+ () => {
543
+ return () => {
544
+ if (canvasContext) {
545
+ canvasContext.unregister(`outlier-plot-autoscale-${chartId}`)
546
+ canvasContext.unregister(`outlier-plot-${chartId}`)
547
+ }
548
+ }
549
+ },
550
+ [canvasContext, chartId]
551
+ )
552
+
553
+ return null
554
+ }
555
+
556
+ /**
557
+ * Finds the topmost (narrowest, most-confident) band whose fill path contains `(localX, localY)`.
558
+ * `bands` is in draw order (widest first, narrowest/topmost last), so this iterates backwards to
559
+ * check the topmost band first, matching visual stacking.
560
+ * @param bands The bands' fill regions, as last drawn, in draw order
561
+ * @param context The canvas 2D context (for `isPointInPath`)
562
+ * @param localX The mouse's x-coordinate, in the coordinates `isPointInPath` tests against (see
563
+ * the caller for why that's the plot-area position scaled by the device-pixel ratio)
564
+ * @param localY The mouse's y-coordinate, in the same coordinates
565
+ * @return The hovered band's series and index, or `undefined` when the mouse isn't over a band
566
+ */
567
+ function findHoveredBand<M extends readonly number[]>(
568
+ bands: Array<BandHitRegion<M>>,
569
+ context: CanvasRenderingContext2D,
570
+ localX: number,
571
+ localY: number
572
+ ): {kind: 'band', seriesName: string, bandIndex: number} | undefined {
573
+ for (let i = bands.length - 1; i >= 0; i--) {
574
+ const band = bands[i]
575
+ if (context.isPointInPath(band.path, localX, localY)) {
576
+ return {kind: 'band', seriesName: band.seriesName, bandIndex: band.bandIndex}
577
+ }
578
+ }
579
+ return undefined
580
+ }
581
+
582
+ /**
583
+ * Returns the index of the largest bound the datum's y-value is outside of, or -1 if the value
584
+ * is inside every bound. Bounds are expected to be ordered from tightest (index 0) to widest
585
+ * (highest index), so the highest-index match identifies the most severe outlier tier.
586
+ * @param datum The datum
587
+ * @return The index of the largest bound the datum is outside of, or -1
588
+ */
589
+ function largestExceededBoundIndex<M extends readonly number[]>(datum: OutlierDatum<M>): number {
590
+ for (let i = datum.bounds.length - 1; i >= 0; i--) {
591
+ const {lower, upper} = datum.bounds[i]
592
+ if (datum.datum.y < lower || datum.datum.y > upper) return i
593
+ }
594
+
595
+ return -1
596
+ }
597
+
598
+ /**
599
+ * Attempts to locate the x- and y-axes for the specified series. If the series has no axis
600
+ * assignment, then uses the default (first) x- and y-axes.
601
+ * @param seriesName Name of the series for which to retrieve the axis
602
+ * @param axisAssignments A map holding the series name and the associated x- and y-axes assigned
603
+ * to that series
604
+ * @param xAxisFor The function that accepts an axis ID and returns the corresponding x-axis
605
+ * @param yAxisFor The function that accepts an axis ID and returns the corresponding y-axis
606
+ * @return The x- and y-axes (`undefined` when not found)
607
+ * @throws Error when either axis isn't a continuous numeric axis
608
+ */
609
+ function axesFor(
610
+ seriesName: string,
611
+ axisAssignments: Map<string, AxesAssignment>,
612
+ xAxisFor: (id: string) => BaseAxis | undefined,
613
+ yAxisFor: (id: string) => BaseAxis | undefined,
614
+ ): [xAxis: ContinuousNumericAxis | undefined, yAxis: ContinuousNumericAxis | undefined] {
615
+ const assigned = axisAssignments.get(seriesName)
616
+ const xAxisRaw = xAxisFor(assigned?.xAxis || "")
617
+ const yAxisRaw = yAxisFor(assigned?.yAxis || "")
618
+ checkAxisType("OutlierPlot", "x", xAxisRaw, AxisType.ContinuousNumeric)
619
+ checkAxisType("OutlierPlot", "y", yAxisRaw, AxisType.ContinuousNumeric)
620
+ const xAxis = xAxisRaw as ContinuousNumericAxis | undefined
621
+ const yAxis = yAxisRaw as ContinuousNumericAxis | undefined
622
+ return [xAxis, yAxis]
623
+ }
624
+
625
+ /**
626
+ * Calculate the number of points within a specific outlier band.
627
+ * @param plotData The array of outlier data points.
628
+ * @param bandIndex The index of the outlier band to calculate points for.
629
+ * @param [subtractLowerBandCount=true] Whether to subtract the count of points in the lower band. Defaults to true.
630
+ * @returns The number of points within the specified outlier band.
631
+ */
632
+ function calcPointsInBand<M extends readonly number[]>(
633
+ plotData: Array<OutlierDatum<M>> | FastShiftArray<OutlierDatum<M>>,
634
+ bandIndex: number,
635
+ subtractLowerBandCount: boolean = true
636
+ ): number {
637
+ // function to count the points in a specific outlier band
638
+ const countPointsInBand = (bandIndex: number) =>
639
+ plotData
640
+ .filter(datum => {
641
+ if (bandIndex < datum.bounds.length) {
642
+ return datum.datum.y >= datum.bounds[bandIndex].lower && datum.datum.y <= datum.bounds[bandIndex].upper
643
+ }
644
+ return true
645
+ }
646
+ ).length
647
+
648
+ const pointsInBand = countPointsInBand(bandIndex)
649
+ // we need to subtract the points in the lower band. recall that each band has ALL the points
650
+ // in the band's range, not what we may think of visually
651
+ if (bandIndex > 0 && subtractLowerBandCount) {
652
+ return pointsInBand - countPointsInBand(bandIndex - 1)
653
+ }
654
+ return pointsInBand
655
+ }
656
+
657
+ type CategorizedData<M extends readonly number[]> = {
658
+ regular: Array<OutlierDatum<M>>,
659
+ outlier: Array<OutlierDatumColor<M>>
660
+ }
661
+
662
+ /**
663
+ * Categorizes outlier data into regular and outlier points based on provided marker colors.
664
+ * @param data Array of outlier data to categorize
665
+ * @param outlierMarkerColors Array of colors to use for outlier markers
666
+ * @returns Object with regular and outlier data arrays
667
+ * @template M Type of the measure
668
+ */
669
+ function categorizePoints<M extends readonly number[]>(
670
+ data: FastShiftArray<OutlierDatum<M>>,
671
+ outlierMarkerColors: ReadonlyArray<string> = []
672
+ ): CategorizedData<M> {
673
+ return data.reduce<CategorizedData<M>>(
674
+ (accum, datum) => {
675
+ const bandIndex = largestExceededBoundIndex(datum)
676
+ if (bandIndex < 0) {
677
+ accum.regular.push(datum)
678
+ } else {
679
+ accum.outlier.push({datum, color: outlierMarkerColors[bandIndex] ?? "red"})
680
+ }
681
+ return accum
682
+ },
683
+ {regular: [], outlier: []} as CategorizedData<M>
684
+ )
685
+ }