stream-charts 1.0.0 → 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 +758 -345
- package/changes.md +10 -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} +17 -11
- package/dist/src/app/charts/{tooltipUtils.js → tooltips/tooltipUtils.js} +52 -25
- 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,319 @@
|
|
|
1
|
+
import {Series} from "../plots/plot";
|
|
2
|
+
import {Dimensions, Margin} from "../styling/margins";
|
|
3
|
+
import {boundingPoints, defaultTooltipStyle, TooltipDimensions, TooltipStyle, tooltipX, tooltipY} from "./tooltipUtils";
|
|
4
|
+
import * as d3 from "d3";
|
|
5
|
+
import {formatTime, formatTimeChange, formatValue, formatValueChange} from "../utils";
|
|
6
|
+
import {TextSelection} from "../d3types";
|
|
7
|
+
import {useEffect, useMemo} from "react";
|
|
8
|
+
import {NoTooltipMetadata, useChart} from "../hooks/useChart";
|
|
9
|
+
import {usePlotDimensions} from "../hooks/usePlotDimensions";
|
|
10
|
+
import {ContinuousNumericAxis, SeriesLineStyle} from "../axes/axes";
|
|
11
|
+
import {Datum, emptyDatum} from "../series/timeSeries";
|
|
12
|
+
import {TooltipData} from "../hooks/useTooltip";
|
|
13
|
+
import {ContinuousAxisRange} from "../axes/ContinuousAxisRange";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
# Want to write your own tooltip-content component?
|
|
17
|
+
|
|
18
|
+
Here's how to write your own tooltip-content component.
|
|
19
|
+
|
|
20
|
+
To create your own tooltip content `<MyTooltipContent/>` you must do the following:
|
|
21
|
+
1. Create a react component for your tooltip content (see for example, {@link ScatterPlotTooltipContent}
|
|
22
|
+
as a reference.
|
|
23
|
+
2. Use the {@link useChart} hook to get the {@link registerTooltipContentProvider} registration function.
|
|
24
|
+
3. When your tooltip content component (`<MyTooltipContent/>`) mounts, use the {@link registerTooltipContentProvider}
|
|
25
|
+
function to register your tooltip content provider.
|
|
26
|
+
4. When the chart dimensions, margin, container, etc, change, register your tooltip content provider
|
|
27
|
+
again (you can register as many times as you like because it only uses the last content provider
|
|
28
|
+
registered).
|
|
29
|
+
|
|
30
|
+
That's it! A bit more details below.
|
|
31
|
+
|
|
32
|
+
The {@link registerTooltipContentProvider} function from the {@link useChart} hook allows you to register
|
|
33
|
+
one tooltip content provider. A second call to this function will cause the {@link useChart} hook to drop
|
|
34
|
+
the first one in favor of the second one.
|
|
35
|
+
|
|
36
|
+
The {@link registerTooltipContentProvider} function from the {@link useChart} hook accepts a higher-order
|
|
37
|
+
function that allowing a closure on content/chart-specific data. Specifically, you must hand the
|
|
38
|
+
{@link registerTooltipContentProvider} a function of the form:
|
|
39
|
+
|
|
40
|
+
`(seriesName: string, time: number, series: TimeSeries, mouseCoords: [x: number, y: number]) => TooltipDimensions`
|
|
41
|
+
|
|
42
|
+
Your function to add that actual content will be what this function calls whenever d3 fires a mouse-over
|
|
43
|
+
event on one you the time-series in the chart.
|
|
44
|
+
|
|
45
|
+
The code snippet below is from the `useEffect` call in the {@link ScatterPlotTooltipContent}.
|
|
46
|
+
Note that the first four arguments to the {@link addTooltipContent} function are those provided by the {@link useChart} hook
|
|
47
|
+
when a d3 mouse-over event occurs on one of your series. The additional six arguments are from the closure formed
|
|
48
|
+
on the variables in your component. The `chartId`, `container`, `margin`, and `plotDimensions` are from the
|
|
49
|
+
{@link useChart} hook called by the {@link ScatterPlotTooltipContent} component. The last two
|
|
50
|
+
arguments, `defaultTooltipStyle` and `options` are specific to the {@link ScatterPlotTooltipContent}.
|
|
51
|
+
For example, the `options` property is set by the caller of the {@link ScatterPlotTooltipContent}
|
|
52
|
+
component.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// register the tooltip content provider function with the chart hook (useChart) so that
|
|
56
|
+
// it is visible to all children of the Chart (i.e. the <Tooltip>).
|
|
57
|
+
registerTooltipContentProvider(
|
|
58
|
+
(seriesName: string, time: number, series: TimeSeries, mouseCoords: [x: number, y: number]) =>
|
|
59
|
+
addTooltipContent(
|
|
60
|
+
seriesName, time, series, mouseCoords,
|
|
61
|
+
chartId, container, margin, plotDimensions,
|
|
62
|
+
defaultTooltipStyle, options
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This pattern allows you to supplement that `useChart` mouse-over callback with information specific to you component.
|
|
68
|
+
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Options for displaying the tooltip content. These options are specific to this
|
|
73
|
+
* particular implementation of a tooltip content. The options effect are applied
|
|
74
|
+
* as shown below.
|
|
75
|
+
* ```
|
|
76
|
+
* series name
|
|
77
|
+
* headers.before headers.after headers.delta
|
|
78
|
+
* labels.x formatters.x.value formatters.x.value formatters.x.change
|
|
79
|
+
* labels.y formatters.y.value formatters.y.value formatters.y.change
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
interface TooltipOptions {
|
|
83
|
+
labels: { x: string, y: string }
|
|
84
|
+
headers: {before: string, after: string, delta: string}
|
|
85
|
+
formatters: {
|
|
86
|
+
x: { value: (value: number) => string, change: (value1: number, value2: number) => string },
|
|
87
|
+
y: { value: (value: number) => string, change: (value1: number, value2: number) => string },
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Properties for rendering the tooltip content. The properties are applied as
|
|
93
|
+
* shown below.
|
|
94
|
+
* ```
|
|
95
|
+
* series name
|
|
96
|
+
* beforeHeader afterHeader deltaHeader
|
|
97
|
+
* xLabel xValueFormatter xValueFormatter xChangeFormatter
|
|
98
|
+
* yLabel yValueFormatter yValueFormatter yChangeFormatter
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
interface Props {
|
|
102
|
+
// label for the x-values (x-value row header)
|
|
103
|
+
xLabel: string
|
|
104
|
+
yLabel: string
|
|
105
|
+
beforeHeader?: string
|
|
106
|
+
afterHeader?: string
|
|
107
|
+
deltaHeader?: string
|
|
108
|
+
xValueFormatter?: (value: number) => string
|
|
109
|
+
yValueFormatter?: (value: number) => string
|
|
110
|
+
xChangeFormatter?: (value1: number, value2: number) => string,
|
|
111
|
+
yChangeFormatter?: (value1: number, value2: number) => string,
|
|
112
|
+
style?: Partial<TooltipStyle>
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Adds tooltip content as a table. The columns of the table are the "label", the value before
|
|
117
|
+
* the mouse cursor, then value after the mouse cursor, and the difference between the two values.
|
|
118
|
+
* The rows of the table are x-values for the first row, and the y-values for the second row.
|
|
119
|
+
* The table has the following form.
|
|
120
|
+
* ```
|
|
121
|
+
* series name
|
|
122
|
+
* before after ∆
|
|
123
|
+
* x-label x_tb x_ta x_ta - x_tb
|
|
124
|
+
* y-label y_tb y_ta y_ta - y_tb
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* Registers the tooltip-content provider with the `ChartContext` so that when d3 fires a mouse-over
|
|
128
|
+
* event on a series. The content provider is returns the {@link addTooltipContent} function
|
|
129
|
+
* when called. And when called the {@link addTooltipContent} function adds the actual tooltip
|
|
130
|
+
* content to the SVG element.
|
|
131
|
+
* @param props The properties describing the tooltip content
|
|
132
|
+
* @return null
|
|
133
|
+
* @constructor
|
|
134
|
+
*/
|
|
135
|
+
export function ScatterPlotTooltipContent(props: Props): null {
|
|
136
|
+
const {
|
|
137
|
+
chartId,
|
|
138
|
+
container,
|
|
139
|
+
tooltip
|
|
140
|
+
} = useChart<Datum, SeriesLineStyle, NoTooltipMetadata, ContinuousAxisRange, ContinuousNumericAxis>()
|
|
141
|
+
|
|
142
|
+
const {registerTooltipContentProvider} = tooltip
|
|
143
|
+
|
|
144
|
+
const {margin, plotDimensions} = usePlotDimensions()
|
|
145
|
+
|
|
146
|
+
const {
|
|
147
|
+
xLabel,
|
|
148
|
+
yLabel,
|
|
149
|
+
beforeHeader = 'before',
|
|
150
|
+
afterHeader = 'after',
|
|
151
|
+
deltaHeader = '∆',
|
|
152
|
+
xValueFormatter = formatTime,
|
|
153
|
+
yValueFormatter = formatValue,
|
|
154
|
+
xChangeFormatter = formatTimeChange,
|
|
155
|
+
yChangeFormatter = formatValueChange,
|
|
156
|
+
style,
|
|
157
|
+
} = props
|
|
158
|
+
|
|
159
|
+
const tooltipStyle = useMemo(() => ({...defaultTooltipStyle, ...style}), [style])
|
|
160
|
+
|
|
161
|
+
// register the tooltip content provider, which when called on mouse-enter-series events
|
|
162
|
+
// will render the tooltip container and then the tooltip content. recall that that the
|
|
163
|
+
// tooltip content is generated in this plot (because this is the plot that holds all the
|
|
164
|
+
// information needed to render it), and the container for the content is rendered by
|
|
165
|
+
// the <Tooltip>, which this know nothing about.
|
|
166
|
+
//
|
|
167
|
+
// the 'registration function accepts a function of the form (seriesName, time, series) => TooltipDimensions.
|
|
168
|
+
// and that function has a closure on the content-specific information needed to add the
|
|
169
|
+
// actual content
|
|
170
|
+
useEffect(
|
|
171
|
+
() => {
|
|
172
|
+
if (container) {
|
|
173
|
+
// assemble the options for adding the tooltip
|
|
174
|
+
const options: TooltipOptions = {
|
|
175
|
+
labels: {x: xLabel, y: yLabel},
|
|
176
|
+
headers: {before: beforeHeader, after: afterHeader, delta: deltaHeader},
|
|
177
|
+
formatters: {
|
|
178
|
+
x: {value: xValueFormatter, change: xChangeFormatter},
|
|
179
|
+
y: {value: yValueFormatter, change: yChangeFormatter},
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// register the tooltip content provider function with the chart hook (useChart) so that
|
|
184
|
+
// it is visible to all children of the Chart (i.e. the <Tooltip>).
|
|
185
|
+
registerTooltipContentProvider(
|
|
186
|
+
(seriesName: string, time: number, tooltipData: TooltipData<Datum, NoTooltipMetadata>, mouseCoords: [x: number, y: number]) =>
|
|
187
|
+
addTooltipContent(
|
|
188
|
+
seriesName, time, tooltipData.series, mouseCoords,
|
|
189
|
+
chartId, container, margin, plotDimensions, tooltipStyle,
|
|
190
|
+
options
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
[
|
|
196
|
+
chartId, container, margin, plotDimensions, registerTooltipContentProvider,
|
|
197
|
+
xLabel, xChangeFormatter, xValueFormatter,
|
|
198
|
+
yLabel, yChangeFormatter, yValueFormatter,
|
|
199
|
+
beforeHeader, afterHeader, deltaHeader,
|
|
200
|
+
tooltipStyle
|
|
201
|
+
]
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return null
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Callback function that adds tooltip content and returns the tooltip width and text height
|
|
209
|
+
* @param seriesName The name of the series (i.e. the neuron ID)
|
|
210
|
+
* @param time The time (x-coordinate value) corresponding to the mouse location
|
|
211
|
+
* @param series The datum (t ms, s mV)
|
|
212
|
+
* @param mouseCoords The coordinates of the mouse when the event was fired (relative to the plot container)
|
|
213
|
+
* @param chartId The ID of this chart
|
|
214
|
+
* @param container The plot container (SVGSVGElement)
|
|
215
|
+
* @param margin The plot margins
|
|
216
|
+
* @param tooltipStyle The style properties for the tooltip
|
|
217
|
+
* @param plotDimensions The dimensions of the plot
|
|
218
|
+
* @param options The options passed through the function that adds the tooltip content
|
|
219
|
+
* @return The width and text height of the tooltip content
|
|
220
|
+
*/
|
|
221
|
+
function addTooltipContent(
|
|
222
|
+
seriesName: string,
|
|
223
|
+
time: number,
|
|
224
|
+
series: Series<Datum>,
|
|
225
|
+
mouseCoords: [x: number, y: number],
|
|
226
|
+
chartId: number,
|
|
227
|
+
container: SVGSVGElement,
|
|
228
|
+
margin: Margin,
|
|
229
|
+
plotDimensions: Dimensions,
|
|
230
|
+
tooltipStyle: TooltipStyle,
|
|
231
|
+
options: TooltipOptions
|
|
232
|
+
): TooltipDimensions {
|
|
233
|
+
const {labels, formatters} = options
|
|
234
|
+
const [x, y] = mouseCoords
|
|
235
|
+
const [lower, upper] = boundingPoints(series, time, value => value.time, () => emptyDatum())
|
|
236
|
+
|
|
237
|
+
// display the neuron ID in the tooltip
|
|
238
|
+
const header = d3.select<SVGSVGElement | null, any>(container)
|
|
239
|
+
.append<SVGTextElement>("text")
|
|
240
|
+
.attr('id', `tn${time}-${seriesName}-${chartId}`)
|
|
241
|
+
.attr('class', 'tooltip')
|
|
242
|
+
.attr('fill', tooltipStyle.fontColor)
|
|
243
|
+
.attr('font-family', 'sans-serif')
|
|
244
|
+
.attr('font-size', tooltipStyle.fontSize)
|
|
245
|
+
.attr('font-weight', tooltipStyle.fontWeight)
|
|
246
|
+
.text(() => seriesName)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
// create the table that shows the points that come before and after the mouse time, and the
|
|
250
|
+
// changes in the time and value
|
|
251
|
+
const table = d3.select<SVGSVGElement | null, any>(container)
|
|
252
|
+
.append("g")
|
|
253
|
+
.attr('id', `t${time}-${seriesName}-header-${chartId}`)
|
|
254
|
+
.attr('class', 'tooltip')
|
|
255
|
+
.attr('fill', tooltipStyle.fontColor)
|
|
256
|
+
.attr('font-family', 'sans-serif')
|
|
257
|
+
.attr('font-size', tooltipStyle.fontSize + 2)
|
|
258
|
+
.attr('font-weight', tooltipStyle.fontWeight + 150)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
const headerRow = table.append('g').attr('font-weight', tooltipStyle.fontWeight + 550)
|
|
262
|
+
const hrLower = headerRow.append<SVGTextElement>("text").text(() => options.headers.before)
|
|
263
|
+
const hrUpper = headerRow.append<SVGTextElement>("text").text(() => options.headers.after)
|
|
264
|
+
const hrDelta = headerRow.append<SVGTextElement>("text").text(() => options.headers.delta)
|
|
265
|
+
|
|
266
|
+
const trHeader = table.append<SVGTextElement>("text").text(() => labels.x)
|
|
267
|
+
const trLower = table.append<SVGTextElement>("text").text(() => formatters.x.value(lower.time))
|
|
268
|
+
const trUpper = table.append<SVGTextElement>("text").text(() => formatters.x.value(upper.time))
|
|
269
|
+
const trDelta = table.append<SVGTextElement>("text").text(() => formatters.x.change(lower.time, upper.time))
|
|
270
|
+
|
|
271
|
+
const vrHeader = table.append<SVGTextElement>("text").text(() => labels.y)
|
|
272
|
+
const vrLower = table.append<SVGTextElement>("text").text(() => formatters.y.value(lower.value))
|
|
273
|
+
const vrUpper = table.append<SVGTextElement>("text").text(() => formatters.y.value(upper.value))
|
|
274
|
+
const vrDelta = table.append<SVGTextElement>("text").text(() => formatters.y.change(lower.value, upper.value))
|
|
275
|
+
|
|
276
|
+
const textWidthOf = (elem: TextSelection) => elem.node()?.getBBox()?.width || 0
|
|
277
|
+
const textHeightOf = (elem: TextSelection) => elem.node()?.getBBox()?.height || 0
|
|
278
|
+
const spacesWidthFor = (spaces: number) => spaces * textWidthOf(hrLower) / 5
|
|
279
|
+
|
|
280
|
+
// calculate the max width and height of the text
|
|
281
|
+
const tooltipWidth = Math.max(textWidthOf(header), spacesWidthFor(33))
|
|
282
|
+
const headerTextHeight = textHeightOf(header)
|
|
283
|
+
const headerRowHeight = textHeightOf(hrLower)
|
|
284
|
+
const timeRowHeight = textHeightOf(trHeader)
|
|
285
|
+
const valueRowHeight = textHeightOf(vrHeader)
|
|
286
|
+
const textHeight = headerTextHeight + headerRowHeight + timeRowHeight + valueRowHeight
|
|
287
|
+
|
|
288
|
+
// set the header text location
|
|
289
|
+
const xCoord = tooltipX(x, tooltipWidth, plotDimensions, tooltipStyle, margin)
|
|
290
|
+
const yCoord = tooltipY(y, textHeight, plotDimensions, tooltipStyle, margin)
|
|
291
|
+
const xTooltip = xCoord + tooltipStyle.paddingLeft
|
|
292
|
+
const yTooltip = yCoord + tooltipStyle.paddingTop
|
|
293
|
+
header
|
|
294
|
+
.attr('x', () => xTooltip)
|
|
295
|
+
.attr('y', () => yTooltip - (headerRowHeight + timeRowHeight + valueRowHeight) + textHeight)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
const hrRowY = yTooltip + headerTextHeight + headerRowHeight
|
|
299
|
+
const hrLowerX = spacesWidthFor(14)
|
|
300
|
+
const hrUpperX = spacesWidthFor(24)
|
|
301
|
+
const hrDeltaX = spacesWidthFor(32)
|
|
302
|
+
hrLower.attr('x', () => xTooltip + hrLowerX - textWidthOf(hrLower)).attr('y', () => hrRowY)
|
|
303
|
+
hrUpper.attr('x', () => xTooltip + hrUpperX - textWidthOf(hrUpper)).attr('y', () => hrRowY)
|
|
304
|
+
hrDelta.attr('x', () => xTooltip + hrDeltaX - textWidthOf(hrDelta)).attr('y', () => hrRowY)
|
|
305
|
+
|
|
306
|
+
const trRowY = hrRowY + timeRowHeight
|
|
307
|
+
trHeader.attr('x', () => xTooltip).attr('y', () => trRowY)
|
|
308
|
+
trLower.attr('x', () => xTooltip + hrLowerX - textWidthOf(trLower)).attr('y', () => trRowY)
|
|
309
|
+
trUpper.attr('x', () => xTooltip + hrUpperX - textWidthOf(trUpper)).attr('y', () => trRowY)
|
|
310
|
+
trDelta.attr('x', () => xTooltip + hrDeltaX - textWidthOf(trDelta)).attr('y', () => trRowY)
|
|
311
|
+
|
|
312
|
+
const vrRowY = trRowY + valueRowHeight
|
|
313
|
+
vrHeader.attr('x', () => xTooltip).attr('y', () => vrRowY)
|
|
314
|
+
vrLower.attr('x', () => xTooltip + hrLowerX - textWidthOf(vrLower)).attr('y', () => vrRowY)
|
|
315
|
+
vrUpper.attr('x', () => xTooltip + hrUpperX - textWidthOf(vrUpper)).attr('y', () => vrRowY)
|
|
316
|
+
vrDelta.attr('x', () => xTooltip + hrDeltaX - textWidthOf(vrDelta)).attr('y', () => vrRowY)
|
|
317
|
+
|
|
318
|
+
return {x: xCoord, y: yCoord, contentWidth: tooltipWidth, contentHeight: textHeight}
|
|
319
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {cloneElement, useEffect, useMemo, JSX} from "react";
|
|
2
|
+
import {defaultTooltipStyle, removeTooltip, TooltipStyle} from "./tooltipUtils";
|
|
3
|
+
import * as d3 from "d3";
|
|
4
|
+
import {useChart} from "../hooks/useChart";
|
|
5
|
+
import {usePlotDimensions} from "../hooks/usePlotDimensions";
|
|
6
|
+
import {BaseAxis, SeriesStyle} from "../axes/axes";
|
|
7
|
+
import {TooltipData} from "../hooks/useTooltip";
|
|
8
|
+
import {BaseAxisRange} from "../axes/BaseAxisRange";
|
|
9
|
+
|
|
10
|
+
export interface Props {
|
|
11
|
+
visible: boolean
|
|
12
|
+
style?: Partial<TooltipStyle>
|
|
13
|
+
children: JSX.Element
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Tooltip component triggered by mouse-over-series events. When mounted, the tooltip component
|
|
18
|
+
* registers a mouse-over handler with the {@link useChart} hook using the
|
|
19
|
+
* {@link UseChartValues.registerTooltipContentProvider} function. The handler renders the tooltip
|
|
20
|
+
* by adding it to the SVG plot container. However, to remain general, this {@link Tooltip} also
|
|
21
|
+
* uses the {@link useChart}'s {@link UseChartValues.tooltipContentProvider} to render the actual
|
|
22
|
+
* content of the tooltip. The content provider is given the series' name on which the mouse-over
|
|
23
|
+
* event occurred, the plot time associated with mouse-over event, and the {@link TimeSeries}
|
|
24
|
+
* associated with the mouse-over event. The plot (for example {@link ScatterPlot}) is responsible
|
|
25
|
+
* for rendering the tooltip, and it registers this tooltip-content provider with the {@link useChart}
|
|
26
|
+
* hook.
|
|
27
|
+
*
|
|
28
|
+
* The {@link useChart} hook allows the plots and this tooltip to register methods needed its siblings.
|
|
29
|
+
*
|
|
30
|
+
* ## on mount
|
|
31
|
+
* 1. The plot (for example {@link ScatterPlot}) adds a d3 mouse-enter and mouse-leave event handlers
|
|
32
|
+
* to each series. The mouse-enter event handler performs a few tasks and then calls the
|
|
33
|
+
* mouse-over event handler registered by the {@link Tooltip} via the {@link useChart} hook.
|
|
34
|
+
* 2. The plot (for example {@link ScatterPlot}) registers the tooltip-content provider with the {@link useChart}
|
|
35
|
+
* hook using the {@link UseChartValues.registerTooltipContentProvider} function.
|
|
36
|
+
* 3. {@link Tooltip} registers the handler for mouse-over events. The handler accepts the series
|
|
37
|
+
* name, plot time, and time-series associated with the mouse-over event, and returns the tooltip
|
|
38
|
+
* dimensions. In order to create/render the tooltip, it uses the {@link UseChartValues.tooltipContentProvider}
|
|
39
|
+
* function that was registered via the {@link useChart} hook.
|
|
40
|
+
*
|
|
41
|
+
* ## on mouse-over event
|
|
42
|
+
* When the plot's (for example {@link ScatterPlot}) d3 mouse-enter event handler fires, the plot calls this
|
|
43
|
+
* {@link Tooltip}'s mouse-over handler, which creates the tooltip, and calls the {@link UseChartValues.tooltipContentProvider}
|
|
44
|
+
* registered by the plot to render the tooltip content, and get the tooltip size.
|
|
45
|
+
*
|
|
46
|
+
* @param props The properties of the tooltip (i.e. visibility and style)
|
|
47
|
+
* @return null
|
|
48
|
+
* @constructor
|
|
49
|
+
*/
|
|
50
|
+
export function Tooltip<D, S extends SeriesStyle, TM, AR extends BaseAxisRange, A extends BaseAxis>(props: Props): JSX.Element {
|
|
51
|
+
const {
|
|
52
|
+
chartId,
|
|
53
|
+
container,
|
|
54
|
+
tooltip,
|
|
55
|
+
mouse
|
|
56
|
+
} = useChart<D, S, TM, AR, A>()
|
|
57
|
+
|
|
58
|
+
const {
|
|
59
|
+
tooltipContentProvider,
|
|
60
|
+
setVisibilityState,
|
|
61
|
+
} = tooltip
|
|
62
|
+
|
|
63
|
+
const {margin, plotDimensions} = usePlotDimensions()
|
|
64
|
+
|
|
65
|
+
const {
|
|
66
|
+
visible,
|
|
67
|
+
style,
|
|
68
|
+
children
|
|
69
|
+
} = props
|
|
70
|
+
|
|
71
|
+
const {
|
|
72
|
+
registerMouseOverHandler,
|
|
73
|
+
unregisterMouseOverHandler,
|
|
74
|
+
registerMouseLeaveHandler,
|
|
75
|
+
unregisterMouseLeaveHandler
|
|
76
|
+
} = mouse
|
|
77
|
+
|
|
78
|
+
const tooltipStyle = useMemo(() => ({...defaultTooltipStyle, ...style}), [style])
|
|
79
|
+
|
|
80
|
+
// let the tooltip context know whether the tooltip is visible
|
|
81
|
+
setVisibilityState(visible)
|
|
82
|
+
|
|
83
|
+
useEffect(
|
|
84
|
+
() => {
|
|
85
|
+
const handlerId = `tooltip-${chartId}`
|
|
86
|
+
if (visible && container) {
|
|
87
|
+
const contentProvider = tooltipContentProvider()
|
|
88
|
+
if (contentProvider) {
|
|
89
|
+
// register this tooltip's mouse-over event handler with the useCharts hook
|
|
90
|
+
// so that the plots can call it when mouse-enter events are triggered (for
|
|
91
|
+
// example, a mouse-over a time-series in the plot).
|
|
92
|
+
registerMouseOverHandler(
|
|
93
|
+
handlerId,
|
|
94
|
+
(
|
|
95
|
+
(
|
|
96
|
+
seriesName: string,
|
|
97
|
+
time: number,
|
|
98
|
+
tooltipData: TooltipData<D, TM>,
|
|
99
|
+
mouseCoords: [x: number, y: number],
|
|
100
|
+
providerId?: string
|
|
101
|
+
) => {
|
|
102
|
+
// create the rounded rectangle for the tooltip's background
|
|
103
|
+
const rect = d3.select<SVGSVGElement | null, any>(container)
|
|
104
|
+
.append<SVGRectElement>('rect')
|
|
105
|
+
.attr('id', `r${time}-${seriesName}-${chartId}`)
|
|
106
|
+
.attr('class', 'tooltip')
|
|
107
|
+
.attr('rx', tooltipStyle.borderRadius)
|
|
108
|
+
.attr('fill', tooltipStyle.backgroundColor)
|
|
109
|
+
.attr('fill-opacity', tooltipStyle.backgroundOpacity)
|
|
110
|
+
.attr('stroke', tooltipStyle.borderColor)
|
|
111
|
+
.attr('stroke-width', tooltipStyle.borderWidth)
|
|
112
|
+
|
|
113
|
+
// call the callback to add the content
|
|
114
|
+
const {
|
|
115
|
+
x,
|
|
116
|
+
y,
|
|
117
|
+
contentWidth,
|
|
118
|
+
contentHeight
|
|
119
|
+
} = contentProvider(seriesName, time, tooltipData, mouseCoords, providerId)
|
|
120
|
+
|
|
121
|
+
// set the position, width, and height of the tooltip rect based on the text height and width and the padding
|
|
122
|
+
rect.attr('x', () => x)
|
|
123
|
+
.attr('y', () => y)
|
|
124
|
+
.attr('width', contentWidth + tooltipStyle.paddingLeft + tooltipStyle.paddingRight)
|
|
125
|
+
.attr('height', contentHeight + tooltipStyle.paddingTop + tooltipStyle.paddingBottom)
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
registerMouseLeaveHandler(handlerId, () => removeTooltip())
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return () => {
|
|
135
|
+
unregisterMouseOverHandler(handlerId)
|
|
136
|
+
unregisterMouseLeaveHandler(handlerId)
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
[
|
|
140
|
+
chartId, container, margin, plotDimensions,
|
|
141
|
+
registerMouseOverHandler, tooltipContentProvider, tooltipStyle,
|
|
142
|
+
unregisterMouseOverHandler, visible, registerMouseLeaveHandler,
|
|
143
|
+
unregisterMouseLeaveHandler
|
|
144
|
+
]
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return <>{cloneElement(children, props)}</>
|
|
148
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import {Series} from "../plots/plot";
|
|
2
|
+
import * as d3 from "d3";
|
|
3
|
+
import {Datum} from "../series/timeSeries";
|
|
4
|
+
import {Dimensions, Margin} from "../styling/margins";
|
|
5
|
+
import {OrdinalStringAxis} from "../axes/axes";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Properties for rendering the tooltip. This is the style for the container
|
|
9
|
+
* of the content.
|
|
10
|
+
*/
|
|
11
|
+
export interface TooltipStyle {
|
|
12
|
+
/**
|
|
13
|
+
* Visibility of the tooltip when the mouse hovers over a data series or point.
|
|
14
|
+
*/
|
|
15
|
+
visible: boolean
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The size of the font displayed in the tooltip
|
|
19
|
+
*/
|
|
20
|
+
fontSize: number
|
|
21
|
+
/**
|
|
22
|
+
* The color of the text displayed in the tooltip
|
|
23
|
+
*/
|
|
24
|
+
fontColor: string
|
|
25
|
+
/**
|
|
26
|
+
* The font family for the text displayed in the tooltip
|
|
27
|
+
*/
|
|
28
|
+
fontFamily: string
|
|
29
|
+
/**
|
|
30
|
+
* The font weight for the text displayed in the tooltip
|
|
31
|
+
*/
|
|
32
|
+
fontWeight: number
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The background color
|
|
36
|
+
*/
|
|
37
|
+
backgroundColor: string;
|
|
38
|
+
backgroundOpacity: number;
|
|
39
|
+
|
|
40
|
+
borderColor: string;
|
|
41
|
+
borderOpacity: number;
|
|
42
|
+
borderWidth: number;
|
|
43
|
+
borderRadius: number;
|
|
44
|
+
|
|
45
|
+
paddingLeft: number;
|
|
46
|
+
paddingRight: number;
|
|
47
|
+
paddingTop: number;
|
|
48
|
+
paddingBottom: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const defaultTooltipStyle: TooltipStyle = {
|
|
52
|
+
visible: false,
|
|
53
|
+
|
|
54
|
+
fontSize: 12,
|
|
55
|
+
fontColor: 'rgba(10,31,58,0.99)',
|
|
56
|
+
// fontColor: '#d2933f',
|
|
57
|
+
// fontColor: '#202020',
|
|
58
|
+
fontFamily: 'sans-serif',
|
|
59
|
+
fontWeight: 250,
|
|
60
|
+
|
|
61
|
+
backgroundColor: '#202020',
|
|
62
|
+
backgroundOpacity: 0.8,
|
|
63
|
+
|
|
64
|
+
borderColor: '#d2933f',
|
|
65
|
+
borderOpacity: 1,
|
|
66
|
+
borderWidth: 1,
|
|
67
|
+
borderRadius: 5,
|
|
68
|
+
|
|
69
|
+
paddingLeft: 10,
|
|
70
|
+
paddingRight: 10,
|
|
71
|
+
paddingTop: 5,
|
|
72
|
+
paddingBottom: 10,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export interface TooltipDimensions {
|
|
76
|
+
x: number,
|
|
77
|
+
y: number,
|
|
78
|
+
contentWidth: number,
|
|
79
|
+
contentHeight: number
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Removes the tooltip when the mouse has moved away from the spike
|
|
84
|
+
*/
|
|
85
|
+
export function removeTooltip() {
|
|
86
|
+
d3.selectAll<SVGPathElement, Datum>('.tooltip').remove()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Calculates the x-coordinate of the upper left-hand side of the tooltip rectangle (obviously without
|
|
91
|
+
* "rounded corners"). Adjusts the x-coordinate so that tooltip is visible on the edges of the plot.
|
|
92
|
+
* @param x The current x-coordinate of the mouse
|
|
93
|
+
* @param textWidth The width of the tooltip text
|
|
94
|
+
* @param plotDimensions The dimensions of the plot
|
|
95
|
+
* @param tooltipStyle The tooltip style information
|
|
96
|
+
* @param margin The plot margin
|
|
97
|
+
* @return The x-coordinate of the lower left-hand side of the tooltip rectangle
|
|
98
|
+
*/
|
|
99
|
+
export function tooltipX(x: number, textWidth: number, plotDimensions: Dimensions, tooltipStyle: TooltipStyle, margin: Margin): number {
|
|
100
|
+
if (x > plotDimensions.width + margin.left - (textWidth + tooltipStyle.paddingLeft + tooltipStyle.paddingRight)) {
|
|
101
|
+
return x - textWidth - margin.right + tooltipStyle.paddingRight + tooltipStyle.paddingLeft
|
|
102
|
+
}
|
|
103
|
+
return x + tooltipStyle.paddingLeft
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Calculates the y-coordinate of the upper-left-hand corner of the tooltip rectangle. Adjusts the y-coordinate
|
|
108
|
+
* so that the tooltip is visible on the upper edge of the plot
|
|
109
|
+
* @param y The y-coordinate of the mouse
|
|
110
|
+
* @param textHeight The height of the header and neuron ID text
|
|
111
|
+
* @param plotDimensions The dimensions of the plot
|
|
112
|
+
* @param tooltipStyle The tooltip style information
|
|
113
|
+
* @param margin The plot margin
|
|
114
|
+
* @return The y-coordinate of the lower-left-hand corner of the tooltip rectangle
|
|
115
|
+
*/
|
|
116
|
+
export function tooltipY(y: number, textHeight: number, plotDimensions: Dimensions, tooltipStyle: TooltipStyle, margin: Margin): number {
|
|
117
|
+
if (y > plotDimensions.height + margin.top - (textHeight + tooltipStyle.paddingTop + tooltipStyle.paddingBottom)) {
|
|
118
|
+
return plotDimensions.height + margin.top - (textHeight + tooltipStyle.paddingTop + tooltipStyle.paddingBottom)
|
|
119
|
+
}
|
|
120
|
+
return y + tooltipStyle.paddingTop
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Calculates the y-coordinate for the upper-left-hand the tooltip rectangle. Adjusts the y-coordinate so
|
|
125
|
+
* that the tooltip is visible on the upper and lower edges of the plot
|
|
126
|
+
* @param seriesName The name of the series
|
|
127
|
+
* @param textHeight The height of the header and neuron ID text
|
|
128
|
+
* @param axis The category axis for determining the y-value of the tooltip
|
|
129
|
+
* @param tooltipStyle The tooltip style
|
|
130
|
+
* @param margin The plot margin
|
|
131
|
+
* @param categoryHeight The height (in pixels) of the category
|
|
132
|
+
* @param plotDimensions The dimensions of the plot
|
|
133
|
+
* @return The y-coordinate of the lower-left-hand corner of the tooltip rectangle
|
|
134
|
+
*/
|
|
135
|
+
export function categoryTooltipY(
|
|
136
|
+
seriesName: string,
|
|
137
|
+
textHeight: number,
|
|
138
|
+
axis: OrdinalStringAxis,
|
|
139
|
+
tooltipStyle: TooltipStyle,
|
|
140
|
+
margin: Margin,
|
|
141
|
+
categoryHeight: number,
|
|
142
|
+
plotDimensions: Dimensions
|
|
143
|
+
): number {
|
|
144
|
+
const y = axis.scale(seriesName) || 0
|
|
145
|
+
const halfHeight = (tooltipStyle.paddingBottom + textHeight + tooltipStyle.paddingTop) / 2
|
|
146
|
+
if (y < halfHeight) {
|
|
147
|
+
return margin.top
|
|
148
|
+
}
|
|
149
|
+
if (y > plotDimensions.height + margin.top - halfHeight) {
|
|
150
|
+
return plotDimensions.height + margin.top - textHeight
|
|
151
|
+
}
|
|
152
|
+
return y + margin.top - halfHeight + categoryHeight / 2
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Returns the index of the data point whose time is the upper boundary on the specified
|
|
157
|
+
* time. If the specified time is larger than any time in the specified data, the returns
|
|
158
|
+
* the length of the data array. If the specified time is smaller than all the values in
|
|
159
|
+
* the specified array, then returns -1.
|
|
160
|
+
* @param data The array of points from which to select the
|
|
161
|
+
* boundary.
|
|
162
|
+
* @param value The time for which to find the bounding points
|
|
163
|
+
* @param xFrom A function that extracts the x-value from the datum
|
|
164
|
+
* @template D The type of the datum in the data-series
|
|
165
|
+
* @return The index of the upper boundary.
|
|
166
|
+
*/
|
|
167
|
+
function boundingPointsIndex<D>(data: Series<D>, value: number, xFrom: (value: D) => number): number {
|
|
168
|
+
const length = data.length
|
|
169
|
+
if (value > xFrom(data[length - 1])) {
|
|
170
|
+
return length
|
|
171
|
+
}
|
|
172
|
+
if (value < xFrom(data[0])) {
|
|
173
|
+
return 0
|
|
174
|
+
}
|
|
175
|
+
return data.findIndex((_, index, array) => {
|
|
176
|
+
const lowerIndex = Math.max(0, index - 1)
|
|
177
|
+
return xFrom(array[lowerIndex]) <= value && value <= xFrom(array[index])
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Returns the (time, value) point that comes just before the mouse and just after the mouse
|
|
183
|
+
* @param data The time-series data
|
|
184
|
+
* @param value The time represented by the mouse's x-coordinate
|
|
185
|
+
* @param xFrom A function that extracts the x-value from the datum
|
|
186
|
+
* @param emptyDatum A function that returns an empty datum
|
|
187
|
+
* @template D The type of the datum in the data-series
|
|
188
|
+
* @return {[[number, number], [number, number]]} the (time, value) point that comes just before
|
|
189
|
+
* the mouse and just after the mouse. If the mouse is after the last point, then the "after" point
|
|
190
|
+
* is `[NaN, NaN]`. If the mouse is before the first point, then the "before" point is `[NaN, NaN]`.
|
|
191
|
+
*/
|
|
192
|
+
export function boundingPoints<D>(data: Series<D>, value: number, xFrom: (value: D) => number, emptyDatum: () => D): [D, D] {
|
|
193
|
+
const upperIndex = boundingPointsIndex<D>(data, value, xFrom)
|
|
194
|
+
if (upperIndex <= 0) {
|
|
195
|
+
return [emptyDatum(), data[0]]
|
|
196
|
+
}
|
|
197
|
+
if (upperIndex >= data.length) {
|
|
198
|
+
return [data[data.length - 1], emptyDatum()]
|
|
199
|
+
}
|
|
200
|
+
return [data[upperIndex - 1], data[upperIndex]]
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function findPointAndNeighbors<D>(
|
|
204
|
+
data: Series<D>,
|
|
205
|
+
value: number,
|
|
206
|
+
tolerance: number,
|
|
207
|
+
xFrom: (value: D) => number,
|
|
208
|
+
emptyDatum: () => D
|
|
209
|
+
): [D, D, D, number] {
|
|
210
|
+
// find the index of the point
|
|
211
|
+
const index = data.findIndex((_, index, array) => Math.abs(xFrom(array[index]) - value) <= tolerance)
|
|
212
|
+
const point = index > -1 ? data[index] : emptyDatum()
|
|
213
|
+
const lower = (index > 0) ? data[index-1] : emptyDatum()
|
|
214
|
+
const upper = (index < data.length - 1) ? data[index+1] : emptyDatum()
|
|
215
|
+
return [lower, point, upper, index]
|
|
216
|
+
}
|