stream-charts 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/README.md +756 -343
  2. package/changes.md +7 -0
  3. package/dist/src/app/charts/Chart.d.ts +116 -87
  4. package/dist/src/app/charts/Chart.js +96 -101
  5. package/dist/src/app/charts/Chart.js.map +1 -1
  6. package/dist/src/app/charts/axes/AxesState.d.ts +64 -0
  7. package/dist/src/app/charts/axes/AxesState.js +97 -0
  8. package/dist/src/app/charts/axes/AxesState.js.map +1 -0
  9. package/dist/src/app/charts/axes/AxesState.test.js +30 -0
  10. package/dist/src/app/charts/axes/AxesState.test.js.map +1 -0
  11. package/dist/src/app/charts/axes/AxisInterval.d.ts +92 -0
  12. package/dist/src/app/charts/axes/AxisInterval.js +126 -0
  13. package/dist/src/app/charts/axes/AxisInterval.js.map +1 -0
  14. package/dist/src/app/charts/axes/AxisInterval.test.js +120 -0
  15. package/dist/src/app/charts/axes/AxisInterval.test.js.map +1 -0
  16. package/dist/src/app/charts/axes/BaseAxisRange.d.ts +83 -0
  17. package/dist/src/app/charts/axes/BaseAxisRange.js +58 -0
  18. package/dist/src/app/charts/axes/BaseAxisRange.js.map +1 -0
  19. package/dist/src/app/charts/axes/ContinuousAxis.d.ts +22 -0
  20. package/dist/src/app/charts/axes/ContinuousAxis.js +96 -0
  21. package/dist/src/app/charts/axes/ContinuousAxis.js.map +1 -0
  22. package/dist/src/app/charts/axes/ContinuousAxisRange.d.ts +59 -0
  23. package/dist/src/app/charts/axes/ContinuousAxisRange.js +84 -0
  24. package/dist/src/app/charts/axes/ContinuousAxisRange.js.map +1 -0
  25. package/dist/src/app/charts/axes/ContinuousAxisRange.test.js +47 -0
  26. package/dist/src/app/charts/axes/ContinuousAxisRange.test.js.map +1 -0
  27. package/dist/src/app/charts/axes/OrdinalAxis.d.ts +23 -0
  28. package/dist/src/app/charts/axes/OrdinalAxis.js +124 -0
  29. package/dist/src/app/charts/axes/OrdinalAxis.js.map +1 -0
  30. package/dist/src/app/charts/axes/OrdinalAxisRange.d.ts +69 -0
  31. package/dist/src/app/charts/axes/OrdinalAxisRange.js +100 -0
  32. package/dist/src/app/charts/axes/OrdinalAxisRange.js.map +1 -0
  33. package/dist/src/app/charts/axes/OrdinalAxisRange.test.js +48 -0
  34. package/dist/src/app/charts/axes/OrdinalAxisRange.test.js.map +1 -0
  35. package/dist/src/app/charts/axes/axes.d.ts +391 -0
  36. package/dist/src/app/charts/axes/axes.js +1046 -0
  37. package/dist/src/app/charts/axes/axes.js.map +1 -0
  38. package/dist/src/app/charts/d3types.d.ts +10 -10
  39. package/dist/src/app/charts/filters/regexFilter.d.ts +9 -0
  40. package/dist/src/app/charts/filters/regexFilter.js +18 -0
  41. package/dist/src/app/charts/filters/regexFilter.js.map +1 -0
  42. package/dist/src/app/charts/filters/regexFilter.test.d.ts +1 -0
  43. package/dist/src/app/charts/filters/regexFilter.test.js +11 -0
  44. package/dist/src/app/charts/filters/regexFilter.test.js.map +1 -0
  45. package/dist/src/app/charts/hooks/useAxes.d.ts +144 -0
  46. package/dist/src/app/charts/hooks/useAxes.js +187 -0
  47. package/dist/src/app/charts/hooks/useAxes.js.map +1 -0
  48. package/dist/src/app/charts/hooks/useChart.d.ts +30 -201
  49. package/dist/src/app/charts/hooks/useChart.js +33 -134
  50. package/dist/src/app/charts/hooks/useChart.js.map +1 -1
  51. package/dist/src/app/charts/hooks/useDataObservable.d.ts +74 -0
  52. package/dist/src/app/charts/hooks/useDataObservable.js +76 -0
  53. package/dist/src/app/charts/hooks/useDataObservable.js.map +1 -0
  54. package/dist/src/app/charts/hooks/useInitialData.d.ts +38 -0
  55. package/dist/src/app/charts/hooks/useInitialData.js +29 -0
  56. package/dist/src/app/charts/hooks/useInitialData.js.map +1 -0
  57. package/dist/src/app/charts/hooks/useMouse.d.ts +53 -0
  58. package/dist/src/app/charts/hooks/useMouse.js +65 -0
  59. package/dist/src/app/charts/hooks/useMouse.js.map +1 -0
  60. package/dist/src/app/charts/hooks/usePlotDimensions.d.ts +34 -0
  61. package/dist/src/app/charts/hooks/usePlotDimensions.js +73 -0
  62. package/dist/src/app/charts/hooks/usePlotDimensions.js.map +1 -0
  63. package/dist/src/app/charts/hooks/useTooltip.d.ts +64 -0
  64. package/dist/src/app/charts/hooks/useTooltip.js +52 -0
  65. package/dist/src/app/charts/hooks/useTooltip.js.map +1 -0
  66. package/dist/src/app/charts/observables/ChartData.d.ts +23 -0
  67. package/dist/src/app/charts/observables/ChartData.js +20 -0
  68. package/dist/src/app/charts/observables/ChartData.js.map +1 -0
  69. package/dist/src/app/charts/observables/iterates.d.ts +52 -0
  70. package/dist/src/app/charts/observables/iterates.js +121 -0
  71. package/dist/src/app/charts/observables/iterates.js.map +1 -0
  72. package/dist/src/app/charts/observables/iterates.test.d.ts +1 -0
  73. package/dist/src/app/charts/observables/iterates.test.js +180 -0
  74. package/dist/src/app/charts/observables/iterates.test.js.map +1 -0
  75. package/dist/src/app/charts/observables/ordinals.d.ts +75 -0
  76. package/dist/src/app/charts/observables/ordinals.js +141 -0
  77. package/dist/src/app/charts/observables/ordinals.js.map +1 -0
  78. package/dist/src/app/charts/observables/ordinals.test.d.ts +1 -0
  79. package/dist/src/app/charts/observables/ordinals.test.js +102 -0
  80. package/dist/src/app/charts/observables/ordinals.test.js.map +1 -0
  81. package/dist/src/app/charts/plots/BarPlot.d.ts +81 -0
  82. package/dist/src/app/charts/plots/BarPlot.js +557 -0
  83. package/dist/src/app/charts/plots/BarPlot.js.map +1 -0
  84. package/dist/src/app/charts/plots/PoincarePlot.d.ts +78 -0
  85. package/dist/src/app/charts/plots/PoincarePlot.js +504 -0
  86. package/dist/src/app/charts/plots/PoincarePlot.js.map +1 -0
  87. package/dist/src/app/charts/{RasterPlot.d.ts → plots/RasterPlot.d.ts} +1 -1
  88. package/dist/src/app/charts/{RasterPlot.js → plots/RasterPlot.js} +152 -133
  89. package/dist/src/app/charts/plots/RasterPlot.js.map +1 -0
  90. package/dist/src/app/charts/{ScatterPlot.d.ts → plots/ScatterPlot.d.ts} +10 -8
  91. package/dist/src/app/charts/plots/ScatterPlot.js +379 -0
  92. package/dist/src/app/charts/plots/ScatterPlot.js.map +1 -0
  93. package/dist/src/app/charts/{plot.d.ts → plots/plot.d.ts} +12 -14
  94. package/dist/src/app/charts/{plot.js → plots/plot.js} +19 -17
  95. package/dist/src/app/charts/plots/plot.js.map +1 -0
  96. package/dist/src/app/charts/series/baseSeries.d.ts +38 -0
  97. package/dist/src/app/charts/series/baseSeries.js +36 -0
  98. package/dist/src/app/charts/series/baseSeries.js.map +1 -0
  99. package/dist/src/app/charts/series/datumSeries.test.d.ts +1 -0
  100. package/dist/src/app/charts/series/datumSeries.test.js +50 -0
  101. package/dist/src/app/charts/series/datumSeries.test.js.map +1 -0
  102. package/dist/src/app/charts/series/iterateSeries.d.ts +42 -0
  103. package/dist/src/app/charts/series/iterateSeries.js +34 -0
  104. package/dist/src/app/charts/series/iterateSeries.js.map +1 -0
  105. package/dist/src/app/charts/series/ordinalSeries.d.ts +54 -0
  106. package/dist/src/app/charts/series/ordinalSeries.js +98 -0
  107. package/dist/src/app/charts/series/ordinalSeries.js.map +1 -0
  108. package/dist/src/app/charts/series/timeSeries.d.ts +30 -0
  109. package/dist/src/app/charts/series/timeSeries.js +19 -0
  110. package/dist/src/app/charts/series/timeSeries.js.map +1 -0
  111. package/dist/src/app/charts/{chartData.d.ts → series/timeSeriesChartData.d.ts} +11 -6
  112. package/dist/src/app/charts/series/timeSeriesChartData.js +32 -0
  113. package/dist/src/app/charts/series/timeSeriesChartData.js.map +1 -0
  114. package/dist/src/app/charts/styling/barPlotStyle.d.ts +41 -0
  115. package/dist/src/app/charts/styling/barPlotStyle.js +84 -0
  116. package/dist/src/app/charts/styling/barPlotStyle.js.map +1 -0
  117. package/dist/src/app/charts/{margins.d.ts → styling/margins.d.ts} +7 -5
  118. package/dist/src/app/charts/{margins.js → styling/margins.js} +19 -9
  119. package/dist/src/app/charts/styling/margins.js.map +1 -0
  120. package/dist/src/app/charts/styling/margins.test.d.ts +1 -0
  121. package/dist/src/app/charts/styling/margins.test.js +19 -0
  122. package/dist/src/app/charts/styling/margins.test.js.map +1 -0
  123. package/dist/src/app/charts/styling/svgStyle.d.ts +76 -0
  124. package/dist/src/app/charts/styling/svgStyle.js +84 -0
  125. package/dist/src/app/charts/styling/svgStyle.js.map +1 -0
  126. package/dist/src/app/charts/subscriptions/subscription.test.d.ts +1 -0
  127. package/dist/src/app/charts/subscriptions/subscription.test.js +395 -0
  128. package/dist/src/app/charts/subscriptions/subscription.test.js.map +1 -0
  129. package/dist/src/app/charts/subscriptions/subscriptions.d.ts +100 -0
  130. package/dist/src/app/charts/subscriptions/subscriptions.js +449 -0
  131. package/dist/src/app/charts/subscriptions/subscriptions.js.map +1 -0
  132. package/dist/src/app/charts/tooltips/BarPlotTooltipContent.d.ts +90 -0
  133. package/dist/src/app/charts/tooltips/BarPlotTooltipContent.js +302 -0
  134. package/dist/src/app/charts/tooltips/BarPlotTooltipContent.js.map +1 -0
  135. package/dist/src/app/charts/tooltips/PoincarePlotTooltipContent.d.ts +100 -0
  136. package/dist/src/app/charts/tooltips/PoincarePlotTooltipContent.js +190 -0
  137. package/dist/src/app/charts/tooltips/PoincarePlotTooltipContent.js.map +1 -0
  138. package/dist/src/app/charts/{RasterPlotTooltipContent.js → tooltips/RasterPlotTooltipContent.js} +40 -44
  139. package/dist/src/app/charts/tooltips/RasterPlotTooltipContent.js.map +1 -0
  140. package/dist/src/app/charts/tooltips/ScatterPlotTooltipContent.js +154 -0
  141. package/dist/src/app/charts/tooltips/ScatterPlotTooltipContent.js.map +1 -0
  142. package/dist/src/app/charts/{Tooltip.d.ts → tooltips/Tooltip.d.ts} +4 -2
  143. package/dist/src/app/charts/{Tooltip.js → tooltips/Tooltip.js} +23 -28
  144. package/dist/src/app/charts/tooltips/Tooltip.js.map +1 -0
  145. package/dist/src/app/charts/{tooltipUtils.d.ts → tooltips/tooltipUtils.d.ts} +11 -6
  146. package/dist/src/app/charts/{tooltipUtils.js → tooltips/tooltipUtils.js} +33 -17
  147. package/dist/src/app/charts/tooltips/tooltipUtils.js.map +1 -0
  148. package/dist/src/app/charts/{Tracker.d.ts → trackers/Tracker.d.ts} +6 -5
  149. package/dist/src/app/charts/trackers/Tracker.js +60 -0
  150. package/dist/src/app/charts/trackers/Tracker.js.map +1 -0
  151. package/dist/src/app/charts/{trackerUtils.d.ts → trackers/trackerUtils.d.ts} +13 -8
  152. package/dist/src/app/charts/trackers/trackerUtils.js +359 -0
  153. package/dist/src/app/charts/trackers/trackerUtils.js.map +1 -0
  154. package/dist/src/app/charts/utils.d.ts +22 -32
  155. package/dist/src/app/charts/utils.js +36 -37
  156. package/dist/src/app/charts/utils.js.map +1 -1
  157. package/dist/src/index.d.ts +48 -15
  158. package/dist/src/index.js +71 -15
  159. package/dist/src/index.js.map +1 -1
  160. package/package.json +30 -20
  161. package/src/app/charts/Chart.tsx +334 -0
  162. package/src/app/charts/axes/AxesState.test.ts +34 -0
  163. package/src/app/charts/axes/AxesState.ts +112 -0
  164. package/src/app/charts/axes/AxisInterval.test.ts +143 -0
  165. package/src/app/charts/axes/AxisInterval.ts +139 -0
  166. package/src/app/charts/axes/BaseAxisRange.ts +113 -0
  167. package/src/app/charts/axes/ContinuousAxis.tsx +192 -0
  168. package/src/app/charts/axes/ContinuousAxisRange.test.ts +55 -0
  169. package/src/app/charts/axes/ContinuousAxisRange.ts +96 -0
  170. package/src/app/charts/axes/OrdinalAxis.tsx +223 -0
  171. package/src/app/charts/axes/OrdinalAxisRange.test.ts +55 -0
  172. package/src/app/charts/axes/OrdinalAxisRange.ts +112 -0
  173. package/src/app/charts/axes/axes.ts +1569 -0
  174. package/src/app/charts/d3types.ts +16 -0
  175. package/src/app/charts/filters/regexFilter.test.ts +13 -0
  176. package/src/app/charts/filters/regexFilter.ts +17 -0
  177. package/src/app/charts/hooks/useAxes.tsx +341 -0
  178. package/src/app/charts/hooks/useChart.tsx +159 -0
  179. package/src/app/charts/hooks/useDataObservable.tsx +175 -0
  180. package/src/app/charts/hooks/useInitialData.tsx +65 -0
  181. package/src/app/charts/hooks/useMouse.tsx +125 -0
  182. package/src/app/charts/hooks/usePlotDimensions.tsx +120 -0
  183. package/src/app/charts/hooks/useTooltip.tsx +112 -0
  184. package/src/app/charts/observables/ChartData.ts +31 -0
  185. package/src/app/charts/observables/iterates.test.ts +201 -0
  186. package/src/app/charts/observables/iterates.ts +203 -0
  187. package/src/app/charts/observables/ordinals.test.ts +135 -0
  188. package/src/app/charts/observables/ordinals.ts +248 -0
  189. package/src/app/charts/plots/BarPlot.tsx +1088 -0
  190. package/src/app/charts/plots/PoincarePlot.tsx +832 -0
  191. package/src/app/charts/plots/RasterPlot.tsx +676 -0
  192. package/src/app/charts/plots/ScatterPlot.tsx +653 -0
  193. package/src/app/charts/plots/plot.ts +75 -0
  194. package/src/app/charts/series/baseSeries.ts +52 -0
  195. package/src/app/charts/series/datumSeries.test.ts +60 -0
  196. package/src/app/charts/series/iterateSeries.ts +56 -0
  197. package/src/app/charts/series/ordinalSeries.ts +164 -0
  198. package/src/app/charts/series/timeSeries.ts +40 -0
  199. package/src/app/charts/series/timeSeriesChartData.ts +68 -0
  200. package/src/app/charts/styling/barPlotStyle.ts +126 -0
  201. package/src/app/charts/styling/margins.test.ts +22 -0
  202. package/src/app/charts/styling/margins.ts +58 -0
  203. package/src/app/charts/styling/svgStyle.ts +118 -0
  204. package/src/app/charts/subscriptions/subscription.test.ts +499 -0
  205. package/src/app/charts/subscriptions/subscriptions.ts +635 -0
  206. package/src/app/charts/tooltips/BarPlotTooltipContent.tsx +440 -0
  207. package/src/app/charts/tooltips/PoincarePlotTooltipContent.tsx +352 -0
  208. package/src/app/charts/tooltips/RasterPlotTooltipContent.tsx +274 -0
  209. package/src/app/charts/tooltips/ScatterPlotTooltipContent.tsx +319 -0
  210. package/src/app/charts/tooltips/Tooltip.tsx +148 -0
  211. package/src/app/charts/tooltips/tooltipUtils.ts +216 -0
  212. package/src/app/charts/trackers/Tracker.tsx +129 -0
  213. package/src/app/charts/trackers/trackerUtils.ts +494 -0
  214. package/src/app/charts/utils.ts +109 -0
  215. package/dist/src/app/charts/CategoryAxis.d.ts +0 -20
  216. package/dist/src/app/charts/CategoryAxis.js +0 -112
  217. package/dist/src/app/charts/CategoryAxis.js.map +0 -1
  218. package/dist/src/app/charts/ContinuousAxis.d.ts +0 -16
  219. package/dist/src/app/charts/ContinuousAxis.js +0 -186
  220. package/dist/src/app/charts/ContinuousAxis.js.map +0 -1
  221. package/dist/src/app/charts/RasterPlot.js.map +0 -1
  222. package/dist/src/app/charts/RasterPlotTooltipContent.js.map +0 -1
  223. package/dist/src/app/charts/ScatterPlot.js +0 -351
  224. package/dist/src/app/charts/ScatterPlot.js.map +0 -1
  225. package/dist/src/app/charts/ScatterPlotTooltipContent.js +0 -163
  226. package/dist/src/app/charts/ScatterPlotTooltipContent.js.map +0 -1
  227. package/dist/src/app/charts/Tooltip.js.map +0 -1
  228. package/dist/src/app/charts/Tracker.js +0 -65
  229. package/dist/src/app/charts/Tracker.js.map +0 -1
  230. package/dist/src/app/charts/axes.d.ts +0 -132
  231. package/dist/src/app/charts/axes.js +0 -265
  232. package/dist/src/app/charts/axes.js.map +0 -1
  233. package/dist/src/app/charts/chartData.js +0 -32
  234. package/dist/src/app/charts/chartData.js.map +0 -1
  235. package/dist/src/app/charts/continuousAxisRangeFor.d.ts +0 -20
  236. package/dist/src/app/charts/continuousAxisRangeFor.js +0 -68
  237. package/dist/src/app/charts/continuousAxisRangeFor.js.map +0 -1
  238. package/dist/src/app/charts/datumSeries.d.ts +0 -59
  239. package/dist/src/app/charts/datumSeries.js +0 -55
  240. package/dist/src/app/charts/datumSeries.js.map +0 -1
  241. package/dist/src/app/charts/datumSeries.test.js +0 -50
  242. package/dist/src/app/charts/datumSeries.test.js.map +0 -1
  243. package/dist/src/app/charts/hooks/AxesState.d.ts +0 -37
  244. package/dist/src/app/charts/hooks/AxesState.js +0 -44
  245. package/dist/src/app/charts/hooks/AxesState.js.map +0 -1
  246. package/dist/src/app/charts/margins.js.map +0 -1
  247. package/dist/src/app/charts/margins.test.js +0 -14
  248. package/dist/src/app/charts/margins.test.js.map +0 -1
  249. package/dist/src/app/charts/plot.js.map +0 -1
  250. package/dist/src/app/charts/regexFilter.d.ts +0 -9
  251. package/dist/src/app/charts/regexFilter.js +0 -17
  252. package/dist/src/app/charts/regexFilter.js.map +0 -1
  253. package/dist/src/app/charts/regexFilter.test.js +0 -11
  254. package/dist/src/app/charts/regexFilter.test.js.map +0 -1
  255. package/dist/src/app/charts/subscriptions.d.ts +0 -43
  256. package/dist/src/app/charts/subscriptions.js +0 -213
  257. package/dist/src/app/charts/subscriptions.js.map +0 -1
  258. package/dist/src/app/charts/svgStyle.d.ts +0 -27
  259. package/dist/src/app/charts/svgStyle.js +0 -32
  260. package/dist/src/app/charts/svgStyle.js.map +0 -1
  261. package/dist/src/app/charts/timeRange.test.js +0 -54
  262. package/dist/src/app/charts/timeRange.test.js.map +0 -1
  263. package/dist/src/app/charts/tooltipUtils.js.map +0 -1
  264. package/dist/src/app/charts/trackerUtils.js +0 -130
  265. package/dist/src/app/charts/trackerUtils.js.map +0 -1
  266. /package/dist/src/app/charts/{datumSeries.test.d.ts → axes/AxesState.test.d.ts} +0 -0
  267. /package/dist/src/app/charts/{margins.test.d.ts → axes/AxisInterval.test.d.ts} +0 -0
  268. /package/dist/src/app/charts/{regexFilter.test.d.ts → axes/ContinuousAxisRange.test.d.ts} +0 -0
  269. /package/dist/src/app/charts/{timeRange.test.d.ts → axes/OrdinalAxisRange.test.d.ts} +0 -0
  270. /package/dist/src/app/charts/{RasterPlotTooltipContent.d.ts → tooltips/RasterPlotTooltipContent.d.ts} +0 -0
  271. /package/dist/src/app/charts/{ScatterPlotTooltipContent.d.ts → tooltips/ScatterPlotTooltipContent.d.ts} +0 -0
