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.
- package/README.md +756 -343
- package/changes.md +7 -0
- package/dist/src/app/charts/Chart.d.ts +116 -87
- package/dist/src/app/charts/Chart.js +96 -101
- package/dist/src/app/charts/Chart.js.map +1 -1
- package/dist/src/app/charts/axes/AxesState.d.ts +64 -0
- package/dist/src/app/charts/axes/AxesState.js +97 -0
- package/dist/src/app/charts/axes/AxesState.js.map +1 -0
- package/dist/src/app/charts/axes/AxesState.test.js +30 -0
- package/dist/src/app/charts/axes/AxesState.test.js.map +1 -0
- package/dist/src/app/charts/axes/AxisInterval.d.ts +92 -0
- package/dist/src/app/charts/axes/AxisInterval.js +126 -0
- package/dist/src/app/charts/axes/AxisInterval.js.map +1 -0
- package/dist/src/app/charts/axes/AxisInterval.test.js +120 -0
- package/dist/src/app/charts/axes/AxisInterval.test.js.map +1 -0
- package/dist/src/app/charts/axes/BaseAxisRange.d.ts +83 -0
- package/dist/src/app/charts/axes/BaseAxisRange.js +58 -0
- package/dist/src/app/charts/axes/BaseAxisRange.js.map +1 -0
- package/dist/src/app/charts/axes/ContinuousAxis.d.ts +22 -0
- package/dist/src/app/charts/axes/ContinuousAxis.js +96 -0
- package/dist/src/app/charts/axes/ContinuousAxis.js.map +1 -0
- package/dist/src/app/charts/axes/ContinuousAxisRange.d.ts +59 -0
- package/dist/src/app/charts/axes/ContinuousAxisRange.js +84 -0
- package/dist/src/app/charts/axes/ContinuousAxisRange.js.map +1 -0
- package/dist/src/app/charts/axes/ContinuousAxisRange.test.js +47 -0
- package/dist/src/app/charts/axes/ContinuousAxisRange.test.js.map +1 -0
- package/dist/src/app/charts/axes/OrdinalAxis.d.ts +23 -0
- package/dist/src/app/charts/axes/OrdinalAxis.js +124 -0
- package/dist/src/app/charts/axes/OrdinalAxis.js.map +1 -0
- package/dist/src/app/charts/axes/OrdinalAxisRange.d.ts +69 -0
- package/dist/src/app/charts/axes/OrdinalAxisRange.js +100 -0
- package/dist/src/app/charts/axes/OrdinalAxisRange.js.map +1 -0
- package/dist/src/app/charts/axes/OrdinalAxisRange.test.js +48 -0
- package/dist/src/app/charts/axes/OrdinalAxisRange.test.js.map +1 -0
- package/dist/src/app/charts/axes/axes.d.ts +391 -0
- package/dist/src/app/charts/axes/axes.js +1046 -0
- package/dist/src/app/charts/axes/axes.js.map +1 -0
- package/dist/src/app/charts/d3types.d.ts +10 -10
- package/dist/src/app/charts/filters/regexFilter.d.ts +9 -0
- package/dist/src/app/charts/filters/regexFilter.js +18 -0
- package/dist/src/app/charts/filters/regexFilter.js.map +1 -0
- package/dist/src/app/charts/filters/regexFilter.test.d.ts +1 -0
- package/dist/src/app/charts/filters/regexFilter.test.js +11 -0
- package/dist/src/app/charts/filters/regexFilter.test.js.map +1 -0
- package/dist/src/app/charts/hooks/useAxes.d.ts +144 -0
- package/dist/src/app/charts/hooks/useAxes.js +187 -0
- package/dist/src/app/charts/hooks/useAxes.js.map +1 -0
- package/dist/src/app/charts/hooks/useChart.d.ts +30 -201
- package/dist/src/app/charts/hooks/useChart.js +33 -134
- package/dist/src/app/charts/hooks/useChart.js.map +1 -1
- package/dist/src/app/charts/hooks/useDataObservable.d.ts +74 -0
- package/dist/src/app/charts/hooks/useDataObservable.js +76 -0
- package/dist/src/app/charts/hooks/useDataObservable.js.map +1 -0
- package/dist/src/app/charts/hooks/useInitialData.d.ts +38 -0
- package/dist/src/app/charts/hooks/useInitialData.js +29 -0
- package/dist/src/app/charts/hooks/useInitialData.js.map +1 -0
- package/dist/src/app/charts/hooks/useMouse.d.ts +53 -0
- package/dist/src/app/charts/hooks/useMouse.js +65 -0
- package/dist/src/app/charts/hooks/useMouse.js.map +1 -0
- package/dist/src/app/charts/hooks/usePlotDimensions.d.ts +34 -0
- package/dist/src/app/charts/hooks/usePlotDimensions.js +73 -0
- package/dist/src/app/charts/hooks/usePlotDimensions.js.map +1 -0
- package/dist/src/app/charts/hooks/useTooltip.d.ts +64 -0
- package/dist/src/app/charts/hooks/useTooltip.js +52 -0
- package/dist/src/app/charts/hooks/useTooltip.js.map +1 -0
- package/dist/src/app/charts/observables/ChartData.d.ts +23 -0
- package/dist/src/app/charts/observables/ChartData.js +20 -0
- package/dist/src/app/charts/observables/ChartData.js.map +1 -0
- package/dist/src/app/charts/observables/iterates.d.ts +52 -0
- package/dist/src/app/charts/observables/iterates.js +121 -0
- package/dist/src/app/charts/observables/iterates.js.map +1 -0
- package/dist/src/app/charts/observables/iterates.test.d.ts +1 -0
- package/dist/src/app/charts/observables/iterates.test.js +180 -0
- package/dist/src/app/charts/observables/iterates.test.js.map +1 -0
- package/dist/src/app/charts/observables/ordinals.d.ts +75 -0
- package/dist/src/app/charts/observables/ordinals.js +141 -0
- package/dist/src/app/charts/observables/ordinals.js.map +1 -0
- package/dist/src/app/charts/observables/ordinals.test.d.ts +1 -0
- package/dist/src/app/charts/observables/ordinals.test.js +102 -0
- package/dist/src/app/charts/observables/ordinals.test.js.map +1 -0
- package/dist/src/app/charts/plots/BarPlot.d.ts +81 -0
- package/dist/src/app/charts/plots/BarPlot.js +557 -0
- package/dist/src/app/charts/plots/BarPlot.js.map +1 -0
- package/dist/src/app/charts/plots/PoincarePlot.d.ts +78 -0
- package/dist/src/app/charts/plots/PoincarePlot.js +504 -0
- package/dist/src/app/charts/plots/PoincarePlot.js.map +1 -0
- package/dist/src/app/charts/{RasterPlot.d.ts → plots/RasterPlot.d.ts} +1 -1
- package/dist/src/app/charts/{RasterPlot.js → plots/RasterPlot.js} +152 -133
- package/dist/src/app/charts/plots/RasterPlot.js.map +1 -0
- package/dist/src/app/charts/{ScatterPlot.d.ts → plots/ScatterPlot.d.ts} +10 -8
- package/dist/src/app/charts/plots/ScatterPlot.js +379 -0
- package/dist/src/app/charts/plots/ScatterPlot.js.map +1 -0
- package/dist/src/app/charts/{plot.d.ts → plots/plot.d.ts} +12 -14
- package/dist/src/app/charts/{plot.js → plots/plot.js} +19 -17
- package/dist/src/app/charts/plots/plot.js.map +1 -0
- package/dist/src/app/charts/series/baseSeries.d.ts +38 -0
- package/dist/src/app/charts/series/baseSeries.js +36 -0
- package/dist/src/app/charts/series/baseSeries.js.map +1 -0
- package/dist/src/app/charts/series/datumSeries.test.d.ts +1 -0
- package/dist/src/app/charts/series/datumSeries.test.js +50 -0
- package/dist/src/app/charts/series/datumSeries.test.js.map +1 -0
- package/dist/src/app/charts/series/iterateSeries.d.ts +42 -0
- package/dist/src/app/charts/series/iterateSeries.js +34 -0
- package/dist/src/app/charts/series/iterateSeries.js.map +1 -0
- package/dist/src/app/charts/series/ordinalSeries.d.ts +54 -0
- package/dist/src/app/charts/series/ordinalSeries.js +98 -0
- package/dist/src/app/charts/series/ordinalSeries.js.map +1 -0
- package/dist/src/app/charts/series/timeSeries.d.ts +30 -0
- package/dist/src/app/charts/series/timeSeries.js +19 -0
- package/dist/src/app/charts/series/timeSeries.js.map +1 -0
- package/dist/src/app/charts/{chartData.d.ts → series/timeSeriesChartData.d.ts} +11 -6
- package/dist/src/app/charts/series/timeSeriesChartData.js +32 -0
- package/dist/src/app/charts/series/timeSeriesChartData.js.map +1 -0
- package/dist/src/app/charts/styling/barPlotStyle.d.ts +41 -0
- package/dist/src/app/charts/styling/barPlotStyle.js +84 -0
- package/dist/src/app/charts/styling/barPlotStyle.js.map +1 -0
- package/dist/src/app/charts/{margins.d.ts → styling/margins.d.ts} +7 -5
- package/dist/src/app/charts/{margins.js → styling/margins.js} +19 -9
- package/dist/src/app/charts/styling/margins.js.map +1 -0
- package/dist/src/app/charts/styling/margins.test.d.ts +1 -0
- package/dist/src/app/charts/styling/margins.test.js +19 -0
- package/dist/src/app/charts/styling/margins.test.js.map +1 -0
- package/dist/src/app/charts/styling/svgStyle.d.ts +76 -0
- package/dist/src/app/charts/styling/svgStyle.js +84 -0
- package/dist/src/app/charts/styling/svgStyle.js.map +1 -0
- package/dist/src/app/charts/subscriptions/subscription.test.d.ts +1 -0
- package/dist/src/app/charts/subscriptions/subscription.test.js +395 -0
- package/dist/src/app/charts/subscriptions/subscription.test.js.map +1 -0
- package/dist/src/app/charts/subscriptions/subscriptions.d.ts +100 -0
- package/dist/src/app/charts/subscriptions/subscriptions.js +449 -0
- package/dist/src/app/charts/subscriptions/subscriptions.js.map +1 -0
- package/dist/src/app/charts/tooltips/BarPlotTooltipContent.d.ts +90 -0
- package/dist/src/app/charts/tooltips/BarPlotTooltipContent.js +302 -0
- package/dist/src/app/charts/tooltips/BarPlotTooltipContent.js.map +1 -0
- package/dist/src/app/charts/tooltips/PoincarePlotTooltipContent.d.ts +100 -0
- package/dist/src/app/charts/tooltips/PoincarePlotTooltipContent.js +190 -0
- package/dist/src/app/charts/tooltips/PoincarePlotTooltipContent.js.map +1 -0
- package/dist/src/app/charts/{RasterPlotTooltipContent.js → tooltips/RasterPlotTooltipContent.js} +40 -44
- package/dist/src/app/charts/tooltips/RasterPlotTooltipContent.js.map +1 -0
- package/dist/src/app/charts/tooltips/ScatterPlotTooltipContent.js +154 -0
- package/dist/src/app/charts/tooltips/ScatterPlotTooltipContent.js.map +1 -0
- package/dist/src/app/charts/{Tooltip.d.ts → tooltips/Tooltip.d.ts} +4 -2
- package/dist/src/app/charts/{Tooltip.js → tooltips/Tooltip.js} +23 -28
- package/dist/src/app/charts/tooltips/Tooltip.js.map +1 -0
- package/dist/src/app/charts/{tooltipUtils.d.ts → tooltips/tooltipUtils.d.ts} +11 -6
- package/dist/src/app/charts/{tooltipUtils.js → tooltips/tooltipUtils.js} +33 -17
- package/dist/src/app/charts/tooltips/tooltipUtils.js.map +1 -0
- package/dist/src/app/charts/{Tracker.d.ts → trackers/Tracker.d.ts} +6 -5
- package/dist/src/app/charts/trackers/Tracker.js +60 -0
- package/dist/src/app/charts/trackers/Tracker.js.map +1 -0
- package/dist/src/app/charts/{trackerUtils.d.ts → trackers/trackerUtils.d.ts} +13 -8
- package/dist/src/app/charts/trackers/trackerUtils.js +359 -0
- package/dist/src/app/charts/trackers/trackerUtils.js.map +1 -0
- package/dist/src/app/charts/utils.d.ts +22 -32
- package/dist/src/app/charts/utils.js +36 -37
- package/dist/src/app/charts/utils.js.map +1 -1
- package/dist/src/index.d.ts +48 -15
- package/dist/src/index.js +71 -15
- package/dist/src/index.js.map +1 -1
- package/package.json +30 -20
- package/src/app/charts/Chart.tsx +334 -0
- package/src/app/charts/axes/AxesState.test.ts +34 -0
- package/src/app/charts/axes/AxesState.ts +112 -0
- package/src/app/charts/axes/AxisInterval.test.ts +143 -0
- package/src/app/charts/axes/AxisInterval.ts +139 -0
- package/src/app/charts/axes/BaseAxisRange.ts +113 -0
- package/src/app/charts/axes/ContinuousAxis.tsx +192 -0
- package/src/app/charts/axes/ContinuousAxisRange.test.ts +55 -0
- package/src/app/charts/axes/ContinuousAxisRange.ts +96 -0
- package/src/app/charts/axes/OrdinalAxis.tsx +223 -0
- package/src/app/charts/axes/OrdinalAxisRange.test.ts +55 -0
- package/src/app/charts/axes/OrdinalAxisRange.ts +112 -0
- package/src/app/charts/axes/axes.ts +1569 -0
- package/src/app/charts/d3types.ts +16 -0
- package/src/app/charts/filters/regexFilter.test.ts +13 -0
- package/src/app/charts/filters/regexFilter.ts +17 -0
- package/src/app/charts/hooks/useAxes.tsx +341 -0
- package/src/app/charts/hooks/useChart.tsx +159 -0
- package/src/app/charts/hooks/useDataObservable.tsx +175 -0
- package/src/app/charts/hooks/useInitialData.tsx +65 -0
- package/src/app/charts/hooks/useMouse.tsx +125 -0
- package/src/app/charts/hooks/usePlotDimensions.tsx +120 -0
- package/src/app/charts/hooks/useTooltip.tsx +112 -0
- package/src/app/charts/observables/ChartData.ts +31 -0
- package/src/app/charts/observables/iterates.test.ts +201 -0
- package/src/app/charts/observables/iterates.ts +203 -0
- package/src/app/charts/observables/ordinals.test.ts +135 -0
- package/src/app/charts/observables/ordinals.ts +248 -0
- package/src/app/charts/plots/BarPlot.tsx +1088 -0
- package/src/app/charts/plots/PoincarePlot.tsx +832 -0
- package/src/app/charts/plots/RasterPlot.tsx +676 -0
- package/src/app/charts/plots/ScatterPlot.tsx +653 -0
- package/src/app/charts/plots/plot.ts +75 -0
- package/src/app/charts/series/baseSeries.ts +52 -0
- package/src/app/charts/series/datumSeries.test.ts +60 -0
- package/src/app/charts/series/iterateSeries.ts +56 -0
- package/src/app/charts/series/ordinalSeries.ts +164 -0
- package/src/app/charts/series/timeSeries.ts +40 -0
- package/src/app/charts/series/timeSeriesChartData.ts +68 -0
- package/src/app/charts/styling/barPlotStyle.ts +126 -0
- package/src/app/charts/styling/margins.test.ts +22 -0
- package/src/app/charts/styling/margins.ts +58 -0
- package/src/app/charts/styling/svgStyle.ts +118 -0
- package/src/app/charts/subscriptions/subscription.test.ts +499 -0
- package/src/app/charts/subscriptions/subscriptions.ts +635 -0
- package/src/app/charts/tooltips/BarPlotTooltipContent.tsx +440 -0
- package/src/app/charts/tooltips/PoincarePlotTooltipContent.tsx +352 -0
- package/src/app/charts/tooltips/RasterPlotTooltipContent.tsx +274 -0
- package/src/app/charts/tooltips/ScatterPlotTooltipContent.tsx +319 -0
- package/src/app/charts/tooltips/Tooltip.tsx +148 -0
- package/src/app/charts/tooltips/tooltipUtils.ts +216 -0
- package/src/app/charts/trackers/Tracker.tsx +129 -0
- package/src/app/charts/trackers/trackerUtils.ts +494 -0
- package/src/app/charts/utils.ts +109 -0
- package/dist/src/app/charts/CategoryAxis.d.ts +0 -20
- package/dist/src/app/charts/CategoryAxis.js +0 -112
- package/dist/src/app/charts/CategoryAxis.js.map +0 -1
- package/dist/src/app/charts/ContinuousAxis.d.ts +0 -16
- package/dist/src/app/charts/ContinuousAxis.js +0 -186
- package/dist/src/app/charts/ContinuousAxis.js.map +0 -1
- package/dist/src/app/charts/RasterPlot.js.map +0 -1
- package/dist/src/app/charts/RasterPlotTooltipContent.js.map +0 -1
- package/dist/src/app/charts/ScatterPlot.js +0 -351
- package/dist/src/app/charts/ScatterPlot.js.map +0 -1
- package/dist/src/app/charts/ScatterPlotTooltipContent.js +0 -163
- package/dist/src/app/charts/ScatterPlotTooltipContent.js.map +0 -1
- package/dist/src/app/charts/Tooltip.js.map +0 -1
- package/dist/src/app/charts/Tracker.js +0 -65
- package/dist/src/app/charts/Tracker.js.map +0 -1
- package/dist/src/app/charts/axes.d.ts +0 -132
- package/dist/src/app/charts/axes.js +0 -265
- package/dist/src/app/charts/axes.js.map +0 -1
- package/dist/src/app/charts/chartData.js +0 -32
- package/dist/src/app/charts/chartData.js.map +0 -1
- package/dist/src/app/charts/continuousAxisRangeFor.d.ts +0 -20
- package/dist/src/app/charts/continuousAxisRangeFor.js +0 -68
- package/dist/src/app/charts/continuousAxisRangeFor.js.map +0 -1
- package/dist/src/app/charts/datumSeries.d.ts +0 -59
- package/dist/src/app/charts/datumSeries.js +0 -55
- package/dist/src/app/charts/datumSeries.js.map +0 -1
- package/dist/src/app/charts/datumSeries.test.js +0 -50
- package/dist/src/app/charts/datumSeries.test.js.map +0 -1
- package/dist/src/app/charts/hooks/AxesState.d.ts +0 -37
- package/dist/src/app/charts/hooks/AxesState.js +0 -44
- package/dist/src/app/charts/hooks/AxesState.js.map +0 -1
- package/dist/src/app/charts/margins.js.map +0 -1
- package/dist/src/app/charts/margins.test.js +0 -14
- package/dist/src/app/charts/margins.test.js.map +0 -1
- package/dist/src/app/charts/plot.js.map +0 -1
- package/dist/src/app/charts/regexFilter.d.ts +0 -9
- package/dist/src/app/charts/regexFilter.js +0 -17
- package/dist/src/app/charts/regexFilter.js.map +0 -1
- package/dist/src/app/charts/regexFilter.test.js +0 -11
- package/dist/src/app/charts/regexFilter.test.js.map +0 -1
- package/dist/src/app/charts/subscriptions.d.ts +0 -43
- package/dist/src/app/charts/subscriptions.js +0 -213
- package/dist/src/app/charts/subscriptions.js.map +0 -1
- package/dist/src/app/charts/svgStyle.d.ts +0 -27
- package/dist/src/app/charts/svgStyle.js +0 -32
- package/dist/src/app/charts/svgStyle.js.map +0 -1
- package/dist/src/app/charts/timeRange.test.js +0 -54
- package/dist/src/app/charts/timeRange.test.js.map +0 -1
- package/dist/src/app/charts/tooltipUtils.js.map +0 -1
- package/dist/src/app/charts/trackerUtils.js +0 -130
- package/dist/src/app/charts/trackerUtils.js.map +0 -1
- /package/dist/src/app/charts/{datumSeries.test.d.ts → axes/AxesState.test.d.ts} +0 -0
- /package/dist/src/app/charts/{margins.test.d.ts → axes/AxisInterval.test.d.ts} +0 -0
- /package/dist/src/app/charts/{regexFilter.test.d.ts → axes/ContinuousAxisRange.test.d.ts} +0 -0
- /package/dist/src/app/charts/{timeRange.test.d.ts → axes/OrdinalAxisRange.test.d.ts} +0 -0
- /package/dist/src/app/charts/{RasterPlotTooltipContent.d.ts → tooltips/RasterPlotTooltipContent.d.ts} +0 -0
- /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
|
+
});
|