stream-charts 1.2.1 → 2.0.1-snapshot

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
package/README.md CHANGED
@@ -1,1225 +1,1012 @@
1
- # <span id="top">stream-charts</span>
2
- [Homepage](https://robphilipp.github.io/stream-charts/) •
3
- [Code Docs](docs/index.html) •
4
- [Change History](changes.md) •
5
- [Example Project](https://github.com/robphilipp/stream-charts-examples)
6
-
7
- ## [&#10514;](#top) <span id="intro">intro</span>
8
- `stream-charts` is a [react](https://reactjs.org)-based library for building real-time time-series charts for viewing high frequency data. Ingesting and plotting new data for about 100 time-series every 25 ms isn't generally a problem.
9
-
10
- The following plot types are currently available:
11
-
12
- 1. **Scatter Plot**: for plotting time-series data.
13
- 2. **Raster Plot**: for plotting event-timing data.
14
- 3. **Bar Plot**: for plotting time-series as bars that show the current value and its minimum, maximum, and mean values for all times and time-windows.
15
- 4. **Poincare Plot**: for plotting function iterates (`f[n](t)` vs `f[n-m](t)`).
16
-
17
- > Try it on codesandbox [stream-charts](https://codesandbox.io/p/sandbox/n7c8jm)
18
-
19
- `Charts` are composable using react components. So you can add different axis types, multiple axes, trackers, tooltips, and plots. `stream-charts` provides the following react components from which to build your own charts:
20
- 1. The [Chart](#chart-usage) serves as the container, holding the other components as children, and providing the data to the plot.
21
- 2. The [ContinuousAxis](#continuous-axes-usage) and [OrdinalAxis](#ordinal-axes-usage) components define the x- and y-axes, and provide the scales for the axes. Charts are required to have at least one x-axis and one y-axis.
22
- 3. The optional [Tracker](#tracker-usage) component shows the value associated with the current mouse location of the axes to which it is assigned.
23
- 4. The optional [Tooltip](#tooltip-usage) component shows the `TooltipContent` for the datum under the mouse cursor. Each plot type has a default tooltip content component ([ScatterPlotTooltipContent](#scatterplot-tooltip-usage), [RasterPlotTooltipContent](#rasterplot-tooltip-usage), [BarPlotTooltipContent](#barplot-tooltip-usage), [PoincarePlotTooltipContent](#poincareplot-tooltip-usage)).
24
- 5. The `Plot` component displays the data. Currently, there are four plot components: [ScatterPlot](#scatter-plot-usage), [RasterPlot](#raster-plot-usage), [BarPlot](#bar-plot-usage), and [PoincarePlot](#poincare-plot-usage).
25
-
26
- ### [&#10514;](#content) <span id="axes">capabilities</span>
27
- Charts can have multiple x-axes and y-axes, and these axes can have different scales. Time series can be assigned to an axis or use the default one. Currently, there are continuous and ordinal (category) axes. Continuous axes are available with all the scales supported by [d3](https://d3js.org) (for example, linear scales, logarithmic scales, and power scales).
28
-
29
- Each plot type comes with a default tooltip and tooltip content. However, you can customize the tooltip and tooltip content to suit your needs.
30
-
31
- The charts are designed to be responsive to changes in size, for example, to support window resizing.
32
-
33
- With `stream-charts` you can display initial (static) data as well as live data from an [Observable](https://rxjs-dev.firebaseapp.com/api/index/class/Observable).
34
-
35
- All aspects of the `Charts` style are exposed allowing you to use your favorite frameworks theme provider, or none at all.
36
-
37
- `Charts` provide zooming and panning that can be enabled or disabled. And zooming is set to use a modifier key by default so that it doesn't interfere with normal scrolling.
38
-
39
- ### [&#10514;](#top) <span id="content">rendering performance</span>
40
- Charts can easily ingest and plot new data for about 100 time-series every 25 ms.
41
-
42
- When using lower update frequencies, such as < 250 ms, and data updates are sparse, you can set an update cadence so that the plot's time smoothly scrolls, rather than updating only when new data arrives.
43
-
44
- `Charts` can have a data TTL (time-to-live) to drop older data to allow long-running streams of data. With the data-update callback you can capture that data and store it in local storage, memory, or a location of your choice.
45
-
46
- To achieve this type of performance, the charts:
47
- 1. accept an [rxjs](https://rxjs-dev.firebaseapp.com) [Observable](https://rxjs-dev.firebaseapp.com/api/index/class/Observable) as the source of the time-series data, and
48
- 2. are implemented using [d3](https://d3js.org) SVG elements wrapped in [react](https://reactjs.org) functional components, and
49
- 3. keep chart updates outside [react](https://reactjs.org)'s render cycle.
50
-
51
-
52
- ### [&#10514;](#top) <span id="content">project status</span>
53
- The following plots are currently available:
54
-
55
- 1. **Scatter Plot**: for plotting time-series data.
56
- 2. **Raster Plot**: for plotting event-timing data.
57
- 3. **Bar Plot**: for plotting windowed stats (min, max, mean).
58
- 4. **Poincare Plot**: for plotting iterates (n vs n-1).
59
-
60
- Over time, I'll add additional chart types. In the meantime, I welcome any contributions to create new chart types (gauges, etc).
61
-
62
- ## [&#10514;](#top) <span id="content">content</span>
63
-
64
- **[quick start](#quick-start)**
65
- - [example raster chart code](#example-raster-chart-code)
66
- - [example scatter chart code](#example-scatter-chart-code)
67
- - [example bar chart code](#example-bar-chart-code)
68
- - [example poincare chart code](#example-poincare-chart-code)
69
-
70
- **[intro](#intro)**
71
- - [terminology](#terminology)
72
- - [data ttl, time-windows, performance](#data-ttl)
73
-
74
- **[chart](#chart)**
75
- - [&lt;Chart/&gt;](#chart-usage)
76
- - [dimensions](#chart-usage-dimensions)
77
- - [styling](#chart-usage-styling)
78
- - [initial data](#chart-usage-initial-data)
79
- - [streaming data](#chart-usage-streaming-data)
80
-
81
- **[axes](#axes)**
82
- - [&lt;ContinuousAxis/&gt;](#continuous-axes-usage)
83
- - [base properties](#continuous-axes-usage-base)
84
- - [styling](#continuous-axes-usage-styling)
85
-
86
- - [&lt;OrdinalAxis/&gt;](#ordinal-axes-usage)
87
- - [base properties](#ordinal-axes-usage-base)
88
- - [styling](#ordinal-axes-usage-styling)
89
-
90
- **[plots](#plots)**
91
- - [&lt;ScatterPlot/&gt;](#scatter-plot-usage)
92
- - [base properties](#scatter-plot-usage-base)
93
- - [view-modifying interactions](#scatter-plot-usage-view)
1
+ [![npm](https://img.shields.io/npm/v/stream-charts)](https://www.npmjs.com/package/stream-charts) [![build](https://github.com/robphilipp/stream-charts/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/robphilipp/stream-charts/actions/workflows/main.yml) [![license: MIT](https://img.shields.io/npm/l/stream-charts)](LICENSE)
94
2
 
95
- - [&lt;RasterPlot/&gt;](#raster-plot-usage)
96
- - [base properties](#raster-plot-usage-base)
97
- - [view-modifying interactions](#raster-plot-usage-view)
98
-
99
- - [&lt;BarPlot/&gt;](#bar-plot-usage)
100
- - [base properties](#bar-plot-usage-base)
101
- - [view-modifying interactions](#bar-plot-usage-view)
102
-
103
- - [&lt;PoincarePlot/&gt;](#poincare-plot-usage)
104
- - [base properties](#poincare-plot-usage-base)
105
- - [view-modifying interactions](#poincare-plot-usage-view)
106
-
107
- **[utilities](#utilities)**
108
- - [&lt;Tracker/&gt;](#tracker-usage)
109
- - [base properties](#tracker-usage-base)
110
- - [styling](#tracker-usage-styling)
111
- - [&lt;Legend/&gt;](#legend-usage)
112
- - [base properties](#legend-usage-base)
113
- - [styling](#legend-usage-styling)
114
- - [&lt;Tooltip/&gt;](#tooltip-usage)
115
- - [&lt;RasterPlotTooltipContent/&gt;](#rasterplot-tooltip-usage)
116
- - [&lt;ScatterPlotTooltipContent/&gt;](#scatterplot-tooltip-usage)
117
- - [&lt;BarPlotTooltipContent/&gt;](#barplot-tooltip-usage)
118
- - [&lt;PoincarePlotTooltipContent/&gt;](#poincareplot-tooltip-usage)
3
+ [Homepage](https://robphilipp.github.io/stream-charts/) •
4
+ [API Docs](https://robphilipp.github.io/stream-charts/docs/modules.html) •
5
+ [Change History](changes.md)
6
+
7
+ **`stream-charts`** is a [React](https://react.dev) library for real-time charts of high-frequency streaming data. This repository holds the library, and a **demo app** that shows each of its charts at work and lets you explore them.
8
+
9
+ <picture>
10
+ <source media="(prefers-color-scheme: dark)" srcset="packages/examples/public/images/charts-collage-dark.gif">
11
+ <img src="packages/examples/public/images/charts-collage-light.gif" alt="the five stream-charts plots: a scatter chart streaming live, scrolling smoothly with a 25 ms cadence, with two x-axes, a linear and a logarithmic y-axis, and a legend whose hovered entry highlights its series; a raster chart of neuron spikes with a legend and a tooltip; a bar chart with windowed statistics and a tooltip; a Poincaré plot of an iterated map with a tooltip; and an outlier chart with confidence bands, flagged outliers, and a tooltip">
12
+ </picture>
13
+
14
+ *Scatter, streaming (two x-axes, linear and log y-axes, hovering the legend highlights a series, 25 ms cadence) · raster (legend, tooltip) · bar (running and windowed statistics, tooltip) · Poincaré (tooltip) · outlier (confidence bands, flagged outliers, tooltip)*
15
+
16
+ **The library** charts data that never stops arriving. You hand it an [RxJS](https://rxjs.dev) observable of new data, and it buffers the updates, scrolls the time axes, and redraws – on a single canvas, so plotting new data for about 100 series every 25 ms isn't generally a problem. Along the way:
17
+
18
+ - **The data lives in a data source that your application owns**, not in the chart, so it keeps streaming while the chart is off screen, and the chart picks up where it left off when it comes back.
19
+ - **The charts stay usable while the data streams**: zoom and pan without losing "now", filter the series with a regular expression, and hover for tooltips (with statistics and sparklines), trackers, and legends that highlight the hovered series.
20
+ - **The charts are built from React components** – axes, a plot, and the extras you want – with up to two x-axes and two y-axes per chart, linear or logarithmic scales, and y-axes that can grow to fit the data.
21
+
22
+ **The demo app** (in [`packages/examples`](#run-demo)) has a tab for each chart type, each with a control bar for trying out the chart's options while the data streams – buffering, update rates, markers, interpolation, legends, and so on. Its intro walks through the charts and the library's design, with tutorials on adding your own data source, writing your own tooltips, and building a scatter chart from sensor data.
23
+
24
+ Five plot types are available:
25
+
26
+ | plot | for | example |
27
+ |---|---|---|
28
+ | [&lt;ScatterPlot/&gt;](#scatter-plot) | values over time (lines, with optional markers) | sensor readings, weights |
29
+ | [&lt;RasterPlot/&gt;](#raster-plot) | events over time, one row per category | neuron spikes |
30
+ | [&lt;BarPlot/&gt;](#bar-plot) | the current value and running statistics of each category | min, max, mean, and windowed min/max/mean |
31
+ | [&lt;PoincarePlot/&gt;](#poincare-plot) | a sequence plotted against itself (`f[n+m](x)` vs `f[n](x)`) | iterated maps, chaotic systems |
32
+ | [&lt;OutlierPlot/&gt;](#outlier-plot) | values over time, with confidence bands that flag outliers | a price index with 1σ, 2σ, 3σ bands |
33
+
34
+ > Try the demo app on CodeSandbox: [stream-charts demo](https://codesandbox.io/p/github/robphilipp/stream-charts/master). It opens this repository, installs it, and runs the demo app (it takes a minute or so to start).
35
+
36
+ ## <span id="content">content</span>
37
+
38
+ **[getting started](#getting-started)**
39
+ - [using the library in your application](#installation)
40
+ - [quick start](#quick-start)
41
+ - [how it works](#how-it-works)
42
+ - [migrating from 1.x](#migrating)
43
+ - [this repository: the library and the demo app](#repository)
44
+ - [running the demo app](#run-demo)
45
+ - [working on the library](#contributing)
46
+
47
+ **[data](#data)**
48
+ - [data sources](#data-sources)
49
+ - [generators](#generators)
50
+ - [chart data, one shape for each plot](#chart-data)
51
+ - [series and datum helpers](#series-helpers)
52
+ - [performance: data TTL, windowing, and cadence](#performance)
53
+
54
+ **[components](#components)**
55
+ - [&lt;Chart/&gt;](#chart)
56
+ - [axes: &lt;ContinuousAxis/&gt;, &lt;OrdinalAxis/&gt;, &lt;EmptyAxis/&gt;](#axes)
57
+ - [plots](#plots)
58
+ - [properties common to the plots](#plot-common)
59
+ - [&lt;ScatterPlot/&gt;](#scatter-plot)
60
+ - [&lt;RasterPlot/&gt;](#raster-plot)
61
+ - [&lt;BarPlot/&gt;](#bar-plot)
62
+ - [&lt;PoincarePlot/&gt;](#poincare-plot)
63
+ - [&lt;OutlierPlot/&gt;](#outlier-plot)
64
+ - [&lt;Tracker/&gt;](#tracker)
65
+ - [&lt;Legend/&gt;](#legend)
66
+ - [&lt;Tooltip/&gt; and tooltip content](#tooltip)
67
+ - [writing your own tooltip content](#custom-tooltip)
68
+
69
+ **[more](#more)**
70
+ - [theming](#theming)
71
+ - [filtering series](#filtering)
119
72
  - [hooks](#hooks)
73
+ - [further reading](#further-reading)
120
74
 
75
+ ---
121
76
 
122
- ## [&#10514;](#content) <span id="quick-start">quick start</span>
77
+ ## <span id="getting-started">getting started</span> [↑](#content "back to contents")
123
78
 
124
- ```shell
125
- npm install stream-charts
126
- ```
79
+ What to install depends on what you'd like to do:
127
80
 
128
- ### [&#10514;](#content) <span id="example-raster-chart-code">example raster chart</span>
129
- An example raster chart (see [example](https://github.com/robphilipp/stream-charts-examples/blob/master/src/app/examples/StreamingRasterChart.tsx))
81
+ | you'd like to... | install | then see |
82
+ |---|---|---|
83
+ | **use the charts in your own application** | the library, from npm | [using the library in your application](#installation), then the [quick start](#quick-start) |
84
+ | **explore the charts**, see how each one is put together, and try out ideas – a chart of your own, say – in a working app | the demo app: clone this repository | [running the demo app](#run-demo) |
85
+ | **add charts or features to the library**, and contribute them back with a pull request | the library's source, which comes with the demo app: fork this repository | [working on the library](#contributing) |
130
86
 
131
- ![raster-chart](https://github.com/robphilipp/stream-charts/blob/develop/images/raster-chart-video.gif)
87
+ ### <span id="installation">using the library in your application</span> [↑](#content "back to contents")
132
88
 
133
-
134
- ```typescript jsx
135
- <Chart
136
- chartId={chartId.current}
137
- width={useGridCellWidth()}
138
- height={useGridCellHeight()}
139
- margin={{...defaultMargin, top: 40, right: 75, left: 70, bottom: 40}}
140
- // svgStyle={{'background-color': 'pink'}}
141
- color={theme.color}
142
- backgroundColor={theme.backgroundColor}
143
- seriesStyles={new Map([
144
- ['neuron1', {
145
- ...defaultLineStyle(),
146
- color: 'orange',
147
- lineWidth: 2,
148
- highlightColor: 'orange'
149
- }],
150
- ['neuron2', {
151
- ...defaultLineStyle(),
152
- color: 'orange',
153
- lineWidth: 2,
154
- highlightColor: 'orange'
155
- }],
156
- ['neuron3', {
157
- ...defaultLineStyle(),
158
- color: 'orange',
159
- lineWidth: 2,
160
- highlightColor: 'orange'
161
- }],
162
- ['neuron4', {
163
- ...defaultLineStyle(),
164
- color: 'orange',
165
- lineWidth: 2,
166
- highlightColor: 'orange'
167
- }],
168
- ['neuron5', {
169
- ...defaultLineStyle(),
170
- color: 'orange',
171
- lineWidth: 2,
172
- highlightColor: 'orange'
173
- }],
174
- ['neuron6', {
175
- ...defaultLineStyle(),
176
- color: theme.name === 'light' ? 'blue' : 'gray',
177
- lineWidth: 3,
178
- highlightColor: theme.name === 'light' ? 'blue' : 'gray',
179
- highlightWidth: 5
180
- }],
181
- // ['test3', {...defaultLineStyle, color: 'dodgerblue', lineWidth: 1, highlightColor: 'dodgerblue', highlightWidth: 3}],
182
- ])}
183
- initialData={initialDataRef.current}
184
- seriesFilter={filter}
185
- seriesObservable={observableRef.current}
186
- shouldSubscribe={running}
187
- onUpdateAxesBounds={handleChartTimeUpdate}
188
- windowingTime={25}
189
- // onSubscribe={subscription => console.log("subscribed raster")}
190
- >
191
- <ContinuousAxis
192
- axisId="x-axis-1"
193
- location={AxisLocation.Bottom}
194
- domain={[0, 5000]}
195
- label="t (ms)"
196
- // font={{color: theme.color}}
197
- />
198
- <ContinuousAxis
199
- axisId="x-axis-2"
200
- location={AxisLocation.Top}
201
- domain={[0, 5000]}
202
- label="t (ms)"
203
- // font={{color: theme.color}}
204
- />
205
- <OrdinalAxis
206
- axisId="y-axis-1"
207
- location={AxisLocation.Left}
208
- categories={initialDataRef.current.map(series => series.name)}
209
- label="neuron"
210
- axisTickStyle={{rotation: 25}}
211
- />
212
- <OrdinalAxis
213
- axisId="y-axis-2"
214
- location={AxisLocation.Right}
215
- categories={initialDataRef.current.map(series => series.name)}
216
- label="neuron"
217
- />
218
- <Tracker
219
- visible={visibility.tracker}
220
- labelLocation={TrackerLabelLocation.ByAxis}
221
- labelFormatter={x => `${d3.format(",.0f")(x)} ms`}
222
- style={{color: theme.color}}
223
- font={{color: theme.color}}
224
- // onTrackerUpdate={update => console.dir(update)}
225
- />
226
- <Tooltip
227
- visible={visibility.tooltip}
228
- style={{
229
- fontColor: theme.color,
230
- backgroundColor: theme.backgroundColor,
231
- borderColor: theme.color,
232
- backgroundOpacity: 0.9,
233
- }}
234
- >
235
- <RasterPlotTooltipContent
236
- xFormatter={value => formatNumber(value, " ,.0f") + ' ms'}
237
- yFormatter={value => formatNumber(value, " ,.1f") + ' mV'}
238
- />
239
- </Tooltip>
240
- <RasterPlot
241
- axisAssignments={new Map([
242
- // ['test', assignAxes("x-axis-1", "y-axis-1")],
243
- ['neuron1', assignAxes("x-axis-2", "y-axis-2")],
244
- ['neuron2', assignAxes("x-axis-2", "y-axis-2")],
245
- ['neuron3', assignAxes("x-axis-2", "y-axis-2")],
246
- ['neuron4', assignAxes("x-axis-2", "y-axis-2")],
247
- ['neuron5', assignAxes("x-axis-2", "y-axis-2")],
248
- ['neuron6', assignAxes("x-axis-2", "y-axis-2")],
249
- // ['test3', assignAxes("x-axis-1", "y-axis-1")],
250
- ])}
251
- spikeMargin={1}
252
- dropDataAfter={5000}
253
- panEnabled={true}
254
- zoomEnabled={true}
255
- zoomKeyModifiersRequired={true}
256
- withCadenceOf={50}
257
- />
258
- </Chart>
89
+ ```shell
90
+ npm install stream-charts react react-dom rxjs d3
259
91
  ```
260
92
 
261
- ### [&#10514;](#content) <span id="example-scatter-chart-code">example scatter chart</span>
93
+ > This README describes version 2, whose API is different from version 1's. To upgrade from 1.x, see [migrating from 1.x](#migrating).
262
94
 
263
- An example scatter chart (see [example](https://github.com/robphilipp/stream-charts-examples/blob/master/src/app/examples/StreamingScatterChart.tsx))
95
+ `stream-charts` has four peer dependencies: React 19 (`react`, `react-dom`), [RxJS](https://rxjs.dev) 7, and [d3](https://d3js.org) 7. Your application uses them directly – to render the components, create the observables that feed the charts, and pass d3 scales and curves to the axes and plots – so the library shares your copies rather than bringing its own. TypeScript users get d3's type definitions (`@types/d3`) with the library.
264
96
 
265
- ![scatter-chart-tooltip](https://github.com/robphilipp/stream-charts/blob/develop/images/scatter-chart-video.gif)
97
+ ### <span id="quick-start">quick start</span> [↑](#content "back to contents")
266
98
 
99
+ This complete example streams three made-up temperature sensors, each reporting every 100 ms, into a scatter chart with a **Run**/**Pause** button. There are four steps: an [RxJS](https://rxjs.dev) observable that emits the data, a generator that creates that observable, a data source that holds the data, and a chart that shows it.
267
100
 
268
- ```typescript jsx
269
- <Chart
270
- chartId={chartId.current}
271
- width={useGridCellWidth()}
272
- height={useGridCellHeight()}
273
- margin={{...defaultMargin, top: 40, bottom: 40, right: 60}}
274
- // svgStyle={{'background-color': 'pink'}}
275
- color={theme.color}
276
- backgroundColor={theme.backgroundColor}
277
- seriesStyles={new Map([
278
- ['test1', {
279
- ...defaultLineStyle(),
280
- color: 'orange',
281
- lineWidth: 1,
282
- highlightColor: 'orange'
283
- }],
284
- ['test2', {
285
- ...defaultLineStyle(),
286
- color: theme.name === 'light' ? 'blue' : 'gray',
287
- lineWidth: 3,
288
- highlightColor: theme.name === 'light' ? 'blue' : 'gray',
289
- highlightWidth: 5
290
- }],
291
- ['test3', {
292
- ...defaultLineStyle(),
293
- color: theme.name === 'light' ? 'dodgerblue' : 'gray',
294
- lineWidth: 3,
295
- highlightColor: theme.name === 'light' ? 'dodgerblue' : 'gray',
296
- highlightWidth: 5
297
- }],
298
- ])}
299
- initialData={initialDataRef.current}
300
- seriesFilter={filter}
301
- seriesObservable={observableRef.current}
302
- shouldSubscribe={running}
303
- onUpdateAxesBounds={handleChartTimeUpdate}
304
- windowingTime={25}
305
- >
306
- <ContinuousAxis
307
- axisId="x-axis-1"
308
- location={AxisLocation.Bottom}
309
- domain={[10, 10000]}
310
- label="x-axis"
311
- />
312
- <ContinuousAxis
313
- axisId="y-axis-1"
314
- location={AxisLocation.Left}
315
- domain={[0, 1000]}
316
- label="y-axis"
317
- />
318
- <ContinuousAxis
319
- axisId="x-axis-2"
320
- location={AxisLocation.Top}
321
- domain={[100, 5000]}
322
- label="x-axis (2)"
323
- />
324
- <ContinuousAxis
325
- axisId="y-axis-2"
326
- location={AxisLocation.Right}
327
- scale={d3.scaleLog()}
328
- domain={[100, 1200]}
329
- label="y-axis (2)"
330
- />
331
- <Tracker
332
- visible={visibility.tracker}
333
- labelLocation={TrackerLabelLocation.ByAxis}
334
- labelFormatter={x => `${d3.format(",.0f")(x)} ms`}
335
- style={{color: theme.color}}
336
- font={{color: theme.color}}
337
- // onTrackerUpdate={update => console.dir(update)}
338
- />
339
- <Tooltip
340
- visible={visibility.tooltip}
341
- style={{
342
- fontColor: theme.color,
343
- backgroundColor: theme.backgroundColor,
344
- borderColor: theme.color,
345
- backgroundOpacity: 0.9,
346
- }}
347
- >
348
- <ScatterPlotTooltipContent
349
- xLabel="t (ms)"
350
- yLabel="count"
351
- yValueFormatter={value => formatNumber(value, " ,.0f")}
352
- yChangeFormatter={(y1, y2) => formatNumber(y2 - y1, " ,.0f")}
353
- />
354
- </Tooltip>
355
- <ScatterPlot
356
- interpolation={interpolation}
357
- axisAssignments={new Map([
358
- // ['test1', assignAxes("x-axis-1", "y-axis-1")],
359
- ['test2', assignAxes("x-axis-2", "y-axis-2")],
360
- ['test3', assignAxes("x-axis-2", "y-axis-1")],
361
- ])}
362
- dropDataAfter={20000}
363
- panEnabled={true}
364
- zoomEnabled={true}
365
- zoomKeyModifiersRequired={true}
366
- // withCadenceOf={30}
367
- // timeWindowBehavior={TimeWindowBehavior.SQUEEZE}
368
- />
369
- </Chart>
101
+ ```tsx
102
+ import {type JSX} from "react"
103
+ import {interval, type Observable} from "rxjs"
104
+ import {map} from "rxjs/operators"
105
+ import {
106
+ AxisLocation,
107
+ Chart,
108
+ ContinuousAxis,
109
+ type DataGenerator,
110
+ type Datum,
111
+ emptySeries,
112
+ ScatterPlot,
113
+ type TimeSeries,
114
+ type TimeSeriesChartData,
115
+ TimeSeriesDataSource,
116
+ useDataSourceRunning,
117
+ } from "stream-charts"
118
+
119
+ // 1. an observable that emits each sensor's *new* readings, every `updatePeriod` ms
120
+ function sensorReadings(names: Array<string>, startTime: number, updatePeriod: number): Observable<TimeSeriesChartData> {
121
+ const clockStart = performance.now()
122
+ return interval(updatePeriod).pipe(
123
+ // use the wall clock (not a tick count), so the time stays right when the browser throttles timers
124
+ map(() => startTime + (performance.now() - clockStart)),
125
+ map(time => ({
126
+ seriesNames: new Set(names),
127
+ maxTime: time,
128
+ maxTimes: new Map(names.map(name => [name, time])),
129
+ newPoints: new Map<string, Array<Datum>>(names.map(name => [
130
+ name,
131
+ [{x: time, y: 20 + 5 * Math.sin(time / 1000) + Math.random()}]
132
+ ])),
133
+ }))
134
+ )
135
+ }
370
136
 
137
+ // 2. a generator: the data source calls it each time it starts, handing it the series it holds
138
+ // right now, so that Run after Pause carries on from where the data left off
139
+ const sensorGenerator = (updatePeriod: number): DataGenerator<TimeSeriesChartData, TimeSeries> =>
140
+ currentSeries => {
141
+ const latestTime = currentSeries.reduce(
142
+ (latest, series) => Math.max(latest, series.last().map(datum => datum.x).getOrElse(0)),
143
+ 0
144
+ )
145
+ return sensorReadings(currentSeries.map(series => series.name), latestTime, updatePeriod)
146
+ }
147
+
148
+ // 3. the data source holds the data. It lives outside the component (here, at module level),
149
+ // so it keeps its data, and keeps streaming, even when the chart is unmounted
150
+ const dataSource = new TimeSeriesDataSource({
151
+ initialData: ['kitchen', 'garage', 'attic'].map(name => emptySeries<Datum>(name)),
152
+ generator: sensorGenerator(100),
153
+ dropDataAfter: 30_000, // keep the last 30 seconds of data
154
+ })
155
+
156
+ // 4. the chart reads from the data source
157
+ export function SensorChart(): JSX.Element {
158
+ const running = useDataSourceRunning(dataSource)
159
+ return (
160
+ <div>
161
+ <button onClick={() => running ? dataSource.stop() : dataSource.start()}>
162
+ {running ? 'Pause' : 'Run'}
163
+ </button>
164
+ <Chart chartId={1} width={800} height={400} dataSource={dataSource} windowingTime={100} dataUpdatePeriod={100}>
165
+ <ContinuousAxis axisId="x-axis" location={AxisLocation.Bottom} domain={[0, 10_000]} label="time (ms)"/>
166
+ <ContinuousAxis axisId="y-axis" location={AxisLocation.Left} domain={[10, 30]} label="temperature (°C)"/>
167
+ <ScatterPlot/>
168
+ </Chart>
169
+ </div>
170
+ )
171
+ }
371
172
  ```
372
173
 
373
- ### [&#10514;](#content) <span id="example-bar-chart-code">example bar chart</span>
174
+ Click **Run** and the three sensors start drawing. When the data reaches the end of the x-axis, the axis scrolls to keep up. To show your real data, replace `sensorReadings` with an observable of your own: a web socket (`rxjs/webSocket`), server-sent events, polling (`interval` + `switchMap` to a `fetch`), or a simulation. Nothing else needs to change.
374
175
 
375
- Example bar chart showing windowed statistics (see [example](https://github.com/robphilipp/stream-charts-examples/blob/develop/src/app/examples/StreamingBarChart.tsx)).
176
+ From here you can add [colors](#chart-styling), a [tooltip](#tooltip), a [tracker](#tracker), a [legend](#legend), and [zooming and panning](#plot-common). The [demo app](#run-demo), in this repository's `packages/examples`, has a complete, working example of each plot type. Its tutorial, [adding your own data source](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/adding-a-data-source.md), builds this quick start one step at a time.
376
177
 
377
- ![bar-chart-tooltip](https://github.com/robphilipp/stream-charts/blob/develop/images/bar-chart-video.gif)
178
+ ### <span id="how-it-works">how it works</span> [↑](#content "back to contents")
378
179
 
379
- ```typescript jsx
180
+ <picture>
181
+ <source media="(prefers-color-scheme: dark)" srcset="packages/examples/public/images/data-flow-dark.png">
182
+ <img src="packages/examples/public/images/data-flow-light.png" alt="how the data flows: the generator feeds the data source, which feeds a view driver for each mounted plot, which feeds the plot">
183
+ </picture>
380
184
 
381
- <Chart<OrdinalChartData, OrdinalDatum, BarSeriesStyle, WindowedOrdinalStats>
382
- chartId={chartId.current}
383
- width={useGridCellWidth()}
384
- height={useGridCellHeight()}
385
- margin={{...defaultMargin, top: 60, bottom: 80, right: 75, left: 70}}
386
- // svgStyle={{'background-color': 'pink'}}
387
- color={theme.color}
388
- backgroundColor={theme.backgroundColor}
389
- seriesStyles={new Map<string, BarSeriesStyle>([
390
- ['neuron1', {
391
- ...defaultBarSeriesStyle('orange'),
392
- lineWidth: 2,
393
- minMaxBar: {
394
- ...defaultBarSeriesStyle('orange').minMaxBar,
395
- stroke: {
396
- ...defaultBarSeriesStyle('orange').minMaxBar.stroke,
397
- width: 0
398
- }
399
- }
400
- } as BarSeriesStyle],
401
- ['neuron14', {
402
- ...defaultBarSeriesStyle(theme.name === 'light' ? 'blue' : 'gray'),
403
- lineWidth: 3,
404
- highlightWidth: 5,
405
- minMaxBar: {
406
- ...defaultBarSeriesStyle(theme.name === 'light' ? 'blue' : 'gray').minMaxBar,
407
- widthFraction: 1
408
- }
409
-
410
- } as BarSeriesStyle],
411
- ['neuron31', {
412
- ...defaultBarSeriesStyle('green'),
413
- lineWidth: 2,
414
- } as BarSeriesStyle],
415
- ])}
416
- initialData={initialDataRef.current}
417
- seriesObservable={observableRef.current}
418
- seriesFilter={filter}
419
- shouldSubscribe={running}
420
- onUpdateChartTime={handleChartTimeUpdate}
421
- onUpdateAxesBounds={handleChartRangeUpdate}
422
- windowingTime={25}
423
- >
424
- <OrdinalAxis
425
- axisId="x-axis-1"
426
- location={AxisLocation.Bottom}
427
- categories={initialDataRef.current.map(series => series.name)}
428
- label="neuron"
429
- axisTickStyle={{rotation: 90}}
430
- />
431
- <OrdinalAxis
432
- axisId="x-axis-2"
433
- location={AxisLocation.Top}
434
- categories={initialDataRef.current.map(series => series.name)}
435
- label="neuron"
436
- axisTickStyle={{rotation: 40}}
437
- />
438
- <ContinuousAxis
439
- axisId="y-axis-1"
440
- location={AxisLocation.Left}
441
- domain={[-1.1, 1.1]}
442
- label="ρ (mV)"
443
- />
444
- <ContinuousAxis
445
- axisId="y-axis-2"
446
- location={AxisLocation.Right}
447
- domain={[-1.1, 1.1]}
448
- label="ρ (mV)"
449
- />
450
- <Tracker
451
- visible={visibility.tracker}
452
- trackerAxis={AxisLocation.Left}
453
- labelLocation={TrackerLabelLocation.ByAxis}
454
- labelFormatter={x => `${d3.format(".2f")(x)} mV`}
455
- style={{color: theme.color}}
456
- font={{color: theme.color}}
457
- // onTrackerUpdate={update => console.dir(update)}
458
- />
459
- <Tooltip
460
- visible={visibility.tooltip}
461
- style={{
462
- fontColor: theme.color,
463
- backgroundColor: theme.backgroundColor,
464
- borderColor: theme.color,
465
- backgroundOpacity: 0.9,
466
- }}
467
- >
468
- <BarPlotTooltipContent ordinalUnits="mV"/>
469
- </Tooltip>
470
- <BarPlot
471
- barMargin={1}
472
- dropDataAfter={5000}
473
- panEnabled={true}
474
- zoomEnabled={true}
475
- zoomKeyModifiersRequired={true}
476
- // withCadenceOf={50}
477
-
478
- // to have the upper axis zoom when the series zoom, we must have at
479
- // least one series assigned to this axis.
480
- axisAssignments={new Map([
481
- ['neuron1', assignAxes("x-axis-2", "y-axis-2")],
482
- ])}
483
-
484
- showMinMaxBars={showMinMax}
485
- showValueLines={showValue}
486
- showMeanValueLines={showMean}
487
- showWindowedMinMaxBars={showWinMinMax}
488
- showWindowedMeanValueLines={showWinMean}
489
- />
490
- </Chart>
491
- ```
185
+ (The diagram is from the demo app, whose data generators stand in for your application's.)
492
186
 
493
- ### [&#10514;](#content) <span id="example-poincare-chart-code">example poincare chart</span>
187
+ 1. **The generator** is a function that creates an RxJS observable of chart data. Each emission holds only the *new* points since the last one.
188
+ 2. **The data source** subscribes to the generator's observable when you call `start()`, adds each chunk of new points to its series, and drops the points that are older than its `dropDataAfter` setting. `stop()` unsubscribes, and nothing keeps running in the background.
189
+ 3. **The `<Chart>`** is handed the data source. The chart and its plots only *read* from the data source; they never subscribe to the stream themselves. While a plot is mounted, its *view driver* listens for the data source's updates, batches them (see `windowingTime`), scrolls the time axes, and has the plot redraw.
190
+ 4. **The plot** draws the data onto a single `<canvas>`, outside of React's render cycle.
494
191
 
495
- Example Poincare (iterates) chart (see [example](https://github.com/robphilipp/stream-charts-examples/blob/develop/src/app/examples/StreamingPoincareChart.tsx)).
192
+ Because the data source isn't tied to any component, *you* decide how long it lives. Keep it in a store, a context, or at module level, and it outlives the chart. The user can start a chart, navigate to another page, and come back to find that it kept streaming the whole time. (The examples keep theirs in [zustand](https://www.npmjs.com/package/zustand) stores, but any of these works.) For a longer walk through this, see [how the data flows](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/how-the-data-flows.md).
496
193
 
497
- ![poincare-chart-with-tooltip](https://github.com/robphilipp/stream-charts/blob/develop/images/poincare-video.gif)
194
+ The chart is composed from React components, so you pick the pieces you need: up to two x-axes and two y-axes, a plot, and optionally a tracker, a tooltip, and a legend.
498
195
 
499
- ```typescript jsx
500
- <Chart
501
- chartId={chartId.current}
502
- width={useGridCellWidth()}
503
- height={useGridCellHeight()}
504
- margin={{...defaultMargin, top: 40, bottom: 30, right: 60}}
505
- // svgStyle={{'background-color': 'pink'}}
506
- color={theme.color}
507
- backgroundColor={theme.backgroundColor}
508
- seriesStyles={new Map([
509
- ['test1', {
510
- ...defaultLineStyle(),
511
- color: 'orange',
512
- lineWidth: 1,
513
- highlightColor: 'orange'
514
- }],
515
- ['test2', {
516
- ...defaultLineStyle(),
517
- color: theme.name === 'light' ? 'blue' : 'gray',
518
- lineWidth: 1,
519
- highlightColor: theme.name === 'light' ? 'blue' : 'gray',
520
- highlightWidth: 5
521
- }],
522
- ['test3', {
523
- ...defaultLineStyle(),
524
- color: theme.name === 'light' ? 'red' : 'gray',
525
- lineWidth: 1,
526
- highlightColor: theme.name === 'light' ? 'dodgerblue' : 'gray',
527
- highlightWidth: 5
528
- }],
529
- ])}
530
- initialData={initialDataRef.current}
531
- // seriesFilter={filter}
532
- seriesObservable={observableRef.current}
533
- shouldSubscribe={running}
534
- onUpdateChartTime={handleChartTimeUpdate}
535
- windowingTime={25}
536
- >
537
- <ContinuousAxis
538
- axisId="x-axis-1"
539
- location={AxisLocation.Bottom}
540
- domain={axesRange}
541
- label="f[n](x)"
542
- updateAxisBasedOnDomainValues={false}
543
- />
544
- <ContinuousAxis
545
- axisId="y-axis-1"
546
- location={AxisLocation.Left}
547
- domain={axesRange}
548
- label={`f[n+${lagN}](x)`}
549
- updateAxisBasedOnDomainValues={false}
550
- />
551
- <ContinuousAxis
552
- axisId="x-axis-2"
553
- location={AxisLocation.Top}
554
- domain={axesRange}
555
- label="f[n](x)"
556
- updateAxisBasedOnDomainValues={false}
557
- />
558
- <ContinuousAxis
559
- axisId="y-axis-2"
560
- location={AxisLocation.Right}
561
- domain={axesRange}
562
- label={`f[n+${lagN}](x)`}
563
- updateAxisBasedOnDomainValues={false}
564
- />
565
- <Tracker
566
- key="tracker-x-axis"
567
- visible={visibility.tracker}
568
- // labelLocation={TrackerLabelLocation.Nowhere}
569
- labelLocation={TrackerLabelLocation.ByAxis}
570
- trackerAxis={AxisLocation.Bottom}
571
- labelFormatter={x => `${d3.format(",.3f")(x)}`}
572
- style={{color: theme.color}}
573
- font={{color: theme.color}}
574
- // onTrackerUpdate={update => console.dir("bottom", update)}
575
- />
576
- <Tracker
577
- key="tracker-y-axis"
578
- visible={visibility.tracker}
579
- // labelLocation={TrackerLabelLocation.Nowhere}
580
- labelLocation={TrackerLabelLocation.ByAxis}
581
- trackerAxis={AxisLocation.Left}
582
- labelFormatter={x => `${d3.format(",.3f")(x)}`}
583
- style={{color: theme.color}}
584
- font={{color: theme.color}}
585
- // onTrackerUpdate={update => console.dir("left", update)}
586
- />
587
- <Tooltip
588
- visible={visibility.tooltip}
589
- style={{
590
- fontColor: theme.color,
591
- backgroundColor: theme.backgroundColor,
592
- borderColor: theme.color,
593
- backgroundOpacity: 0.9,
594
- }}
595
- >
596
- <PoincarePlotTooltipContent
597
- xLabel="t (ms)"
598
- yLabel="f(x)"
599
- yValueFormatter={value => formatNumber(value, " ,.4f")}
600
- style={{
601
- ...defaultTooltipStyle,
602
- fontColor: 'black',
603
- // fontColor: theme.color,
604
- fontWeight: 650
605
- }}
606
- />
607
- </Tooltip>
608
- <PoincarePlot
609
- interpolation={interpolation}
610
- dropDataAfter={dropAfterMs}
611
- panEnabled={true}
612
- zoomEnabled={true}
613
- zoomKeyModifiersRequired={true}
614
- // withCadenceOf={30}
615
- />
196
+ ```tsx
197
+ <Chart dataSource={dataSource} ...>
198
+ <ContinuousAxis axisId="x-axis-1" location={AxisLocation.Bottom} .../>
199
+ <ContinuousAxis axisId="y-axis-1" location={AxisLocation.Left} .../>
200
+ <Tracker .../>
201
+ <Tooltip ...><ScatterPlotTooltipContent .../></Tooltip>
202
+ <Legend .../>
203
+ <ScatterPlot .../>
616
204
  </Chart>
617
205
  ```
618
206
 
619
- ## [&#10514;](#content) <span id="intro">intro</span>
207
+ ### <span id="migrating">migrating from 1.x</span> [↑](#content "back to contents")
620
208
 
621
- `stream-charts` aim to provide high-performance charts for displaying large amounts of data in real-time. The examples (above) show various plot types whose data can be updated about every 25 ms.
209
+ This version is a substantial redesign, and code written for 1.x needs updating.
622
210
 
623
- There are obviously limits to the amount of data, and the performance of the plots. For example, the raster chart plots thousands of lines in the chart. When displaying more data in the raster plot, update performance will suffer. Therefore, you must tune your plots somewhat when you are working in the limits of their performance.
211
+ | 1.x | now |
212
+ |---|---|
213
+ | `<Chart initialData={...} seriesObservable={...} shouldSubscribe={running}>` | Create a [data source](#data-sources) with your initial data and a [generator](#generators), and pass it as `<Chart dataSource={...}>`. Start and stop it with `dataSource.start()` and `dataSource.stop()`. |
214
+ | `onSubscribe`, `onUpdateData` on the `<Chart>` | `onUpdateData` is now an option of the data source. To watch the stream, subscribe to `dataSource.chartDataUpdatesObservable`, or to `dataSource.isRunningObservable` to follow start/stop. |
215
+ | `dropDataAfter` on the plot | `dropDataAfter` is an option of the data source (change it later with `setDropDataAfter`). |
216
+ | `Datum` was `{time, value}` | `Datum` is `{x, y}`. Create one with `datumOf(time, value)`. |
217
+ | `seriesFromTuples(name, tuples)` | `timeSeriesFromTuples(name, tuples)` (and `iterateSeriesFromTuples`, `ordinateSeriesFromTuples` for the other shapes). |
218
+ | charts drawn with SVG elements | charts drawn on a single `<canvas>`. The `svgStyle` prop is still accepted, and styles the chart's container. |
219
+ | `TrackerLabelLocation.WithMouse` | removed. Use `TrackerLabelLocation.ByAxis` (the default) or `TrackerLabelLocation.Nowhere`. |
220
+ | `zoomKeyModifiersRequired` defaulted to `false` | it now defaults to `true`: hold shift (or ctrl) while scrolling to zoom. |
221
+ | `useDataObservable()`, `useInitialData()` hooks | `useDataSource()`, and `useDataSourceRunning(dataSource)`. |
222
+ | deep imports, such as `stream-charts/dist/app/charts/...` | import everything from `stream-charts`. Anything not exported from the package root is internal and may change. |
624
223
 
625
- ### [&#10514;](#content) <span id="terminology">terminology</span>
224
+ New in this version: the [&lt;OutlierPlot/&gt;](#outlier-plot), [&lt;EmptyAxis/&gt;](#empty-axis), [auto-scaling y-axes](#continuous-axis-autoscale), markers on the scatter plot, axis highlighting on hover, colorized lines in the Poincaré plot, zoom state that survives remounting (bar and Poincaré plots), [custom tooltip content](#custom-tooltip) built on `<TooltipContent>`, and data that keeps streaming while the chart is unmounted.
626
225
 
627
- A &lt;Chart/&gt; holds a `plot`, the `axes`, and optionally, a &lt;Tracker/&gt;, and a &lt;Tooltip/&gt;. A &lt;Chart/&gt; is generic, and holds a specific type of `plot`, for example, a &lt;RasterPlot/&gt;, &lt;ScatterPlot/&gt;, &lt;BarPlot/&gt;, or &lt;PoincarePlot/&gt;. The `plot` holds the data, and optionally provides pan and zoom. The `axes` provide the scale of the data. For example, the scale could be a continuous numeric logarithmic scale, or an ordinal scale. The `axes` are also generic. Though, a `plot` can restrict the type of `axes` allowed. For example, a &lt;RasterPlot/&gt; requires that the y-axes are ordinal axes. &lt;Trackers/&gt; are generic. &lt;Tooltips/&gt; are also generic, though the tooltip content is a child, specific to a plot type, that it knows how to interpret and present the data.
226
+ ### <span id="repository">this repository: the library and the demo app</span> [↑](#content "back to contents")
628
227
 
629
- **&lt;Chart/&gt;** [&#8628;](#chart)<br>
630
- Generic container which holds the `Axes`, `Plot`, `Tracker`, `Tooltip`.
228
+ This repository is a monorepo (npm workspaces) holding two packages: the library, and a demo app that uses it.
631
229
 
632
- **Axes** [&#8628;](#axes)<br>
633
- Defines the scale of the data. `stream-charts` current has two axis types: [&lt;ContinuousAxis/&gt;](#continuous-axes-usage) and [&lt;OrdinalAxis/&gt;](#ordinal-axes-usage). The &lt;ContinuousAxis/&gt; can be used as an x-axis or y-axis. However, the &lt;OrdinalAxis/&gt; can only be used as a y-axes because these are all time-series charts and the x-axis currently only represents time.
230
+ | where | what |
231
+ |---|---|
232
+ | `packages/stream-charts` | **The library**, published to npm as `stream-charts`. Its code is in `src/app/charts`, and its public API – everything you can import from `"stream-charts"` – is `src/index.ts`. |
233
+ | `packages/examples` | **The demo app** (not published). The examples are in `src/app/examples`, and the guide pages shown in its intro are the markdown files in `src/app/examples/docs`. |
234
+ | `docs/` | The API docs, generated from the library's source, and served at [robphilipp.github.io/stream-charts/docs](https://robphilipp.github.io/stream-charts/docs/modules.html). |
235
+ | `README.md`, `changes.md` | This page and the change history – the library's, copied into its package when it's published. |
634
236
 
635
- **Plot** [&#8628;](#plots)<br>
636
- The plot is a container for the data that uses the `axes` for scale, domain, and range information. Plots provide panning and zooming of the x-axis (time), interacting with the `axes` to update the time-range. The plot is the visual representation of the data. Currently, four types of plots are available: [&lt;RasterPlot/&gt;](#raster-plot-usage), [&lt;ScatterPlot/&gt;](#scatter-plot-usage), [&lt;BarPlot/&gt;](#bar-plot-usage), and [&lt;PoincarePlot/&gt;](#poincare-plot-usage).
237
+ While you're working in this repository, the demo app's `"stream-charts"` import resolves to the library's TypeScript *source* (through a `source` condition in the library's `exports`), so a change to the library shows up in the running app right away, with no build step. Published copies of the library don't use that condition, and get the compiled `dist/`.
637
238
 
638
- **&lt;Tracker/&gt;** [&#8628;](#tracker-usage)<br>
639
- The tracker displays the current plot time of the mouse. When multiple x-axes are used in the chart, then the tracker displays both times (i.e. from the upper and lower x-axis).
239
+ ### <span id="run-demo">running the demo app</span> [↑](#content "back to contents")
640
240
 
641
- **&lt;Tooltip/&gt;** [&#8628;](#tooltip-usage)<br>
642
- The tooltip is a generic component for rendering information about the data when the user mouses over a series or datum. The `<Tooltip/>` expects a child component that understands the data and renders the information show in the tooltip. For example, when using a [&lt;RasterPlot/&gt;](#raster-plot-usage), the [&lt;RasterPlotTooltipContent/&gt;](#rasterplot-tooltip-usage) renders the data for the raster plot tooltip. And when using a [&lt;ScatterPlot/&gt;](#scatter-plot-usage), the [&lt;ScatterPlotTooltipContent/&gt;](#scatterplot-tooltip-usage) renders the data for the scatter plot tooltip. The tooltip content can be extended.
241
+ The demo app, in `packages/examples`, has one tab for each plot type, with a control bar for trying out the plots' options, and an intro made of guide pages and tutorials. You'll need [Node.js](https://nodejs.org) 22.12+ (or 20.19+) and npm 7+. Clone the repository and start the app:
643
242
 
644
- ### [&#10514;](#content) <span id="data-ttl">data ttl, time-windows, performance</span>
243
+ ```shell
244
+ git clone https://github.com/robphilipp/stream-charts.git
245
+ cd stream-charts
246
+ npm install # installs both packages (one node_modules, one lockfile, at the root)
247
+ npm run dev # starts the demo app (Vite), at http://localhost:5173
248
+ ```
645
249
 
646
- The `stream-charts` have been designed to work with fairly large amounts of high frequency data. When the data time exceeds the larges time on the x-axes, the chart begins to scroll, shifting the time-window to keep up with the present data time. The data that *falls off* the chart is held in memory and can be viewed by changing the time-window with "zoom" or panning to earlier times.
250
+ <picture>
251
+ <source media="(prefers-color-scheme: dark)" srcset="packages/examples/public/images/intro-example-parts-dark.png">
252
+ <img src="packages/examples/public/images/intro-example-parts-light.png" alt="the anatomy of an example chart: the tabs and control bar are the demo app's, and the chart and its legend are the library's">
253
+ </picture>
647
254
 
648
- For long-running charts, the amount of data may get large. Because of this, each `stream-charts` plot provides a data time-to-live (TTL) setting called `dropDataAfter` which is the number of milliseconds, in chart time, after which the data is dropped. The TTL applies to data that *falls off* the chart. The TTL does not refer to real time. The data will not dissappear fro the plot of the `dropDataAfter` milliseconds (see [dropDataAfter](#raster-plot-drop) for raster plots, and [dropDataAfter](#scatter-plot-drop) for scatter plots).
255
+ Each tab runs one chart: click **Run**, and open its control bar to try the options while the data streams. The **Intro** tab walks through the charts and how they work. The tabs and control bars are the demo app's; the chart and its legend are the library's.
649
256
 
650
- As the amount of data and the update frequency increase, you have three levers to make your plots run more smoothly when the start to stutter. Each set of streaming data is unique and may require tuning when the default settings aren't giving the desired results.
257
+ To try out an idea, change an example in `packages/examples/src/app/examples` (for example, `StreamingScatterChart.tsx`), and the app reloads as you save. The tutorial [your own scatter chart, from sensor data](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/scatter-chart-from-sensor-data.md) builds a new chart from scratch. To give a new example its own tab:
651
258
 
652
- 1. **Drop data with the data TTL.** By dropping data, `d3` has less data to manage and will run faster. Without TTD, the data the falls off the time-window as the plot scroll is still kept in memory, and is still processed by `d3`, but it is clipped, and so not shown. Dropping data will reduce the processing load as data is streamed in. To later view that dropped data, you can store the data from your observable with a separate observer, and when the streaming is complete, load that data once into the plot.
653
- 2. **Decrease the windowing-time.** The windowing-time, not to be confused with the time-window described below, determines how long (in milliseconds) the incoming data is buffered before the plot is updated. Longer windowing times give better performance, though, at the cost of more choppy scrolling.
654
- 3. **Decrease the time-window.** The time-window is defined by the (min, max) time values of your axes. Smaller time-windows mean less data, but also it will stream by faster.
259
+ 1. Put the example component in `packages/examples/src/app/examples`.
260
+ 2. Add a route component for it in `packages/examples/src/app/routeComponents.tsx`.
261
+ 3. Add a route for it to the route tree in `packages/examples/src/app/router.tsx`.
262
+ 4. Add `{name, path}` for the tab to `TABS` in `packages/examples/src/app/routeData.ts`.
655
263
 
656
- As a final note, generally, the &lt;ScatterPlot/&gt; performs better than the &lt;RasterPlot/&gt;.
264
+ The demo app imports everything from `"stream-charts"`, just as your application would, so the chart code carries over to your application. The app's stores (zustand), control bars, and routing are its own.
657
265
 
658
- ## [&#10514;](#content) <span id="chart">chart</span>
266
+ ### <span id="contributing">working on the library</span> [↑](#content "back to contents")
659
267
 
660
- The `stream-charts` module wraps [d3](http://d3js.org) elements with functional [react](http://reactjs.org) in a way that keeps the chart (d3) updates out of the react render cycle. All `stream-charts` start with the [&lt;Chart/&gt;](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/Chart.tsx) root element.
268
+ To add a chart or a feature to the library (see [this repository](#repository) for where everything is):
661
269
 
662
- ### [&#10514;](#content) <span id="chart-usage">&lt;Chart/&gt;</span>
270
+ 1. **Fork the repository** on GitHub, clone your fork (instead of this repository), and make a branch from `develop` for your change. You'll need the same Node.js and npm as for [running the demo app](#run-demo).
271
+ 2. **Run the demo app** (`npm run dev`), and change the library in `packages/stream-charts/src/app/charts`. The demo app runs on the library's source, so your change shows up as you save. Add or change an example in the demo app to show it off.
272
+ 3. **Add tests** next to the code you changed: the library's tests are the `*.test.ts` files in `packages/stream-charts/src/app/charts`, and the demo app's are the `*.test.ts` files in `packages/examples/src`. Check that everything passes: `npm test`, `npm run lint`, and `npm run build`.
273
+ 4. **Update the docs**: this README for anything that changes the library's API, and `changes.md` for the change history. Leave the `version` in `packages/stream-charts/package.json` alone: a merge to `master` with a new version stages the library on npm, for a maintainer to approve and release.
274
+ 5. **Open a pull request** against `develop`. The repository's CI runs only for `master` (where the releases are), not for pull requests to `develop`, so run the checks in step 3 yourself before you open it.
663
275
 
664
- The `Chart` component creates the main SVG element (container) holding the chart, manages a reference to that container, and is the wraps the children in the chart context provider so that they have access to the [useChart](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/hooks/useChart.tsx) hook which holds properties, styles, callbacks, subscription needed to construct the charts and make them interactive.
276
+ The commands, all from the repository's root:
665
277
 
666
- The `Chart`s properties fall into four categories:
667
- 1. container dimensions
668
- 2. chart style
669
- 3. initial (static data)
670
- 4. streamed data and how to manage the stream of data
278
+ ```shell
279
+ npm run dev # runs the demo app, on the library's source
280
+ npm run build # compiles the library to packages/stream-charts/dist, then builds the demo app
281
+ npm test # runs both packages' tests
282
+ npm run lint # lints both packages
283
+ npm run docs # regenerates the API docs into docs/
284
+ ```
671
285
 
672
- #### [&#10514;](#content) <span id="chart-usage-dimensions">&lt;Chart/&gt; dimensions</span>
673
- **width (pixels)**<br>
674
- The width (in pixels) of the container that holds the chart. The actual plot will be smaller based on the margins.
286
+ To work on one package, add `-w` and its name: for example, `npm test -w stream-charts` runs only the library's tests, and `npm run preview -w stream-charts-examples` serves the demo app's production build (after `npm run build`). To see what would be published, `npm pack -w stream-charts --dry-run`.
675
287
 
676
- **height (pixels)**<br>
677
- The height (in pixels) of the container that holds the chart. The actual plot will be smaller based on the margins.
288
+ ---
678
289
 
679
- #### [&#10514;](#content) <span id="chart-usage-styling">&lt;Chart/&gt; styling</span>
680
- **margin ([Margin](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/margins.ts), optional)**<br>
681
- The margin (in pixels) around plot. For example, if the container has a (h, w) = (300, 600) and a margin of 10 pixels for the top, left, right, bottom, then the actual plot will have a (h, w) = (290, 590), leaving only 10 pixels around the plot for axis titles, ticks, and axis labels.
290
+ ## <span id="data">data</span> [↑](#content "back to contents")
682
291
 
683
- The Margin has the following shape
684
- >```typescript
685
- >interface Margin {
686
- top: number
687
- bottom: number
688
- left: number
689
- right: number
690
- >}
691
- >```
292
+ ### <span id="data-sources">data sources</span> [↑](#content "back to contents")
692
293
 
294
+ A data source owns the stream. It holds the series, subscribes to the generator while it's running, and applies the data-retention rules. Each plot type has its own kind of data, and so its own data source.
693
295
 
694
- **color (string, optional)**<br>
695
- The color of the axis lines and text, which can be overridden specifically by the axes styles.
296
+ | your data is... | data source | chart data | datum | plot |
297
+ |---|---|---|---|---|
298
+ | values over time | `TimeSeriesDataSource` | `TimeSeriesChartData` | `Datum` (`{x, y}`) | `<ScatterPlot>` |
299
+ | events over time | `TimeSeriesDataSource` | `TimeSeriesChartData` | `Datum` (`{x, y}`) | `<RasterPlot>` |
300
+ | a sequence plotted against itself | `IteratesDataSource` | `IterateChartData` | `IterateDatum` (`{time, iterateN, iterateN_1}`) | `<PoincarePlot>` |
301
+ | the latest value of each category | `OrdinalDataSource` | `OrdinalChartData` | `OrdinalDatum` (`{time, ordinal, value}`) | `<BarPlot>` |
302
+ | values over time, with confidence bounds | `OutlierDataSource` | `OutlierChartData` | `OutlierDatum` (`{datum, bounds}`) | `<OutlierPlot>` |
696
303
 
697
- **backgroundColor (string, optional)**<br>
698
- The color of the chart background (the whole chart, not just the plot).
304
+ Every data source is created with an options object:
699
305
 
700
- **svgStyle ([SvgStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/svgStyle.ts), optional)**<br>
701
- The style attributes for the main SVG element, in case you want to change those. Generally, this is not needed.
306
+ ```ts
307
+ const dataSource = new TimeSeriesDataSource({
308
+ initialData, // the series to start with
309
+ generator, // creates the observable each time the source starts
310
+ dropDataAfter: 20_000, // optional: drop data older than 20 s (default: never drop)
311
+ onUpdateData: (seriesName, newData) => {/* e.g. archive the data */}, // optional
312
+ })
313
+ ```
702
314
 
703
- The SvgStyle has the following shape
704
- >```typescript
705
- >interface SvgStyle {
706
- height?: string | number
707
- width?: string | number
708
- outline?: string
709
- // any valid SVG CSS attribute
710
- [propName: string]: any
711
- >}
712
- >```
315
+ | option | description |
316
+ |---|---|
317
+ | `initialData` | The series to start with. The data source takes ownership of these series, and changes them in place as data arrives. For the time-series, iterates, and ordinal data sources, **the initial data decides the series**: points for a series that isn't in the initial data are ignored. So start with one (possibly empty) series for each series you expect. The outlier data source is the exception: it adds a series the first time it appears in the stream. |
318
+ | `generator` | A [generator](#generators): the function that creates the observable of chart data each time the data source starts. |
319
+ | `dropDataAfter` | Optional time-to-live (ms, in data time, not wall-clock time) for the data. As new data arrives, points older than this, relative to the latest time, are dropped. Defaults to `Infinity` (keep everything). See [performance](#performance). |
320
+ | `onUpdateData` | Optional callback that is handed each series' new points as they arrive, for example to store the data elsewhere before it's dropped. |
321
+ | `timeGroupFor` | `TimeSeriesDataSource` only, optional. Assigns each series to a "time group" whose latest time `dropDataAfter` is measured against. Use it when series on different x-axes run on different clocks, for example `name => axisAssignments.get(name)?.xAxis ?? 'x-axis-1'`. By default, all series share one clock. |
322
+
323
+ Every data source has the same lifecycle and the same read-only view of its data:
324
+
325
+ | member | description |
326
+ |---|---|
327
+ | `start()` | Subscribes to the generator, handing it the current series. Does nothing when already running. A data source starts out stopped. |
328
+ | `stop()` | Unsubscribes from the generator. Nothing keeps generating in the background. The data stays. |
329
+ | `running` | Whether the data source is running. |
330
+ | `isRunningObservable` | Emits whether the data source is running, starting with the current value. Useful for a Run/Pause button (or use the [`useDataSourceRunning`](#hooks) hook). The data source also stops on its own when the generator's observable completes or fails. |
331
+ | `chartDataUpdatesObservable` | Emits each chunk of chart data *after* it has been added to the series. |
332
+ | `series`, `seriesList()` | The live series (a map by name, and an array, in insertion order). They change in place as data arrives, so copy what you need. |
333
+ | `latestTime()` | The latest time of any datum in any series (`-Infinity` when there's no data). |
334
+ | `setDropDataAfter(ms)` | Changes the data TTL. It takes effect as the next data arrives. |
335
+ | `setGenerator(generator)` | Replaces the generator, for example when the update period changes. Only allowed while the data source is stopped; the next `start()` uses it. |
336
+ | `dispose()` | Stops the data source and completes its observables. A disposed data source can't be started again. To start over with new data (a "Reset"), dispose of the old data source and create a new one. |
337
+
338
+ Two data sources keep extra state that their plots use:
339
+ - `OrdinalDataSource.stats` holds the lifetime and windowed statistics (min, max, mean) of each category, which keep accumulating while no plot is mounted.
340
+ - `IteratesDataSource.droppedCountFor(seriesName)` counts the points dropped from a series, so that the Poincaré plot's colorized lines keep their colors as old points are dropped.
341
+
342
+ To build a data source for some other shape of data, extend `BaseStreamingDataSource` and implement `ingest(data)` and `latestTime()`.
343
+
344
+ ### <span id="generators">generators</span> [↑](#content "back to contents")
345
+
346
+ A data source doesn't take an observable. It takes a `DataGenerator`, a function that *creates* the observable:
347
+
348
+ ```ts
349
+ type DataGenerator<CD extends ChartData, S> = (currentSeries: Array<S>) => Observable<CD>
350
+ ```
713
351
 
714
- **seriesStyles (Map&lt;string, [SeriesLineStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/axes.ts) &gt;, optional)**<br>
715
- A map holding the data series name with an associated SeriesLineStyle. Any series listed in this map will use the associated styles for that series. Any series not in the map will use the default series styles.
352
+ The data source calls the generator each time it starts, and hands it the series as they are *right now*. That's what lets **Run** after **Pause** carry on from the latest data, rather than starting over from the initial data. A typical generator reads the latest time from the current series and starts the new stream from there (see the [quick start](#quick-start)).
716
353
 
717
- The SeriesLineStyle has the following shape
718
- ```typescript
719
- interface SeriesLineStyle {
720
- color: string
721
- lineWidth: number
722
- // the color of the series when the user mouses over the series
723
- highlightColor: string
724
- // the line width of the series when the user mouses over the series
725
- highlightWidth: number
726
- // the line margin used for raster charts
727
- margin?: number
728
- }
729
- >```
354
+ Two things to get right in your observable:
355
+ - **Emit only what's new.** Each emission's `newPoints` holds the points since the last emission. The data source keeps everything that came before.
356
+ - **Make each emission a new object.** Give every emission its own `newPoints` map and its own arrays. The chart can buffer several emissions before it draws them, and if they share (and change) the same map, the buffered emissions all end up holding the latest values.
730
357
 
731
- #### [&#10514;](#content) <span id="chart-usage-initial-data">&lt;Chart/&gt; initial data</span>
358
+ ### <span id="chart-data">chart data, one shape for each plot</span> [↑](#content "back to contents")
732
359
 
733
- Holds the initial (static data). This data is displayed in the chart even before subscribing to the chart-data observable. The initial data can be used to generate static charts.
360
+ Every chart data object holds `seriesNames: Set<string>`, and a `newPoints` map from each series' name to its new points. Each plot's chart data adds a little more.
734
361
 
735
- **initialData (Array<[Series](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/datumSeries.ts) >)**<br>
736
- An array holding the initial data series to be plotted before subscribing to the chart-data observable.
737
- >
738
- The Series has the following shape
739
- ```typescript
740
- interface Series {
741
- // the series name
742
- readonly name: string
743
- // the array of time-value pairs
744
- data: Array<Datum>
745
- // ... accessor functions
746
- // .
747
- // .
748
- // .
362
+ **`TimeSeriesChartData`** (scatter and raster plots)
363
+ ```ts
364
+ interface TimeSeriesChartData {
365
+ seriesNames: Set<string>
366
+ newPoints: Map<string, Array<Datum>> // each series' new {x: time, y: value} points
367
+ maxTimes: Map<string, number> // each series' latest time
368
+ maxTime: number // the latest time of all of them
369
+ currentTime?: number
749
370
  }
750
371
  ```
751
- And the [Datum](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/datumSeries.ts) is an immutable object that has the following shape
752
- ```typescript
753
- interface Datum {
754
- readonly time: number;
755
- readonly value: number;
756
- }
757
- >```
758
- There are a number of helper functions for creating `Series` and `Datum`.
759
- >> `seriesFrom(name: string, data: Array<Datum> = []): Series`<br>
760
- >> `seriesFromTuples(name: string, data: Array<[number, number]> = []): Series`<br>
761
- >> `emptySeries(name: string): Series`<br>
762
- >> `emptySeriesFor(names: Array<string>): Array<Series>`<br>
763
-
764
- #### [&#10514;](#content) <span id="chart-usage-streaming-data">&lt;Chart/&gt; streaming data</span>
765
-
766
- A set of properties, functions, and callbacks to control and observe the streaming of live data into the chart.
767
-
768
- **seriesObservable (Observable<[ChartData](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/chartData.ts) >)**<br>
769
- An observable of (source for) chart-data. The `shouldSubscribe` property controls whether the chart subscribes to the observable, or unsubscribes. This is the source of live data to the chart. An example of an observable is shown below.
770
- ```typescript
771
- function randomSpikeDataObservable(
772
- series: Array<Series>,
773
- updatePeriod: number = UPDATE_PERIOD_MS,
774
- spikeProbability: number = 0.1
775
- ): Observable<ChartData> {
776
- const seriesNames = series.map(series => series.name)
777
- const initialData = initialChartData(series)
778
- return interval(updatePeriod).pipe(
779
- // convert the number sequence to a time
780
- map(sequence => (sequence + 1) * updatePeriod),
781
- // create a random spike for each series
782
- map((time) => randomSpikeData(time, seriesNames, initialData.maxTimes, updatePeriod, spikeProbability))
783
- )
784
- }
372
+ For a raster plot, each datum is an event (for example, a neuron's spike) at time `x`. Its `y` is shown in the tooltip.
373
+
374
+ **`IterateChartData`** (Poincaré plot)
375
+
376
+ You rarely build iterate data by hand. Write an observable of `TimeSeriesChartData` holding the sequence's values over time, and convert it with `iteratesObservable(observable, n)`, which pairs each value `f[m](x)` with the value `n` steps later, `f[m+n](x)`:
377
+
378
+ ```ts
379
+ import {interval, map, scan, type Observable} from "rxjs"
380
+ import {iteratesObservable, type IterateChartData, type IterateSeries, type DataGenerator, type TimeSeriesChartData} from "stream-charts"
381
+
382
+ // the logistic map, x[n+1] = r x[n] (1 - x[n]), one value every 25 ms
383
+ const logisticMap = (r: number, x0: number): Observable<TimeSeriesChartData> =>
384
+ interval(25).pipe(
385
+ scan(({x}, tick) => ({time: (tick + 1) * 25, x: r * x * (1 - x)}), {time: 0, x: x0}),
386
+ map(({time, x}) => ({
387
+ seriesNames: new Set(['logistic']),
388
+ maxTime: time,
389
+ maxTimes: new Map([['logistic', time]]),
390
+ newPoints: new Map([['logistic', [{x: time, y: x}]]]),
391
+ }))
392
+ )
393
+
394
+ const generator: DataGenerator<IterateChartData, IterateSeries> =
395
+ () => iteratesObservable(logisticMap(3.8, 0.2), 1) // 1-iterates: (f[m](x), f[m+1](x))
785
396
  ```
786
397
 
787
- **shouldSubscribe (boolean, optional, default = false)**<br>
788
- Optional property, that when set from `false` to `true`, causes the &lt;Chart/&gt; to subscribe to the chart-data observable. When set to `false` after a subscription, causes the &lt;Chart/&gt; to unsubscribe from the chart-data observable.
789
-
790
- **windowingTime (number, milliseconds, optional, default = 100 ms)**<br>
791
- Optional property that defines a time-window during which incoming events are buffered, and then handed to plot, causing the plot to update. The `windowingTime` defines the maximum plot update rate, though not the maximum data update rate. The larger the windowing time, the fewer updates per unit time, and the more choppy the updates. Large amounts of data with high update rates can cause rendering delays. The windowing time provides a lever to manage the plot update rates to get the smoothest plot updates that keep up with real-time.
792
-
793
- **shouldSubscribe (number, optional, default = false)**<br>
794
- Optional property that default to `false`. When changed to `true`, from `false`, signals the &lt;Chart/&gt; to subscribe to the `seriesObservable`, streaming in the `ChartData` and updating the &lt;Chart/&gt; in real-time.
795
-
796
- **onSubscribe (callback function, (subscription: Subscription) => void, optional, default = noop**<br>
797
- Optional callback function that is called when the &lt;Chart/&gt; subscribes to the `ChartData` observable.
798
-
799
- **onUpdateData (callback function, (seriesName: string, data: Array<Datum>)) => void, optional, default = noop)**<br>
800
- Optional callback function that is called when the data updates. This callback can be used if you would like to respond to data updates. For example, use this callback if you would like to have the plot drop data after 10 seconds, but would like to store that data in an in-browser database. Though, a more efficient way to store the data would be to subscribe to the series-observable separately, and then use that observer to stream the data to the storage.
801
-
802
- **onUpdateTime (callback function, (times: Map<string, [start: number, end: number]>) => void, optional, default = noop)**<br>
803
- Optional callback this is called whenever the time-ranges change. Use this to track the current time of the plot.
804
-
805
- ## [&#10514;](#content) <span id="axes">axes</span>
806
-
807
- ### [&#10514;](#content) <span id="continuous-axes-usage">&lt;ContinuousAxis/&gt;</span>
808
-
809
- The &lt;ContinuousAxis/&gt; must be a child of the &lt;Chart/&gt;. Each &lt;Chart/&gt can one or two continuous axes for the x-axes and for the y-axes. When a &lt;Chart/&gt has multiple x-axes or y-axes then you must assign series to the axes in one of the `Plot` components. Any series that are not explicitly assigned an axis will be assigned to the default x-axis or y-axis. The default x-axis is the bottom axis in the &lt;Chart/&gt, and the default y-axis is the left-hand side axis in the &lt;Chart/&gt.
810
-
811
- When creating a &lt;ContinuousAxis/&gt;, you must specify its location using the `AxisLocation` enum defined in the [axes.ts](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/axes.ts) file. Each axis must have a unique axis ID. By default a &lt;ContinuousAxis/&gt; will have a linear scale (d3.scaleLinear). The `scale` property can by used to set the scale to a `log` (d3.scaleLog) or `power` (d3.scalePow) scale or any other continuous numeric scale available in `d3`. The `domain` property defines the initial min and max values for the axis, and when data streams into the plot, that defines the time-window displayed for the axis (unless changed by a zoom event).
812
-
813
- #### [&#10514;](#content) <span id="continuous-axes-usage-base">&lt;ContinuousAxis/&gt; base properties</span>
814
-
815
- The base properties defining the axis.
816
-
817
- **axisId (string)**<br>
818
- The unique ID of the axis. The axis can then be referred to by this ID. For example, when assigning axes to series, the assignment is made by associating the axis ID to the series name.
819
-
820
- **location ([AxisLocation](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/axes.ts) )**<br>
821
- The location of the axis. As defined by the `AxisLocation` in the the [axes.ts](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/axes.ts) file, x-axes can be placed on the `bottom` or the `top`, and y-axes can be placed on the `left` or the `right`.
822
-
823
- **scale (ScaleContinuousNumeric<number, number>, optional, default = d3.scaleLinear)**<br>
824
- The optional scale (factory) of the continuous axis. The scale of the axis is like the axis ruler and determines how the points are placed on the screen. For example, a linear scale is like an evenly spaced ruler, and the mapping between screen location and data value are linear. As another example, the log scale has a logarithmic mapping between the screen location and the data. The scale can be a linear scale (default scale, d3.scaleLinear), a logarithmic scale (d3.scaleLog), a power scale (d3.scalePower), or any other d3 continouos numeric scale that works. Not that if a chart, for example, has two x-axes, that the x-axes are **not** required to have the same scale.
825
-
826
- **domain ([min: number, max: number])**<br>
827
- The domain of the axis (in d3 terminology) is effectively the minimum value of the axis and the maximum value of the axis when the initial data is displayed. The domain defines the time-window of the displayed data. For example, if the `domain` for an x-axis is specified as `[1000, 6000]`, then the axis starts at `1000` and ends at `6000`, and the time-window is `5000`. Once data starts to stream past the axis end, the plot starts to scroll, maintaining the calculated time-window (in out example, 5000). Of course, a zooming event will change the domain, and also the time-window.
828
-
829
- **label (string)**<br>
830
- The axis label.
831
-
832
- #### [&#10514;](#content) <span id="continuous-axes-usage-styling">&lt;ContinuousAxis/&gt; styling</span>
398
+ **`OrdinalChartData`** (bar plot)
833
399
 
834
- A set of properties to update the style of the axes.
400
+ Like the iterate data, ordinal data is usually derived: `ordinalsObservable(timeSeriesObservable, compactingSize?, initialStats?)` turns a `TimeSeriesChartData` observable (one series per category) into ordinal data, accumulating each category's statistics. Pass `calculateOrdinalStats(currentSeries)` as the `initialStats`, so that a restarted stream's statistics include the data the data source already holds:
835
401
 
836
- **font (Partial<[AxesLabelFont](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/axes.ts) >)**<br>
837
- An optional CSS properties specifying the font for the axis and tick labels.
402
+ ```ts
403
+ const generator: DataGenerator<OrdinalChartData, OrdinalSeries> =
404
+ currentSeries => ordinalsObservable(myCategoryValues(currentSeries), undefined, calculateOrdinalStats(currentSeries))
405
+ ```
838
406
 
407
+ **`OutlierChartData<M>`** (outlier plot)
408
+ ```ts
409
+ interface OutlierChartData<M extends readonly number[]> {
410
+ seriesNames: Set<string>
411
+ newPoints: Map<string, Array<OutlierDatum<M>>>
412
+ currentTime?: number
413
+ }
414
+ ```
415
+ `M` is a tuple of *measures*, the confidence level of each band, for example `[0.68, 0.95, 0.997] as const` for 1σ, 2σ, and 3σ. Each `OutlierDatum<M>` holds the point (`datum: {x, y}`) and one `{lower, upper}` bound for each measure (`bounds`). Create them with `outlierDatumFor(datum, measures, bounds)` and `outlierBoundsFor(lower, upper)`. Both check that there's one bound for each measure, and return a `Result`.
839
416
 
840
- ### [&#10514;](#content) <span id="ordinal-axes-usage">&lt;OrdinalAxis/&gt;</span>
417
+ ### <span id="series-helpers">series and datum helpers</span> [↑](#content "back to contents")
841
418
 
842
- The &lt;OrdinalAxis/&gt; must be a child of the &lt;Chart/&gt;. Each &lt;Chart/&gt; can have one or two ordinal axes for the y-axes. Unlike the [&lt;ContinuousAxis/&gt;](#continuous-axes-usage), the &lt;OrdinalAxis/&gt; can only be used as a y-axis because for stream charts (at this point) the x-axes represent time. In the same way as with the [&lt;ContinuousAxis/&gt;](#continuous-axes-usage), when using multiple multiple y-axes you must assign the series to the axes in one of the `Plot` components. Any series that are not explicitly assigned an axis will be assigned to the default y-axis, which is the left-hand side axis in the &lt;Chart/&gt;.
419
+ A series is a named array of data. Its data is held in a `FastShiftArray`, so that dropping the oldest points is cheap.
843
420
 
844
- When creating an &lt;OrdinalAxis/&gt;, you must specify its location using the `AxisLocation` enum defined in the [axes.ts](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/axes.ts) file. Each axis must have a unique axis ID. The &lt;OrdinalAxis/&gt; uses a band scale (d3.scaleBand).
421
+ | function | creates |
422
+ |---|---|
423
+ | `emptySeries<D>(name)`, `emptySeriesFor<D>(names)` | empty series (a good start for streaming data) |
424
+ | `seriesFrom<D>(name, data)` | a series holding the data |
425
+ | `timeSeriesFromTuples(name, [[time, value], ...])` | a time series (scatter, raster) |
426
+ | `iterateSeriesFromTuples(name, [[time, iterateN, iterateN_1], ...])` | an iterate series (Poincaré) |
427
+ | `ordinateSeriesFromTuples(name, [[time, ordinal, value], ...])` | an ordinal series (bar) |
428
+ | `outlierSeriesFor(name, measures, [[time, value, bounds], ...])`, `outlierSeriesFrom(name, data, measures)` | an outlier series |
429
+ | `datumOf(time, value)`, `iterateDatumOf(...)`, `ordinalDatumOf(...)` | a single datum |
430
+ | `initialTimeSeriesChartData(series)`, `initialOrdinalChartData(series)` | chart data to start a stream from |
845
431
 
846
- #### [&#10514;](#content) <span id="ordinal-axes-usage-base">&lt;OrdinalAxis/&gt; base properties</span>
432
+ > **Ownership:** `seriesFrom` wraps the array you hand it *by reference*. As data streams in and ages out, the library changes that array in place. If you keep your own reference to it, pass a copy (`data.slice()`) instead.
847
433
 
848
- The base properties defining the axis.
434
+ `series.last()` returns a `Result` (from [result-fn](https://github.com/robphilipp/result-fn)), which is a failure when the series is empty: `series.last().map(datum => datum.x).getOrElse(0)`.
849
435
 
850
- **axisId (string)**<br>
851
- The unique ID of the axis. The axis can then be referred to by this ID. For example, when assigning axes to series, the assignment is made by associating the axis ID to the series name.
436
+ ### <span id="performance">performance: data TTL, windowing, and cadence</span> [↑](#content "back to contents")
852
437
 
853
- **location ([AxisLocation](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/axes.ts) )**<br>
854
- The location of the axis. As defined by the `AxisLocation` in the the [axes.ts](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/axes.ts) file, x-axes can be placed on the `bottom` or the `top`, and y-axes can be placed on the `left` or the `right`.
438
+ `stream-charts` is built for large amounts of high-frequency data. When the data's time passes the end of the x-axis, the chart scrolls to keep up. The data that scrolls off the chart is still held, so you can pan or zoom back to it. To get there, the charts:
439
+ 1. take their data from an [RxJS](https://rxjs.dev) observable, through a data source,
440
+ 2. draw onto a single `<canvas>` (with [d3](https://d3js.org) scales and shapes), and
441
+ 3. keep the drawing outside React's render cycle.
855
442
 
856
- **categories (Array<string>)**<br>
857
- The required `categories` property holds the names of the categories, in the order that they will be displayed on the axis. The first element in the array will be shown at the top of the axis. The second element will be lower, and the last element will be at the bottom of the axis.
443
+ There are limits, of course. When a chart starts to stutter, you have these levers. Each stream is different, so expect to tune them.
858
444
 
859
- **domain ([min: number, max: number])**<br>
860
- The domain of the axis (in d3 terminology) is effectively the minimum value of the displayed axis and the maximum value of the displayed axis when the initial data is displayed. The domain defines the time-window of the displayed data. For example, if the `domain` for an x-axis is specified as `[1000, 6000]`, then the axis starts at `1000` and ends at `6000`, and the time-window is `5000`. Once data starts to stream past the axis end, the plot starts to scroll, maintaining the calculated time-window (in our example, 5000). Of course, a zooming event will change the domain, and also the time-window.
445
+ 1. **Drop old data (`dropDataAfter`, on the data source).** Without it, the data that scrolls off the chart is kept, and processed, forever. With it, a scrolling chart can run indefinitely. The TTL is measured in data time, not wall-clock time. To keep the dropped data, store it with the data source's `onUpdateData` callback (or a separate subscription to your observable).
446
+ 2. **Batch the updates (`windowingTime`, on the chart).** The chart collects incoming data for this long (ms) before redrawing. Longer windows mean fewer redraws, and so better performance, at the cost of choppier scrolling.
447
+ 3. **Tell the chart your update period (`dataUpdatePeriod`, on the chart).** When your data arrives at a known, regular period, the chart buffers a fixed number of updates rather than a fixed amount of time, which makes scrolling smoother. Leave it out when your data arrives at irregular times.
448
+ 4. **Use a cadence for slow data (`withCadenceOf`, on the plot).** When updates are slow (more than about 100 ms apart), a cadence of 10–25 ms advances the time axis smoothly between updates. At high update rates it's unnecessary, and a cadence that's too short hurts performance.
449
+ 5. **Shorten the time window (the x-axis `domain`).** A shorter window holds less data, though the data streams by faster.
861
450
 
862
- **label (string)**<br>
863
- The axis label.
451
+ ---
864
452
 
865
- #### [&#10514;](#content) <span id="ordinal-axes-usage-styling">&lt;OrdinalAxis/&gt; styling</span>
453
+ ## <span id="components">components</span> [↑](#content "back to contents")
866
454
 
867
- A set of properties to update the style of the axes.
455
+ ### <span id="chart">&lt;Chart/&gt;</span> [↑](#content "back to contents")
868
456
 
869
- **font (Partial<[AxesLabelFont](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/axes.ts) >, optional)**<br>
870
- An optional CSS properties specifying the font for the axis and tick labels.
457
+ The `<Chart>` is the container for everything else: the axes, the plot, and the tracker, tooltip, and legend. It creates the canvas, reads from the data source, and provides the shared state that its children use (see [hooks](#hooks)).
871
458
 
459
+ ```tsx
460
+ <Chart
461
+ chartId={1}
462
+ width={800}
463
+ height={400}
464
+ margin={{...defaultMargin, top: 40, right: 60}}
465
+ color={theme.color}
466
+ backgroundColor={theme.backgroundColor}
467
+ seriesStyles={new Map([['kitchen', {...defaultLineStyle(), color: 'orange', highlightColor: 'orange'}]])}
468
+ seriesFilter={filter}
469
+ dataSource={dataSource}
470
+ windowingTime={50}
471
+ dataUpdatePeriod={50}
472
+ onUpdateAxesBounds={ranges => setXAxisRange(ranges.get('x-axis-1'))}
473
+ >
474
+ {/* axes, plot, tracker, tooltip, legend */}
475
+ </Chart>
476
+ ```
872
477
 
873
- ## [&#10514;](#content) <span id="plots">plots</span>
478
+ The chart is generic in its data: `Chart<CD, D, S, TM, AR, A>`, where `CD` is the chart data, `D` the datum, `S` the series style, `TM` the tooltip metadata, `AR` the axis range, and `A` the axis type. TypeScript infers these for most charts. For a bar chart, spell them out:
874
479
 
875
- ### [&#10514;](#content) <span id="scatter-plot-usage">&lt;ScatterPlot/&gt;</span>
480
+ ```tsx
481
+ <Chart<OrdinalChartData, OrdinalDatum, BarSeriesStyle, WindowedOrdinalStats, OrdinalAxisRange, OrdinalStringAxis> ...>
482
+ ```
876
483
 
877
- In `stream-charts`, the `plot` is the data visualization component. To work, a plot must be a child of the &lt;Chart/&gt; component so that it is plugged into the `stream-charts` ecosystem. The plot determines how to render the data. But it relies on the axes to determine scaling information so that it can map data to screen locations. And therefore, a plot must have sibling axes ([&lt;ContinousAxis/&gt;](#continuous-axes-usage), [&lt;OrdinalAxis/&gt;](#ordinal-axes-usage)) components for the x-axis and the y-axis. Because the plot is responsible for data visualization, it is also where the data series are assigned to axes. The assignment of axes to series is optional, and any series not explicitly assigned to an axis will be assigned to the default axes. The default x-axis is the bottom axis, and the default y-axis is the axis on the left-hand side of the plot.
484
+ #### <span id="chart-dimensions">dimensions</span>
878
485
 
879
- The plot determines what view-modifying user interactions are available. Specifically, panning the data to the left and right in time, and zooming in time.
486
+ | prop | type | description |
487
+ |---|---|---|
488
+ | `chartId` | `number` | **Required.** A unique ID for the chart. |
489
+ | `width` | `number` | **Required.** The width (px) of the chart's container. The plot area is this, less the margins. |
490
+ | `height` | `number` | **Required.** The height (px) of the chart's container. The plot area is this, less the margins. |
491
+ | `margin` | `Partial<Margin>` | The space (px) between the container's edges and the plot area, which holds the axes, their ticks, and labels. Any side you leave out uses `defaultMargin`. `Margin` is `{top, bottom, left, right}`. |
880
492
 
881
- The &lt;ScatterPlot/&gt; specifically is used to plot time-series data, where the x-values are time. The x-axis and y-axis are required to be a [&lt;ContinousAxis/&gt;](#continuous-axes-usage).
493
+ The chart draws onto a canvas, which must know its exact size in pixels, so the chart doesn't size itself to its container: you hand it `width` and `height`. It redraws when they change. To follow a resizing window or layout, measure the chart's container, for example with a `ResizeObserver`, and pass the size along. (The examples get their sizes from the grid cells of [react-resizable-grid-layout](https://www.npmjs.com/package/react-resizable-grid-layout), with `useGridCellWidth()` and `useGridCellHeight()`.)
882
494
 
883
- #### [&#10514;](#content) <span id="scatter-plot-usage-base">&lt;ScatterPlot/&gt; base properties</span>
495
+ #### <span id="chart-styling">styling</span>
884
496
 
885
- **axisAssignments (Map<string, [AxesAssignment](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/plot.ts) >, optional, default = Map())**<br>
886
- An optional property that assigns data series to (x, y)-axes. Any series not assigned to an axis will use the default axis. The default x-axis is the bottom axis, and the default y-axis is the axis on the left-hand side of the plot. The `Map` associates the series name with an [AxesAssignment](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/plot.ts), which is a simple object (`{xAxis: string, yAxis: string}`) that holds the axis ID for the x-axis and for the y-axis assigned to the series.
497
+ | prop | type | description |
498
+ |---|---|---|
499
+ | `color` | `string` | The default color of the axes and text (default `'#d2933f'`). The axes' own styles can override it. |
500
+ | `backgroundColor` | `string` | The background color of the whole chart (default `'#202020'`). |
501
+ | `seriesStyles` | `Map<string, S>` | Maps a series' name to its style. A series that isn't in the map uses the default style. For the scatter, raster, Poincaré, and outlier plots, the style is a `SeriesLineStyle` (start from `defaultLineStyle()`). For the bar plot, it's a `BarSeriesStyle` (start from `defaultBarSeriesStyle(color)`). |
502
+ | `svgStyle` | `Partial<SvgStyle>` | CSS overrides for the chart's container element. Rarely needed. |
887
503
 
888
- **interpolation (d3.CurveFactory, optional, default = d3.curveLinear)**<br>
889
- An optional property that defines how the data series line will be interpolated between individual data points. You can use any valid [d3.CurveFactory](https://github.com/d3/d3-shape#curves) for the interpolation. Changing the interpolation once the data is already display, will cause a re-render of the data with the new interpolation.
504
+ ```ts
505
+ interface SeriesLineStyle {
506
+ color: string
507
+ lineWidth: number
508
+ highlightColor: string // the color of the series when the mouse is over it
509
+ highlightWidth: number // the line width of the series when the mouse is over it
510
+ margin?: number // the raster plot's spike margin, for this series
511
+ }
512
+ ```
890
513
 
891
- <span id="scatter-plot-drop">**dropDataAfter (number, milliseconds, opitional, default = Infinity)**</span><br>
892
- Optional property that sets when to drop data (effectively a TTL). This only drops data while streaming. Don't worry, your data won't disappear after the streaming has stopped. By default, none of the data is dropped. However, when large amounts of data are being streamed and plotted over long periods of time, memory and performance may become an issue. Setting this value allows a scrolling chart to run forever without running into resource issues.
514
+ All styles are exposed as props, so you can drive them from your theme provider, or not use one at all (see [theming](#theming)).
893
515
 
894
- #### [&#10514;](#content) <span id="scatter-plot-usage-view">&lt;ScatterPlot/&gt; view-modifying interactions</span>
516
+ #### <span id="chart-data-props">data</span>
895
517
 
896
- View-modifying interactions are those that change the way the data is displayed. For example, zooming in time, panning in time.
518
+ | prop | type | description |
519
+ |---|---|---|
520
+ | `dataSource` | `StreamingDataSource` | **Required.** The [data source](#data-sources) that holds the chart's data. The chart only reads from it; you start and stop it. Handing the chart a *new* data source resets the plots to that data source's data. |
521
+ | `windowingTime` | `number` (ms) | How long the chart collects incoming data before it redraws (default 100 ms). See [performance](#performance). |
522
+ | `dataUpdatePeriod` | `number` (ms) | The period at which your generator emits, when you know it. The chart then buffers a fixed number of updates (`windowingTime / dataUpdatePeriod`), which makes scrolling smoother. |
523
+ | `seriesFilter` | `RegExp` | Only the series whose names match are shown. It can change while streaming. Patterns that risk catastrophic backtracking are ignored, with a warning (see [filtering series](#filtering)). |
524
+ | `onUpdateAxesBounds` | `(ranges: Map<string, AxisInterval>) => void` | Called when the axes' ranges change, for example as the x-axis scrolls, or when the user zooms or pans. The map is keyed by axis ID. Use it to keep the current time range in your state (and to hand it back as the axis `domain` when the chart remounts). |
525
+ | `onUpdateChartTime` | `(time: number) => void` | Called with the current chart time. Mainly for plots whose axes don't show time, like the Poincaré and bar plots. |
526
+ | `children` | | The axes, the plot, and optionally the tracker, tooltip, and legend. |
897
527
 
898
- **panEnabled (boolean, optional, default = false)**<br>
899
- Optional property that defaults to `false`. When set to `true` then enables "panning" which allows the user to drag the plot to the left and right.
528
+ ### <span id="axes">axes</span> [↑](#content "back to contents")
900
529
 
901
- **zoomEnabled (boolean, optional, default = false)**<br>
902
- Optional property that defaults for `false`. When set to `true` then enables "zooming" which allows the user to increase or decrease the displayed time range. By default, scrolling will cause the zoom effect. See the `zoomKeyModifiersRequired` property which requires the `shift` key to be pressed in order for the zoom action to apply.
530
+ A chart has one or two x-axes (`AxisLocation.Bottom`, `AxisLocation.Top`) and one or two y-axes (`AxisLocation.Left`, `AxisLocation.Right`). Each axis has a unique `axisId`. When a chart has more than one x-axis or y-axis, you assign series to axes with the plot's [`axisAssignments`](#plot-common). A series without an assignment uses the default axes: the first x-axis and the first y-axis in the chart's children, whatever their location.
903
531
 
904
- **zoomKeyModifiersRequired (boolean, optional, default = false)**<br>
905
- An optional property that defaults to `false`. When set to `true`, and the `zoomEnabled` property is also set to `true`, then requires that the `shift` key be pressed when scrolling in order to activate the zoom. This is nice when a user can scroll through your page containing the plot, so that zooming doesn't interfere with scrolling.
532
+ #### <span id="plot-axes">which axes each plot takes</span>
906
533
 
907
- **withCadenceOf (number, optional, default = undefinded)**<br>
908
- An optional property that defaults to `undefined`. When set, uses a cadence with the specified refresh period (in milliseconds). For plots with slow data updates (> 100 ms) using a cadence of 10 to 25 ms smooths out the updates so the time scrolling doesn't appear choppy. When updates are around 25 ms or less, then setting the cadence period too small will result in poor update performance. Generally at high update speeds, the cadence is unnecessary. Finally, using cadence, sets the max time to the current time. See also the related &lt;Chart/&gt's `windowingTime` property.
534
+ Each plot needs particular kinds of axes:
909
535
 
536
+ | plot | x-axis | y-axis |
537
+ |---|---|---|
538
+ | `<ScatterPlot>` | `<ContinuousAxis>` | `<ContinuousAxis>` |
539
+ | `<RasterPlot>` | `<ContinuousAxis>` | `<OrdinalAxis>` |
540
+ | `<PoincarePlot>` | `<ContinuousAxis>` | `<ContinuousAxis>` |
541
+ | `<BarPlot>` | `<OrdinalAxis>` | `<ContinuousAxis>` |
542
+ | `<OutlierPlot>` | `<ContinuousAxis>` | `<ContinuousAxis>` |
910
543
 
911
- ### [&#10514;](#content) <span id="raster-plot-usage">&lt;RasterPlot/&gt;</span>
544
+ - **An `<EmptyAxis>` counts as a continuous axis.** It's fine as a raster chart's top x-axis, but a raster chart's series can't use one as their y-axis.
545
+ - **The order of the axes matters.** Unassigned series use the *first* x-axis and the *first* y-axis, so put the axes your series use before any `<EmptyAxis>`. In a raster chart with an `<EmptyAxis>` on the right, list the `<OrdinalAxis>` first; otherwise the empty (continuous) axis becomes the default y-axis.
546
+ - **The Poincaré plot only uses the first x-axis and the first y-axis** (it has no `axisAssignments`). Any others just mirror them.
547
+ - **A series assigned to an axis ID that doesn't exist isn't drawn.** It doesn't fall back to the default axes.
548
+ - **The wrong kind of axis is reported when the plot draws.** The chart logs the error to the console, along the lines of `<RasterPlot> needs its y-axis to be an <OrdinalAxis>, but the y-axis "y-axis-2" is a <ContinuousAxis> (or an <EmptyAxis>)`, and skips that drawing. The axes are child components, so TypeScript can't catch the mismatch.
912
549
 
913
- In `stream-charts`, the `plot` is the data visualization component. To work, a plot must be a child of the &lt;Chart/&gt; component so that it is plugged into the `stream-charts` ecosystem. The plot determines how to render the data. But it relies on the axes to determine scaling information so that it can map data to screen locations. And therefore, a plot must have sibling axes ([&lt;ContinousAxis/&gt;](#continuous-axes-usage), [&lt;OrdinalAxis/&gt;](#ordinal-axes-usage)) components for the x-axis and the y-axis. Because the plot is responsible for data visualization, it is also where the data series are assigned to axes. The assignment of axes to series is optional, and any series not explicitly assigned to an axis will be assigned to the default axes. The default x-axis is the bottom axis, and the default y-axis is the axis on the left-hand side of the plot.
550
+ #### <span id="continuous-axis">&lt;ContinuousAxis/&gt;</span>
914
551
 
915
- The plot determines what view-modifying user interactions are available. Specifically, panning the data to the left and right in time, and zooming in time.
552
+ A numeric axis, for time on the x-axis, or for values on either axis.
916
553
 
917
- The &lt;RasterPlot/&gt; specifically is used to plot event-timing data, where the x-values are time, and the y-values are a category to which the event belongs. The x-axis is required to be a [&lt;ContinuousAxis/&gt;](#continuous-axes-usage), and the y-axis is required to be an [&lt;OrdinalAxis/&gt;](#ordinal-axes-usage).
554
+ ```tsx
555
+ <ContinuousAxis axisId="y-axis-2" location={AxisLocation.Right} scale={d3.scaleLog()} domain={[100, 1200]} label="distance (µm)"/>
556
+ ```
918
557
 
919
- #### [&#10514;](#content) <span id="raster-plot-usage-base">&lt;RasterPlot/&gt; base properties</span>
558
+ | prop | type | description |
559
+ |---|---|---|
560
+ | `axisId` | `string` | **Required.** The axis' unique ID, used to assign series to it. |
561
+ | `location` | `AxisLocation` | **Required.** `Bottom` or `Top` for an x-axis; `Left` or `Right` for a y-axis. |
562
+ | `domain` | `[min, max]` | **Required.** The axis' starting range. For a time axis, this is also the time window: a domain of `[0, 5000]` shows 5 seconds, and once data passes 5000 ms, the axis scrolls, keeping that window (until a zoom changes it). |
563
+ | `label` | `string` | **Required.** The axis label. |
564
+ | `scale` | `ScaleContinuousNumeric` | The d3 scale (default `d3.scaleLinear()`). Any continuous numeric d3 scale works, such as `d3.scaleLog()` or `d3.scalePow()`. Two x-axes (or y-axes) can have different scales. |
565
+ | `font` | `Partial<AxesFont>` | The font of the ticks and label. |
566
+ | `updateAxisBasedOnDomainValues` | `boolean` | When `true` (the default), the axis resets when the *values* of `domain` change, so you can pass a new tuple literal on each render. For charts that don't scroll, such as the Poincaré plot, set it to `false` to reset when the `domain` *object* changes instead, for example when the user picks a new function. |
920
567
 
921
- **axisAssignments (Map<string, [AxesAssignment](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/plot.ts) >, optional, default = Map())**<br>
922
- An optional property that assigns data series to (x, y)-axes. Any series not assigned to an axis will use the default axis. The default x-axis is the bottom axis, and the default y-axis is the axis on the left-hand side of the plot. The `Map` associates the series name with an [AxesAssignment](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/plot.ts), which is a simple object (`{xAxis: string, yAxis: string}`) that holds the axis ID for the x-axis and for the y-axis assigned to the series.
568
+ <span id="continuous-axis-autoscale">**Auto-scaling y-axes**</span> (scatter, bar, and outlier plots). With `autoScale`, a y-axis grows to fit the data on screen. It only ever grows, and goes back to `domain` when the plot's data is replaced, or when `domain` or the limits change.
923
569
 
924
- <span id="raster-plot-drop">**dropDataAfter (number, milliseconds, opitional, default = Infinity)**</span><br>
925
- Optional property that sets when to drop data (effectively a TTL). This only drops data while streaming. Don't worry, your data won't disappear after the streaming has stopped. By default, none of the data is dropped. However, when large amounts of data are being streamed and plotted over long periods of time, memory and performance may become an issue. Setting this value allows a scrolling chart to run forever without running into resource issues.
570
+ | prop | type | description |
571
+ |---|---|---|
572
+ | `autoScale` | `boolean` | Turns auto-scaling on (default `false`). Ignored, with a warning, on x-axes and in plots that don't support it. |
573
+ | `autoScaleMin`, `autoScaleMax` | `number` | The limits the domain may grow to (default `-Infinity` and `Infinity`). |
574
+ | `autoScalePadding` | `number` | The fraction of the axis left beyond the data's extreme (0 to 0.45, default 0.05). |
575
+ | `autoScaledDomain`, `onAutoScale` | `[min, max]`, `(domain) => void` | Keep the grown domain across remounts: store what `onAutoScale` hands you, and pass it back as `autoScaledDomain`. |
926
576
 
927
- **spikeMargin (number, pixels, default = 2)**<br>
928
- Optional property that adds a margin to the top and bottom of the raster (event) lines to give vertical spacing to the events in the plot. Margins on individual series can also be set through the [Chart.seriesStyles](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/hooks/useChart.tsx) property.
577
+ #### <span id="ordinal-axis">&lt;OrdinalAxis/&gt;</span>
929
578
 
930
- #### [&#10514;](#content) <span id="raster-plot-usage-view">&lt;RasterPlot/&gt; view-modifying interactions</span>
579
+ A category axis (a d3 band scale). The raster plot uses one for its y-axis (one row per series), and the bar plot uses one for its x-axis (one bar per series).
931
580
 
932
- View-modifying interactions are those that change the way the data is displayed. For example, zooming in time, panning in time.
581
+ ```tsx
582
+ <OrdinalAxis axisId="y-axis-1" location={AxisLocation.Left} categories={dataSource.seriesList().map(s => s.name)} label="neuron"/>
583
+ ```
933
584
 
934
- **panEnabled (boolean, optional, default = false)**<br>
935
- Optional property that defaults to `false`. When set to `true` then enables "panning" which allows the user to drag the plot to the left and right.
585
+ | prop | type | description |
586
+ |---|---|---|
587
+ | `axisId` | `string` | **Required.** The axis' unique ID. |
588
+ | `location` | `AxisLocation` | **Required.** Where the axis goes. |
589
+ | `categories` | `Array<string>` | **Required.** The category names, in display order. On a y-axis, the first category is at the top. |
590
+ | `label` | `string` | **Required.** The axis label. |
591
+ | `axisTickStyle` | `Partial<AxisTickStyle>` | The style of the tick labels, for example `{rotation: 45}` for long category names. |
592
+ | `font` | `Partial<AxesFont>` | The font of the ticks and label. |
593
+ | `scale` | `ScaleBand<string>` | The d3 band scale (rarely needed). |
936
594
 
937
- **zoomEnabled (boolean, optional, default = false)**<br>
938
- Optional property that defaults for `false`. When set to `true` then enables "zooming" which allows the user to increase or decrease the displayed time range. By default, scrolling will cause the zoom effect. See the `zoomKeyModifiersRequired` property which requires the `shift` key to be pressed in order for the zoom action to apply.
595
+ #### <span id="empty-axis">&lt;EmptyAxis/&gt;</span>
939
596
 
940
- **zoomKeyModifiersRequired (boolean, optional, default = false)**<br>
941
- An optional property that defaults to `false`. When set to `true`, and the `zoomEnabled` property is also set to `true`, then requires that the `shift` key be pressed when scrolling in order to activate the zoom. This is nice when a user can scroll through your page containing the plot, so that zooming doesn't interfere with scrolling.
597
+ A plain line where an axis would be, without ticks or a label. Use it to frame the plot on a side that has no axis of its own. It counts as a continuous axis, so list it after the axes your series use (see [which axes each plot takes](#plot-axes)).
942
598
 
943
- **withCadenceOf (number, optional, default = undefinded)**<br>
944
- An optional property that defaults to `undefined`. When set, uses a cadence with the specified refresh period (in milliseconds). For plots with slow data updates (> 100 ms) using a cadence of 10 to 25 ms smooths out the updates so the time scrolling doesn't appear choppy. When updates are around 25 ms or less, then setting the cadence period too small will result in poor update performance. Generally at high update speeds, the cadence is unnecessary. Finally, using cadence, sets the max time to the current time. See also the related &lt;Chart/&gt's `windowingTime` property.
599
+ ```tsx
600
+ <EmptyAxis axisId="x-axis-2" location={AxisLocation.Top}/>
601
+ ```
945
602
 
946
- ### [&#10514;](#content) <span id="bar-plot-usage">&lt;BarPlot/&gt;</span>
603
+ ### <span id="plots">plots</span> [↑](#content "back to contents")
947
604
 
948
- The &lt;BarPlot/&gt; is used to plot windowed statistics for a data series. It can display the current value, the mean value, and the min/max range for the windowed data.
605
+ The plot draws the data. It reads the series from the chart's data source and the scales from its sibling axes. A chart holds one plot.
949
606
 
950
- #### [&#10514;](#content) <span id="bar-plot-usage-base">&lt;BarPlot/&gt; base properties</span>
607
+ #### <span id="plot-common">properties common to the plots</span>
951
608
 
952
- **axisAssignments (Map<string, [AxesAssignment](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/plot.ts) >, optional, default = Map())**<br>
953
- An optional property that assigns data series to (x, y)-axes. Any series not assigned to an axis will use the default axis. The default x-axis is the bottom axis, and the default y-axis is the axis on the left-hand side of the plot.
609
+ | prop | type | plots | description |
610
+ |---|---|---|---|
611
+ | `axisAssignments` | `Map<string, AxesAssignment>` | all but Poincaré | Assigns series to axes: `new Map([['kitchen', assignAxes('x-axis-2', 'y-axis-2')]])`. Unassigned series use the first x-axis and the first y-axis (see [which axes each plot takes](#plot-axes)). |
612
+ | `panEnabled` | `boolean` | all | Lets the user drag the plot to pan (default `false`). |
613
+ | `zoomEnabled` | `boolean` | all | Lets the user scroll to zoom (default `false`). |
614
+ | `zoomKeyModifiersRequired` | `boolean` | all | When `true` (the default), zooming requires holding shift (or ctrl) while scrolling, so that zooming doesn't get in the way of scrolling the page. |
615
+ | `withCadenceOf` | `number` (ms) | all | Advances the time axis on this cadence, between data updates, for smooth scrolling of slow data. See [performance](#performance). (The Poincaré plot accepts it, but doesn't use it, since its axes don't show time.) |
616
+ | `highlightAxesOnMouseOver` | `boolean` | all | When `true`, hovering over a series also highlights the axes it's plotted against (default `false`). |
617
+ | `onZoomReset` | `(resetZoom: () => void) => void` | all | Hands you a `resetZoom` function, to call when you reset the axes yourself (for example, from a **Reset** button). The zoom keeps its own state, separate from the axes' ranges. Without `resetZoom`, the next zoom gesture after such a reset jumps back to the old zoom. |
954
618
 
955
- **showMinMaxBars (boolean, optional, default = false)**<br>
956
- When `true`, displays bars representing the minimum and maximum values in the data.
619
+ #### <span id="scatter-plot">&lt;ScatterPlot/&gt;</span>
957
620
 
958
- **showWindowedMinMaxBars (boolean, optional, default = false)**<br>
959
- When `true`, displays bars representing the windowed minimum and maximum values.
621
+ Values over time, drawn as lines, with optional markers. Both axes are `<ContinuousAxis>`. Uses a `TimeSeriesDataSource`. ([full example](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/StreamingScatterChart.tsx), [walkthrough](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/scatter-chart.md))
960
622
 
961
- **showValueLines (boolean, optional, default = false)**<br>
962
- When `true`, displays lines representing the current values of the data series.
623
+ <picture>
624
+ <source media="(prefers-color-scheme: dark)" srcset="packages/examples/public/images/scatter-chart-dark.png">
625
+ <img src="packages/examples/public/images/scatter-chart-light.png" alt="the scatter chart example: ten random walks on two x-axes and two y-axes, with an external legend">
626
+ </picture>
963
627
 
964
- **showMeanValueLines (boolean, optional, default = false)**<br>
965
- When `true`, displays lines representing the mean values of the data series.
628
+ [▶ watch it stream](packages/examples/public/images/scatter-chart-streaming.gif) (animated)
966
629
 
967
- **showWindowedMeanValueLines (boolean, optional, default = false)**<br>
968
- When `true`, displays lines representing the windowed mean values.
630
+ | prop | type | description |
631
+ |---|---|---|
632
+ | `interpolation` | `d3.CurveFactory` | How the line is drawn between points (default `d3.curveLinear`). Any [d3 curve](https://d3js.org/d3-shape/curve) works, such as `d3.curveMonotoneX` or `d3.curveStep`. |
633
+ | `timeWindowBehavior` | `TimeWindowBehavior` | `SCROLL` (the default) scrolls the time axis as data passes its end. `SQUEEZE` compresses the time axis to fit all the data instead. |
634
+ | `markerRadius` | `number` (px) | Draws a circle of this radius at each point. No markers when omitted. |
635
+ | `hideMarkersWhileRunning` | `boolean` | Hides the markers while the data source is running, showing them when paused (default `true`). |
969
636
 
970
- **barMargin (number, pixels, optional, default = 2)**<br>
971
- Optional property that adds a margin to the top and bottom of the bars.
637
+ ```tsx
638
+ <Chart chartId={1} width={width} height={height} dataSource={dataSource} seriesStyles={seriesStyles}
639
+ windowingTime={50} dataUpdatePeriod={50} onUpdateAxesBounds={handleAxesBounds}>
640
+ <ContinuousAxis axisId="x-axis-1" location={AxisLocation.Bottom} domain={[0, 10_000]} label="time (ms)"/>
641
+ <ContinuousAxis axisId="y-axis-1" location={AxisLocation.Left} domain={[0, 1000]} label="distance (µm)"/>
642
+ <ContinuousAxis axisId="x-axis-2" location={AxisLocation.Top} domain={[0, 5000]} label="expanded time (ms)"/>
643
+ <ContinuousAxis axisId="y-axis-2" location={AxisLocation.Right} scale={d3.scaleLog()} domain={[100, 1200]} label="distance (µm)"/>
644
+ <Tracker visible={true} labelFormatter={x => `${d3.format(",.0f")(x)} ms`}/>
645
+ <Tooltip visible={true}>
646
+ <ScatterPlotTooltipContent xLabel="t (ms)" yLabel="distance" yValueFormatter={value => formatNumber(value, " ,.0f")}/>
647
+ </Tooltip>
648
+ <ScatterPlot
649
+ interpolation={d3.curveMonotoneX}
650
+ axisAssignments={new Map([['test2', assignAxes('x-axis-2', 'y-axis-2')]])}
651
+ panEnabled={true}
652
+ zoomEnabled={true}
653
+ markerRadius={2}
654
+ />
655
+ </Chart>
656
+ ```
972
657
 
973
- #### [&#10514;](#content) <span id="bar-plot-usage-view">&lt;BarPlot/&gt; view-modifying interactions</span>
658
+ #### <span id="raster-plot">&lt;RasterPlot/&gt;</span>
974
659
 
975
- **panEnabled**, **zoomEnabled**, **zoomKeyModifiersRequired**, **withCadenceOf**<br>
976
- These properties behave the same as in [&lt;ScatterPlot/&gt;](#scatter-plot-usage-view).
660
+ Events over time: each series is a row, and each datum is a short vertical line (a "spike") at its time. The x-axis is a `<ContinuousAxis>`, and the y-axis is an `<OrdinalAxis>` whose categories are the series names. Uses a `TimeSeriesDataSource`. ([full example](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/StreamingRasterChart.tsx), [walkthrough](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/raster-chart.md))
977
661
 
978
- ### [&#10514;](#content) <span id="poincare-plot-usage">&lt;PoincarePlot/&gt;</span>
662
+ <picture>
663
+ <source media="(prefers-color-scheme: dark)" srcset="packages/examples/public/images/raster-chart-dark.png">
664
+ <img src="packages/examples/public/images/raster-chart-light.png" alt="the raster chart example: fifty neurons, one row each, with a spike for each event">
665
+ </picture>
979
666
 
980
- The &lt;PoincarePlot/&gt; (also known as an iterates plot) is used to plot the current value of a series against its previous value (n vs n-1). This is useful for identifying patterns or oscillations in the data.
667
+ [▶ watch it stream](packages/examples/public/images/raster-chart-streaming.gif) (animated)
981
668
 
982
- #### [&#10514;](#content) <span id="poincare-plot-usage-base">&lt;PoincarePlot/&gt; base properties</span>
669
+ | prop | type | description |
670
+ |---|---|---|
671
+ | `spikeMargin` | `number` (px) | Space above and below each spike, within its row (default 2). Set it per series with the series style's `margin`. |
983
672
 
984
- **interpolation (d3.CurveFactory, optional, default = undefined)**<br>
985
- An optional property that defines how the line between iterates will be interpolated. Use `NoCurveFactory` to disable the line.
673
+ ```tsx
674
+ <Chart chartId={2} width={width} height={height} dataSource={dataSource} seriesStyles={seriesStyles} windowingTime={50}>
675
+ <ContinuousAxis axisId="x-axis-1" location={AxisLocation.Bottom} domain={[0, 5000]} label="t (ms)"/>
676
+ <EmptyAxis axisId="x-axis-2" location={AxisLocation.Top}/>
677
+ <OrdinalAxis axisId="y-axis-1" location={AxisLocation.Left} categories={dataSource.seriesList().map(s => s.name)} label="neuron"/>
678
+ <EmptyAxis axisId="y-axis-2" location={AxisLocation.Right}/>
679
+ <Tracker visible={true} labelFormatter={x => `${d3.format(",.0f")(x)} ms`}/>
680
+ <Tooltip visible={true}>
681
+ <RasterPlotTooltipContent xFormatter={value => formatNumber(value, " ,.0f") + ' ms'}/>
682
+ </Tooltip>
683
+ <RasterPlot spikeMargin={1} panEnabled={true} zoomEnabled={true}/>
684
+ </Chart>
685
+ ```
986
686
 
987
- **showPoints (boolean, optional, default = false)**<br>
988
- When `true`, displays individual data points on the plot.
687
+ #### <span id="bar-plot">&lt;BarPlot/&gt;</span>
989
688
 
990
- #### [&#10514;](#content) <span id="poincare-plot-usage-view">&lt;PoincarePlot/&gt; view-modifying interactions</span>
689
+ One bar per category, showing the category's latest value and its statistics, both over all time ("lifetime") and over the data currently held ("windowed", which depends on the data source's `dropDataAfter`). The x-axis is an `<OrdinalAxis>`, and the y-axis is a `<ContinuousAxis>`. Uses an `OrdinalDataSource`. ([full example](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/StreamingBarChart.tsx), [walkthrough](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/bar-chart.md))
991
690
 
992
- **panEnabled**, **zoomEnabled**, **zoomKeyModifiersRequired**, **withCadenceOf**<br>
993
- These properties behave the same as in [&lt;ScatterPlot/&gt;](#scatter-plot-usage-view).
691
+ <picture>
692
+ <source media="(prefers-color-scheme: dark)" srcset="packages/examples/public/images/bar-chart-dark.png">
693
+ <img src="packages/examples/public/images/bar-chart-light.png" alt="the bar chart example: fifty categories, each with its value, min/max, and mean">
694
+ </picture>
994
695
 
995
- ## [&#10514;](#content) <span id="utilities">utilities</span>
696
+ [▶ watch it stream](packages/examples/public/images/bar-chart-streaming.gif) (animated)
996
697
 
997
- ### [&#10514;](#content) <span id="tracker-usage">&lt;Tracker/&gt;</span>
698
+ | prop | type | description |
699
+ |---|---|---|
700
+ | `showValueLines` | `boolean` | Shows each category's latest value (default `true`). |
701
+ | `showMinMaxBars` | `boolean` | Shows each category's lifetime min-to-max bar (default `true`). |
702
+ | `showMeanValueLines` | `boolean` | Shows each category's lifetime mean (default `true`). |
703
+ | `showWindowedMinMaxBars` | `boolean` | Shows each category's windowed min-to-max bar (default `true`). |
704
+ | `showWindowedMeanValueLines` | `boolean` | Shows each category's windowed mean (default `true`). |
705
+ | `barMargin` | `number` (px) | Space between a category's edges and its bars (default 2). |
706
+ | `barSeriesStyle` | `BarSeriesStyle` | The default style for categories without one in the chart's `seriesStyles` (default `defaultBarSeriesStyle()`). |
707
+ | `zoomState`, `onZoomStateChange` | `BarPlotZoomState`, `(state) => void` | Keep the zoom across remounts: store what `onZoomStateChange` hands you, and pass it back as `zoomState`. It's read once, when the plot mounts. |
998
708
 
999
- The tracker, when enabled, follows the mouse when it is in the plot area, renders a vertical line from the bottom x-axis to the top of the plot, and displays the time value for either one or both the x-axes.
709
+ Style the bars with `defaultBarSeriesStyle(color)`, and override the parts you want:
1000
710
 
1001
- #### [&#10514;](#content) <span id="tracker-usage-base">&lt;Tracker/&gt; base properties</span>
711
+ ```tsx
712
+ seriesStyles={new Map<string, BarSeriesStyle>([
713
+ ['HC 1', {...defaultBarSeriesStyle('orange'), lineWidth: 2}],
714
+ ['HC 14', {
715
+ ...defaultBarSeriesStyle('blue'),
716
+ minMaxBar: {...defaultBarSeriesStyle('blue').minMaxBar, widthFraction: 1}
717
+ }],
718
+ ])}
719
+ ```
1002
720
 
1003
- **visible (boolean)**<br>
1004
- When set to `true` the tracker is visible when the mouse is in the plot area. When set to `false` the tracker is not shown.
721
+ ```tsx
722
+ <Chart<OrdinalChartData, OrdinalDatum, BarSeriesStyle, WindowedOrdinalStats, OrdinalAxisRange, OrdinalStringAxis>
723
+ chartId={3} width={width} height={height} dataSource={dataSource} seriesStyles={seriesStyles}
724
+ windowingTime={50} onUpdateChartTime={setChartTime}
725
+ >
726
+ <OrdinalAxis axisId="x-axis-1" location={AxisLocation.Bottom} categories={categories} label="neuron" axisTickStyle={{rotation: 90}}/>
727
+ <ContinuousAxis axisId="y-axis-1" location={AxisLocation.Left} domain={[-1.1, 1.1]} label="ρ (mV)"/>
728
+ <Tracker visible={true} trackerAxis={AxisLocation.Left} labelFormatter={y => `${d3.format(".2f")(y)} mV`}/>
729
+ <Tooltip visible={true}>
730
+ <BarPlotTooltipContent ordinalUnits="mV"/>
731
+ </Tooltip>
732
+ <BarPlot barMargin={1} panEnabled={true} zoomEnabled={true} showWindowedMinMaxBars={false}/>
733
+ </Chart>
734
+ ```
1005
735
 
1006
- **labelLocation ([TrackerLabelLocation](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/Tracker.tsx), optional, default = TrackerLabelLocation.WithMouse)**<br>
1007
- Optional property specifies the location of the label. The `TrackerLabelLocation` is an enumeration with three values `Nowhere`, `WithMouse`, and `ByAxes`. By default, the label location is with the mouse (i.e. `WithMouse`). The `Nowhere` location can be used to hide the label, in cases where you would like to use your own. The `WithMouse` value has the label follow the mouse in the vertical direction along the tracker line. And the `ByAxes` displays the labels next to their respective axes, with the tracker line.
736
+ #### <span id="poincare-plot">&lt;PoincarePlot/&gt;</span>
1008
737
 
1009
- **onTrackerUpdate ((update: TrackerAxisUpdate) => void, optional, default = noop)**<br>
1010
- Optional callback function that accepts the update from the tracker. Use this to display the tracker and data information outside of the &lt;Chart/&gt. The `TrackerAxisUpdate` is a `Map<string, TrackerAxisInfo>`, the `TrackerAxisInfo` has the shape `{x: number, axisLocation: AxisLocation}`, and the `AxisLocation` is an same enumeration used for placing the axes on the chart (i.e. `AxisLocation.Bottom`, `AxisLocation.Top`).
738
+ A Poincaré (iterates) plot shows each value of a sequence against a later value of the same sequence: `(f[m](x), f[m+n](x))`. Patterns that are hard to see over time, such as cycles and attractors, show up as shapes. Random noise fills the plot with a shapeless cloud, but a series generated by a rule, even a chaotic one that *looks* random over time, traces out a distinct shape. That makes the plot handy for spotting auto-correlations, and for telling random apart from complicated-but-deterministic. Both axes are `<ContinuousAxis>` and don't scroll, so set `updateAxisBasedOnDomainValues={false}` on them. Uses an `IteratesDataSource`, usually fed by [`iteratesObservable`](#chart-data). ([full example](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/StreamingPoincareChart.tsx), [walkthrough](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/poincare-chart.md), with the tent, logistic, and Gauss maps)
1011
739
 
740
+ <picture>
741
+ <source media="(prefers-color-scheme: dark)" srcset="packages/examples/public/images/poincare-chart-dark.png">
742
+ <img src="packages/examples/public/images/poincare-chart-light.png" alt="the Poincaré chart example: the tent map at a lag of 1, with colorized lines">
743
+ </picture>
1012
744
 
1013
- #### [&#10514;](#content) <span id="tracker-usage-styling">&lt;Tracker/&gt; styling</span>
745
+ [▶ watch it stream](packages/examples/public/images/poincare-chart-streaming.gif) (animated)
1014
746
 
1015
- **style ([TrackerStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/trackerUtils.ts), optional, default = {visible: false, color: '#d2933f', lineWidth: 1})**<br>
1016
- Optional property defining the style for the tracker line. Has a shape of `{visible: boolean, color: string, lineWidth: number}`.
747
+ | prop | type | description |
748
+ |---|---|---|
749
+ | `interpolation` | `d3.CurveFactory` | How the line between successive points is drawn. Use `NoCurveFactory` to draw no line. |
750
+ | `showPoints` | `boolean` | Draws the points as well as the line (default `true`). |
751
+ | `colorizeLines` | `boolean` | Colors the line along its length, one step per point, so that its direction and age can be seen (default `false`). |
752
+ | `lineStartColor`, `lineEndColor` | `string` | The colors the colorized line sweeps between (defaults `DEFAULT_LINE_START_COLOR`, a blue, and `DEFAULT_LINE_END_COLOR`, a red). |
753
+ | `lineColorSteps` | `number` | The number of points it takes the color to sweep from start to end, and as many to sweep back (default `DEFAULT_LINE_COLOR_STEPS`, 1,000). |
754
+ | `zoomMinScaleFactor`, `zoomMaxScaleFactor` | `number` | Limit how far the user can zoom in and out. The plot starts at a scale factor of 1; smaller factors zoom in, and larger ones zoom out (2 shows twice the range). The defaults, 0 and 1, let the user zoom in as far as they like, but not out past the starting view. |
755
+ | `zoomState`, `onZoomStateChange` | `PoincarePlotZoomState`, `(state) => void` | Keep the zoom across remounts, as for the bar plot. |
1017
756
 
1018
- **font ([TrackerLabelFont](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/trackerUtils.ts), optional, default = {size: 12, color: '#d2933f', weight: 300, family: 'sans-serif'})**<br>
1019
- Optional property defining the font for the tracker label. Has a shape of `{size: number, color: string, family: string, weight: number}`.
757
+ ```tsx
758
+ <Chart chartId={4} width={width} height={height} dataSource={dataSource} seriesStyles={seriesStyles}
759
+ windowingTime={25} onUpdateChartTime={setChartTime}>
760
+ <ContinuousAxis axisId="x-axis-1" location={AxisLocation.Bottom} domain={[0, 1]} label="f[n](x)" updateAxisBasedOnDomainValues={false}/>
761
+ <ContinuousAxis axisId="y-axis-1" location={AxisLocation.Left} domain={[0, 1]} label="f[n+1](x)" updateAxisBasedOnDomainValues={false}/>
762
+ <Tracker visible={true} trackerAxis={AxisLocation.Bottom} labelFormatter={x => d3.format(",.3f")(x)}/>
763
+ <Tracker visible={true} trackerAxis={AxisLocation.Left} labelFormatter={y => d3.format(",.3f")(y)}/>
764
+ <Tooltip visible={true}>
765
+ <PoincarePlotTooltipContent xLabel="t (ms)" yLabel="f(x)" yValueFormatter={value => formatNumber(value, " ,.4f")}/>
766
+ </Tooltip>
767
+ <PoincarePlot interpolation={d3.curveLinear} colorizeLines={true} panEnabled={true} zoomEnabled={true}/>
768
+ </Chart>
769
+ ```
1020
770
 
771
+ #### <span id="outlier-plot">&lt;OutlierPlot/&gt;</span>
1021
772
 
1022
- ### [&#10514;](#content) <span id="legend-usage">&lt;Legend/&gt;</span>
773
+ Values over time, like the scatter plot, with a translucent band for each confidence level (measure) around the line. Points that fall outside a band are marked, so outliers stand out. Both axes are `<ContinuousAxis>`. Uses an `OutlierDataSource`. ([full example](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/StreamingOutlierChart.tsx), [walkthrough](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/outlier-chart.md))
1023
774
 
1024
- The `<Legend/>` component displays a legend for the chart, mapping series names to their respective colors. It can be placed inside the `<Chart/>` component and can be rendered either within the chart's SVG area or into an external HTML container.
775
+ <picture>
776
+ <source media="(prefers-color-scheme: dark)" srcset="packages/examples/public/images/outlier-chart-dark.png">
777
+ <img src="packages/examples/public/images/outlier-chart-light.png" alt="the outlier chart example: a price index inside its 1σ, 2σ, and 3σ bands, with the points outside the bands marked">
778
+ </picture>
1025
779
 
1026
- #### [&#10514;](#content) <span id="legend-usage-base">&lt;Legend/&gt; base properties</span>
780
+ [▶ watch it stream](packages/examples/public/images/outlier-chart-streaming.gif) (animated)
1027
781
 
1028
- **visible (boolean)**<br>
1029
- When set to `true`, the legend is visible.
782
+ | prop | type | description |
783
+ |---|---|---|
784
+ | `interpolation` | `d3.CurveFactory` | How the central line is drawn between points (default `d3.curveLinear`). |
785
+ | `timeWindowBehavior` | `TimeWindowBehavior` | `SCROLL` (the default) or `SQUEEZE`, as for the scatter plot. |
786
+ | `bandOpacity` | `number` | The fill opacity of the outermost (widest) band (default 0.15). |
787
+ | `bandOpacityStep` | `number` | The opacity added for each tighter band (default 0.12). |
788
+ | `markerRadius` | `number` (px) | Draws a marker at each point while the data source is paused. |
789
+ | `outlierMarkerColors` | `Array<string>` | The color of an outlier's marker, by band: `outlierMarkerColors[i]` marks a point outside band `i`. When a point is outside several bands, the widest band's color wins (default red). |
1030
790
 
1031
- **location ([LegendLocation](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/legends/Legend.tsx), optional, default = LegendLocation.TOP_RIGHT)**<br>
1032
- Specifies where to anchor the legend within the plot area. Ignored when `container` is provided. Available locations are `TOP_LEFT`, `TOP_RIGHT`, `BOTTOM_LEFT`, and `BOTTOM_RIGHT`.
791
+ The plot's type parameter is the measures tuple:
1033
792
 
1034
- **offset ({ x: number; y: number }, optional, default = { x: 10, y: 10 })**<br>
1035
- Optional offset in pixels from the chosen corner, applied after the margin. Ignored when `container` is provided.
793
+ ```tsx
794
+ const MEASURES = [0.68, 0.95, 0.997] as const // 1σ, 2σ, 3σ
795
+ type Measures = typeof MEASURES
796
+
797
+ <Chart chartId={5} width={width} height={height} dataSource={dataSource} windowingTime={50} dataUpdatePeriod={25}>
798
+ <ContinuousAxis axisId="x-axis-1" location={AxisLocation.Bottom} domain={[0, 10_000]} label="time (ms)"/>
799
+ <ContinuousAxis axisId="y-axis-1" location={AxisLocation.Left} domain={[-50, 150]} label="spot price index (USD)" autoScale={true}/>
800
+ <OutlierPlot<Measures>
801
+ bandOpacity={0.12}
802
+ bandOpacityStep={0.14}
803
+ outlierMarkerColors={['#f4c542', '#f08a3b', '#d62728']}
804
+ panEnabled={true}
805
+ zoomEnabled={true}
806
+ />
807
+ <Tooltip visible={true}>
808
+ <OutlierPlotTooltipContent
809
+ datumFormatter={(x, y) => `${y.toFixed(2)} USD @ ${x.toFixed(0)} ms`}
810
+ measureFormatter={(inside, outside) => `${(inside * 100).toFixed(1)}% inside, ${(outside * 100).toFixed(1)}% outside`}
811
+ />
812
+ </Tooltip>
813
+ </Chart>
814
+ ```
1036
815
 
1037
- **container (React.RefObject<HTMLElement | null>, optional)**<br>
1038
- When provided, the legend renders as an HTML element portal into this external container instead of inside the chart SVG. Position the container however you like — the legend fills it.
816
+ ### <span id="tracker">&lt;Tracker/&gt;</span> [↑](#content "back to contents")
1039
817
 
1040
- #### [&#10514;](#content) <span id="legend-usage-styling">&lt;Legend/&gt; styling</span>
818
+ The tracker follows the mouse over the plot with a line, and labels the axes' values at the mouse. By default it's a vertical line that tracks the x-axes. Use `trackerAxis={AxisLocation.Left}` (or `Right`) for a horizontal line that tracks the y-axes, and add two trackers to track both.
1041
819
 
1042
- **style ([LegendStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/legends/Legend.tsx), optional, default = [defaultLegendStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/legends/Legend.tsx))**<br>
1043
- Optional property defining the style for the legend. The following style elements are available:
820
+ | prop | type | description |
821
+ |---|---|---|
822
+ | `visible` | `boolean` | **Required.** Whether the tracker is shown. |
823
+ | `trackerAxis` | `AxisLocation` | The axes the tracker reports (default `AxisLocation.Bottom`). `Bottom` or `Top` gives a vertical line; `Left` or `Right` a horizontal one. |
824
+ | `labelLocation` | `TrackerLabelLocation` | `ByAxis` (the default) shows each value next to its axis. `Nowhere` hides the labels, for when you show the values yourself with `onTrackerUpdate`. |
825
+ | `labelFormatter` | `(value: number) => string` | Formats the labels (default: a whole number, with thousands separators). |
826
+ | `style` | `Partial<TrackerStyle>` | The line's style: `{visible, color, lineWidth}` (default color `'#d2933f'`, width 1). |
827
+ | `font` | `Partial<TrackerLabelFont>` | The labels' font: `{size, color, family, weight}`. |
828
+ | `onTrackerUpdate` | `(update: TrackerAxisUpdate) => void` | Called as the mouse moves, with a map from axis ID to `{x, axisLocation}`, where `x` is the axis' value at the mouse (for a y-axis, too). Use it to show the values outside the chart. |
1044
829
 
1045
- 1. **fontSize** (number): Font size for the legend labels.
1046
- 2. **fontFamily** (string): Font family for the legend labels.
1047
- 3. **fontColor** (string): Font color for the legend labels.
1048
- 4. **backgroundColor** (string): Background fill color for the legend box.
1049
- 5. **backgroundOpacity** (number): Background opacity for the legend box.
1050
- 6. **borderColor** (string): Border/stroke color for the legend box.
1051
- 7. **borderWidth** (number): Border width for the legend box.
1052
- 8. **borderOpacity** (number): Border opacity for the legend box.
1053
- 9. **borderRadius** (number): Corner radius of the legend box.
1054
- 10. **padding** (number): Padding inside the legend box (in pixels).
1055
- 11. **rowGap** (number): Vertical space between legend entries.
1056
- 12. **swatchWidth** (number): Width of the color swatch next to each label.
1057
- 13. **swatchHeight** (number): Height of the color swatch next to each label.
1058
- 14. **swatchLabelGap** (number): Gap between the swatch and the label text.
1059
- 15. **maxHeight** (number, optional): Maximum height of the legend before it starts scrolling.
1060
- 16. **transitionDuration** (number): Duration of the visibility transition in milliseconds.
830
+ ### <span id="legend">&lt;Legend/&gt;</span> [↑](#content "back to contents")
1061
831
 
1062
- #### [&#10514;](#content) <span id="legend-usage-examples">&lt;Legend/&gt; examples</span>
832
+ The legend shows each series' name and color. It reads the names from the chart's data and the colors from the chart's `seriesStyles`, so there's nothing to keep in sync. It lists only the series that pass the chart's `seriesFilter`, and scrolls when there are more series than fit. Hovering over a legend entry highlights its series (and fades the rest), and hovering over a series highlights its entry. It can sit over a corner of the plot, or render into a container of your own, outside the chart. ([walkthrough](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/legends.md))
1063
833
 
1064
- **Basic internal legend**
1065
834
  ```tsx
835
+ // over the plot's top-right corner
836
+ <Legend visible={true} location={LegendLocation.TOP_RIGHT}/>
837
+
838
+ // in your own container, anywhere on the page
839
+ const legendContainer = useRef<HTMLDivElement>(null)
840
+ ...
841
+ <div ref={legendContainer} style={{width: 200}}/>
1066
842
  <Chart ...>
1067
843
  ...
1068
- <Legend visible={true} location={LegendLocation.TOP_RIGHT} />
844
+ <Legend visible={true} container={legendContainer}/>
1069
845
  </Chart>
1070
846
  ```
1071
847
 
1072
- **External container legend**
1073
- ```tsx
1074
- const legendContainer = useRef<HTMLDivElement>(null);
1075
- ...
1076
- <>
1077
- <div ref={legendContainer} style={{ width: 200 }} />
1078
- <Chart ...>
1079
- ...
1080
- <Legend visible={true} container={legendContainer} />
1081
- </Chart>
1082
- </>
1083
- ```
848
+ | prop | type | description |
849
+ |---|---|---|
850
+ | `visible` | `boolean` | **Required.** Whether the legend is shown. |
851
+ | `location` | `LegendLocation` | The corner of the plot the legend sits in: `TOP_LEFT`, `TOP_RIGHT` (the default), `BOTTOM_LEFT`, or `BOTTOM_RIGHT`. Ignored when `container` is set. |
852
+ | `offset` | `{x, y}` | The offset (px) from that corner (default `{x: 10, y: 10}`). Ignored when `container` is set. |
853
+ | `container` | `RefObject<HTMLElement \| null>` | Renders the legend into this element (through a React portal), instead of over the plot. The legend fills the container. |
854
+ | `style` | `Partial<LegendStyle>` | `fontSize`, `fontFamily`, `fontColor`, `backgroundColor`, `backgroundOpacity`, `borderColor`, `borderWidth`, `borderOpacity`, `borderRadius`, `padding`, `rowGap`, `swatchWidth`, `swatchHeight`, `swatchLabelGap`, `maxHeight` (the legend scrolls beyond it), and `transitionDuration` (ms). Defaults are in `defaultLegendStyle`. |
1084
855
 
1085
- ### [&#10514;](#content) <span id="tooltip-usage">&lt;Tooltip/&gt;</span>
856
+ ### <span id="tooltip">&lt;Tooltip/&gt; and tooltip content</span> [↑](#content "back to contents")
1086
857
 
1087
- A tooltip renders information about the data over which the mouse is hovering. The &lt;Tooltip/&gt; component in `stream-charts` is the container for the tooltip content. It is responsible for rendering the area in which the tooltip contents are displayed. The tooltip content is a child of the &lt;Tooltip/&gt; component so that it can be (relatively) easily extended. Currently `stream-charts` has two tooltip contents, one for each plot type (&lt;ScatterPlotTooltipContent/&gt; and &lt;RasterPlotTooltipContent/&gt;). Each of these will be covered after we discuss the &lt;Tooltip/&gt;.
1088
-
1089
- The properties for the lt;Tooltip/&gt; are simple and limited. The tooltip content components have more options.
1090
-
1091
- **visible (boolean)**<br>
1092
- When set to `true` the the tooltip is shown when the mouse hovers over a data series or point.
1093
-
1094
- **style ([TooltipStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/tooltipUtils.ts), optional, default = [defaultTooltipStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/tooltipUtils.ts) )**<br>
1095
- Optional property defining the style of the tooltip. The following style elements are available
1096
- 1. **visible**<br>Visibility of the tooltip when the mouse hovers over a data series or point
1097
- ---
1098
- 2. **fontSize**<br>The size of the font displayed in the tooltip
1099
- 3. **fontColor**<br>The color of the text displayed in the tooltip
1100
- 4. **fontFamily**<br>The font weight for the text displayed in the tooltip
1101
- 5. **fontWeight**<br>The font weight for the text displayed in the tooltip
1102
- ---
1103
- 6. **backgroundColor**<br>The background color of the tooltip
1104
- 7. **backgroundOpacity**<br>The opacity of the background (i.e. how transparent it is)
1105
- ---
1106
- 8. **borderColor**<br>The color of the border surrounding the tooltip content
1107
- 9. **borderWidth**<br>The width of the border surrounding the tooltip content
1108
- 10. **borderRadius**<br>The radius of the border surrounding the tooltip content
1109
- ---
1110
- 11. **paddingLeft**<br>The padding to the left of the tooltip content
1111
- 12. **paddingRight**<br>The padding to the right of the tooltip content
1112
- 13. **paddingTop**<br>The padding to the top of the tooltip content
1113
- 14. **paddingBottom**<br>The padding to the bottom of the tooltip content
858
+ The tooltip shows information about the *data* under the mouse: a line, a spike, a point, a bar, or a band. (The [tracker](#tracker), by contrast, shows the mouse's *position*.) The `<Tooltip>` provides the box and its style. Its child, the *tooltip content*, decides what goes in the box. Each plot has its own content component, most of which take formatters for their values, and you can [write your own](#custom-tooltip). ([walkthrough](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/tooltips-and-trackers.md))
1114
859
 
1115
- **children (&lt;ScatterPlotTooltipContent/&gt; and &lt;RasterPlotTooltipContent/&gt;)**<br>
1116
- The tooltip content.
860
+ ```tsx
861
+ <Tooltip visible={true} style={{fontColor: theme.color, backgroundColor: theme.backgroundColor, borderColor: theme.color, backgroundOpacity: 0.9}}>
862
+ <ScatterPlotTooltipContent xLabel="t (ms)" yLabel="count"/>
863
+ </Tooltip>
864
+ ```
1117
865
 
866
+ | `<Tooltip>` prop | type | description |
867
+ |---|---|---|
868
+ | `visible` | `boolean` | **Required.** Whether the tooltip is shown when the mouse is over one of the plot's elements. |
869
+ | `style` | `Partial<TooltipStyle>` | The box's style: `fontSize`, `fontColor`, `fontFamily`, `fontWeight`, `backgroundColor`, `backgroundOpacity`, `borderColor`, `borderOpacity`, `borderWidth`, `borderRadius`, and `paddingLeft`/`Right`/`Top`/`Bottom`. Defaults are in `defaultTooltipStyle`. |
870
+ | `children` | | **Required.** The tooltip content. |
1118
871
 
1119
- ### [&#10514;](#content) <span id="scatterplot-tooltip-usage">&lt;ScatterPlotTooltipContent/&gt;</span>
872
+ Each content component also takes a `style` that overrides parts of the `<Tooltip>`'s style.
1120
873
 
1121
- The &lt;ScatterPlotTooltipContent/&gt; renders a table displaying the series datum that immediately precedes the mouse location, and immediately follows the mouse location. The first row shows the x-values, and the second row shows the y-values. The table below maps the properties to the table elements they effect.
874
+ **&lt;ScatterPlotTooltipContent/&gt;** shows a table of the points around the mouse – the point before and the point after (or the point under the mouse, when it's on one), and the change between them – followed by statistics of the series (its number of points, mean value, standard deviation, and mean interval between points) and a sparkline of its autocorrelation.
1122
875
 
1123
- ---
1124
- series name
876
+ | prop | description |
877
+ |---|---|
878
+ | `xLabel`, `yLabel` | **Required.** The row headers for the x-values and y-values. |
879
+ | `beforeHeader`, `pointHeader`, `afterHeader`, `deltaHeader` | The column headers (defaults: before, point, after, ∆). |
880
+ | `xValueFormatter`, `yValueFormatter` | Format the values (defaults `formatTime` and `formatValue`). |
881
+ | `xChangeFormatter`, `yChangeFormatter` | Format the change, `(value1, value2) => string` (defaults `formatTimeChange` and `formatValueChange`). |
882
+ | `intervalFormatter` | Formats the statistics' mean interval between points, a duration (defaults to the `xValueFormatter`). Give it its own formatter when the x-values aren't formatted as durations, for example as the time of day. |
1125
883
 
1126
- | | beforeHeader | afterHeader | deltaHeader |
1127
- |---|-------------|-------------|-------------|
1128
- | xLabel | xValueFormatter | xValueFormatter | xChangeFormatter |
1129
- | yLabel | yValueFormatter | yValueFormatter | yChangeFormatter |
884
+ **&lt;RasterPlotTooltipContent/&gt;** shows the spike under the mouse (its series, time, and value) along with statistics of the series' spikes: their count, the mean and standard deviation of their values, and the mean time between them, with a sparkline of their values.
1130
885
 
1131
- ---
886
+ | prop | description |
887
+ |---|---|
888
+ | `xFormatter` | Formats the spike's time. |
889
+ | `yFormatter` | Formats the values (the spike's value, and the mean and standard deviation of the values). |
890
+ | `intervalFormatter` | Formats the mean time between spikes, a duration (defaults to the `xFormatter`). Give it its own formatter when the times aren't formatted as durations, for example as the time of day. |
1132
891
 
1133
- **xLabel (string)**<br>
1134
- Required property that gives a name to the x-values. This is the label for the x-values in the tooltip.
892
+ **&lt;BarPlotTooltipContent/&gt;** shows the category under the mouse, with its value and statistics, and a sparkline of its values over the data the chart holds.
1135
893
 
1136
- **yLabel (string)**<br>
1137
- Required property that gives a name to the y-values. This is the label for the y-values in the tooltip.
894
+ | prop | description |
895
+ |---|---|
896
+ | `ordinalUnits` | The units shown after the values, for example `"mV"`. |
1138
897
 
1139
- **beforeHeader (string, optional, default = 'before')**<br>
1140
- Optional property that gives a name to the data point that is immediately before the mouse location.
898
+ **&lt;PoincarePlotTooltipContent/&gt;** shows the point under the mouse, with the iterates before and after it, and a sparkline of the series' values around it.
1141
899
 
1142
- **afterHeader (string, optional, default = 'after')**<br>
1143
- Optional property that gives a name to the data point that is immediately after the mouse location.
900
+ | prop | description |
901
+ |---|---|
902
+ | `xLabel`, `yLabel` | **Required.** The row headers. |
903
+ | `nMinusLagHeader`, `nHeader`, `nPlusLagHeader` | The column headers for the previous, current, and next iterates. |
904
+ | `xValueFormatter`, `yValueFormatter`, `xChangeFormatter`, `yChangeFormatter` | Format the values and their changes. |
1144
905
 
1145
- **deltaHeader (string, optional, default = '∆')**<br>
1146
- Optional property that gives a name to the difference between the datum immediately before and after the mouse location.
906
+ **&lt;OutlierPlotTooltipContent/&gt;** shows the point and band under the mouse.
1147
907
 
1148
- **xValueFormatter ((value: number) => string, optional, default =** [**formatTime**](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/utils.ts) <br>
1149
- Optional function that formats the x-value immediately before and after the mouse location. The default function formats that x-values as natural numbers representing milliseconds.
908
+ | prop | description |
909
+ |---|---|
910
+ | `datumFormatter` | `(x, y) => string \| JSX.Element`: describes the point. |
911
+ | `bandFormatter` | `(lower, upper) => string \| JSX.Element`: describes the band. It's handed the band's *measures* (for example 0.68 and 0.95), not its bounds. |
912
+ | `measureFormatter` | `(innerProb, outerProb) => string \| JSX.Element \| Array<string>`: describes the probabilities of a point being inside and outside the band. |
913
+ | `children` | Optional custom content. Inside it, `useOutlierTooltip()` hands you the hovered point and band (`tooltipContent`), the style, and the formatters. Defaults to `<DefaultOutlierTooltipContent/>`. |
1150
914
 
1151
- **yValueFormatter ((value: number) => string, optional, default =** [**formatValue**](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/utils.ts) <br>
1152
- Optional function that formats the x-value immediately before and after the mouse location. The default function formats the y-values as floating point with 3 values to the right of the decimal point.
915
+ #### <span id="custom-tooltip">writing your own tooltip content</span>
1153
916
 
1154
- **xChangeFormatter ((value1: number, value2: number) => string, optional, default =** [**formatTimeChange**](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/utils.ts) <br>
1155
- Optional function that formats the change in the x-value of the points immediately before and after the mouse location. The default function formats the value as a natural number.
917
+ All of the content components above are built on `<TooltipContent>`, and so can yours. It does the plumbing (it registers with the chart, hides when the mouse leaves, and places the box next to the mouse, flipping it to stay on the chart). You supply two functions:
1156
918
 
1157
- **yChangeFormatter ((value1: number, value2: number) => string, optional, default =** [**formatValueChange**](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/utils.ts) <br>
1158
- Optional function that formats the change in the y-value of the points immediately before and after the mouse location. The default function formats the value as floating point with 3 values to the right of the decimal point.
919
+ - **`capture(seriesName, time, tooltipData, providerId?)`** turns what the plot knows about the element under the mouse into your own content (any value you like), or `undefined` to show nothing. It's called on every mouse move, so keep it quick, and copy what you need out of the series, which keep changing.
920
+ - **`children(content, style)`** renders that content.
1159
921
 
1160
- **style ([TooltipStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/tooltipUtils.ts), optional, default = [defaultTooltipStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/tooltipUtils.ts) )**<br>
1161
- Optional styles for the tooltip content. The styles are the same as those for the &lt;Tooltip/&gt;.
922
+ ```tsx
923
+ import {type ReactElement} from "react"
924
+ import {TooltipContent, type Datum, type ScatterTooltipMetadata} from "stream-charts"
925
+
926
+ function MyScatterTooltip(): ReactElement {
927
+ return (
928
+ <TooltipContent<Datum, ScatterTooltipMetadata, {seriesName: string, count: number}>
929
+ capture={(seriesName, _time, {series}) => ({seriesName, count: series.length})}
930
+ >
931
+ {({seriesName, count}) => <div>{seriesName}: {count} points</div>}
932
+ </TooltipContent>
933
+ )
934
+ }
1162
935
 
936
+ <Tooltip visible={true}>
937
+ <MyScatterTooltip/>
938
+ </Tooltip>
939
+ ```
1163
940
 
1164
- ### [&#10514;](#content) <span id="rasterplot-tooltip-usage">&lt;RasterTooltipContent/&gt;</span>
1165
- The &lt;RasterPlotTooltipContent/&gt; shows the series name, the time, and value of the point over which the mouse is hovering. There are only three properties.
941
+ What `tooltipData` holds (`{series, metadata}`) depends on the plot:
1166
942
 
1167
- **xFormatter ((value: number) => string, optional, default =** [**formatTime**](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/utils.ts) <br>
1168
- Optional function that formats the x-value immediately before and after the mouse location. The default function formats that x-values as natural numbers representing milliseconds.
943
+ | plot | `series` | `metadata` |
944
+ |---|---|---|
945
+ | scatter | the series' data (`Datum`s) | `ScatterTooltipMetadata`: `points`, either `{kind: 'point', before, point, after}` or `{kind: 'segment', before, after}` |
946
+ | raster | the spike under the mouse (`series[0]`) | `RasterTooltipMetadata`: `spikes`, all of the series' spikes |
947
+ | bar | the category's data | `WindowedOrdinalStats`, the lifetime and windowed statistics. `providerId` says which element is under the mouse (see `BAR_CHART_TOOLTIP_PROVIDER_IDS`). |
948
+ | outlier | the series' data | `OutlierBandTooltipMetadata`: the `datum`, the band's `upperMeasure` and `lowerMeasure`, its `bandIndex`, and `pointsInBand` |
1169
949
 
1170
- **yFormatter ((value: number) => string, optional, default =** [**formatValue**](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/utils.ts) <br>
1171
- Optional function that formats the x-value immediately before and after the mouse location. The default function formats the y-values as floating point with 3 values to the right of the decimal point.
950
+ Other props: `style` overrides the tooltip's style, `boxStyle` adds CSS to the box, and `topFor(content, height)` places the box's top yourself. The tutorial [writing your own tooltips](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/writing-tooltips.md) builds a custom tooltip for each plot.
1172
951
 
1173
- **style ([TooltipStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/tooltipUtils.ts), optional, default = [defaultTooltipStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/tooltipUtils.ts) )**<br>
1174
- Optional styles for the tooltip content. The styles are the same as those for the &lt;Tooltip/&gt;.
952
+ ---
1175
953
 
1176
- ### [&#10514;](#content) <span id="barplot-tooltip-usage">&lt;BarPlotTooltipContent/&gt;</span>
954
+ ## <span id="more">more</span> [↑](#content "back to contents")
1177
955
 
1178
- The &lt;BarPlotTooltipContent/&gt; displays the series name and current statistics in a table format.
956
+ ### <span id="theming">theming</span> [↑](#content "back to contents")
1179
957
 
1180
- **ordinalUnits (string, optional)**<br>
1181
- Optional units to display alongside the values.
958
+ The library has no theme object. It has colors: every component takes its colors as props, so to theme a chart, you hand it a different set of colors. When the colors change, the chart redraws in them, even while it's paused.
1182
959
 
1183
- **style ([TooltipStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/tooltipUtils.ts), optional)**<br>
1184
- Optional styles for the tooltip content.
960
+ | component | its colors come from |
961
+ |---|---|
962
+ | `<Chart>` | `color` (the axes, their ticks, and labels) and `backgroundColor` |
963
+ | the series | the chart's `seriesStyles`, one style per series, with its `color` and `highlightColor` |
964
+ | `<ContinuousAxis>`, `<OrdinalAxis>` | the chart's `color`, unless you give the axis its own `font` |
965
+ | `<Tooltip>` | its `style`: `fontColor`, `backgroundColor`, `borderColor`, and their opacities. The tooltip content (tables, sparklines) draws in the `fontColor`. |
966
+ | `<Tracker>` | its `style` (the line) and `font` (the labels) |
967
+ | `<Legend>` | its `style`: `fontColor`, `backgroundColor`, and `borderColor` |
968
+ | `<BarPlot>` | its series styles (start from `defaultBarSeriesStyle(color)`) |
969
+ | `<OutlierPlot>` | the series' color (the line and its bands), and `outlierMarkerColors` |
970
+ | `<PoincarePlot>` | the series' colors, and `lineStartColor`/`lineEndColor` when colorizing |
1185
971
 
1186
- ### [&#10514;](#content) <span id="poincareplot-tooltip-usage">&lt;PoincarePlotTooltipContent/&gt;</span>
972
+ A component you don't hand colors to uses the library's defaults, which are tuned for a dark background: orange (`#d2933f`) axes on near-black (`#202020`), with teal (`#008aad`) series. On a light page, pass colors to every component. The simplest way is to collect them in one theme object and spread it into each component's props. Themes change only colors; fonts, line widths, and margins each have their own props.
1187
973
 
1188
- The &lt;PoincarePlotTooltipContent/&gt; displays the series name and the current, previous, and next iterates in a table format.
974
+ The demo app's [theming page](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/theming.md) shows a light/dark theme held in a store, and how to add a third theme.
1189
975
 
1190
- **xLabel (string)**<br>
1191
- Required label for the x-axis values.
976
+ ### <span id="filtering">filtering series</span> [↑](#content "back to contents")
1192
977
 
1193
- **yLabel (string)**<br>
1194
- Required label for the y-axis values.
978
+ The chart's `seriesFilter` is a `RegExp` that's tested against each series' name on every redraw. For a filter typed in by the user, build the regex with one of these helpers. Each returns a `Result` rather than throwing, so a half-typed pattern is easy to handle:
1195
979
 
1196
- **nMinusLagHeader**, **nHeader**, **nPlusLagHeader** (string, optional)<br>
1197
- Optional headers for the previous, current, and next iterates.
980
+ - `regexFilter(text)` compiles `text` as a regex. It fails when the pattern is invalid, *or* when it risks catastrophic backtracking (which would freeze the render loop).
981
+ - `literalFilter(text)` matches `text` literally, escaping regex characters such as `.` and `*`.
982
+ - `isSafeRegex(pattern)` checks a pattern on its own.
1198
983
 
1199
- **xValueFormatter**, **yValueFormatter**, **xChangeFormatter**, **yChangeFormatter** (optional)<br>
1200
- Optional formatters for values and their changes.
984
+ ```ts
985
+ const filter = regexFilter(filterText).getOrElse(/./)
986
+ ```
1201
987
 
1202
- **style ([TooltipStyle](https://github.com/robphilipp/stream-charts/blob/master/src/app/charts/tooltipUtils.ts), optional)**<br>
1203
- Optional styles for the tooltip content.
988
+ ### <span id="hooks">hooks</span> [↑](#content "back to contents")
1204
989
 
1205
- ## [&#10514;](#content) <span id="hooks">hooks</span>
990
+ For advanced use, such as building your own components inside a `<Chart>`:
1206
991
 
1207
- For advanced usage, `stream-charts` provides several hooks to interact with the chart's internal state.
992
+ | hook | description |
993
+ |---|---|
994
+ | `useDataSourceRunning(dataSource)` | Whether the data source is running, re-rendering when it starts or stops. Works anywhere, inside a chart or not. Handy for a Run/Pause button. |
995
+ | `useDataSource()` | The chart's data source (inside a `<Chart>`). |
996
+ | `useChart()` | The chart's shared state: its ID, canvas and drawing context, colors, series styles, series filter, axes, mouse, and tooltip. Custom components draw onto the canvas by registering a draw function with `useChart().canvasContext`. |
997
+ | `usePlotDimensions()` | The plot area's dimensions and the margins. |
998
+ | `useHoveredSeries()` | The name of the series under the mouse (in the plot or the legend), and a setter. |
999
+ | `useTooltipStyle()` | The enclosing tooltip's style (inside a tooltip's content). |
1000
+ | `useOutlierTooltip()` | The hovered point and band, the style, and the formatters (inside an `<OutlierPlotTooltipContent>`). |
1208
1001
 
1209
- - **useChart()**: Access the chart's context, including dimensions, scales, and registration functions for tooltips and trackers.
1210
- - **useAxes()**: Access the state of the x and y axes.
1211
- - **usePlotDimensions()**: Access the dimensions of the plot area and margins.
1212
- - **useDataObservable()**: Access the data stream observable.
1002
+ The full API, including the formatting helpers (`formatNumber`, `formatTime`, `formatValue`, and friends), is in the [API docs](https://robphilipp.github.io/stream-charts/docs/modules.html).
1213
1003
 
1214
- ## [&#10514;](#content) <span id="building">building</span>
1004
+ ### <span id="further-reading">further reading</span> [↑](#content "back to contents")
1215
1005
 
1216
- Testing
1217
- ```shell
1218
- npm run test
1219
- ```
1006
+ The [demo app](#run-demo) in `packages/examples` has one tab for each plot type, and a set of guide pages (shown in the app's intro, and readable on GitHub):
1220
1007
 
1221
- Creating docs
1222
- ```shell
1223
- npm install typedoc
1224
- npx typedoc src/app/charts/*
1225
- ```
1008
+ - **Getting oriented:** [intro](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/intro.md), [what the pages cover](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/contents.md), [anatomy of an example](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/anatomy-of-example.md), and [the common controls](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/common-controls.md), which says which library option each control is wired to.
1009
+ - **The plots:** [scatter](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/scatter-chart.md), [raster](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/raster-chart.md), [Poincaré](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/poincare-chart.md), [bar](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/bar-chart.md), and [outlier](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/outlier-chart.md).
1010
+ - **The extras:** [axes](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/axes.md) (the kinds of axes, their options, and which plots take which), [legends](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/legends.md), [theming](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/theming.md), and [tooltips and trackers](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/tooltips-and-trackers.md).
1011
+ - **Under the hood:** [some context](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/some-context.md) (which supporting libraries you need, sizing charts, and keeping state), [where the code lives](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/where-the-code-lives.md), and [how the data flows](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/how-the-data-flows.md).
1012
+ - **Tutorials:** [adding your own data source](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/adding-a-data-source.md), from an RxJS observable to a chart; [writing your own tooltips](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/writing-tooltips.md), one for each plot; and [your own scatter chart, from sensor data](https://github.com/robphilipp/stream-charts/blob/master/packages/examples/src/app/examples/docs/scatter-chart-from-sensor-data.md), with irregular readings, two y-axes, and a legend, tracker, and tooltip.