@@ -0,0 +1,192 @@
1
+ import {
2
+ addContinuousNumericXAxis,
3
+ addContinuousNumericYAxis,
4
+ AxesFont,
5
+ AxisLocation,
6
+ ContinuousNumericAxis,
7
+ defaultAxesFont,
8
+ labelIdFor
9
+ } from "./axes";
10
+ import {useChart} from "../hooks/useChart";
11
+ import {useEffect, useRef} from "react";
12
+ import * as d3 from "d3";
13
+ import {ScaleContinuousNumeric} from "d3";
14
+ import {Dimensions, Margin} from "../styling/margins";
15
+ import {usePlotDimensions} from "../hooks/usePlotDimensions";
16
+ import {Datum} from "../series/timeSeries";
17
+ import {AxisInterval} from "./AxisInterval";
18
+ import {ContinuousAxisRange} from "./ContinuousAxisRange";
19
+
20
+ interface Props {
21
+ // the unique ID of the axis
22
+ axisId: string
23
+ // the location of the axis. for x-axes, this must be either top or bottom. for
24
+ // y-axes, this mut be either left or right
25
+ location: AxisLocation
26
+ // linear, log, or power scale that defaults to linear scale when not specified
27
+ scale?: ScaleContinuousNumeric<number, number>
28
+ // the min and max values for the axis
29
+ domain: [min: number, max: number]
30
+ // the font for drawing the axis ticks and labels
31
+ font?: Partial<AxesFont>
32
+ // the axis label
33
+ label: string
34
+ // The domain prop holds the axis bounds as a (min, max) tuple. The default
35
+ // behavior is to update the axis bounds when the **values** of the domain
36
+ // prop change, rather than when the object reference changes. This allows
37
+ // a user of the chart to specify a tuple-literal as the ranges, rather than
38
+ // forcing the user of the chart to create a ref and use that ref.
39
+ //
40
+ // This behavior is important when allowing the axes to scroll in time, as
41
+ // is done in the scatter plot or the raster plot. In this case, if the user
42
+ // of the raster chart specifies the axis domain as a tuple-literal, then
43
+ // the bounds will get reset to their original value with each render.
44
+ //
45
+ // However, for charts that don't scroll, such as the iterates chart, but where
46
+ // the user would like to change axis-bounds, say for a different iterates
47
+ // function, we would like the axis bounds to be reset based on a change to
48
+ // the object ref instead. In this case, we can set this property to false.
49
+ updateAxisBasedOnDomainValues?: boolean
50
+ }
51
+
52
+ /**
53
+ * Represents a continuous numeric axis (x or y) that can be place on the top, bottom,
54
+ * left, or right of the chart. The domain (axis range) can be managed by this axis
55
+ * component or managed externally (i.e. deferred). This component returns null, meaning
56
+ * React won't render it because we are updating the SVG element and don't want React
57
+ * involved, except to call this function if the props change.
58
+ * @param props The properties for the axis
59
+ * @constructor
60
+ */
61
+ export function ContinuousAxis(props: Props): null {
62
+ const {
63
+ chartId,
64
+ container,
65
+ axes,
66
+ color
67
+ } = useChart<Datum, any, any, ContinuousAxisRange, ContinuousNumericAxis>()
68
+
69
+ const {
70
+ xAxesState,
71
+ yAxesState,
72
+ addXAxis,
73
+ addYAxis,
74
+ axisRangeFor,
75
+ setAxisIntervalFor,
76
+ setAxisRangeFor,
77
+ addAxesRangesUpdateHandler,
78
+ } = axes
79
+
80
+ const {
81
+ plotDimensions,
82
+ margin
83
+ } = usePlotDimensions()
84
+
85
+ const {
86
+ axisId,
87
+ location,
88
+ scale = d3.scaleLinear(),
89
+ domain,
90
+ updateAxisBasedOnDomainValues = true,
91
+ label,
92
+ } = props
93
+
94
+ const axisRef = useRef<ContinuousNumericAxis>(undefined)
95
+ const rangeUpdateHandlerIdRef = useRef<string>(undefined)
96
+
97
+ const axisIdRef = useRef<string>(axisId)
98
+ const marginRef = useRef<Margin>(margin)
99
+ const domainRef = useRef<AxisInterval>(AxisInterval.as(domain))
100
+ useEffect(
101
+ () => {
102
+ axisIdRef.current = axisId
103
+ marginRef.current = margin
104
+ },
105
+ [axisId, plotDimensions, margin]
106
+ )
107
+
108
+ useEffect(
109
+ () => {
110
+ if (container) {
111
+ const svg = d3.select<SVGSVGElement, any>(container)
112
+ const font: AxesFont = {...defaultAxesFont(), color, ...props.font}
113
+
114
+ const handleRangeUpdates = (updates: Map<string, ContinuousAxisRange>, plotDim: Dimensions): void => {
115
+ if (rangeUpdateHandlerIdRef.current && axisRef.current) {
116
+ const range = updates.get(axisId)
117
+ if (range) {
118
+ axisRef.current.update(range.current, plotDim, marginRef.current)
119
+ }
120
+ }
121
+ }
122
+
123
+ if (axisRef.current === undefined) {
124
+ switch (location) {
125
+ case AxisLocation.Bottom:
126
+ case AxisLocation.Top: {
127
+ axisRef.current = addContinuousNumericXAxis(
128
+ chartId, axisId, svg, plotDimensions, location, scale, domain,
129
+ font, margin, label, setAxisIntervalFor
130
+ )
131
+ // add the x-axis to the chart context
132
+ const [start, end] = AxisInterval.as(domain).asTuple()
133
+ addXAxis(axisRef.current, axisId, ContinuousAxisRange.from(start, end))
134
+ // addXAxis(axisRef.current, axisId, AxisInterval.as(domain))
135
+
136
+ // add an update handler
137
+ rangeUpdateHandlerIdRef.current = `x-axis-${chartId}-${location.valueOf()}`
138
+ addAxesRangesUpdateHandler(rangeUpdateHandlerIdRef.current, handleRangeUpdates)
139
+
140
+ break
141
+ }
142
+
143
+ case AxisLocation.Left:
144
+ case AxisLocation.Right: {
145
+ axisRef.current = addContinuousNumericYAxis(
146
+ chartId, axisId, svg, plotDimensions, location, scale, domain,
147
+ font, margin, label, setAxisIntervalFor
148
+ )
149
+ // add the y-axis to the chart context
150
+ const [start, end] = AxisInterval.as(domain).asTuple()
151
+ addYAxis(axisRef.current, axisId, ContinuousAxisRange.from(start, end))
152
+ // addYAxis(axisRef.current, axisId, AxisInterval.as(domain))
153
+
154
+ // add an update handler
155
+ rangeUpdateHandlerIdRef.current = `y-axis-${chartId}-${location.valueOf()}`
156
+ addAxesRangesUpdateHandler(rangeUpdateHandlerIdRef.current, handleRangeUpdates)
157
+ }
158
+ }
159
+ } else {
160
+ const axisRange = axisRangeFor(axisId)
161
+ const domain = axisRange
162
+ .map(range => range.current)
163
+ .getOrElse(AxisInterval.empty())
164
+ if (domain.isNotEmpty()) {
165
+ axisRef.current.update(domain, plotDimensions, margin)
166
+ }
167
+
168
+ if (
169
+ (updateAxisBasedOnDomainValues && (domainRef.current.start !== domain.start || domainRef.current.end !== domain.end)) ||
170
+ (!updateAxisBasedOnDomainValues && domainRef.current !== domain)
171
+ ) {
172
+ domainRef.current = domain
173
+ axisRange.ifPresent(range => setAxisRangeFor(axisId, range.updateOriginal(domain.start, domain.end)))
174
+
175
+ }
176
+
177
+ svg.select(`#${labelIdFor(chartId, location)}`).attr('fill', color)
178
+ }
179
+ }
180
+ },
181
+ [
182
+ chartId, axisId, label, location, props.font, xAxesState, yAxesState, addXAxis, addYAxis, domain,
183
+ scale, container, margin, plotDimensions, setAxisIntervalFor,
184
+ axisRangeFor,
185
+ addAxesRangesUpdateHandler,
186
+ setAxisRangeFor,
187
+ color, updateAxisBasedOnDomainValues
188
+ ]
189
+ )
190
+
191
+ return null
192
+ }
@@ -0,0 +1,55 @@
1
+ import {ContinuousAxisRange} from "./ContinuousAxisRange";
2
+
3
+
4
+ test('creates a time-range', () => {
5
+ const timeRange = ContinuousAxisRange.from(10, 100);
6
+ expect(timeRange.current).toEqual({start: 10, end: 100});
7
+ expect(timeRange.scaleFactor).toBe(1);
8
+ });
9
+
10
+ test('scaling a time-range', () => {
11
+ const timeRange = ContinuousAxisRange.from(0, 100).scale(2, 50);
12
+ // the midpoint must remain at 50, so the new range will be 50 - 2 * (50 - 0) to
13
+ // 50 + 2 * (100 - 50) => (-50, 150)
14
+ expect(timeRange.current).toEqual({start: -50, end: 150})
15
+ expect(timeRange.scaleFactor).toBe(2);
16
+
17
+ // setting the scale factor back to 1, from its current value of 2 should
18
+ // return the original time-range (this may be a bit unintuitive, because it
19
+ // may seem like we should pass in a factor of 0.5 to shrink the scale back, but
20
+ // that would mean the new interval has a scale factor 4 times smaller.)
21
+ // this behavior is implemented so that zooming in at out from a point seems
22
+ // natural to the user.
23
+ const original = timeRange.scale(1, 50);
24
+ expect(original.current).toEqual({start: 0, end: 100});
25
+ expect(original.scaleFactor).toBe(1);
26
+ // the original shouldn't be changed
27
+ expect(timeRange.current).toEqual({start: -50, end: 150});
28
+ expect(timeRange.scaleFactor).toBe(2);
29
+ });
30
+
31
+ test('translating a time-range', () => {
32
+ const timeRange = ContinuousAxisRange.from(0, 100).translate(50);
33
+ expect(timeRange.current).toEqual({start: 50, end: 150});
34
+ expect(timeRange.scaleFactor).toBe(1);
35
+ });
36
+
37
+ test('scaling and translating', () => {
38
+ const original = ContinuousAxisRange.from(0, 100);
39
+ const scaled = original.scale(2, 50);
40
+ const translated = scaled.translate(50);
41
+
42
+ expect(translated.current).toEqual({start: 0, end: 200});
43
+ expect(translated.scaleFactor).toBe(2);
44
+
45
+ // after the translation, the time-range should shrink around the new time
46
+ const rescaled = translated.scale(1, 100);
47
+ expect(rescaled.current).toEqual({start: 50, end: 150});
48
+ expect(rescaled.scaleFactor).toBe(1);
49
+
50
+ // the original interval, maintained by each new time-range, should not change
51
+ expect(original.matchesOriginal(0, 100)).toEqual(true);
52
+ expect(scaled.matchesOriginal(0, 100)).toEqual(true);
53
+ expect(translated.matchesOriginal(0, 100)).toEqual(true);
54
+ expect(rescaled.matchesOriginal(0, 100)).toEqual(true);
55
+ });
@@ -0,0 +1,96 @@
1
+ import {BaseAxisRange} from "./BaseAxisRange";
2
+
3
+ /**
4
+ * An immutable continuous-axis range that holds the current range and the original (no zoom) range.
5
+ * Create an instance using the {@link ContinuousAxisRange.from} factory method.
6
+ */
7
+ export class ContinuousAxisRange extends BaseAxisRange {
8
+
9
+ private constructor(start: number, end: number, originalStart: number = start, originalEnd: number = end) {
10
+ super(start, end, originalStart, originalEnd)
11
+ }
12
+
13
+ /**
14
+ * Factory function for creating a new continuous-axis range instance.
15
+ * @param start The start of the axis-range.
16
+ * @param end The end of the axis-range.
17
+ * @param [originalStart=start] The optional original start of the axis-range (e.g. before any zooming or panning.
18
+ * Defaults to the start value if not specified.)
19
+ * @param [originalEnd=end] The optional original end of the axis-range (e.g. before any zooming or panning.
20
+ * Defaults to the end value if not specified.)
21
+ * @return A new continuous-axis range instance.
22
+ */
23
+ static from(start: number, end: number, originalStart: number = start, originalEnd: number = end): ContinuousAxisRange {
24
+ return new ContinuousAxisRange(start, end, originalStart, originalEnd)
25
+ }
26
+
27
+ /**
28
+ * Scales the axis-range by the specified scale factor from the specified value. The equations
29
+ * are written so that the zooming (scaling) occurs at the specified value and expands/contracts equally
30
+ * from that value.
31
+ * @param factor The scale factor
32
+ * @param value The value from which to scale the interval
33
+ * @return A new continuous-axis range with updated values
34
+ */
35
+ scale(factor: number, value: number): ContinuousAxisRange {
36
+ const scaledInterval = this.scaledRange(factor, value)
37
+ return new ContinuousAxisRange(scaledInterval.start, scaledInterval.end, this.original.start, this.original.end)
38
+ }
39
+
40
+ /**
41
+ * Scales the axis-range by the specified scale factor from the specified value, while keeping
42
+ * the range within the constraints (start, end). The equations are written so that the zooming
43
+ * (scaling) occurs at the specified value, and expands/contracts equally from that value.
44
+ * @param factor The scale factor
45
+ * @param value The value from which to scale the interval
46
+ * @param constraint The minimum and maximum values that range bounds can be
47
+ * @return A new continuous-axis range with updated values
48
+ */
49
+ constrainedScale(factor: number, value: number, constraint: [min: number, max: number]): BaseAxisRange {
50
+ const scaledInterval = this.scaledRange(factor, value)
51
+ const [min, max] = constraint
52
+ return new ContinuousAxisRange(
53
+ Math.max(min, scaledInterval.start),
54
+ Math.min(max, scaledInterval.end),
55
+ this.original.start,
56
+ this.original.end
57
+ )
58
+ }
59
+
60
+ /**
61
+ * Translates the axis-range by the specified amount
62
+ * @param amount The amount by which to translate the axis-range
63
+ * @param [constraint=[-Infinity, Infinity]] Optional constraint interval in which the axis range must be within
64
+ * @return An updated {@link ContinuousAxisRange} that has been translated by the specified amount
65
+ */
66
+ translate(amount: number, constraint: [start: number, end: number] = [-Infinity, Infinity]): ContinuousAxisRange {
67
+ const [cs, ce] = constraint
68
+ // when either of the constraints is infinite, or the pan keeps the new axis range
69
+ // within the origin axis range (i.e. zoomed in and the panned), then allow the pan,
70
+ // otherwise don't update
71
+ if ((!isFinite(cs) && !isFinite(ce)) || (this.current.start + amount >= cs && this.current.end + amount <= ce)) {
72
+ return new ContinuousAxisRange(this.current.start + amount, this.current.end + amount, this.original.start, this.original.end)
73
+ }
74
+ return new ContinuousAxisRange(this.current.start, this.current.end, this.original.start, this.original.end)
75
+ }
76
+
77
+ /**
78
+ * Updates the axis-range with the new start and end values, leaving the original range unchanged.
79
+ * @param start The new start of the axis-range
80
+ * @param end The new end of the axis range
81
+ * @return The updated axis-range type, with all other values unchanged.
82
+ */
83
+ update(start: number, end: number): ContinuousAxisRange {
84
+ return new ContinuousAxisRange(start, end, this.original.start, this.original.end)
85
+ }
86
+
87
+ /**
88
+ * Updates the original range with the new start and end values.
89
+ * @param start The new value for the start of the original range
90
+ * @param end The new value for the end of the original range
91
+ * @return The updated original range.
92
+ */
93
+ updateOriginal(start: number, end: number): BaseAxisRange {
94
+ return new ContinuousAxisRange(this.current.start, this.current.end, start, end)
95
+ }
96
+ }
@@ -0,0 +1,223 @@
1
+ import {
2
+ addOrdinalStringAxis,
3
+ AxesFont,
4
+ AxisLocation,
5
+ AxisTickStyle, ContinuousNumericAxis,
6
+ defaultAxesFont,
7
+ defaultAxisTickStyle,
8
+ labelIdFor,
9
+ OrdinalStringAxis
10
+ } from "./axes"
11
+ import * as d3 from "d3";
12
+ import {ScaleBand} from "d3";
13
+ import {useChart} from "../hooks/useChart";
14
+ import {useEffect, useRef} from "react";
15
+ import {Dimensions} from "../styling/margins";
16
+ import {usePlotDimensions} from "../hooks/usePlotDimensions";
17
+ import {OrdinalAxisRange} from "./OrdinalAxisRange";
18
+ import {Datum} from "../series/timeSeries";
19
+ import {AxisInterval} from "./AxisInterval";
20
+
21
+ interface Props {
22
+ // the unique ID of the axis
23
+ axisId: string
24
+ // the location of the axis. for y-axes, this mut be either left or right,
25
+ // for x-axis, must be either top or bottom.
26
+ location: AxisLocation
27
+ // category axes
28
+ scale?: ScaleBand<string>
29
+ // the min and max values for the axis
30
+ categories: Array<string>
31
+ // the font for drawing the axis ticks and labels
32
+ font?: Partial<AxesFont>
33
+ // styling for the axis ticks (e.g. font, rotation, etc)
34
+ axisTickStyle?: Partial<AxisTickStyle>
35
+ // the axis label
36
+ label: string
37
+ // The domain prop holds the axis bounds as a (min, max) tuple. The default
38
+ // behavior is to update the axis bounds when the **values** of the domain
39
+ // prop change, rather than when the object reference changes. This allows
40
+ // a user of the chart to specify a tuple-literal as the ranges, rather than
41
+ // forcing the user of the chart to create a ref and use that ref.
42
+ //
43
+ // This behavior is important when allowing the axes to scroll in time, as
44
+ // is done in the scatter plot or the raster plot. In this case, if the user
45
+ // of the raster chart specifies the axis domain as a tuple-literal, then
46
+ // the bounds will get reset to their original value with each render.
47
+ //
48
+ // However, for charts that don't scroll, such as the iterates chart, but where
49
+ // the user would like to change axis-bounds, say for a different iterates
50
+ // function, we would like the axis bounds to be reset based on a change to
51
+ // the object ref instead. In this case, we can set this property to false.
52
+ updateAxisBasedOnDomainValues?: boolean
53
+ }
54
+
55
+ /**
56
+ * Category axis that represents the x-axis when its location is either on the bottom or the top.
57
+ * When the axis location is left or right, then the category axis represents the y-axis. The category
58
+ * axis requires a set of categories that will form the axis. Generally, these categories should
59
+ * be the name of the series used to represent each category.
60
+ * @param props The properties for the component
61
+ * @return null
62
+ * @constructor
63
+ */
64
+ export function OrdinalAxis(props: Props): null {
65
+ const {
66
+ chartId,
67
+ container,
68
+ axes,
69
+ color,
70
+ } = useChart<Datum, any, any, OrdinalAxisRange, OrdinalStringAxis>()
71
+
72
+ const {
73
+ xAxesState,
74
+ yAxesState,
75
+ addXAxis,
76
+ addYAxis,
77
+ setAxisIntervalFor,
78
+ axisRangeFor,
79
+ addAxesRangesUpdateHandler,
80
+ setOriginalAxisIntervalFor,
81
+ } = axes
82
+
83
+ const {
84
+ plotDimensions,
85
+ margin,
86
+ registerPlotDimensionChangeHandler,
87
+ unregisterPlotDimensionChangeHandler
88
+ } = usePlotDimensions()
89
+
90
+ const {
91
+ axisId,
92
+ location,
93
+ categories,
94
+ updateAxisBasedOnDomainValues = true,
95
+ label,
96
+ } = props
97
+
98
+ const rangeUpdateHandlerIdRef = useRef<string>(undefined)
99
+ const axis = location === AxisLocation.Top || location === AxisLocation.Bottom ?
100
+ xAxesState.axisFor(axisId).getOrUndefined() :
101
+ yAxesState.axisFor(axisId).getOrUndefined()
102
+
103
+ // handles plot size changes by updating the range of the axis and the original range of the axis
104
+ useEffect(
105
+ () => {
106
+ if (container) {
107
+ const svg = d3.select<SVGSVGElement, any>(container)
108
+ const font: AxesFont = {...defaultAxesFont(), color, ...props.font}
109
+ const axisTickStyle = {...defaultAxisTickStyle(), ...props.axisTickStyle}
110
+
111
+
112
+ // lambda that gets called when the axes need to be updated
113
+ const handleRangeUpdates = (updates: Map<string, OrdinalAxisRange>, plotDim: Dimensions): void => {
114
+ if (rangeUpdateHandlerIdRef.current && axis) {
115
+ const range = updates.get(axisId)
116
+ if (range) {
117
+ axis.update(range.current, range.original, plotDim, margin)
118
+ }
119
+ }
120
+ }
121
+
122
+ if (axis === undefined) {
123
+ // add the x-axis or y-axis to the chart context depending on its
124
+ // location
125
+ switch (location) {
126
+ case AxisLocation.Top:
127
+ case AxisLocation.Bottom: {
128
+ const xAxis = addOrdinalStringAxis(
129
+ chartId, axisId, svg, location, categories,
130
+ label, font, axisTickStyle, plotDimensions, margin,
131
+ setAxisIntervalFor, setOriginalAxisIntervalFor
132
+ )
133
+
134
+ // add the x-axis to the chart context
135
+ const [start, end] = AxisInterval.as(xAxis.scale.range()).asTuple()
136
+ addXAxis(xAxis, axisId, OrdinalAxisRange.from(start, end))
137
+
138
+ // add an update handler
139
+ rangeUpdateHandlerIdRef.current = `x-axis-${chartId}-${location.valueOf()}`
140
+ addAxesRangesUpdateHandler(rangeUpdateHandlerIdRef.current, handleRangeUpdates)
141
+ break
142
+ }
143
+
144
+ case AxisLocation.Left:
145
+ case AxisLocation.Right: {
146
+ const yAxis = addOrdinalStringAxis(
147
+ chartId, axisId, svg, location, categories,
148
+ label, font, axisTickStyle, plotDimensions, margin,
149
+ setAxisIntervalFor, setOriginalAxisIntervalFor
150
+ )
151
+ // add the y-axis to the chart context
152
+ const [start, end] = AxisInterval.as(yAxis.scale.range()).asTuple()
153
+ addYAxis(yAxis, axisId, OrdinalAxisRange.from(start, end))
154
+ // add an update handler
155
+ rangeUpdateHandlerIdRef.current = `y-axis-${chartId}-${location.valueOf()}`
156
+ addAxesRangesUpdateHandler(rangeUpdateHandlerIdRef.current, handleRangeUpdates)
157
+ }
158
+ }
159
+ } else {
160
+ const [range, originalRange] = axisRangeFor(axisId)
161
+ .map(range => ([range.current, range.original]))
162
+ .getOrElse([AxisInterval.empty(), AxisInterval.empty()])
163
+ if (range && originalRange) {
164
+ axis.update(range, originalRange, plotDimensions, margin)
165
+ }
166
+
167
+ svg.select(`#${labelIdFor(chartId, location)}`).attr('fill', color)
168
+ }
169
+ }
170
+ },
171
+ [
172
+ container,
173
+ axis,
174
+ addXAxis, addYAxis,
175
+ addAxesRangesUpdateHandler,
176
+ setAxisIntervalFor, setOriginalAxisIntervalFor, axisRangeFor,
177
+ xAxesState, yAxesState,
178
+ updateAxisBasedOnDomainValues,
179
+ chartId, axisId,
180
+ categories,
181
+ color, label, location, margin, plotDimensions, props.axisTickStyle, props.font,
182
+ ]
183
+ )
184
+
185
+ useEffect(
186
+ () => {
187
+ const handlerId = registerPlotDimensionChangeHandler((oldDimension, newDimension) => {
188
+ if (axis !== undefined) {
189
+ if (location === AxisLocation.Top || location === AxisLocation.Bottom) {
190
+ const [start, end] = axisRangeFor(axisId)
191
+ .map(range => range.current.asTuple())
192
+ .getOrElse(AxisInterval.empty().asTuple())
193
+ const {current, original} = OrdinalAxisRange
194
+ .from(start, end, 0, oldDimension.width)
195
+ .zoom(oldDimension.width, newDimension.width)
196
+ axis.update(current, original, plotDimensions, margin)
197
+ }
198
+ if (location === AxisLocation.Left || location === AxisLocation.Right) {
199
+ const [start, end] = axisRangeFor(axisId)
200
+ .map(range => range.current.asTuple())
201
+ .getOrElse(AxisInterval.empty().asTuple())
202
+ const {current, original} = OrdinalAxisRange
203
+ .from(start, end, 0, oldDimension.height)
204
+ .zoom(oldDimension.height, newDimension.height)
205
+ axis.update(current, original, plotDimensions, margin)
206
+ }
207
+ }
208
+ })
209
+ return () => {
210
+ unregisterPlotDimensionChangeHandler(handlerId)
211
+ }
212
+ },
213
+ [
214
+ axis,
215
+ axisRangeFor, setAxisIntervalFor, setOriginalAxisIntervalFor,
216
+ axisId, location, margin,
217
+ plotDimensions, registerPlotDimensionChangeHandler, unregisterPlotDimensionChangeHandler,
218
+ ]
219
+ );
220
+
221
+ return null
222
+ }
223
+
@@ -0,0 +1,55 @@
1
+ import {OrdinalAxisRange} from './OrdinalAxisRange';
2
+
3
+ describe('scaleOrdinalBounds', () => {
4
+ function validateRanges(actualRanges: OrdinalAxisRange, expectedRanges: OrdinalAxisRange): void {
5
+ const {current: actualRange, original: actualOriginal} = actualRanges
6
+
7
+ expect(actualRange.start).toBeCloseTo(expectedRanges.current.start)
8
+ expect(actualRange.end).toBeCloseTo(expectedRanges.current.end)
9
+ expect(actualOriginal.start).toBe(expectedRanges.original.start)
10
+ expect(actualOriginal.end).toBe(expectedRanges.original.end)
11
+ }
12
+
13
+ function calculateScale(ranges: OrdinalAxisRange): number {
14
+ return ranges.currentDistance / ranges.originalDistance
15
+ }
16
+
17
+ /**
18
+ * Ranges before and after zoom should have the same scale as calculated by the
19
+ * measure of the zoomed range divided by the measure of the original range.
20
+ * @param previousRanges The ranges before the zoom
21
+ * @param currentRanges The ranges after the zoom
22
+ */
23
+ function validateScale(previousRanges: OrdinalAxisRange, currentRanges: OrdinalAxisRange): void {
24
+ expect(Math.abs(calculateScale(previousRanges) - calculateScale(currentRanges))).toBeLessThan(1e-10)
25
+ }
26
+
27
+ it('should scale bounds and original bounds the same when they are both the same', () => {
28
+ const expectedRange = OrdinalAxisRange.from(0, 200, 0, 200)
29
+ const range = OrdinalAxisRange.from(0, 100, 0, 100)
30
+ const zoomedRange = range.zoom(100, 200)
31
+ validateRanges(zoomedRange, expectedRange)
32
+ validateScale(range, zoomedRange)
33
+ })
34
+
35
+ it('should scale bounds and original bounds when ordinal axis is zoomed by 10 percent', () => {
36
+ const expectedRange = OrdinalAxisRange.from(-55, 220, 0, 110)
37
+ const range = OrdinalAxisRange.from(-50, 200, 0, 100)
38
+ const zoomedRange = range.zoom(100, 110)
39
+ validateRanges(zoomedRange, expectedRange)
40
+ })
41
+
42
+ it('should scale bounds and original bounds when ordinal axis is zoomed by 500 percent', () => {
43
+ const expectedRange = OrdinalAxisRange.from(-250, 1000, 0, 500)
44
+ const range = OrdinalAxisRange.from(-50, 200, 0, 100)
45
+ const zoomedRange = range.zoom(100, 500)
46
+ validateRanges(zoomedRange, expectedRange)
47
+ })
48
+
49
+ it('should scale bounds and original bounds when ordinal axis is zoomed by 10 percent and ends are same', () => {
50
+ const expectedRange = OrdinalAxisRange.from(-750, 500, 0, 500)
51
+ const range = OrdinalAxisRange.from(-150, 100, 0, 100)
52
+ const zoomedRange = range.zoom(100, 500)
53
+ validateRanges(zoomedRange, expectedRange)
54
+ })
55
+ });