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,129 @@
|
|
|
1
|
+
import {noop} from "../utils";
|
|
2
|
+
import {useChart} from "../hooks/useChart";
|
|
3
|
+
import {TrackerSelection} from "../d3types";
|
|
4
|
+
import {
|
|
5
|
+
defaultTrackerLabelFont,
|
|
6
|
+
defaultTrackerStyle,
|
|
7
|
+
removeTrackerControl,
|
|
8
|
+
trackerControlInstance,
|
|
9
|
+
TrackerLabelFont,
|
|
10
|
+
TrackerStyle
|
|
11
|
+
} from "./trackerUtils";
|
|
12
|
+
import * as d3 from "d3";
|
|
13
|
+
import {useEffect, useMemo, useRef} from "react";
|
|
14
|
+
import {AxisLocation, BaseAxis, ContinuousNumericAxis} from "../axes/axes";
|
|
15
|
+
import {usePlotDimensions} from "../hooks/usePlotDimensions";
|
|
16
|
+
|
|
17
|
+
export interface TrackerAxisInfo {
|
|
18
|
+
x: number
|
|
19
|
+
axisLocation: AxisLocation
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// map(axis_id -> tracker_axis_info)
|
|
23
|
+
export type TrackerAxisUpdate = Map<string, TrackerAxisInfo>
|
|
24
|
+
|
|
25
|
+
export enum TrackerLabelLocation {
|
|
26
|
+
Nowhere,
|
|
27
|
+
ByAxis,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface Props {
|
|
31
|
+
visible: boolean
|
|
32
|
+
trackerAxis?: AxisLocation
|
|
33
|
+
labelLocation?: TrackerLabelLocation
|
|
34
|
+
labelFormatter?: (value: number) => string
|
|
35
|
+
style?: Partial<TrackerStyle>,
|
|
36
|
+
font?: Partial<TrackerLabelFont>,
|
|
37
|
+
onTrackerUpdate?: (update: TrackerAxisUpdate) => void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A tracker line that displays or reports the x or y coordinates at the mouse location. The tracker
|
|
42
|
+
* handles single axes and dual axes.
|
|
43
|
+
* @param props The tracker control properties
|
|
44
|
+
* @return null component
|
|
45
|
+
* @constructor
|
|
46
|
+
*/
|
|
47
|
+
export function Tracker(props: Props): null {
|
|
48
|
+
const {
|
|
49
|
+
visible,
|
|
50
|
+
trackerAxis = AxisLocation.Bottom,
|
|
51
|
+
labelLocation = TrackerLabelLocation.ByAxis,
|
|
52
|
+
labelFormatter,
|
|
53
|
+
style,
|
|
54
|
+
font,
|
|
55
|
+
onTrackerUpdate = noop
|
|
56
|
+
} = props
|
|
57
|
+
const {
|
|
58
|
+
chartId,
|
|
59
|
+
container,
|
|
60
|
+
axes,
|
|
61
|
+
backgroundColor
|
|
62
|
+
} = useChart()
|
|
63
|
+
|
|
64
|
+
const {xAxesState, yAxesState} = axes
|
|
65
|
+
const axisState = (trackerAxis === AxisLocation.Bottom || trackerAxis === AxisLocation.Top) ?
|
|
66
|
+
xAxesState :
|
|
67
|
+
yAxesState
|
|
68
|
+
|
|
69
|
+
const {plotDimensions, margin} = usePlotDimensions()
|
|
70
|
+
|
|
71
|
+
const trackerStyle = useMemo(() => ({...defaultTrackerStyle, ...style}), [style])
|
|
72
|
+
const trackerFont = useMemo(() => ({...defaultTrackerLabelFont, ...font}), [font])
|
|
73
|
+
const trackerRef = useRef<TrackerSelection>(undefined)
|
|
74
|
+
|
|
75
|
+
const axisRef = useRef<Map<string, ContinuousNumericAxis>>(new Map())
|
|
76
|
+
useEffect(
|
|
77
|
+
() => {
|
|
78
|
+
const axes = new Map<string, ContinuousNumericAxis>()
|
|
79
|
+
axisState.axes.forEach((axis: BaseAxis, id: string) => axes.set(id, axis as ContinuousNumericAxis))
|
|
80
|
+
axisRef.current = axes
|
|
81
|
+
},
|
|
82
|
+
[axisState]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
// when the container, tracker-control function, or visibility change, then we need to update the
|
|
86
|
+
// tracker control
|
|
87
|
+
useEffect(
|
|
88
|
+
() => {
|
|
89
|
+
if (container) {
|
|
90
|
+
const svg = d3.select<SVGSVGElement, any>(container)
|
|
91
|
+
if (visible && container) {
|
|
92
|
+
const trackerLabels = new Map<ContinuousNumericAxis, (x: number) => string>(
|
|
93
|
+
Array.from(axisRef.current.values()).map(axis => {
|
|
94
|
+
const formatter = labelFormatter ??
|
|
95
|
+
((value: number) => labelLocation === TrackerLabelLocation.Nowhere ?
|
|
96
|
+
"" :
|
|
97
|
+
`${d3.format(",.0f")(value)}`
|
|
98
|
+
)
|
|
99
|
+
return [axis, formatter]
|
|
100
|
+
})
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
trackerRef.current = trackerControlInstance(
|
|
104
|
+
chartId,
|
|
105
|
+
container,
|
|
106
|
+
svg,
|
|
107
|
+
plotDimensions,
|
|
108
|
+
margin,
|
|
109
|
+
trackerStyle,
|
|
110
|
+
trackerFont,
|
|
111
|
+
trackerLabels,
|
|
112
|
+
labelLocation,
|
|
113
|
+
onTrackerUpdate,
|
|
114
|
+
trackerAxis,
|
|
115
|
+
backgroundColor
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
// if the tracker was defined and is now no longer defined (i.e., props changed, then remove the tracker)
|
|
119
|
+
else if (!visible && trackerRef.current !== undefined) {
|
|
120
|
+
removeTrackerControl(svg, trackerAxis)
|
|
121
|
+
trackerRef.current = undefined
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
[backgroundColor, chartId, container, labelFormatter, labelLocation, margin, onTrackerUpdate, plotDimensions, trackerAxis, trackerFont, trackerStyle, visible]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import {SvgSelection, TrackerSelection} from "../d3types";
|
|
2
|
+
import * as d3 from "d3";
|
|
3
|
+
import {Selection} from "d3";
|
|
4
|
+
import {Datum} from "../series/timeSeries";
|
|
5
|
+
import {containerDimensionsFrom, Dimensions, Margin, plotDimensionsFrom} from "../styling/margins";
|
|
6
|
+
import {BoundingBox, mouseInPlotAreaFor, textDimensions} from "../utils";
|
|
7
|
+
import {AxisLocation, ContinuousNumericAxis} from "../axes/axes";
|
|
8
|
+
import {TrackerAxisInfo, TrackerAxisUpdate, TrackerLabelLocation} from "./Tracker";
|
|
9
|
+
import React from "react";
|
|
10
|
+
|
|
11
|
+
export interface TrackerLabelFont {
|
|
12
|
+
size: number
|
|
13
|
+
color: string
|
|
14
|
+
family: string
|
|
15
|
+
weight: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const defaultTrackerLabelFont: TrackerLabelFont = {
|
|
19
|
+
size: 12,
|
|
20
|
+
color: '#d2933f',
|
|
21
|
+
weight: 300,
|
|
22
|
+
family: 'sans-serif'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface TrackerStyle {
|
|
26
|
+
visible: boolean;
|
|
27
|
+
color: string,
|
|
28
|
+
lineWidth: number,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const defaultTrackerStyle: TrackerStyle = {
|
|
32
|
+
visible: false,
|
|
33
|
+
color: '#d2933f',
|
|
34
|
+
lineWidth: 1,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// create namespaced "mousemoved" event names to distinguish between vertical and horizontal trackers
|
|
38
|
+
const MOUSE_MOVED_EVENT_VERTICAL_AXIS = "mousemove.vertical"
|
|
39
|
+
const MOUSE_MOVED_EVENT_HORIZONTAL_AXIS = "mousemove.horizontal"
|
|
40
|
+
|
|
41
|
+
const TRACKER_ID = "stream-chart-tracker"
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* SVG ID for the vertical tracker line
|
|
45
|
+
* @param chartId The ID of the chart
|
|
46
|
+
* @return SVG ID for the vertical tracker line
|
|
47
|
+
*/
|
|
48
|
+
function verticalTrackerLineId(chartId: number): string {
|
|
49
|
+
return `${TRACKER_ID}-line-vertical-${chartId}`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* SVG ID for the horizontal tracker line
|
|
54
|
+
* @param chartId The ID of the chart
|
|
55
|
+
* @return SVG ID for the vertical tracker line
|
|
56
|
+
*/
|
|
57
|
+
function horizontalTrackerLineId(chartId: number): string {
|
|
58
|
+
return `${TRACKER_ID}-line-horizontal-${chartId}`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* SVG ID for the tracker label
|
|
63
|
+
* @param chartId The ID of the chart
|
|
64
|
+
* @param axisLocation The location of the axis for which the tracker label is to be shown
|
|
65
|
+
* @return SVG ID for the vertical tracker line
|
|
66
|
+
*/
|
|
67
|
+
function trackerLabelId(chartId: number, axisLocation: AxisLocation): string {
|
|
68
|
+
switch (axisLocation) {
|
|
69
|
+
case AxisLocation.Bottom:
|
|
70
|
+
case AxisLocation.Top:
|
|
71
|
+
return `${TRACKER_ID}-label-vertical-${chartId}-${axisLocation}`
|
|
72
|
+
case AxisLocation.Left:
|
|
73
|
+
case AxisLocation.Right:
|
|
74
|
+
return `${TRACKER_ID}-label-horizontal-${chartId}-${axisLocation}`
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Creates or returns the existing SVG elements for displaying a tracker line that is either
|
|
80
|
+
* vertical (for x-axes) or horizontal (for y-axes).
|
|
81
|
+
* @param chartId The ID of the chart
|
|
82
|
+
* @param container The SVG container
|
|
83
|
+
* @param svg The SVG selection
|
|
84
|
+
* @param plotDimensions The dimensions of the plot
|
|
85
|
+
* @param margin The margins around the plot
|
|
86
|
+
* @param style The tracker style
|
|
87
|
+
* @param labelFont The font used for the axis labels
|
|
88
|
+
* @param label A function that returns the tracker label string for a given x-value
|
|
89
|
+
* @param labelStyle The location style for the tracker (i.e. on the axes, next to the mouse, none shown)
|
|
90
|
+
* @param onTrackerUpdate A callback function that accepts the current tracker's axis information
|
|
91
|
+
* @param [axisLocation = AxisLocation.Bottom] The optional location of the axis for which the tracker is to
|
|
92
|
+
* be shown. Default is bottom.
|
|
93
|
+
* @param backgroundColor The background color of the tracker label
|
|
94
|
+
* @return The tracker selection
|
|
95
|
+
*/
|
|
96
|
+
export function trackerControlInstance(
|
|
97
|
+
chartId: number,
|
|
98
|
+
container: SVGSVGElement,
|
|
99
|
+
svg: SvgSelection,
|
|
100
|
+
plotDimensions: Dimensions,
|
|
101
|
+
margin: Margin,
|
|
102
|
+
style: TrackerStyle,
|
|
103
|
+
labelFont: TrackerLabelFont,
|
|
104
|
+
label: Map<ContinuousNumericAxis, (x: number) => string>,
|
|
105
|
+
labelStyle: TrackerLabelLocation,
|
|
106
|
+
onTrackerUpdate: (update: TrackerAxisUpdate) => void,
|
|
107
|
+
axisLocation: AxisLocation = AxisLocation.Bottom,
|
|
108
|
+
backgroundColor: string
|
|
109
|
+
): TrackerSelection {
|
|
110
|
+
switch (axisLocation) {
|
|
111
|
+
case AxisLocation.Bottom:
|
|
112
|
+
case AxisLocation.Top:
|
|
113
|
+
return verticalTrackerControlInstance(chartId, container, svg, plotDimensions, margin, style, labelFont, label, labelStyle, onTrackerUpdate, backgroundColor)
|
|
114
|
+
case AxisLocation.Left:
|
|
115
|
+
case AxisLocation.Right:
|
|
116
|
+
default:
|
|
117
|
+
return horizontalTrackerControlInstance(chartId, container, svg, plotDimensions, margin, style, labelFont, label, labelStyle, onTrackerUpdate, backgroundColor)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Creates or returns the existing SVG elements for displaying a vertical tracker line
|
|
123
|
+
* @param chartId The ID of the chart
|
|
124
|
+
* @param container The SVG container
|
|
125
|
+
* @param svg The SVG selection
|
|
126
|
+
* @param plotDimensions The dimensions of the plot
|
|
127
|
+
* @param margin The margins around the plot
|
|
128
|
+
* @param style The tracker style
|
|
129
|
+
* @param labelFont The font used for the axis labels
|
|
130
|
+
* @param label A function that returns the tracker label string for a given x-value
|
|
131
|
+
* @param labelStyle The location style for the tracker (i.e. on the axes, next to the mouse, none shown)
|
|
132
|
+
* @param onTrackerUpdate A callback function that accepts the current tracker's axis information
|
|
133
|
+
* @param backgroundColor The background color of the tracker label
|
|
134
|
+
* @return The tracker selection
|
|
135
|
+
*/
|
|
136
|
+
function verticalTrackerControlInstance(
|
|
137
|
+
chartId: number,
|
|
138
|
+
container: SVGSVGElement,
|
|
139
|
+
svg: SvgSelection,
|
|
140
|
+
plotDimensions: Dimensions,
|
|
141
|
+
margin: Margin,
|
|
142
|
+
style: TrackerStyle,
|
|
143
|
+
labelFont: TrackerLabelFont,
|
|
144
|
+
label: Map<ContinuousNumericAxis, (x: number) => string>,
|
|
145
|
+
labelStyle: TrackerLabelLocation,
|
|
146
|
+
onTrackerUpdate: (update: TrackerAxisUpdate) => void,
|
|
147
|
+
backgroundColor: string
|
|
148
|
+
): TrackerSelection {
|
|
149
|
+
const trackerLine = svg
|
|
150
|
+
.append<SVGLineElement>('line')
|
|
151
|
+
.attr('id', verticalTrackerLineId(chartId))
|
|
152
|
+
.attr('class', 'tracker')
|
|
153
|
+
.attr('y1', margin.top)
|
|
154
|
+
.attr('y2', plotDimensions.height + margin.top)
|
|
155
|
+
.attr('stroke', style.color)
|
|
156
|
+
.attr('stroke-width', style.lineWidth)
|
|
157
|
+
.attr('opacity', 0) as Selection<SVGLineElement, Datum, null, undefined>
|
|
158
|
+
|
|
159
|
+
if (labelStyle === TrackerLabelLocation.ByAxis) {
|
|
160
|
+
label.forEach((_, axis) => {
|
|
161
|
+
// create the text element holding the tracker time
|
|
162
|
+
const labelY = axis.location === AxisLocation.Top ?
|
|
163
|
+
Math.max(0, margin.top - 20) :
|
|
164
|
+
Math.max(0, plotDimensions.height + margin.top - 3)
|
|
165
|
+
|
|
166
|
+
svg
|
|
167
|
+
.append<SVGRectElement>('rect')
|
|
168
|
+
.attr('id', `${trackerLabelId(chartId, axis.location)}-background`)
|
|
169
|
+
.attr('y', labelY)
|
|
170
|
+
.attr('fill', backgroundColor)
|
|
171
|
+
.attr('opacity', 0)
|
|
172
|
+
|
|
173
|
+
svg
|
|
174
|
+
.append<SVGTextElement>('text')
|
|
175
|
+
.attr('id', trackerLabelId(chartId, axis.location))
|
|
176
|
+
.attr('y', labelY)
|
|
177
|
+
.attr('fill', labelFont.color)
|
|
178
|
+
.attr('font-family', labelFont.family)
|
|
179
|
+
.attr('font-size', labelFont.size)
|
|
180
|
+
.attr('font-weight', labelFont.weight)
|
|
181
|
+
.attr('opacity', 0)
|
|
182
|
+
.text(() => '')
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const containerDimensions = containerDimensionsFrom(plotDimensions, margin)
|
|
187
|
+
svg.on(
|
|
188
|
+
MOUSE_MOVED_EVENT_VERTICAL_AXIS,
|
|
189
|
+
event => handleShowVerticalTracker(
|
|
190
|
+
chartId, container, event, margin, containerDimensions, style, labelFont, label, labelStyle, onTrackerUpdate, backgroundColor
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return trackerLine
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Creates or returns the existing SVG elements for displaying a horizontal tracker line
|
|
199
|
+
* @param chartId The ID of the chart
|
|
200
|
+
* @param container The SVG container
|
|
201
|
+
* @param svg The SVG selection
|
|
202
|
+
* @param plotDimensions The dimensions of the plot
|
|
203
|
+
* @param margin The margins around the plot
|
|
204
|
+
* @param style The tracker style
|
|
205
|
+
* @param labelFont The font used for the axis labels
|
|
206
|
+
* @param label A function that returns the tracker label string for a given x-value
|
|
207
|
+
* @param labelStyle The location style for the tracker (i.e. on the axes, next to the mouse, none shown)
|
|
208
|
+
* @param onTrackerUpdate A callback function that accepts the current tracker's axis information
|
|
209
|
+
* @param backgroundColor The background color of the tracker label
|
|
210
|
+
* @return The tracker selection
|
|
211
|
+
*/
|
|
212
|
+
function horizontalTrackerControlInstance(
|
|
213
|
+
chartId: number,
|
|
214
|
+
container: SVGSVGElement,
|
|
215
|
+
svg: SvgSelection,
|
|
216
|
+
plotDimensions: Dimensions,
|
|
217
|
+
margin: Margin,
|
|
218
|
+
style: TrackerStyle,
|
|
219
|
+
labelFont: TrackerLabelFont,
|
|
220
|
+
label: Map<ContinuousNumericAxis, (x: number) => string>,
|
|
221
|
+
labelStyle: TrackerLabelLocation,
|
|
222
|
+
onTrackerUpdate: (update: TrackerAxisUpdate) => void,
|
|
223
|
+
backgroundColor: string
|
|
224
|
+
): TrackerSelection {
|
|
225
|
+
const trackerLine = svg
|
|
226
|
+
.append<SVGLineElement>('line')
|
|
227
|
+
.attr('id', horizontalTrackerLineId(chartId))
|
|
228
|
+
.attr('class', 'tracker')
|
|
229
|
+
.attr('x1', margin.left)
|
|
230
|
+
.attr('x2', plotDimensions.width + margin.left)
|
|
231
|
+
.attr('stroke', style.color)
|
|
232
|
+
.attr('stroke-width', style.lineWidth)
|
|
233
|
+
.attr('opacity', 0) as Selection<SVGLineElement, Datum, null, undefined>
|
|
234
|
+
|
|
235
|
+
label.forEach((_, axis) => {
|
|
236
|
+
// create the text element holding the tracker time
|
|
237
|
+
const labelY = axis.location === AxisLocation.Left ?
|
|
238
|
+
Math.max(0, margin.left - 20) :
|
|
239
|
+
Math.max(0, plotDimensions.width + margin.left - 3)
|
|
240
|
+
|
|
241
|
+
svg
|
|
242
|
+
.append<SVGRectElement>('rect')
|
|
243
|
+
.attr('id', `${trackerLabelId(chartId, axis.location)}-background`)
|
|
244
|
+
.attr('y', labelY)
|
|
245
|
+
.attr('fill', backgroundColor)
|
|
246
|
+
.attr('opacity', 0)
|
|
247
|
+
|
|
248
|
+
svg
|
|
249
|
+
.append<SVGTextElement>('text')
|
|
250
|
+
.attr('id', trackerLabelId(chartId, axis.location))
|
|
251
|
+
.attr('y', labelY)
|
|
252
|
+
.attr('fill', labelFont.color)
|
|
253
|
+
.attr('font-family', labelFont.family)
|
|
254
|
+
.attr('font-size', labelFont.size)
|
|
255
|
+
.attr('font-weight', labelFont.weight)
|
|
256
|
+
.attr('opacity', 0)
|
|
257
|
+
.text(() => '')
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const containerDimensions = containerDimensionsFrom(plotDimensions, margin)
|
|
261
|
+
svg.on(
|
|
262
|
+
MOUSE_MOVED_EVENT_HORIZONTAL_AXIS,
|
|
263
|
+
event => handleShowHorizontalTracker(
|
|
264
|
+
chartId, container, event, margin, containerDimensions, style, labelFont, label, labelStyle, onTrackerUpdate, backgroundColor
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return trackerLine
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Removes the tracker control from the chart
|
|
273
|
+
* @param svg The svg selection holding the tracker control
|
|
274
|
+
* @param axisLocation The location of the axis for which the tracker is to be removed.
|
|
275
|
+
*/
|
|
276
|
+
export function removeTrackerControl(svg: SvgSelection, axisLocation: AxisLocation) {
|
|
277
|
+
switch (axisLocation) {
|
|
278
|
+
case AxisLocation.Bottom:
|
|
279
|
+
case AxisLocation.Top:
|
|
280
|
+
svg.on(MOUSE_MOVED_EVENT_VERTICAL_AXIS, () => null)
|
|
281
|
+
break
|
|
282
|
+
case AxisLocation.Left:
|
|
283
|
+
case AxisLocation.Right:
|
|
284
|
+
default:
|
|
285
|
+
svg.on(MOUSE_MOVED_EVENT_HORIZONTAL_AXIS, () => null)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Callback when the vertical mouse tracker is to be shown
|
|
291
|
+
* @param chartId The ID number of the chart
|
|
292
|
+
* @param container The svg container
|
|
293
|
+
* @param event The mouse-over series event
|
|
294
|
+
* @param margin The plot margins
|
|
295
|
+
* @param dimensions The container dimensions (i.e., the plot dimensions plus its margins)
|
|
296
|
+
* @param trackerStyle The style settings for the tracker line
|
|
297
|
+
* @param labelFont The style settings for the tracker font
|
|
298
|
+
* @param label A function that returns the tracker label string
|
|
299
|
+
* @param labelStyle Where to display the tracker labels (i.e., with-mouse, with-axes, no-where)
|
|
300
|
+
* @param onTrackerUpdate A callback function that accepts the current tracker's axis information
|
|
301
|
+
* @param backgroundColor The background color of the tracker label
|
|
302
|
+
*/
|
|
303
|
+
function handleShowVerticalTracker(
|
|
304
|
+
chartId: number,
|
|
305
|
+
container: SVGSVGElement,
|
|
306
|
+
event: React.MouseEvent<SVGSVGElement>,
|
|
307
|
+
margin: Margin,
|
|
308
|
+
dimensions: Dimensions,
|
|
309
|
+
trackerStyle: TrackerStyle,
|
|
310
|
+
labelFont: TrackerLabelFont,
|
|
311
|
+
label: Map<ContinuousNumericAxis, (x: number) => string>,
|
|
312
|
+
labelStyle: TrackerLabelLocation,
|
|
313
|
+
onTrackerUpdate: (update: TrackerAxisUpdate) => void,
|
|
314
|
+
backgroundColor: string
|
|
315
|
+
): void {
|
|
316
|
+
// determine whether the mouse is in the plot area
|
|
317
|
+
const [x, y] = d3.pointer(event, container)
|
|
318
|
+
const inPlot = mouseInPlotAreaFor(x, y, margin, dimensions)
|
|
319
|
+
|
|
320
|
+
const updateInfo: Array<[string, TrackerAxisInfo]> = Array.from(label.entries())
|
|
321
|
+
.map(([axis, trackerLabel]) => {
|
|
322
|
+
// when the mouse is in the plot area, then set the opacity of the tracker line and label to 1,
|
|
323
|
+
// which means it is fully visible. when the mouse is not in the plot area, set the opacity to 0,
|
|
324
|
+
// which means the tracker line and label are invisible.
|
|
325
|
+
d3.select<SVGLineElement, Datum>(`#${verticalTrackerLineId(chartId)}`)
|
|
326
|
+
.attr('x1', x)
|
|
327
|
+
.attr('x2', x)
|
|
328
|
+
.attr('stroke', trackerStyle.color)
|
|
329
|
+
.attr('stroke-width', trackerStyle.lineWidth)
|
|
330
|
+
.attr('opacity', () => inPlot ? 1 : 0)
|
|
331
|
+
|
|
332
|
+
// when the label-style is to be with the mouse
|
|
333
|
+
if (labelStyle === TrackerLabelLocation.ByAxis) {
|
|
334
|
+
const label = d3.select<SVGTextElement, any>(`#${trackerLabelId(chartId, axis.location)}`)
|
|
335
|
+
.attr('fill', labelFont.color)
|
|
336
|
+
.attr('font-family', labelFont.family)
|
|
337
|
+
.attr('font-size', labelFont.size)
|
|
338
|
+
.attr('font-weight', labelFont.weight)
|
|
339
|
+
.attr('opacity', () => inPlot ? 1 : 0)
|
|
340
|
+
.text(() => trackerLabel(axis.scale.invert(x - margin.left)))
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
const labelBackground = d3.select<SVGRectElement, any>(`#${trackerLabelId(chartId, axis.location)}-background`)
|
|
344
|
+
.attr('fill', backgroundColor)
|
|
345
|
+
.attr('opacity', () => inPlot ? 0.8 : 0)
|
|
346
|
+
.attr('rx', '5px')
|
|
347
|
+
|
|
348
|
+
const space = 10
|
|
349
|
+
const {height} = plotDimensionsFrom(dimensions.width, dimensions.height, margin)
|
|
350
|
+
const labelY = axis.location === AxisLocation.Top ?
|
|
351
|
+
margin.top + labelFont.size + space :
|
|
352
|
+
margin.top + height - space
|
|
353
|
+
label.attr('y', labelY)
|
|
354
|
+
|
|
355
|
+
// adjust the label position when the tracker is at the right-most edges of the plot so that
|
|
356
|
+
// the label remains visible (i.e., doesn't get clipped)
|
|
357
|
+
const xOffset = TrackerLabelLocation.ByAxis ? 10 : 0
|
|
358
|
+
const {width: labelWidth, height: labelHeight} = textDimensions(label)
|
|
359
|
+
const labelX = Math.min(dimensions.width - margin.right - labelWidth, x + xOffset)
|
|
360
|
+
label.attr('x', labelX)
|
|
361
|
+
|
|
362
|
+
// set the background location and width
|
|
363
|
+
const padding: number = 3
|
|
364
|
+
labelBackground
|
|
365
|
+
.attr('x', labelX - padding)
|
|
366
|
+
.attr('y', labelY - labelHeight - padding)
|
|
367
|
+
.attr('width', labelWidth + 2 * padding)
|
|
368
|
+
.attr('height', labelHeight + 2 * padding)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const trackerInfo: TrackerAxisInfo = {
|
|
372
|
+
x: axis.scale.invert(x - margin.left),
|
|
373
|
+
axisLocation: axis.location
|
|
374
|
+
}
|
|
375
|
+
return [axis.axisId, trackerInfo]
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
if (inPlot) {
|
|
379
|
+
onTrackerUpdate(new Map<string, TrackerAxisInfo>(updateInfo))
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Callback when the horizontal mouse tracker is to be shown
|
|
385
|
+
* @param chartId The ID number of the chart
|
|
386
|
+
* @param container The svg container
|
|
387
|
+
* @param event The mouse-over series event
|
|
388
|
+
* @param margin The plot margins
|
|
389
|
+
* @param dimensions The container dimensions (i.e., the plot dimensions plus its margins)
|
|
390
|
+
* @param trackerStyle The style settings for the tracker line
|
|
391
|
+
* @param labelFont The style settings for the tracker font
|
|
392
|
+
* @param label A function that returns the tracker label string
|
|
393
|
+
* @param labelStyle Where to display the tracker labels (i.e., with-mouse, with-axes, no-where)
|
|
394
|
+
* @param onTrackerUpdate A callback function that accepts the current tracker's axis information
|
|
395
|
+
* @param backgroundColor The background color of the tracker label
|
|
396
|
+
*/
|
|
397
|
+
function handleShowHorizontalTracker(
|
|
398
|
+
chartId: number,
|
|
399
|
+
container: SVGSVGElement,
|
|
400
|
+
event: React.MouseEvent<SVGSVGElement>,
|
|
401
|
+
margin: Margin,
|
|
402
|
+
dimensions: Dimensions,
|
|
403
|
+
trackerStyle: TrackerStyle,
|
|
404
|
+
labelFont: TrackerLabelFont,
|
|
405
|
+
label: Map<ContinuousNumericAxis, (x: number) => string>,
|
|
406
|
+
labelStyle: TrackerLabelLocation,
|
|
407
|
+
onTrackerUpdate: (update: TrackerAxisUpdate) => void,
|
|
408
|
+
backgroundColor: string
|
|
409
|
+
): void {
|
|
410
|
+
// determine whether the mouse is in the plot area
|
|
411
|
+
const [x, y] = d3.pointer(event, container)
|
|
412
|
+
const inPlot = mouseInPlotAreaFor(x, y, margin, dimensions)
|
|
413
|
+
|
|
414
|
+
type AxisAndLabelInfo = {
|
|
415
|
+
axis: ContinuousNumericAxis
|
|
416
|
+
labelSelection: d3.Selection<SVGTextElement, any, HTMLElement, any>
|
|
417
|
+
labelBoundingBox: BoundingBox,
|
|
418
|
+
labelBackground: d3.Selection<SVGRectElement, any, HTMLElement, any>
|
|
419
|
+
}
|
|
420
|
+
//
|
|
421
|
+
// need to calculate each axis-label position separately, because at the plot edges, the axis-label
|
|
422
|
+
// positions depend on each other. For example, at the left edge, the left-axis label needs to move
|
|
423
|
+
// to the right of the mouse to remain in the plot area. Therefore, the right-axis label needs
|
|
424
|
+
// to move as well but needs to know the width of the left-axis label to do so.
|
|
425
|
+
//
|
|
426
|
+
// add the labels and calculate the bounding boxes
|
|
427
|
+
const boundingBoxes: Map<AxisLocation, AxisAndLabelInfo> = new Map(Array.from(label.entries())
|
|
428
|
+
.map(([axis, trackerLabel]) => {
|
|
429
|
+
// when the mouse is in the plot area, then set the opacity of the tracker line and label to 1,
|
|
430
|
+
// which means it is fully visible. when the mouse is not in the plot area, set the opacity to 0,
|
|
431
|
+
// which means the tracker line and label are invisible.
|
|
432
|
+
d3.select<SVGLineElement, Datum>(`#${horizontalTrackerLineId(chartId)}`)
|
|
433
|
+
.attr('y1', y)
|
|
434
|
+
.attr('y2', y)
|
|
435
|
+
.attr('stroke', trackerStyle.color)
|
|
436
|
+
.attr('stroke-width', trackerStyle.lineWidth)
|
|
437
|
+
.attr('opacity', () => inPlot ? 1 : 0)
|
|
438
|
+
|
|
439
|
+
const label = d3.select<SVGTextElement, any>(`#${trackerLabelId(chartId, axis.location)}`)
|
|
440
|
+
.attr('fill', labelFont.color)
|
|
441
|
+
.attr('font-family', labelFont.family)
|
|
442
|
+
.attr('font-size', labelFont.size)
|
|
443
|
+
.attr('font-weight', labelFont.weight)
|
|
444
|
+
.attr('opacity', () => inPlot ? 1 : 0)
|
|
445
|
+
.text(() => trackerLabel(axis.scale.invert(y - margin.top)))
|
|
446
|
+
|
|
447
|
+
const labelBackground = d3.select<SVGRectElement, any>(`#${trackerLabelId(chartId, axis.location)}-background`)
|
|
448
|
+
.attr('fill', backgroundColor)
|
|
449
|
+
.attr('opacity', () => inPlot && labelStyle !== TrackerLabelLocation.Nowhere ? 0.8 : 0)
|
|
450
|
+
.attr('rx', '5px')
|
|
451
|
+
|
|
452
|
+
return [axis.location, {axis, labelSelection: label, labelBoundingBox: textDimensions(label), labelBackground}]
|
|
453
|
+
}))
|
|
454
|
+
|
|
455
|
+
// place the labels so that they remain in the plot area
|
|
456
|
+
const space = 10
|
|
457
|
+
const rightLabelWidth = (boundingBoxes.get(AxisLocation.Right)?.labelBoundingBox.width || -space) + space
|
|
458
|
+
|
|
459
|
+
const updateInfo: Array<[string, TrackerAxisInfo]> = Array.from(boundingBoxes.entries())
|
|
460
|
+
.map(([location, {axis, labelSelection, labelBoundingBox, labelBackground}]) => {
|
|
461
|
+
if (labelStyle === TrackerLabelLocation.ByAxis) {
|
|
462
|
+
const {width} = plotDimensionsFrom(dimensions.width, dimensions.height, margin)
|
|
463
|
+
const labelX = location === AxisLocation.Left ?
|
|
464
|
+
margin.left + space :
|
|
465
|
+
margin.left + width - rightLabelWidth - space
|
|
466
|
+
labelSelection.attr('x', labelX)
|
|
467
|
+
|
|
468
|
+
// adjust the label position when the tracker is at the right-most edges of the plot so that
|
|
469
|
+
// the label remains visible (i.e., doesn't get clipped)
|
|
470
|
+
const yOffset: number = labelStyle === TrackerLabelLocation.ByAxis ? space : 0
|
|
471
|
+
const labelY = Math.max(y - yOffset, margin.top + yOffset + labelBoundingBox.height)
|
|
472
|
+
labelSelection.attr('y', labelY)
|
|
473
|
+
|
|
474
|
+
// set the background location and width
|
|
475
|
+
const padding: number = 5
|
|
476
|
+
labelBackground
|
|
477
|
+
.attr('x', labelX - padding)
|
|
478
|
+
.attr('y', labelY - labelBoundingBox.height - 1)
|
|
479
|
+
.attr('width', labelBoundingBox.width + 2 * padding)
|
|
480
|
+
.attr('height', labelBoundingBox.height + 2 * padding)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const trackerInfo: TrackerAxisInfo = {
|
|
484
|
+
x: axis.scale.invert(y - margin.top + margin.bottom),
|
|
485
|
+
axisLocation: axis.location
|
|
486
|
+
}
|
|
487
|
+
return [axis.axisId, trackerInfo]
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
if (inPlot) {
|
|
491
|
+
onTrackerUpdate(new Map<string, TrackerAxisInfo>(updateInfo))
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|