stream-charts 1.2.0 → 2.0.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/LICENSE +21 -0
- package/README.md +809 -1022
- package/changes.md +40 -0
- package/dist/app/charts/Chart.d.ts +35 -139
- package/dist/app/charts/Chart.d.ts.map +1 -0
- package/dist/app/charts/Chart.js +74 -65
- package/dist/app/charts/Chart.js.map +1 -1
- package/dist/app/charts/axes/AxesState.d.ts +26 -18
- package/dist/app/charts/axes/AxesState.d.ts.map +1 -0
- package/dist/app/charts/axes/AxesState.js +26 -24
- package/dist/app/charts/axes/AxesState.js.map +1 -1
- package/dist/app/charts/axes/AxisInterval.d.ts +44 -26
- package/dist/app/charts/axes/AxisInterval.d.ts.map +1 -0
- package/dist/app/charts/axes/AxisInterval.js +48 -29
- package/dist/app/charts/axes/AxisInterval.js.map +1 -1
- package/dist/app/charts/axes/BaseAxisRange.d.ts +46 -22
- package/dist/app/charts/axes/BaseAxisRange.d.ts.map +1 -0
- package/dist/app/charts/axes/BaseAxisRange.js +17 -12
- package/dist/app/charts/axes/BaseAxisRange.js.map +1 -1
- package/dist/app/charts/axes/ContinuousAxis.d.ts +47 -6
- package/dist/app/charts/axes/ContinuousAxis.d.ts.map +1 -0
- package/dist/app/charts/axes/ContinuousAxis.js +160 -27
- package/dist/app/charts/axes/ContinuousAxis.js.map +1 -1
- package/dist/app/charts/axes/ContinuousAxisRange.d.ts +29 -10
- package/dist/app/charts/axes/ContinuousAxisRange.d.ts.map +1 -0
- package/dist/app/charts/axes/ContinuousAxisRange.js +29 -11
- package/dist/app/charts/axes/ContinuousAxisRange.js.map +1 -1
- package/dist/app/charts/axes/EmptyAxis.d.ts +5 -2
- package/dist/app/charts/axes/EmptyAxis.d.ts.map +1 -0
- package/dist/app/charts/axes/EmptyAxis.js +30 -9
- package/dist/app/charts/axes/EmptyAxis.js.map +1 -1
- package/dist/app/charts/axes/OrdinalAxis.d.ts +5 -3
- package/dist/app/charts/axes/OrdinalAxis.d.ts.map +1 -0
- package/dist/app/charts/axes/OrdinalAxis.js +39 -16
- package/dist/app/charts/axes/OrdinalAxis.js.map +1 -1
- package/dist/app/charts/axes/OrdinalAxisRange.d.ts +76 -13
- package/dist/app/charts/axes/OrdinalAxisRange.d.ts.map +1 -0
- package/dist/app/charts/axes/OrdinalAxisRange.js +95 -16
- package/dist/app/charts/axes/OrdinalAxisRange.js.map +1 -1
- package/dist/app/charts/axes/autoScale.d.ts +111 -0
- package/dist/app/charts/axes/autoScale.d.ts.map +1 -0
- package/dist/app/charts/axes/autoScale.js +188 -0
- package/dist/app/charts/axes/autoScale.js.map +1 -0
- package/dist/app/charts/axes/axes.d.ts +273 -150
- package/dist/app/charts/axes/axes.d.ts.map +1 -0
- package/dist/app/charts/axes/axes.js +808 -583
- package/dist/app/charts/axes/axes.js.map +1 -1
- package/dist/app/charts/d3types.d.ts +40 -11
- package/dist/app/charts/d3types.d.ts.map +1 -0
- package/dist/app/charts/d3types.js +3 -0
- package/dist/app/charts/d3types.js.map +1 -1
- package/dist/app/charts/datasources/StreamingDataSource.d.ts +149 -0
- package/dist/app/charts/datasources/StreamingDataSource.d.ts.map +1 -0
- package/dist/app/charts/datasources/StreamingDataSource.js +110 -0
- package/dist/app/charts/datasources/StreamingDataSource.js.map +1 -0
- package/dist/app/charts/datasources/iteratesDataSource.d.ts +73 -0
- package/dist/app/charts/datasources/iteratesDataSource.d.ts.map +1 -0
- package/dist/app/charts/datasources/iteratesDataSource.js +87 -0
- package/dist/app/charts/datasources/iteratesDataSource.js.map +1 -0
- package/dist/app/charts/datasources/ordinalDataSource.d.ts +75 -0
- package/dist/app/charts/datasources/ordinalDataSource.d.ts.map +1 -0
- package/dist/app/charts/datasources/ordinalDataSource.js +135 -0
- package/dist/app/charts/datasources/ordinalDataSource.js.map +1 -0
- package/dist/app/charts/datasources/outlierDataSource.d.ts +53 -0
- package/dist/app/charts/datasources/outlierDataSource.d.ts.map +1 -0
- package/dist/app/charts/datasources/outlierDataSource.js +57 -0
- package/dist/app/charts/datasources/outlierDataSource.js.map +1 -0
- package/dist/app/charts/datasources/timeSeriesDataSource.d.ts +77 -0
- package/dist/app/charts/datasources/timeSeriesDataSource.d.ts.map +1 -0
- package/dist/app/charts/datasources/timeSeriesDataSource.js +85 -0
- package/dist/app/charts/datasources/timeSeriesDataSource.js.map +1 -0
- package/dist/app/charts/filters/regexFilter.d.ts +31 -4
- package/dist/app/charts/filters/regexFilter.d.ts.map +1 -0
- package/dist/app/charts/filters/regexFilter.js +38 -4
- package/dist/app/charts/filters/regexFilter.js.map +1 -1
- package/dist/app/charts/hooks/AxesProvider.d.ts +21 -0
- package/dist/app/charts/hooks/AxesProvider.d.ts.map +1 -0
- package/dist/app/charts/hooks/AxesProvider.js +213 -0
- package/dist/app/charts/hooks/AxesProvider.js.map +1 -0
- package/dist/app/charts/hooks/CanvasSurfaceProvider.d.ts +25 -0
- package/dist/app/charts/hooks/CanvasSurfaceProvider.d.ts.map +1 -0
- package/dist/app/charts/hooks/CanvasSurfaceProvider.js +76 -0
- package/dist/app/charts/hooks/CanvasSurfaceProvider.js.map +1 -0
- package/dist/app/charts/hooks/ChartProvider.d.ts +40 -0
- package/dist/app/charts/hooks/ChartProvider.d.ts.map +1 -0
- package/dist/app/charts/hooks/ChartProvider.js +33 -0
- package/dist/app/charts/hooks/ChartProvider.js.map +1 -0
- package/dist/app/charts/hooks/InteractionProvider.d.ts +23 -0
- package/dist/app/charts/hooks/InteractionProvider.d.ts.map +1 -0
- package/dist/app/charts/hooks/InteractionProvider.js +60 -0
- package/dist/app/charts/hooks/InteractionProvider.js.map +1 -0
- package/dist/app/charts/hooks/PlotDimensionsProvider.d.ts +16 -0
- package/dist/app/charts/hooks/PlotDimensionsProvider.d.ts.map +1 -0
- package/dist/app/charts/hooks/PlotDimensionsProvider.js +76 -0
- package/dist/app/charts/hooks/PlotDimensionsProvider.js.map +1 -0
- package/dist/app/charts/hooks/defaultAxesValues.d.ts +11 -0
- package/dist/app/charts/hooks/defaultAxesValues.d.ts.map +1 -0
- package/dist/app/charts/hooks/defaultAxesValues.js +36 -0
- package/dist/app/charts/hooks/defaultAxesValues.js.map +1 -0
- package/dist/app/charts/hooks/defaultMouseValues.d.ts +9 -0
- package/dist/app/charts/hooks/defaultMouseValues.d.ts.map +1 -0
- package/dist/app/charts/hooks/defaultMouseValues.js +23 -0
- package/dist/app/charts/hooks/defaultMouseValues.js.map +1 -0
- package/dist/app/charts/hooks/defaultPlotDimensions.d.ts +13 -0
- package/dist/app/charts/hooks/defaultPlotDimensions.d.ts.map +1 -0
- package/dist/app/charts/hooks/defaultPlotDimensions.js +23 -0
- package/dist/app/charts/hooks/defaultPlotDimensions.js.map +1 -0
- package/dist/app/charts/hooks/defaultTooltipValues.d.ts +9 -0
- package/dist/app/charts/hooks/defaultTooltipValues.d.ts.map +1 -0
- package/dist/app/charts/hooks/defaultTooltipValues.js +21 -0
- package/dist/app/charts/hooks/defaultTooltipValues.js.map +1 -0
- package/dist/app/charts/hooks/defaultUseChartValues.d.ts +14 -0
- package/dist/app/charts/hooks/defaultUseChartValues.d.ts.map +1 -0
- package/dist/app/charts/hooks/defaultUseChartValues.js +30 -0
- package/dist/app/charts/hooks/defaultUseChartValues.js.map +1 -0
- package/dist/app/charts/hooks/useAxes.d.ts +49 -36
- package/dist/app/charts/hooks/useAxes.d.ts.map +1 -0
- package/dist/app/charts/hooks/useAxes.js +16 -176
- package/dist/app/charts/hooks/useAxes.js.map +1 -1
- package/dist/app/charts/hooks/useCanvasSurface.d.ts +25 -0
- package/dist/app/charts/hooks/useCanvasSurface.d.ts.map +1 -0
- package/dist/app/charts/hooks/useCanvasSurface.js +18 -0
- package/dist/app/charts/hooks/useCanvasSurface.js.map +1 -0
- package/dist/app/charts/hooks/useChart.d.ts +30 -44
- package/dist/app/charts/hooks/useChart.d.ts.map +1 -0
- package/dist/app/charts/hooks/useChart.js +26 -48
- package/dist/app/charts/hooks/useChart.js.map +1 -1
- package/dist/app/charts/hooks/useDataSource.d.ts +68 -0
- package/dist/app/charts/hooks/useDataSource.d.ts.map +1 -0
- package/dist/app/charts/hooks/useDataSource.js +63 -0
- package/dist/app/charts/hooks/useDataSource.js.map +1 -0
- package/dist/app/charts/hooks/useHoveredSeries.d.ts +25 -0
- package/dist/app/charts/hooks/useHoveredSeries.d.ts.map +1 -0
- package/dist/app/charts/hooks/useHoveredSeries.js +15 -0
- package/dist/app/charts/hooks/useHoveredSeries.js.map +1 -0
- package/dist/app/charts/hooks/useMouse.d.ts +46 -22
- package/dist/app/charts/hooks/useMouse.d.ts.map +1 -0
- package/dist/app/charts/hooks/useMouse.js +12 -56
- package/dist/app/charts/hooks/useMouse.js.map +1 -1
- package/dist/app/charts/hooks/usePlotDimensions.d.ts +25 -13
- package/dist/app/charts/hooks/usePlotDimensions.d.ts.map +1 -0
- package/dist/app/charts/hooks/usePlotDimensions.js +6 -62
- package/dist/app/charts/hooks/usePlotDimensions.js.map +1 -1
- package/dist/app/charts/hooks/useTooltip.d.ts +24 -26
- package/dist/app/charts/hooks/useTooltip.d.ts.map +1 -0
- package/dist/app/charts/hooks/useTooltip.js +12 -42
- package/dist/app/charts/hooks/useTooltip.js.map +1 -1
- package/dist/app/charts/index.d.ts +66 -0
- package/dist/app/charts/index.d.ts.map +1 -0
- package/dist/app/charts/index.js +87 -0
- package/dist/app/charts/index.js.map +1 -0
- package/dist/app/charts/legends/Legend.d.ts +33 -12
- package/dist/app/charts/legends/Legend.d.ts.map +1 -0
- package/dist/app/charts/legends/Legend.js +138 -379
- package/dist/app/charts/legends/Legend.js.map +1 -1
- package/dist/app/charts/legends/constants.d.ts +19 -0
- package/dist/app/charts/legends/constants.d.ts.map +1 -0
- package/dist/app/charts/legends/constants.js +34 -0
- package/dist/app/charts/legends/constants.js.map +1 -0
- package/dist/app/charts/observables/ChartData.d.ts +1 -0
- package/dist/app/charts/observables/ChartData.d.ts.map +1 -0
- package/dist/app/charts/observables/iterates.d.ts +13 -7
- package/dist/app/charts/observables/iterates.d.ts.map +1 -0
- package/dist/app/charts/observables/iterates.js +42 -9
- package/dist/app/charts/observables/iterates.js.map +1 -1
- package/dist/app/charts/observables/ordinals.d.ts +70 -12
- package/dist/app/charts/observables/ordinals.d.ts.map +1 -0
- package/dist/app/charts/observables/ordinals.js +127 -48
- package/dist/app/charts/observables/ordinals.js.map +1 -1
- package/dist/app/charts/observables/outliers.d.ts +18 -0
- package/dist/app/charts/observables/outliers.d.ts.map +1 -0
- package/dist/app/charts/observables/outliers.js +2 -0
- package/dist/app/charts/observables/outliers.js.map +1 -0
- package/dist/app/charts/plots/BarPlot.d.ts +87 -13
- package/dist/app/charts/plots/BarPlot.d.ts.map +1 -0
- package/dist/app/charts/plots/BarPlot.js +603 -356
- package/dist/app/charts/plots/BarPlot.js.map +1 -1
- package/dist/app/charts/plots/OutlierPlot.d.ts +123 -0
- package/dist/app/charts/plots/OutlierPlot.d.ts.map +1 -0
- package/dist/app/charts/plots/OutlierPlot.js +430 -0
- package/dist/app/charts/plots/OutlierPlot.js.map +1 -0
- package/dist/app/charts/plots/PoincarePlot.d.ts +92 -14
- package/dist/app/charts/plots/PoincarePlot.d.ts.map +1 -0
- package/dist/app/charts/plots/PoincarePlot.js +523 -364
- package/dist/app/charts/plots/PoincarePlot.js.map +1 -1
- package/dist/app/charts/plots/RasterPlot.d.ts +31 -10
- package/dist/app/charts/plots/RasterPlot.d.ts.map +1 -0
- package/dist/app/charts/plots/RasterPlot.js +198 -306
- package/dist/app/charts/plots/RasterPlot.js.map +1 -1
- package/dist/app/charts/plots/ScatterPlot.d.ts +77 -21
- package/dist/app/charts/plots/ScatterPlot.d.ts.map +1 -0
- package/dist/app/charts/plots/ScatterPlot.js +359 -302
- package/dist/app/charts/plots/ScatterPlot.js.map +1 -1
- package/dist/app/charts/plots/autoScaleYAxes.d.ts +43 -0
- package/dist/app/charts/plots/autoScaleYAxes.d.ts.map +1 -0
- package/dist/app/charts/plots/autoScaleYAxes.js +71 -0
- package/dist/app/charts/plots/autoScaleYAxes.js.map +1 -0
- package/dist/app/charts/plots/colorizedLine.d.ts +61 -0
- package/dist/app/charts/plots/colorizedLine.d.ts.map +1 -0
- package/dist/app/charts/plots/colorizedLine.js +157 -0
- package/dist/app/charts/plots/colorizedLine.js.map +1 -0
- package/dist/app/charts/plots/constants.d.ts +7 -0
- package/dist/app/charts/plots/constants.d.ts.map +1 -0
- package/dist/app/charts/plots/constants.js +11 -0
- package/dist/app/charts/plots/constants.js.map +1 -0
- package/dist/app/charts/plots/hitTesting.d.ts +88 -0
- package/dist/app/charts/plots/hitTesting.d.ts.map +1 -0
- package/dist/app/charts/plots/hitTesting.js +136 -0
- package/dist/app/charts/plots/hitTesting.js.map +1 -0
- package/dist/app/charts/plots/plot.d.ts +52 -16
- package/dist/app/charts/plots/plot.d.ts.map +1 -0
- package/dist/app/charts/plots/plot.js +108 -35
- package/dist/app/charts/plots/plot.js.map +1 -1
- package/dist/app/charts/plots/useCanvasZoomPan.d.ts +119 -0
- package/dist/app/charts/plots/useCanvasZoomPan.d.ts.map +1 -0
- package/dist/app/charts/plots/useCanvasZoomPan.js +113 -0
- package/dist/app/charts/plots/useCanvasZoomPan.js.map +1 -0
- package/dist/app/charts/plots/useStreamingTimeAxes.d.ts +103 -0
- package/dist/app/charts/plots/useStreamingTimeAxes.d.ts.map +1 -0
- package/dist/app/charts/plots/useStreamingTimeAxes.js +257 -0
- package/dist/app/charts/plots/useStreamingTimeAxes.js.map +1 -0
- package/dist/app/charts/series/baseSeries.d.ts +60 -8
- package/dist/app/charts/series/baseSeries.d.ts.map +1 -0
- package/dist/app/charts/series/baseSeries.js +53 -8
- package/dist/app/charts/series/baseSeries.js.map +1 -1
- package/dist/app/charts/series/iterateSeries.d.ts +9 -8
- package/dist/app/charts/series/iterateSeries.d.ts.map +1 -0
- package/dist/app/charts/series/iterateSeries.js +7 -7
- package/dist/app/charts/series/iterateSeries.js.map +1 -1
- package/dist/app/charts/series/ordinalSeries.d.ts +27 -13
- package/dist/app/charts/series/ordinalSeries.d.ts.map +1 -0
- package/dist/app/charts/series/ordinalSeries.js +29 -15
- package/dist/app/charts/series/ordinalSeries.js.map +1 -1
- package/dist/app/charts/series/outlierSeries.d.ts +142 -0
- package/dist/app/charts/series/outlierSeries.d.ts.map +1 -0
- package/dist/app/charts/series/outlierSeries.js +82 -0
- package/dist/app/charts/series/outlierSeries.js.map +1 -0
- package/dist/app/charts/series/timeSeries.d.ts +13 -9
- package/dist/app/charts/series/timeSeries.d.ts.map +1 -0
- package/dist/app/charts/series/timeSeries.js +6 -3
- package/dist/app/charts/series/timeSeries.js.map +1 -1
- package/dist/app/charts/series/timeSeriesChartData.d.ts +18 -10
- package/dist/app/charts/series/timeSeriesChartData.d.ts.map +1 -0
- package/dist/app/charts/series/timeSeriesChartData.js +28 -14
- package/dist/app/charts/series/timeSeriesChartData.js.map +1 -1
- package/dist/app/charts/styling/barPlotStyle.d.ts +46 -2
- package/dist/app/charts/styling/barPlotStyle.d.ts.map +1 -0
- package/dist/app/charts/styling/barPlotStyle.js +37 -3
- package/dist/app/charts/styling/barPlotStyle.js.map +1 -1
- package/dist/app/charts/styling/canvasStyle.d.ts +53 -0
- package/dist/app/charts/styling/canvasStyle.d.ts.map +1 -0
- package/dist/app/charts/styling/canvasStyle.js +71 -0
- package/dist/app/charts/styling/canvasStyle.js.map +1 -0
- package/dist/app/charts/styling/margins.d.ts +21 -2
- package/dist/app/charts/styling/margins.d.ts.map +1 -0
- package/dist/app/charts/styling/margins.js +25 -4
- package/dist/app/charts/styling/margins.js.map +1 -1
- package/dist/app/charts/styling/svgStyle.d.ts +10 -56
- package/dist/app/charts/styling/svgStyle.d.ts.map +1 -0
- package/dist/app/charts/styling/svgStyle.js +3 -78
- package/dist/app/charts/styling/svgStyle.js.map +1 -1
- package/dist/app/charts/subscriptions/viewDrivers.d.ts +124 -0
- package/dist/app/charts/subscriptions/viewDrivers.d.ts.map +1 -0
- package/dist/app/charts/subscriptions/viewDrivers.js +338 -0
- package/dist/app/charts/subscriptions/viewDrivers.js.map +1 -0
- package/dist/app/charts/tooltips/BarPlotTooltipContent.d.ts +12 -78
- package/dist/app/charts/tooltips/BarPlotTooltipContent.d.ts.map +1 -0
- package/dist/app/charts/tooltips/BarPlotTooltipContent.js +65 -257
- package/dist/app/charts/tooltips/BarPlotTooltipContent.js.map +1 -1
- package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.d.ts +9 -0
- package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.d.ts.map +1 -0
- package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.js +16 -0
- package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.js.map +1 -0
- package/dist/app/charts/tooltips/OutlierPlotTooltipContent.d.ts +30 -0
- package/dist/app/charts/tooltips/OutlierPlotTooltipContent.d.ts.map +1 -0
- package/dist/app/charts/tooltips/OutlierPlotTooltipContent.js +26 -0
- package/dist/app/charts/tooltips/OutlierPlotTooltipContent.js.map +1 -0
- package/dist/app/charts/tooltips/PoincarePlotTooltipContent.d.ts +22 -72
- package/dist/app/charts/tooltips/PoincarePlotTooltipContent.d.ts.map +1 -0
- package/dist/app/charts/tooltips/PoincarePlotTooltipContent.js +104 -171
- package/dist/app/charts/tooltips/PoincarePlotTooltipContent.js.map +1 -1
- package/dist/app/charts/tooltips/RasterPlotTooltipContent.d.ts +32 -8
- package/dist/app/charts/tooltips/RasterPlotTooltipContent.d.ts.map +1 -0
- package/dist/app/charts/tooltips/RasterPlotTooltipContent.js +62 -115
- package/dist/app/charts/tooltips/RasterPlotTooltipContent.js.map +1 -1
- package/dist/app/charts/tooltips/ScatterPlotTooltipContent.d.ts +39 -12
- package/dist/app/charts/tooltips/ScatterPlotTooltipContent.d.ts.map +1 -0
- package/dist/app/charts/tooltips/ScatterPlotTooltipContent.js +162 -134
- package/dist/app/charts/tooltips/ScatterPlotTooltipContent.js.map +1 -1
- package/dist/app/charts/tooltips/Tooltip.d.ts +27 -32
- package/dist/app/charts/tooltips/Tooltip.d.ts.map +1 -0
- package/dist/app/charts/tooltips/Tooltip.js +37 -81
- package/dist/app/charts/tooltips/Tooltip.js.map +1 -1
- package/dist/app/charts/tooltips/TooltipContent.d.ts +81 -0
- package/dist/app/charts/tooltips/TooltipContent.d.ts.map +1 -0
- package/dist/app/charts/tooltips/TooltipContent.js +99 -0
- package/dist/app/charts/tooltips/TooltipContent.js.map +1 -0
- package/dist/app/charts/tooltips/tooltipUtils.d.ts +186 -36
- package/dist/app/charts/tooltips/tooltipUtils.d.ts.map +1 -0
- package/dist/app/charts/tooltips/tooltipUtils.js +277 -48
- package/dist/app/charts/tooltips/tooltipUtils.js.map +1 -1
- package/dist/app/charts/tooltips/useOutlierTooltip.d.ts +59 -0
- package/dist/app/charts/tooltips/useOutlierTooltip.d.ts.map +1 -0
- package/dist/app/charts/tooltips/useOutlierTooltip.js +16 -0
- package/dist/app/charts/tooltips/useOutlierTooltip.js.map +1 -0
- package/dist/app/charts/tooltips/useTooltipPlacement.d.ts +51 -0
- package/dist/app/charts/tooltips/useTooltipPlacement.d.ts.map +1 -0
- package/dist/app/charts/tooltips/useTooltipPlacement.js +52 -0
- package/dist/app/charts/tooltips/useTooltipPlacement.js.map +1 -0
- package/dist/app/charts/tooltips/useTooltipStyle.d.ts +11 -0
- package/dist/app/charts/tooltips/useTooltipStyle.d.ts.map +1 -0
- package/dist/app/charts/tooltips/useTooltipStyle.js +14 -0
- package/dist/app/charts/tooltips/useTooltipStyle.js.map +1 -0
- package/dist/app/charts/trackers/Tracker.d.ts +22 -6
- package/dist/app/charts/trackers/Tracker.d.ts.map +1 -0
- package/dist/app/charts/trackers/Tracker.js +36 -22
- package/dist/app/charts/trackers/Tracker.js.map +1 -1
- package/dist/app/charts/trackers/trackerUtils.d.ts +40 -18
- package/dist/app/charts/trackers/trackerUtils.d.ts.map +1 -0
- package/dist/app/charts/trackers/trackerUtils.js +213 -300
- package/dist/app/charts/trackers/trackerUtils.js.map +1 -1
- package/dist/app/charts/utils.d.ts +72 -33
- package/dist/app/charts/utils.d.ts.map +1 -0
- package/dist/app/charts/utils.js +87 -42
- package/dist/app/charts/utils.js.map +1 -1
- package/dist/index.d.ts +2 -45
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -72
- package/dist/index.js.map +1 -1
- package/package.json +87 -46
- package/src/app/charts/Chart.tsx +132 -168
- package/src/app/charts/axes/AxesState.ts +27 -29
- package/src/app/charts/axes/AxisInterval.ts +57 -32
- package/src/app/charts/axes/BaseAxisRange.ts +47 -27
- package/src/app/charts/axes/ContinuousAxis.tsx +235 -39
- package/src/app/charts/axes/ContinuousAxisRange.ts +29 -11
- package/src/app/charts/axes/EmptyAxis.tsx +52 -17
- package/src/app/charts/axes/OrdinalAxis.tsx +54 -28
- package/src/app/charts/axes/OrdinalAxisRange.ts +96 -16
- package/src/app/charts/axes/autoScale.ts +244 -0
- package/src/app/charts/axes/axes.ts +954 -735
- package/src/app/charts/d3types.ts +42 -13
- package/src/app/charts/datasources/StreamingDataSource.ts +214 -0
- package/src/app/charts/datasources/iteratesDataSource.ts +122 -0
- package/src/app/charts/datasources/ordinalDataSource.ts +205 -0
- package/src/app/charts/datasources/outlierDataSource.ts +90 -0
- package/src/app/charts/datasources/timeSeriesDataSource.ts +133 -0
- package/src/app/charts/filters/regexFilter.ts +45 -5
- package/src/app/charts/hooks/AxesProvider.tsx +301 -0
- package/src/app/charts/hooks/CanvasSurfaceProvider.tsx +117 -0
- package/src/app/charts/hooks/ChartProvider.tsx +79 -0
- package/src/app/charts/hooks/InteractionProvider.tsx +88 -0
- package/src/app/charts/hooks/PlotDimensionsProvider.tsx +109 -0
- package/src/app/charts/hooks/defaultAxesValues.ts +40 -0
- package/src/app/charts/hooks/defaultMouseValues.ts +24 -0
- package/src/app/charts/hooks/defaultPlotDimensions.ts +27 -0
- package/src/app/charts/hooks/defaultTooltipValues.tsx +23 -0
- package/src/app/charts/hooks/defaultUseChartValues.tsx +36 -0
- package/src/app/charts/hooks/useAxes.tsx +60 -228
- package/src/app/charts/hooks/useCanvasSurface.tsx +35 -0
- package/src/app/charts/hooks/useChart.tsx +47 -106
- package/src/app/charts/hooks/useDataSource.tsx +106 -0
- package/src/app/charts/hooks/useHoveredSeries.tsx +33 -0
- package/src/app/charts/hooks/useMouse.tsx +58 -82
- package/src/app/charts/hooks/usePlotDimensions.tsx +25 -89
- package/src/app/charts/hooks/useTooltip.tsx +32 -63
- package/src/app/charts/index.ts +213 -0
- package/src/app/charts/legends/Legend.tsx +198 -479
- package/src/app/charts/legends/constants.ts +37 -0
- package/src/app/charts/observables/iterates.ts +32 -12
- package/src/app/charts/observables/ordinals.ts +141 -67
- package/src/app/charts/observables/outliers.ts +20 -0
- package/src/app/charts/plots/BarPlot.tsx +784 -666
- package/src/app/charts/plots/OutlierPlot.tsx +685 -0
- package/src/app/charts/plots/PoincarePlot.tsx +707 -489
- package/src/app/charts/plots/RasterPlot.tsx +253 -445
- package/src/app/charts/plots/ScatterPlot.tsx +480 -463
- package/src/app/charts/plots/autoScaleYAxes.ts +102 -0
- package/src/app/charts/plots/colorizedLine.ts +185 -0
- package/src/app/charts/plots/constants.ts +16 -0
- package/src/app/charts/plots/hitTesting.ts +198 -0
- package/src/app/charts/plots/plot.ts +138 -44
- package/src/app/charts/plots/useCanvasZoomPan.ts +240 -0
- package/src/app/charts/plots/useStreamingTimeAxes.ts +384 -0
- package/src/app/charts/series/baseSeries.ts +81 -14
- package/src/app/charts/series/iterateSeries.ts +8 -8
- package/src/app/charts/series/ordinalSeries.ts +35 -20
- package/src/app/charts/series/outlierSeries.ts +188 -0
- package/src/app/charts/series/timeSeries.ts +14 -12
- package/src/app/charts/series/timeSeriesChartData.ts +31 -17
- package/src/app/charts/styling/barPlotStyle.ts +48 -5
- package/src/app/charts/styling/canvasStyle.ts +86 -0
- package/src/app/charts/styling/margins.ts +28 -4
- package/src/app/charts/styling/svgStyle.ts +10 -91
- package/src/app/charts/subscriptions/viewDrivers.ts +447 -0
- package/src/app/charts/tooltips/BarPlotTooltipContent.tsx +156 -380
- package/src/app/charts/tooltips/DefaultOutlierTooltipContent.tsx +40 -0
- package/src/app/charts/tooltips/OutlierPlotTooltipContent.tsx +67 -0
- package/src/app/charts/tooltips/PoincarePlotTooltipContent.tsx +229 -292
- package/src/app/charts/tooltips/RasterPlotTooltipContent.tsx +164 -217
- package/src/app/charts/tooltips/ScatterPlotTooltipContent.tsx +373 -236
- package/src/app/charts/tooltips/Tooltip.tsx +58 -124
- package/src/app/charts/tooltips/TooltipContent.tsx +177 -0
- package/src/app/charts/tooltips/tooltipUtils.ts +359 -61
- package/src/app/charts/tooltips/useOutlierTooltip.tsx +68 -0
- package/src/app/charts/tooltips/useTooltipPlacement.ts +86 -0
- package/src/app/charts/tooltips/useTooltipStyle.ts +15 -0
- package/src/app/charts/trackers/Tracker.tsx +68 -33
- package/src/app/charts/trackers/trackerUtils.ts +263 -376
- package/src/app/charts/utils.ts +90 -48
- package/src/index.ts +2 -322
- package/dist/app/charts/axes/AxesState.test.d.ts +0 -1
- package/dist/app/charts/axes/AxesState.test.js +0 -30
- package/dist/app/charts/axes/AxesState.test.js.map +0 -1
- package/dist/app/charts/axes/AxisInterval.test.d.ts +0 -1
- package/dist/app/charts/axes/AxisInterval.test.js +0 -120
- package/dist/app/charts/axes/AxisInterval.test.js.map +0 -1
- package/dist/app/charts/axes/ContinuousAxisRange.test.d.ts +0 -1
- package/dist/app/charts/axes/ContinuousAxisRange.test.js +0 -47
- package/dist/app/charts/axes/ContinuousAxisRange.test.js.map +0 -1
- package/dist/app/charts/axes/OrdinalAxisRange.test.d.ts +0 -1
- package/dist/app/charts/axes/OrdinalAxisRange.test.js +0 -48
- package/dist/app/charts/axes/OrdinalAxisRange.test.js.map +0 -1
- package/dist/app/charts/filters/regexFilter.test.d.ts +0 -1
- package/dist/app/charts/filters/regexFilter.test.js +0 -11
- package/dist/app/charts/filters/regexFilter.test.js.map +0 -1
- package/dist/app/charts/hooks/useDataObservable.d.ts +0 -72
- package/dist/app/charts/hooks/useDataObservable.js +0 -74
- package/dist/app/charts/hooks/useDataObservable.js.map +0 -1
- package/dist/app/charts/hooks/useInitialData.d.ts +0 -36
- package/dist/app/charts/hooks/useInitialData.js +0 -28
- package/dist/app/charts/hooks/useInitialData.js.map +0 -1
- package/dist/app/charts/observables/iterates.test.d.ts +0 -1
- package/dist/app/charts/observables/iterates.test.js +0 -180
- package/dist/app/charts/observables/iterates.test.js.map +0 -1
- package/dist/app/charts/observables/ordinals.test.d.ts +0 -1
- package/dist/app/charts/observables/ordinals.test.js +0 -102
- package/dist/app/charts/observables/ordinals.test.js.map +0 -1
- package/dist/app/charts/series/datumSeries.test.d.ts +0 -1
- package/dist/app/charts/series/datumSeries.test.js +0 -50
- package/dist/app/charts/series/datumSeries.test.js.map +0 -1
- package/dist/app/charts/styling/margins.test.d.ts +0 -1
- package/dist/app/charts/styling/margins.test.js +0 -19
- package/dist/app/charts/styling/margins.test.js.map +0 -1
- package/dist/app/charts/subscriptions/subscription.test.d.ts +0 -1
- package/dist/app/charts/subscriptions/subscription.test.js +0 -395
- package/dist/app/charts/subscriptions/subscription.test.js.map +0 -1
- package/dist/app/charts/subscriptions/subscriptions.d.ts +0 -100
- package/dist/app/charts/subscriptions/subscriptions.js +0 -449
- package/dist/app/charts/subscriptions/subscriptions.js.map +0 -1
- package/src/app/charts/axes/AxesState.test.ts +0 -34
- package/src/app/charts/axes/AxisInterval.test.ts +0 -143
- package/src/app/charts/axes/ContinuousAxisRange.test.ts +0 -55
- package/src/app/charts/axes/OrdinalAxisRange.test.ts +0 -55
- package/src/app/charts/filters/regexFilter.test.ts +0 -13
- package/src/app/charts/hooks/useDataObservable.tsx +0 -170
- package/src/app/charts/hooks/useInitialData.tsx +0 -64
- package/src/app/charts/observables/iterates.test.ts +0 -201
- package/src/app/charts/observables/ordinals.test.ts +0 -135
- package/src/app/charts/series/datumSeries.test.ts +0 -60
- package/src/app/charts/styling/margins.test.ts +0 -22
- package/src/app/charts/subscriptions/subscription.test.ts +0 -499
- package/src/app/charts/subscriptions/subscriptions.ts +0 -635
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import {Dimensions, Margin} from "../styling/margins";
|
|
1
|
+
import type {Dimensions, Margin} from "../styling/margins";
|
|
2
2
|
import {ContinuousAxisRange} from "./ContinuousAxisRange";
|
|
3
|
+
import type {AutoScale} from "./autoScale";
|
|
3
4
|
import * as d3 from "d3";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import {type ScaleBand, type ScaleContinuousNumeric, type ScaleLinear} from "d3";
|
|
6
|
+
import type {CanvasContext, DrawHandle} from "../d3types";
|
|
6
7
|
import {AxesState} from "./AxesState";
|
|
7
|
-
import {AxesAssignment} from "../plots/plot";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import type {AxesAssignment} from "../plots/plot";
|
|
9
|
+
import {clipToArea} from "../plots/plot";
|
|
10
|
+
import type {BaseSeries} from "../series/baseSeries";
|
|
11
|
+
import {fontStringFor} from "../utils";
|
|
10
12
|
import {OrdinalAxisRange} from "./OrdinalAxisRange";
|
|
11
13
|
import {BaseAxisRange} from "./BaseAxisRange";
|
|
12
14
|
import {AxisInterval} from "./AxisInterval";
|
|
13
15
|
|
|
16
|
+
/** Default tick length, in pixels, matching d3-axis's default `tickSize`. */
|
|
17
|
+
const TICK_SIZE = 6
|
|
18
|
+
/** Default gap between the end of a tick and its label, matching d3-axis's default `tickPadding`. */
|
|
19
|
+
const TICK_PADDING = 3
|
|
20
|
+
|
|
14
21
|
/**
|
|
15
22
|
* Holds the style for the axis tick labels
|
|
16
23
|
*/
|
|
@@ -24,7 +31,8 @@ export type AxisTickStyle = {
|
|
|
24
31
|
|
|
25
32
|
/**
|
|
26
33
|
* Factory function to create a default AxisTickStyle with the default font,
|
|
27
|
-
* no rotation, and no
|
|
34
|
+
* no rotation, and no autorotation.
|
|
35
|
+
* @return The default axis tick style
|
|
28
36
|
*/
|
|
29
37
|
export function defaultAxisTickStyle(): AxisTickStyle {
|
|
30
38
|
return {
|
|
@@ -47,6 +55,7 @@ export interface AxesFont {
|
|
|
47
55
|
/**
|
|
48
56
|
* Factory function to create a default AxesFont with the default font size, color,
|
|
49
57
|
* family, and weight.
|
|
58
|
+
* @return The default axes font
|
|
50
59
|
*/
|
|
51
60
|
export function defaultAxesFont(): AxesFont {
|
|
52
61
|
return {
|
|
@@ -77,6 +86,7 @@ export interface SeriesLineStyle extends SeriesStyle {
|
|
|
77
86
|
/**
|
|
78
87
|
* Factory function to create a default SeriesLineStyle with the default color, line width,
|
|
79
88
|
* highlight color, and highlight width.
|
|
89
|
+
* @return The default series line style
|
|
80
90
|
*/
|
|
81
91
|
export function defaultLineStyle(): SeriesLineStyle {
|
|
82
92
|
return {
|
|
@@ -88,17 +98,74 @@ export function defaultLineStyle(): SeriesLineStyle {
|
|
|
88
98
|
}
|
|
89
99
|
|
|
90
100
|
/**
|
|
91
|
-
* The
|
|
92
|
-
*
|
|
101
|
+
* The concrete kind of an axis, set by every axis-creator function to its own unique value.
|
|
102
|
+
* Lets code that only has a `BaseAxis` (e.g. a plot's `axesFor` validating an `axisAssignments`
|
|
103
|
+
* entry) discriminate the concrete axis shape at runtime -- `BaseAxis`/`ContinuousNumericAxis`/
|
|
104
|
+
* `OrdinalStringAxis` are plain structural interfaces, so a type-level cast (`as ContinuousNumericAxis`)
|
|
105
|
+
* cannot do that; this field is what actually can.
|
|
106
|
+
* Note: replaces enums to support `erasableSyntaxOnly`
|
|
107
|
+
* TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
|
|
108
|
+
*/
|
|
109
|
+
export const AxisType = {
|
|
110
|
+
ContinuousNumeric: "ContinuousNumeric",
|
|
111
|
+
OrdinalString: "OrdinalString",
|
|
112
|
+
} as const
|
|
113
|
+
|
|
114
|
+
export type AxisType = (typeof AxisType)[keyof typeof AxisType]
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The axis component that creates each type of axis (with its article), for messages about axes
|
|
118
|
+
*/
|
|
119
|
+
const AXIS_COMPONENTS: Record<AxisType, string> = {
|
|
120
|
+
[AxisType.ContinuousNumeric]: "a <ContinuousAxis> (or an <EmptyAxis>)",
|
|
121
|
+
[AxisType.OrdinalString]: "an <OrdinalAxis>",
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Checks that a plot's axis is of the type the plot needs (e.g. a bar plot needs an ordinal x-axis).
|
|
126
|
+
* A missing axis passes, since the plots already skip the series whose axes aren't found.
|
|
127
|
+
* @param plotName The name of the plot's component (e.g. "RasterPlot")
|
|
128
|
+
* @param dimension Whether the axis is the plot's x-axis or its y-axis
|
|
129
|
+
* @param axis The axis (`undefined` when it isn't found)
|
|
130
|
+
* @param expected The axis type the plot needs
|
|
131
|
+
* @throws Error when the axis isn't of the expected type, naming the axis component it needs to be
|
|
132
|
+
*/
|
|
133
|
+
export function checkAxisType(plotName: string, dimension: 'x' | 'y', axis: BaseAxis | undefined, expected: AxisType): void {
|
|
134
|
+
if (axis !== undefined && axis.axisType !== expected) {
|
|
135
|
+
throw Error(
|
|
136
|
+
`<${plotName}> needs its ${dimension}-axis to be ${AXIS_COMPONENTS[expected]}, but the ` +
|
|
137
|
+
`${dimension}-axis "${axis.axisId}" is ${AXIS_COMPONENTS[axis.axisType]}`
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The base interface that all Axes must implement. Each axis must have a unique ID and a location
|
|
144
|
+
* (top, bottom, left, right). Under the old SVG-backed implementation this also carried a
|
|
145
|
+
* `selection`; that's gone now that axes draw onto a shared canvas instead of owning their own DOM
|
|
146
|
+
* elements.
|
|
93
147
|
*/
|
|
94
148
|
export interface BaseAxis {
|
|
95
149
|
axisId: string
|
|
96
150
|
location: AxisLocation
|
|
97
|
-
|
|
151
|
+
/**
|
|
152
|
+
* The concrete kind of this axis (see {@link AxisType}). Every axis-creator function sets this
|
|
153
|
+
* to its own unique value.
|
|
154
|
+
*/
|
|
155
|
+
axisType: AxisType
|
|
156
|
+
/**
|
|
157
|
+
* True for a placeholder axis added via `addEmptyXAxis`/`addEmptyYAxis` (just a
|
|
158
|
+
* line, with no real data domain behind it). Consumers that report a value for the mouse
|
|
159
|
+
* position -- e.g., the tracker's axis labels -- should skip axes marked this way, since their
|
|
160
|
+
* scale doesn't reflect anything meaningful.
|
|
161
|
+
*/
|
|
162
|
+
isEmpty?: boolean
|
|
98
163
|
}
|
|
99
164
|
|
|
100
165
|
/**
|
|
101
|
-
* Represents a continuous numeric axis.
|
|
166
|
+
* Represents a continuous numeric axis. Draws itself by registering a draw function with the
|
|
167
|
+
* chart's {@link CanvasContext} (see `addContinuousNumericXAxis` and
|
|
168
|
+
* `addContinuousNumericYAxis`, below) rather than owning an SVG element.
|
|
102
169
|
*/
|
|
103
170
|
export interface ContinuousNumericAxis extends BaseAxis {
|
|
104
171
|
/**
|
|
@@ -106,135 +173,124 @@ export interface ContinuousNumericAxis extends BaseAxis {
|
|
|
106
173
|
*/
|
|
107
174
|
scale: ScaleContinuousNumeric<number, number>
|
|
108
175
|
/**
|
|
109
|
-
*
|
|
176
|
+
* When set, the axis' domain grows to fit the data plotted against it, within the settings'
|
|
177
|
+
* limits (see `ContinuousAxis`'s `autoScale` prop). Set by the axis component, and applied by
|
|
178
|
+
* the plots that support it (for y-axes).
|
|
110
179
|
*/
|
|
111
|
-
|
|
180
|
+
autoScale?: AutoScale
|
|
112
181
|
/**
|
|
113
|
-
* Updates the axis based on the specified domain and plot dimensions
|
|
182
|
+
* Updates the axis based on the specified domain and plot dimensions and requests the plot
|
|
183
|
+
* to be redrawn.
|
|
114
184
|
* @param domain The interval representing the domain of the axis.
|
|
115
185
|
* @param plotDimensions The dimensions of the plot (without the margins).
|
|
116
186
|
* @param margin The margins for the plot
|
|
117
187
|
*/
|
|
118
188
|
update: (domain: AxisInterval, plotDimensions: Dimensions, margin: Margin) => void
|
|
189
|
+
/**
|
|
190
|
+
* Updates the font (color, size, family, weight) used for ticks and the axis label, without
|
|
191
|
+
* recreating the axis or touching its domain. Used to keep the label color in sync with the
|
|
192
|
+
* chart's `color` prop (e.g., on a theme change) between full axis updates.
|
|
193
|
+
*/
|
|
194
|
+
updateFont: (font: AxesFont) => void
|
|
195
|
+
/**
|
|
196
|
+
* Highlights (or un-highlights) this axis -- used, for example, to indicate which axis a
|
|
197
|
+
* currently-hovered series is plotted against. When `highlighted` is true, the axis' domain
|
|
198
|
+
* line, ticks, and labels are drawn in `color` at `lineWidth` instead of the normal font color
|
|
199
|
+
* and 1px line; `color`/`lineWidth` are remembered across calls, so a later `setHighlighted(true)`
|
|
200
|
+
* without them reuses whatever was last passed (or the built-in default on the first call).
|
|
201
|
+
* @param highlighted Whether the axis should be drawn highlighted
|
|
202
|
+
* @param color The highlight color
|
|
203
|
+
* @param lineWidth The highlight line width
|
|
204
|
+
*/
|
|
205
|
+
setHighlighted: (highlighted: boolean, color?: string, lineWidth?: number) => void
|
|
119
206
|
}
|
|
120
207
|
|
|
121
208
|
/**
|
|
122
|
-
* Represents an ordinal string axis.
|
|
209
|
+
* Represents an ordinal string axis. Draws itself by registering a draw function with the chart's
|
|
210
|
+
* {@link CanvasContext} rather than owning an SVG element.
|
|
123
211
|
*/
|
|
124
212
|
export interface OrdinalStringAxis extends BaseAxis {
|
|
125
213
|
/**
|
|
126
214
|
* A d3 scale for an ordinal string axis.
|
|
127
215
|
*/
|
|
128
216
|
scale: ScaleBand<string>
|
|
129
|
-
/**
|
|
130
|
-
* A d3 axis generator for an ordinal string axis.
|
|
131
|
-
*/
|
|
132
|
-
generator: Axis<string>
|
|
133
217
|
/**
|
|
134
218
|
* The size of each category on the axis.
|
|
135
219
|
*/
|
|
136
220
|
categorySize: number
|
|
137
221
|
/**
|
|
138
|
-
* Updates the axis based on the specified
|
|
222
|
+
* Updates the axis based on the specified categories, range, and plot dimensions, and requests
|
|
223
|
+
* the plot to be redrawn.
|
|
224
|
+
* @param categories The category names for the axis, in order -- replaces the scale's domain,
|
|
225
|
+
* so passing a different set of categories (e.g. because the number of series changed) is how
|
|
226
|
+
* an axis reflects a new category count. Pass `axis.scale.domain()` to leave the categories
|
|
227
|
+
* unchanged (e.g. from a pan/zoom call site that isn't varying the category set).
|
|
139
228
|
* @param range An interval representing the range of the axis.
|
|
140
229
|
* @param originalRange An interval representing the original range of the axis (before zooming or panning).
|
|
141
230
|
* @param plotDimensions The dimensions of the plot (without the margins).
|
|
142
231
|
* @param margin The margins for the plot
|
|
143
232
|
* @return The number of pixels for each category on the axis.
|
|
144
233
|
*/
|
|
145
|
-
update: (range: AxisInterval, originalRange: AxisInterval, plotDimensions: Dimensions, margin: Margin) => number
|
|
234
|
+
update: (categories: Array<string>, range: AxisInterval, originalRange: AxisInterval, plotDimensions: Dimensions, margin: Margin) => number
|
|
235
|
+
/**
|
|
236
|
+
* Updates the font used for the axis label without recreating the axis. The font's color is
|
|
237
|
+
* also the color of the axis line, the ticks, and the tick labels; the tick labels' size,
|
|
238
|
+
* family, and weight, though, stay fixed at whatever the tick style's font was set to when
|
|
239
|
+
* the axis was created.
|
|
240
|
+
*/
|
|
241
|
+
updateFont: (font: AxesFont) => void
|
|
242
|
+
/**
|
|
243
|
+
* Highlights (or un-highlights) this axis -- see {@link ContinuousNumericAxis.setHighlighted}
|
|
244
|
+
* for the full explanation. `color`/`lineWidth` are remembered across calls, so a later
|
|
245
|
+
* `setHighlighted(true)` without them reuses whatever was last passed (or the built-in
|
|
246
|
+
* default on the first call).
|
|
247
|
+
* @param highlighted Whether the axis should be drawn highlighted
|
|
248
|
+
* @param color The highlight color
|
|
249
|
+
* @param lineWidth The highlight line width
|
|
250
|
+
*/
|
|
251
|
+
setHighlighted: (highlighted: boolean, color?: string, lineWidth?: number) => void
|
|
146
252
|
}
|
|
147
253
|
|
|
148
254
|
/**
|
|
149
|
-
*
|
|
255
|
+
* The possible locations of an axis.
|
|
256
|
+
* Note: replaces enums to support `erasableSyntaxOnly`
|
|
257
|
+
* TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
|
|
150
258
|
*/
|
|
151
|
-
export
|
|
259
|
+
export const AxisLocation = {
|
|
152
260
|
// y-axes
|
|
153
|
-
Left,
|
|
154
|
-
Right,
|
|
261
|
+
Left : "Left",
|
|
262
|
+
Right: "Right",
|
|
155
263
|
// x-axes
|
|
156
|
-
Bottom,
|
|
157
|
-
Top
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/*
|
|
161
|
-
axis clip path definition
|
|
162
|
-
*/
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Adds a clip area for the chart to the specified SVG element. The clip-area is given
|
|
166
|
-
* an `id` of `clip-series-<chart_id>`, which because the chart ID should be unique, makes
|
|
167
|
-
* this unique as well
|
|
168
|
-
* @param chartId The ID of the chart to which the clip area is to be added
|
|
169
|
-
* @param axisId The ID of the axis to which the clip area is to be added
|
|
170
|
-
* @param svg The SVG element to which the clip area is to be added
|
|
171
|
-
* @param location The location of the axis
|
|
172
|
-
* @param plotDimensions The dimensions of the plot
|
|
173
|
-
* @param margin The margins around the plot
|
|
174
|
-
* @return The ID of the clip-path
|
|
175
|
-
*/
|
|
176
|
-
export function setClipPath(
|
|
177
|
-
chartId: number,
|
|
178
|
-
axisId: string,
|
|
179
|
-
svg: SvgSelection,
|
|
180
|
-
location: AxisLocation,
|
|
181
|
-
plotDimensions: Dimensions,
|
|
182
|
-
margin: Margin
|
|
183
|
-
): string {
|
|
184
|
-
const clipPathId = `chart-clip-path-${chartId}-${axisId}`
|
|
185
|
-
|
|
186
|
-
// calculate the width and height of the clip-area for the axis
|
|
187
|
-
const width = location === AxisLocation.Left ? margin.right :
|
|
188
|
-
location === AxisLocation.Right ? margin.left :
|
|
189
|
-
plotDimensions.width
|
|
190
|
-
|
|
191
|
-
const height = location === AxisLocation.Top ? margin.top + 1 :
|
|
192
|
-
location === AxisLocation.Bottom ? margin.bottom - 1:
|
|
193
|
-
plotDimensions.height
|
|
194
|
-
|
|
195
|
-
// remove the old clipping region and add a new one with the updated plot dimensions
|
|
196
|
-
svg.select(`#${clipPathId}-defs`).remove();
|
|
197
|
-
svg
|
|
198
|
-
.append('defs')
|
|
199
|
-
.attr('id', `${clipPathId}-defs`)
|
|
200
|
-
.append("clipPath")
|
|
201
|
-
.attr("id", clipPathId)
|
|
202
|
-
.append("rect")
|
|
203
|
-
.attr("x", 0)
|
|
204
|
-
.attr("y", location === AxisLocation.Bottom ? -1 : -margin.top)
|
|
205
|
-
.attr("width", width)
|
|
206
|
-
.attr("height", height)
|
|
207
|
-
|
|
208
|
-
return clipPathId
|
|
209
|
-
}
|
|
264
|
+
Bottom: "Bottom",
|
|
265
|
+
Top: "Top"
|
|
266
|
+
} as const
|
|
210
267
|
|
|
268
|
+
export type AxisLocation = (typeof AxisLocation)[keyof typeof AxisLocation];
|
|
211
269
|
|
|
212
270
|
/*
|
|
213
|
-
category axes
|
|
271
|
+
category (ordinal) axes
|
|
214
272
|
*/
|
|
215
273
|
|
|
216
274
|
/**
|
|
217
|
-
* Adds a category axis to the specified location
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* @param
|
|
275
|
+
* Adds a category axis to the specified location, registering its draw function with the canvas
|
|
276
|
+
* context. When the location is top or bottom, the category axis represents the x-axis; when the
|
|
277
|
+
* location is left or right, it represents the y-axis.
|
|
278
|
+
* @param cc The canvas context to register the axis' draw function with
|
|
221
279
|
* @param axisId A unique ID for the axis
|
|
222
|
-
* @param svg The SVG selection (d3)
|
|
223
280
|
* @param location The location of the axis
|
|
224
|
-
* @param categories
|
|
281
|
+
* @param categories The category names (the axis' `update` function can replace them later)
|
|
225
282
|
* @param axisLabel The axis label
|
|
226
283
|
* @param axesLabelFont The font for the axis label
|
|
227
|
-
* @param axisTickStyle Styling information for the ticks (
|
|
284
|
+
* @param axisTickStyle Styling information for the ticks (font, rotation, etc.)
|
|
228
285
|
* @param plotDimensions The dimensions of the plot
|
|
229
286
|
* @param margin The plot margin
|
|
230
|
-
* @param setAxisRangeFor
|
|
231
|
-
* @param setOriginalAxisRangeFor
|
|
232
|
-
* @return
|
|
287
|
+
* @param setAxisRangeFor Callback that sets the axis range for the specified axis
|
|
288
|
+
* @param setOriginalAxisRangeFor Callback that sets the original axis range for the specified axis
|
|
289
|
+
* @return An {@link OrdinalStringAxis}
|
|
233
290
|
*/
|
|
234
291
|
export function addOrdinalStringAxis(
|
|
235
|
-
|
|
292
|
+
cc: CanvasContext,
|
|
236
293
|
axisId: string,
|
|
237
|
-
svg: SvgSelection,
|
|
238
294
|
location: AxisLocation,
|
|
239
295
|
categories: Array<string>,
|
|
240
296
|
axisLabel: string,
|
|
@@ -249,54 +305,46 @@ export function addOrdinalStringAxis(
|
|
|
249
305
|
case AxisLocation.Top:
|
|
250
306
|
case AxisLocation.Bottom:
|
|
251
307
|
return addOrdinalStringXAxis(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
location,
|
|
255
|
-
categories,
|
|
256
|
-
axisLabel, axesLabelFont, axisTickStyle,
|
|
257
|
-
plotDimensions,
|
|
258
|
-
margin,
|
|
259
|
-
setAxisRangeFor,
|
|
260
|
-
setOriginalAxisRangeFor
|
|
308
|
+
cc, axisId, location, categories, axisLabel, axesLabelFont, axisTickStyle,
|
|
309
|
+
plotDimensions, margin, setAxisRangeFor, setOriginalAxisRangeFor
|
|
261
310
|
)
|
|
262
311
|
case AxisLocation.Left:
|
|
263
312
|
case AxisLocation.Right:
|
|
264
313
|
return addOrdinalStringYAxis(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
location,
|
|
268
|
-
categories,
|
|
269
|
-
axisLabel, axesLabelFont, axisTickStyle,
|
|
270
|
-
plotDimensions,
|
|
271
|
-
margin,
|
|
272
|
-
setAxisRangeFor,
|
|
273
|
-
setOriginalAxisRangeFor
|
|
314
|
+
cc, axisId, location, categories, axisLabel, axesLabelFont, axisTickStyle,
|
|
315
|
+
plotDimensions, margin, setAxisRangeFor, setOriginalAxisRangeFor
|
|
274
316
|
)
|
|
275
|
-
|
|
276
317
|
}
|
|
277
318
|
}
|
|
278
319
|
|
|
279
320
|
/**
|
|
280
|
-
* Adds a category axis
|
|
281
|
-
* @
|
|
321
|
+
* Adds a category x-axis (top or bottom), registering its draw function with the canvas context.
|
|
322
|
+
* See {@link addOrdinalStringAxis}.
|
|
323
|
+
*
|
|
324
|
+
* NOTE ON FIDELITY: the original SVG version rotated tick labels using each label's own
|
|
325
|
+
* `getBBox()` to compute a per-label rotation origin, which let long labels pivot around their own
|
|
326
|
+
* visual center/corner. Canvas `measureText` has no bbox-offset equivalent, so this version rotates
|
|
327
|
+
* every label about its anchor point at the tick instead. At `rotation: 0` (the common case) this
|
|
328
|
+
* produces the same centered layout as before; at non-zero rotations the labels will be close but
|
|
329
|
+
* not pixel-identical to the old SVG output -- worth a visual check if you use tick rotation.
|
|
330
|
+
* @param cc The canvas context to register the axis' draw function with
|
|
282
331
|
* @param axisId A unique ID for the axis
|
|
283
|
-
* @param
|
|
284
|
-
* @param
|
|
285
|
-
* @param categories An array holding the category names
|
|
332
|
+
* @param location The location of the axis (top or bottom)
|
|
333
|
+
* @param categories The category names (the axis' `update` function can replace them later)
|
|
286
334
|
* @param axisLabel The axis label
|
|
287
|
-
* @param axesLabelFont The font for the axis label
|
|
288
|
-
*
|
|
335
|
+
* @param axesLabelFont The font for the axis label (its color is also the color of the axis line,
|
|
336
|
+
* ticks, and tick labels)
|
|
337
|
+
* @param axisTickStyle Styling information for the ticks (font, rotation, etc.)
|
|
289
338
|
* @param plotDimensions The dimensions of the plot
|
|
290
339
|
* @param margin The plot margin
|
|
291
|
-
* @param setAxisRangeFor
|
|
292
|
-
* @param setOriginalAxisRangeFor
|
|
293
|
-
* @return
|
|
340
|
+
* @param setAxisRangeFor Callback that sets the axis range for the specified axis
|
|
341
|
+
* @param setOriginalAxisRangeFor Callback that sets the original axis range for the specified axis
|
|
342
|
+
* @return An {@link OrdinalStringAxis}
|
|
294
343
|
*/
|
|
295
344
|
function addOrdinalStringXAxis(
|
|
296
|
-
|
|
345
|
+
cc: CanvasContext,
|
|
297
346
|
axisId: string,
|
|
298
|
-
|
|
299
|
-
location: AxisLocation.Bottom | AxisLocation.Top,
|
|
347
|
+
location: typeof AxisLocation.Bottom | typeof AxisLocation.Top,
|
|
300
348
|
categories: Array<string>,
|
|
301
349
|
axisLabel: string,
|
|
302
350
|
axesLabelFont: AxesFont,
|
|
@@ -306,91 +354,135 @@ function addOrdinalStringXAxis(
|
|
|
306
354
|
setAxisRangeFor: (axisId: string, range: AxisInterval) => void,
|
|
307
355
|
setOriginalAxisRangeFor: (axisId: string, range: AxisInterval) => void,
|
|
308
356
|
): OrdinalStringAxis {
|
|
309
|
-
const scale = d3.scaleBand()
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
.
|
|
331
|
-
.
|
|
332
|
-
.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
357
|
+
const scale = d3.scaleBand<string>().domain(categories).range([0, plotDimensions.width])
|
|
358
|
+
|
|
359
|
+
let currentDimensions = plotDimensions
|
|
360
|
+
let currentMargin = margin
|
|
361
|
+
let currentFont = axesLabelFont
|
|
362
|
+
const currentTickStyle = axisTickStyle
|
|
363
|
+
let highlighted = false
|
|
364
|
+
let highlightColor = defaultLineStyle().highlightColor
|
|
365
|
+
let highlightLineWidth = defaultLineStyle().highlightWidth
|
|
366
|
+
|
|
367
|
+
const drawHandle: DrawHandle = `x-axis-ordinal-${cc.chartId}-${axisId}`
|
|
368
|
+
|
|
369
|
+
const draw = (context: CanvasContext) => {
|
|
370
|
+
const {context2D} = context
|
|
371
|
+
context2D.save()
|
|
372
|
+
context2D.translate(currentMargin.left, yTranslation(location, currentDimensions, currentMargin))
|
|
373
|
+
|
|
374
|
+
context2D.save()
|
|
375
|
+
clipToArea(context, {width: currentDimensions.width, height: currentMargin.bottom}, {x: 0, y: location === AxisLocation.Bottom ? -1 : -currentMargin.top})
|
|
376
|
+
|
|
377
|
+
const axisColor = highlighted ? highlightColor : currentFont.color
|
|
378
|
+
context2D.strokeStyle = axisColor
|
|
379
|
+
context2D.fillStyle = axisColor
|
|
380
|
+
context2D.lineWidth = highlighted ? highlightLineWidth : 1
|
|
381
|
+
context2D.font = fontStringFor(currentTickStyle.font.size, currentTickStyle.font.family, currentTickStyle.font.weight)
|
|
382
|
+
context2D.textBaseline = 'middle'
|
|
383
|
+
|
|
384
|
+
// domain line
|
|
385
|
+
context2D.beginPath()
|
|
386
|
+
context2D.moveTo(0, 0)
|
|
387
|
+
context2D.lineTo(currentDimensions.width, 0)
|
|
388
|
+
context2D.stroke()
|
|
389
|
+
|
|
390
|
+
const tickDirection = location === AxisLocation.Bottom ? 1 : -1
|
|
391
|
+
const degrees = location === AxisLocation.Bottom ? -currentTickStyle.rotation : currentTickStyle.rotation
|
|
392
|
+
const radians = degrees * Math.PI / 180
|
|
393
|
+
|
|
394
|
+
categories.forEach(category => {
|
|
395
|
+
const x = (scale(category) ?? 0) + scale.bandwidth() / 2
|
|
396
|
+
context2D.beginPath()
|
|
397
|
+
context2D.moveTo(x, 0)
|
|
398
|
+
context2D.lineTo(x, TICK_SIZE * tickDirection)
|
|
399
|
+
context2D.stroke()
|
|
400
|
+
|
|
401
|
+
context2D.save()
|
|
402
|
+
context2D.translate(x, (TICK_SIZE + TICK_PADDING) * tickDirection)
|
|
403
|
+
context2D.rotate(radians)
|
|
404
|
+
context2D.textAlign = radians === 0 ? 'center' : 'end'
|
|
405
|
+
context2D.fillText(category, 0, 0)
|
|
406
|
+
context2D.restore()
|
|
346
407
|
})
|
|
347
408
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
.
|
|
409
|
+
context2D.restore() // pop the clip
|
|
410
|
+
|
|
411
|
+
// axis label (absolute position, not clipped, matches the old behavior)
|
|
412
|
+
context2D.fillStyle = axisColor
|
|
413
|
+
context2D.font = fontStringFor(currentFont.size, currentFont.family, currentFont.weight)
|
|
414
|
+
context2D.textAlign = 'center'
|
|
415
|
+
// (a bottom label sits on the canvas' bottom edge, so it's drawn above it, descenders and all)
|
|
416
|
+
context2D.textBaseline = location === AxisLocation.Top ? 'hanging' : 'bottom'
|
|
417
|
+
const labelX = ordinalLabelXTranslation(location, currentDimensions, currentMargin, currentFont) - currentMargin.left
|
|
418
|
+
const labelY = ordinalLabelYTranslation(location, currentDimensions, currentMargin) - yTranslation(location, currentDimensions, currentMargin)
|
|
419
|
+
context2D.fillText(axisLabel, labelX, labelY)
|
|
359
420
|
|
|
360
|
-
|
|
421
|
+
context2D.restore()
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
cc.register(drawHandle, draw, 0)
|
|
425
|
+
|
|
426
|
+
const categorySize = () => scale.bandwidth()
|
|
361
427
|
|
|
362
428
|
return {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
429
|
+
axisId,
|
|
430
|
+
location,
|
|
431
|
+
axisType: AxisType.OrdinalString,
|
|
432
|
+
scale,
|
|
433
|
+
categorySize: categorySize(),
|
|
434
|
+
update: (updatedCategories, range, originalRange, plotDimensions, margin) => {
|
|
435
|
+
// reassign the closed-over `categories` (not just a local variable) so `draw`'s own
|
|
436
|
+
// `categories.forEach(...)` picks up the change on the next redraw too
|
|
437
|
+
categories = updatedCategories
|
|
438
|
+
const updatedRange = AxisInterval.from(
|
|
439
|
+
Math.min(range.start, 0),
|
|
440
|
+
Math.max(range.end, plotDimensions.width)
|
|
441
|
+
)
|
|
442
|
+
scale.domain(categories).range(updatedRange.asTuple())
|
|
443
|
+
currentDimensions = plotDimensions
|
|
444
|
+
currentMargin = margin
|
|
366
445
|
setAxisRangeFor(axisId, range)
|
|
367
446
|
setOriginalAxisRangeFor(axisId, originalRange)
|
|
368
|
-
|
|
447
|
+
cc.requestRedraw()
|
|
448
|
+
return categorySize()
|
|
449
|
+
},
|
|
450
|
+
setHighlighted: (isHighlighted, color, lineWidth) => {
|
|
451
|
+
highlighted = isHighlighted
|
|
452
|
+
if (color !== undefined) highlightColor = color
|
|
453
|
+
if (lineWidth !== undefined) highlightLineWidth = lineWidth
|
|
454
|
+
cc.requestRedraw()
|
|
455
|
+
},
|
|
456
|
+
updateFont: (font) => {
|
|
457
|
+
// the font's color is used for the label, the axis line, the ticks, and the tick
|
|
458
|
+
// labels; the tick labels' size, family, and weight stay at axisTickStyle.font's
|
|
459
|
+
currentFont = font
|
|
460
|
+
cc.requestRedraw()
|
|
369
461
|
}
|
|
370
462
|
}
|
|
371
463
|
}
|
|
372
464
|
|
|
373
465
|
/**
|
|
374
|
-
* Adds a category axis
|
|
375
|
-
* @
|
|
466
|
+
* Adds a category y-axis (left or right), registering its draw function with the canvas context.
|
|
467
|
+
* See {@link addOrdinalStringAxis}.
|
|
468
|
+
* @param cc The canvas context to register the axis' draw function with
|
|
376
469
|
* @param axisId A unique ID for the axis
|
|
377
|
-
* @param
|
|
378
|
-
* @param
|
|
379
|
-
* @param categories An array holding the category names
|
|
470
|
+
* @param location The location of the axis (left or right)
|
|
471
|
+
* @param categories The category names (the axis' `update` function can replace them later)
|
|
380
472
|
* @param axisLabel The axis label
|
|
381
|
-
* @param axesLabelFont The font for the axis label
|
|
382
|
-
*
|
|
473
|
+
* @param axesLabelFont The font for the axis label (its color is also the color of the axis line,
|
|
474
|
+
* ticks, and tick labels)
|
|
475
|
+
* @param axisTickStyle Styling information for the ticks (font, rotation, etc.)
|
|
383
476
|
* @param plotDimensions The dimensions of the plot
|
|
384
477
|
* @param margin The plot margin
|
|
385
|
-
* @param setAxisRangeFor
|
|
386
|
-
* @param setOriginalAxisRangeFor
|
|
387
|
-
* @return
|
|
478
|
+
* @param setAxisRangeFor Callback that sets the axis range for the specified axis
|
|
479
|
+
* @param setOriginalAxisRangeFor Callback that sets the original axis range for the specified axis
|
|
480
|
+
* @return An {@link OrdinalStringAxis}
|
|
388
481
|
*/
|
|
389
482
|
function addOrdinalStringYAxis(
|
|
390
|
-
|
|
483
|
+
cc: CanvasContext,
|
|
391
484
|
axisId: string,
|
|
392
|
-
|
|
393
|
-
location: AxisLocation.Left | AxisLocation.Right,
|
|
485
|
+
location: typeof AxisLocation.Left | typeof AxisLocation.Right,
|
|
394
486
|
categories: Array<string>,
|
|
395
487
|
axisLabel: string,
|
|
396
488
|
axesLabelFont: AxesFont,
|
|
@@ -400,173 +492,154 @@ function addOrdinalStringYAxis(
|
|
|
400
492
|
setAxisRangeFor: (axisId: string, range: AxisInterval) => void,
|
|
401
493
|
setOriginalAxisRangeFor: (axisId: string, range: AxisInterval) => void,
|
|
402
494
|
): OrdinalStringAxis {
|
|
403
|
-
const scale = d3.scaleBand()
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
.
|
|
422
|
-
|
|
423
|
-
.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
495
|
+
const scale = d3.scaleBand<string>().domain(categories).range([0, plotDimensions.height])
|
|
496
|
+
|
|
497
|
+
let currentDimensions = plotDimensions
|
|
498
|
+
let currentMargin = margin
|
|
499
|
+
let currentFont = axesLabelFont
|
|
500
|
+
const currentTickStyle = axisTickStyle
|
|
501
|
+
let highlighted = false
|
|
502
|
+
let highlightColor = defaultLineStyle().highlightColor
|
|
503
|
+
let highlightLineWidth = defaultLineStyle().highlightWidth
|
|
504
|
+
|
|
505
|
+
const drawHandle: DrawHandle = `y-axis-ordinal-${cc.chartId}-${axisId}`
|
|
506
|
+
|
|
507
|
+
const draw = (context: CanvasContext) => {
|
|
508
|
+
const {context2D} = context
|
|
509
|
+
context2D.save()
|
|
510
|
+
context2D.translate(xTranslation(location, currentDimensions, currentMargin), currentMargin.top)
|
|
511
|
+
|
|
512
|
+
context2D.save()
|
|
513
|
+
const clipWidth = location === AxisLocation.Left ? currentMargin.left : currentMargin.right
|
|
514
|
+
// the boundary that abuts the domain line (x=0) is nudged 1px past it (into the plot side),
|
|
515
|
+
// so the line's 1px-wide stroke -- centered on x=0, spanning -0.5 to +0.5 -- isn't clipped
|
|
516
|
+
// in half; mirrors the same -1 fudge used for the bottom x-axis's domain line
|
|
517
|
+
clipToArea(context, {width: clipWidth, height: currentDimensions.height}, {x: location === AxisLocation.Left ? -clipWidth + 1 : -1, y: 0})
|
|
518
|
+
|
|
519
|
+
const axisColor = highlighted ? highlightColor : currentFont.color
|
|
520
|
+
context2D.strokeStyle = axisColor
|
|
521
|
+
context2D.fillStyle = axisColor
|
|
522
|
+
context2D.lineWidth = highlighted ? highlightLineWidth : 1
|
|
523
|
+
context2D.font = fontStringFor(currentTickStyle.font.size, currentTickStyle.font.family, currentTickStyle.font.weight)
|
|
524
|
+
// matches the old SVG version, which forced text-anchor "end" for ordinal ticks
|
|
525
|
+
// regardless of Left/Right location
|
|
526
|
+
context2D.textAlign = 'end' as CanvasTextAlign
|
|
527
|
+
context2D.textBaseline = 'middle'
|
|
528
|
+
|
|
529
|
+
// domain line
|
|
530
|
+
context2D.beginPath()
|
|
531
|
+
context2D.moveTo(0, 0)
|
|
532
|
+
context2D.lineTo(0, currentDimensions.height)
|
|
533
|
+
context2D.stroke()
|
|
534
|
+
|
|
535
|
+
const degrees = location === AxisLocation.Left ? -currentTickStyle.rotation : currentTickStyle.rotation
|
|
536
|
+
const radians = degrees * Math.PI / 180
|
|
537
|
+
const tickDirection = location === AxisLocation.Left ? -1 : 1
|
|
538
|
+
// offset the label past the tick mark plus a little padding, rather than flush against
|
|
539
|
+
// the axis line -- this was previously just `0` (Left) / `scale.bandwidth()` (Right),
|
|
540
|
+
// which put the label's text-anchor edge right on top of the axis line with no gap
|
|
541
|
+
const xTick = (TICK_SIZE + TICK_PADDING) * tickDirection
|
|
542
|
+
|
|
543
|
+
categories.forEach(category => {
|
|
544
|
+
const y = (scale(category) ?? 0) + scale.bandwidth() / 2
|
|
545
|
+
context2D.beginPath()
|
|
546
|
+
context2D.moveTo(0, y)
|
|
547
|
+
context2D.lineTo(TICK_SIZE * tickDirection, y)
|
|
548
|
+
context2D.stroke()
|
|
549
|
+
|
|
550
|
+
context2D.save()
|
|
551
|
+
context2D.translate(xTick, y)
|
|
552
|
+
context2D.rotate(radians)
|
|
553
|
+
context2D.fillText(category, 0, 0)
|
|
554
|
+
context2D.restore()
|
|
432
555
|
})
|
|
433
556
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
.
|
|
557
|
+
context2D.restore() // pop the clip
|
|
558
|
+
|
|
559
|
+
// axis label, rotated -90deg, matching the old SVG version
|
|
560
|
+
context2D.save()
|
|
561
|
+
const labelX = ordinalLabelXTranslation(location, currentDimensions, currentMargin, currentFont) - xTranslation(location, currentDimensions, currentMargin)
|
|
562
|
+
const labelY = ordinalLabelYTranslation(location, currentDimensions, currentMargin) - currentMargin.top
|
|
563
|
+
context2D.translate(labelX, labelY)
|
|
564
|
+
context2D.rotate(-Math.PI / 2)
|
|
565
|
+
context2D.fillStyle = axisColor
|
|
566
|
+
context2D.font = fontStringFor(currentFont.size, currentFont.family, currentFont.weight)
|
|
567
|
+
context2D.textAlign = 'center'
|
|
568
|
+
context2D.textBaseline = 'alphabetic'
|
|
569
|
+
context2D.fillText(axisLabel, 0, 0)
|
|
570
|
+
context2D.restore()
|
|
571
|
+
|
|
572
|
+
context2D.restore()
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
cc.register(drawHandle, draw, 0)
|
|
444
576
|
|
|
445
|
-
const
|
|
577
|
+
const categorySize = () => scale.bandwidth()
|
|
446
578
|
|
|
447
579
|
return {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
580
|
+
axisId,
|
|
581
|
+
location,
|
|
582
|
+
axisType: AxisType.OrdinalString,
|
|
583
|
+
scale,
|
|
584
|
+
categorySize: categorySize(),
|
|
585
|
+
update: (updatedCategories, range, originalRange, plotDimensions, margin) => {
|
|
586
|
+
// reassign the closed-over `categories` (not just a local variable) so `draw`'s own
|
|
587
|
+
// `categories.forEach(...)` picks up the change on the next redraw too
|
|
588
|
+
categories = updatedCategories
|
|
589
|
+
const updatedRange = AxisInterval.from(
|
|
590
|
+
Math.min(range.start, 0),
|
|
591
|
+
Math.max(range.end, plotDimensions.height)
|
|
592
|
+
)
|
|
593
|
+
scale.domain(categories).range(updatedRange.asTuple())
|
|
594
|
+
currentDimensions = plotDimensions
|
|
595
|
+
currentMargin = margin
|
|
451
596
|
setAxisRangeFor(axisId, range)
|
|
452
597
|
setOriginalAxisRangeFor(axisId, originalRange)
|
|
453
|
-
|
|
598
|
+
cc.requestRedraw()
|
|
599
|
+
return categorySize()
|
|
600
|
+
},
|
|
601
|
+
setHighlighted: (isHighlighted, color, lineWidth) => {
|
|
602
|
+
highlighted = isHighlighted
|
|
603
|
+
if (color !== undefined) highlightColor = color
|
|
604
|
+
if (lineWidth !== undefined) highlightLineWidth = lineWidth
|
|
605
|
+
cc.requestRedraw()
|
|
606
|
+
},
|
|
607
|
+
updateFont: (font) => {
|
|
608
|
+
// the font's color is used for the label, the axis line, the ticks, and the tick
|
|
609
|
+
// labels; the tick labels' size, family, and weight stay at axisTickStyle.font's
|
|
610
|
+
currentFont = font
|
|
611
|
+
cc.requestRedraw()
|
|
454
612
|
}
|
|
455
613
|
}
|
|
456
614
|
}
|
|
457
615
|
|
|
458
616
|
/**
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
* @param
|
|
462
|
-
* @param axis The category axis to be updated
|
|
463
|
-
* @param svg The SVG selection (d3)
|
|
464
|
-
* @param location The location of the axis
|
|
465
|
-
* @param names An array holding the category names
|
|
466
|
-
* @param axesLabelFont The font for the axis label
|
|
467
|
-
* @param plotDimensions The dimensions of the plot
|
|
468
|
-
* @param margin The plot margin
|
|
469
|
-
* @param range The range of the axis (e.g., start and end) in pixels
|
|
470
|
-
* @param axesLabelFont The font for the axis label
|
|
471
|
-
* @param plotDimensions The dimensions of the plot
|
|
472
|
-
* @param margin The plot margin
|
|
473
|
-
* adjust the axis label location when needed
|
|
474
|
-
* @return The number of pixels for each category
|
|
617
|
+
* Removes a category x-axis' draw function from the canvas context (e.g. when the axis unmounts)
|
|
618
|
+
* @param cc The canvas context the axis' draw function is registered with
|
|
619
|
+
* @param axisId The ID of the axis
|
|
475
620
|
*/
|
|
476
|
-
function
|
|
477
|
-
chartId
|
|
478
|
-
axis: OrdinalStringAxis,
|
|
479
|
-
svg: SvgSelection,
|
|
480
|
-
location: AxisLocation.Bottom | AxisLocation.Top,
|
|
481
|
-
names: Array<string>, // domain (categories)
|
|
482
|
-
range: AxisInterval, // range (pixels)
|
|
483
|
-
axesLabelFont: AxesFont,
|
|
484
|
-
plotDimensions: Dimensions,
|
|
485
|
-
margin: Margin
|
|
486
|
-
): number {
|
|
487
|
-
const updatedRange = AxisInterval.from(
|
|
488
|
-
Math.min(range.start, 0),
|
|
489
|
-
Math.max(range.end, plotDimensions.width)
|
|
490
|
-
)
|
|
491
|
-
|
|
492
|
-
axis.scale = axis.scale
|
|
493
|
-
.domain(names)
|
|
494
|
-
.range(updatedRange.asTuple())
|
|
495
|
-
|
|
496
|
-
axis.categorySize = axis.scale.bandwidth()
|
|
497
|
-
|
|
498
|
-
const clipPathId = setClipPath(chartId, axis.axisId, svg, location, plotDimensions, margin)
|
|
499
|
-
axis.selection = axis.selection
|
|
500
|
-
.attr('transform', `translate(${margin.left}, ${yTranslation(location, plotDimensions, margin)})`)
|
|
501
|
-
.attr("clip-path", `url(#${clipPathId})`)
|
|
502
|
-
.call(axis.generator)
|
|
503
|
-
|
|
504
|
-
const xLabelTranslation = ordinalLabelXTranslation(location, plotDimensions, margin, axesLabelFont)
|
|
505
|
-
const yLabelTranslation = ordinalLabelYTranslation(location, plotDimensions, margin)
|
|
506
|
-
svg
|
|
507
|
-
.select(`#${labelIdFor(chartId, location)}`)
|
|
508
|
-
.attr('transform', `translate(${xLabelTranslation}, ${yLabelTranslation})`)
|
|
509
|
-
|
|
510
|
-
return axis.categorySize
|
|
621
|
+
export function removeOrdinalXAxis(cc: CanvasContext, axisId: string): void {
|
|
622
|
+
cc.unregister(`x-axis-ordinal-${cc.chartId}-${axisId}`)
|
|
511
623
|
}
|
|
512
624
|
|
|
513
625
|
/**
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
* @param
|
|
517
|
-
* @param axis The category axis to be updated
|
|
518
|
-
* @param svg The SVG selection (d3)
|
|
519
|
-
* @param location The location of the axis
|
|
520
|
-
* @param names An array holding the category names
|
|
521
|
-
* @param range The range of the axis (e.g. start and end)
|
|
522
|
-
* @param axesLabelFont The font for the axis label
|
|
523
|
-
* @param plotDimensions The dimensions of the plot
|
|
524
|
-
* @param margin The plot margin
|
|
525
|
-
* @param axesLabelFont The font for the axis label
|
|
526
|
-
* @param plotDimensions The dimensions of the plot
|
|
527
|
-
* @param margin The plot margin
|
|
528
|
-
* @return The number of pixels for each category
|
|
626
|
+
* Removes a category y-axis' draw function from the canvas context (e.g. when the axis unmounts)
|
|
627
|
+
* @param cc The canvas context the axis' draw function is registered with
|
|
628
|
+
* @param axisId The ID of the axis
|
|
529
629
|
*/
|
|
530
|
-
function
|
|
531
|
-
chartId
|
|
532
|
-
axis: OrdinalStringAxis,
|
|
533
|
-
svg: SvgSelection,
|
|
534
|
-
location: AxisLocation.Left | AxisLocation.Right,
|
|
535
|
-
names: Array<string>, // domain
|
|
536
|
-
range: AxisInterval, // range
|
|
537
|
-
axesLabelFont: AxesFont,
|
|
538
|
-
plotDimensions: Dimensions,
|
|
539
|
-
margin: Margin,
|
|
540
|
-
): number {
|
|
541
|
-
const updatedRange = AxisInterval.from(
|
|
542
|
-
Math.min(range.start, 0),
|
|
543
|
-
Math.max(range.end, plotDimensions.height)
|
|
544
|
-
)
|
|
545
|
-
|
|
546
|
-
axis.scale = axis.scale
|
|
547
|
-
.domain(names)
|
|
548
|
-
.range(updatedRange.asTuple())
|
|
549
|
-
axis.categorySize = axis.scale.bandwidth()
|
|
550
|
-
axis.selection = axis.selection
|
|
551
|
-
.attr('transform', `translate(${xTranslation(location, plotDimensions, margin)}, ${margin.top})`)
|
|
552
|
-
.call(axis.generator)
|
|
553
|
-
|
|
554
|
-
svg
|
|
555
|
-
.select(`#${labelIdFor(chartId, location)}`)
|
|
556
|
-
.attr('transform', `translate(${ordinalLabelXTranslation(location, plotDimensions, margin, axesLabelFont)}, ${ordinalLabelYTranslation(location, plotDimensions, margin)}) rotate(-90)`)
|
|
557
|
-
|
|
558
|
-
return axis.categorySize
|
|
630
|
+
export function removeOrdinalYAxis(cc: CanvasContext, axisId: string): void {
|
|
631
|
+
cc.unregister(`y-axis-ordinal-${cc.chartId}-${axisId}`)
|
|
559
632
|
}
|
|
560
633
|
|
|
561
634
|
/**
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
* axis
|
|
565
|
-
* @param location The
|
|
635
|
+
* The x-coordinate (in canvas pixels, from the canvas' left edge) of a category axis' label: the
|
|
636
|
+
* label's font size in from the canvas' left or right edge for a y-axis, or the canvas' center for
|
|
637
|
+
* an x-axis
|
|
638
|
+
* @param location The location of the axis
|
|
566
639
|
* @param plotDimensions The dimensions of the plot
|
|
567
|
-
* @param margin The
|
|
640
|
+
* @param margin The plot margin
|
|
568
641
|
* @param axesLabelFont The font for the axis label
|
|
569
|
-
* @return The
|
|
642
|
+
* @return The x-coordinate of the axis label
|
|
570
643
|
*/
|
|
571
644
|
function ordinalLabelXTranslation(
|
|
572
645
|
location: AxisLocation,
|
|
@@ -587,13 +660,12 @@ function ordinalLabelXTranslation(
|
|
|
587
660
|
}
|
|
588
661
|
|
|
589
662
|
/**
|
|
590
|
-
*
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
* @param location The axis location (i.e. top, bottom, left, right)
|
|
663
|
+
* The y-coordinate (in canvas pixels, from the canvas' top edge) of a category axis' label: the
|
|
664
|
+
* canvas' middle for a y-axis, or the canvas' top or bottom edge for an x-axis
|
|
665
|
+
* @param location The location of the axis
|
|
594
666
|
* @param plotDimensions The dimensions of the plot
|
|
595
|
-
* @param margin The
|
|
596
|
-
* @return The
|
|
667
|
+
* @param margin The plot margin
|
|
668
|
+
* @return The y-coordinate of the axis label
|
|
597
669
|
*/
|
|
598
670
|
function ordinalLabelYTranslation(
|
|
599
671
|
location: AxisLocation,
|
|
@@ -617,142 +689,190 @@ function ordinalLabelYTranslation(
|
|
|
617
689
|
*/
|
|
618
690
|
|
|
619
691
|
/**
|
|
620
|
-
* Adds a new empty x-axis
|
|
692
|
+
* Adds a new, empty x-axis (a line with no ticks or label) to the canvas context. An empty axis
|
|
621
693
|
* is just a line where the axis would be, without any ticks or labels.
|
|
694
|
+
* @param canvasContext The canvas context to register the axis' draw function with
|
|
622
695
|
* @param axisId The ID of the axis
|
|
623
|
-
* @param svg The SVG selection to which to add the axis
|
|
624
696
|
* @param plotDimensions The dimensions of the plot
|
|
625
697
|
* @param location The location of the axis
|
|
626
|
-
* @param scaleGenerator The
|
|
627
|
-
* @param
|
|
628
|
-
* @param
|
|
629
|
-
* @param
|
|
630
|
-
* @
|
|
698
|
+
* @param scaleGenerator The d3 scale to use for the axis (its domain and range get overwritten)
|
|
699
|
+
* @param margin The plot margins
|
|
700
|
+
* @param setAxisRangeFor A callback used to report the axis' domain when it's updated
|
|
701
|
+
* @param color The color of the axis line (the axis' `updateFont` updates it with the font's color)
|
|
702
|
+
* @param [domain=[0, 1]] The axis' initial domain (min, max)
|
|
703
|
+
* @return A {@link ContinuousNumericAxis} (marked `isEmpty`) based on the arguments to this function
|
|
631
704
|
*/
|
|
632
705
|
export function addEmptyXAxis(
|
|
706
|
+
canvasContext: CanvasContext,
|
|
633
707
|
axisId: string,
|
|
634
|
-
svg: SvgSelection,
|
|
635
708
|
plotDimensions: Dimensions,
|
|
636
|
-
location: AxisLocation.Bottom | AxisLocation.Top,
|
|
709
|
+
location: typeof AxisLocation.Bottom | typeof AxisLocation.Top,
|
|
637
710
|
scaleGenerator: ScaleContinuousNumeric<number, number>,
|
|
638
711
|
margin: Margin,
|
|
639
712
|
setAxisRangeFor: (axisId: string, timeRange: AxisInterval) => void,
|
|
713
|
+
color: string,
|
|
640
714
|
domain: [minValue: number, maxValue: number] = [0, 1],
|
|
641
715
|
): ContinuousNumericAxis {
|
|
642
716
|
const scale = scaleGenerator.domain(domain).range([0, plotDimensions.width])
|
|
643
717
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
718
|
+
let currentDimensions = plotDimensions
|
|
719
|
+
let currentMargin = margin
|
|
720
|
+
// NOTE: previously this read `ctx.fillStyle` directly, assuming it reflected "the chart's
|
|
721
|
+
// current color". But `ctx` is shared across every draw function registered on the canvas
|
|
722
|
+
// (all axes, all plots, the tracker). Whichever one last set `fillStyle` before this one
|
|
723
|
+
// ran left that value behind. It had nothing to do with this axis' actual color and never
|
|
724
|
+
// updated when the chart's `color`/theme changed, since nothing here ever assigned a new
|
|
725
|
+
// value in response to that. The `currentColor` is now explicit, passed in and refreshable via
|
|
726
|
+
// `updateFont` below.
|
|
727
|
+
let currentColor = color
|
|
728
|
+
let highlighted = false
|
|
729
|
+
let highlightColor = defaultLineStyle().highlightColor
|
|
730
|
+
let highlightLineWidth = defaultLineStyle().highlightWidth
|
|
731
|
+
|
|
732
|
+
const drawHandle: DrawHandle = `x-axis-empty-${canvasContext.chartId}-${axisId}`
|
|
733
|
+
|
|
734
|
+
const draw = (context: CanvasContext) => {
|
|
735
|
+
const {context2D} = context
|
|
736
|
+
context2D.save()
|
|
737
|
+
context2D.translate(currentMargin.left, yTranslation(location, currentDimensions, currentMargin))
|
|
738
|
+
context2D.strokeStyle = highlighted ? highlightColor : currentColor
|
|
739
|
+
context2D.lineWidth = highlighted ? highlightLineWidth : 1
|
|
740
|
+
context2D.beginPath()
|
|
741
|
+
context2D.moveTo(0, 0)
|
|
742
|
+
context2D.lineTo(currentDimensions.width, 0)
|
|
743
|
+
context2D.stroke()
|
|
744
|
+
context2D.restore()
|
|
745
|
+
}
|
|
647
746
|
|
|
648
|
-
|
|
649
|
-
.attr('x1', 0)
|
|
650
|
-
.attr('y1', 0)
|
|
651
|
-
.attr('x2', plotDimensions.width)
|
|
652
|
-
.attr('y2', 0)
|
|
653
|
-
.attr('stroke', 'currentColor')
|
|
747
|
+
canvasContext.register(drawHandle, draw, 0)
|
|
654
748
|
|
|
655
|
-
|
|
749
|
+
return {
|
|
656
750
|
axisId,
|
|
657
751
|
location,
|
|
658
|
-
|
|
752
|
+
axisType: AxisType.ContinuousNumeric,
|
|
753
|
+
isEmpty: true,
|
|
659
754
|
scale,
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
...axis,
|
|
665
|
-
update: (domain: AxisInterval, plotDimensions: Dimensions, margin: Margin) => {
|
|
666
|
-
axis.scale.domain(domain.asTuple()).range([0, plotDimensions.width])
|
|
667
|
-
axis.selection
|
|
668
|
-
.attr('transform', `translate(${margin.left}, ${yTranslation(location, plotDimensions, margin)})`)
|
|
669
|
-
|
|
670
|
-
line.attr('x2', plotDimensions.width)
|
|
671
|
-
|
|
755
|
+
update: (domain, plotDimensions, margin) => {
|
|
756
|
+
scale.domain(domain.asTuple()).range([0, plotDimensions.width])
|
|
757
|
+
currentDimensions = plotDimensions
|
|
758
|
+
currentMargin = margin
|
|
672
759
|
setAxisRangeFor(axisId, domain)
|
|
760
|
+
canvasContext.requestRedraw()
|
|
761
|
+
},
|
|
762
|
+
// an empty axis has no ticks or label text but does have a colored line -- reuse
|
|
763
|
+
// updateFont's color field to keep that line in sync with the chart's color (e.g., on a
|
|
764
|
+
// theme change), matching the pattern used by the other axis types
|
|
765
|
+
updateFont: (font) => {
|
|
766
|
+
currentColor = font.color
|
|
767
|
+
canvasContext.requestRedraw()
|
|
768
|
+
},
|
|
769
|
+
setHighlighted: (isHighlighted, color, lineWidth) => {
|
|
770
|
+
highlighted = isHighlighted
|
|
771
|
+
if (color !== undefined) highlightColor = color
|
|
772
|
+
if (lineWidth !== undefined) highlightLineWidth = lineWidth
|
|
773
|
+
canvasContext.requestRedraw()
|
|
673
774
|
}
|
|
674
775
|
}
|
|
675
776
|
}
|
|
676
777
|
|
|
677
778
|
/**
|
|
678
|
-
* Adds a new empty y-axis
|
|
679
|
-
*
|
|
779
|
+
* Adds a new, empty y-axis (a line with no ticks or label) to the canvas context.
|
|
780
|
+
* @param cc The canvas context to register the axis' draw function with
|
|
680
781
|
* @param axisId The ID of the axis
|
|
681
|
-
* @param svg The SVG selection to which to add the axis
|
|
682
782
|
* @param plotDimensions The dimensions of the plot
|
|
683
783
|
* @param location The location of the axis
|
|
684
|
-
* @param scaleGenerator The
|
|
685
|
-
* @param
|
|
686
|
-
* @param
|
|
687
|
-
* @param
|
|
688
|
-
* @
|
|
784
|
+
* @param scaleGenerator The d3 scale to use for the axis (its domain and range get overwritten)
|
|
785
|
+
* @param margin The plot margins
|
|
786
|
+
* @param setAxisRangeFor A callback used to report the axis' domain when it's updated
|
|
787
|
+
* @param color The color of the axis line (the axis' `updateFont` updates it with the font's color)
|
|
788
|
+
* @param [domain=[0, 1]] The axis' initial domain (min, max)
|
|
789
|
+
* @return A {@link ContinuousNumericAxis} (marked `isEmpty`) based on the arguments to this function
|
|
689
790
|
*/
|
|
690
791
|
export function addEmptyYAxis(
|
|
792
|
+
cc: CanvasContext,
|
|
691
793
|
axisId: string,
|
|
692
|
-
svg: SvgSelection,
|
|
693
794
|
plotDimensions: Dimensions,
|
|
694
|
-
location: AxisLocation.Left | AxisLocation.Right,
|
|
795
|
+
location: typeof AxisLocation.Left | typeof AxisLocation.Right,
|
|
695
796
|
scaleGenerator: ScaleContinuousNumeric<number, number>,
|
|
696
797
|
margin: Margin,
|
|
697
798
|
setAxisRangeFor: (axisId: string, timeRange: AxisInterval) => void,
|
|
799
|
+
color: string,
|
|
698
800
|
domain: [minValue: number, maxValue: number] = [0, 1],
|
|
699
801
|
): ContinuousNumericAxis {
|
|
700
802
|
const scale = scaleGenerator.domain(domain).range([plotDimensions.height, 0])
|
|
701
803
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
804
|
+
let currentDimensions = plotDimensions
|
|
805
|
+
let currentMargin = margin
|
|
806
|
+
let currentColor = color
|
|
807
|
+
let highlighted = false
|
|
808
|
+
let highlightColor = defaultLineStyle().highlightColor
|
|
809
|
+
let highlightLineWidth = defaultLineStyle().highlightWidth
|
|
810
|
+
|
|
811
|
+
const drawHandle: DrawHandle = `y-axis-empty-${cc.chartId}-${axisId}`
|
|
812
|
+
|
|
813
|
+
const draw = (context: CanvasContext) => {
|
|
814
|
+
const {context2D} = context
|
|
815
|
+
context2D.save()
|
|
816
|
+
context2D.translate(xTranslation(location, currentDimensions, currentMargin), currentMargin.top)
|
|
817
|
+
context2D.strokeStyle = highlighted ? highlightColor : currentColor
|
|
818
|
+
context2D.lineWidth = highlighted ? highlightLineWidth : 1
|
|
819
|
+
context2D.beginPath()
|
|
820
|
+
context2D.moveTo(0, 0)
|
|
821
|
+
context2D.lineTo(0, currentDimensions.height)
|
|
822
|
+
context2D.stroke()
|
|
823
|
+
context2D.restore()
|
|
824
|
+
}
|
|
705
825
|
|
|
706
|
-
|
|
707
|
-
.attr('x1', 0)
|
|
708
|
-
.attr('y1', 0)
|
|
709
|
-
.attr('x2', 0)
|
|
710
|
-
.attr('y2', plotDimensions.height)
|
|
711
|
-
.attr('stroke', 'currentColor')
|
|
826
|
+
cc.register(drawHandle, draw, 0)
|
|
712
827
|
|
|
713
|
-
|
|
828
|
+
return {
|
|
714
829
|
axisId,
|
|
715
830
|
location,
|
|
716
|
-
|
|
831
|
+
axisType: AxisType.ContinuousNumeric,
|
|
832
|
+
isEmpty: true,
|
|
717
833
|
scale,
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
...axis,
|
|
723
|
-
update: (domain: AxisInterval, plotDimensions: Dimensions, margin: Margin) => {
|
|
724
|
-
axis.scale.domain(domain.asTuple()).range([plotDimensions.height, 0])
|
|
725
|
-
axis.selection
|
|
726
|
-
.attr('transform', `translate(${xTranslation(location, plotDimensions, margin)}, ${margin.top})`)
|
|
727
|
-
|
|
728
|
-
line.attr('y2', plotDimensions.height)
|
|
729
|
-
|
|
834
|
+
update: (domain, plotDimensions, margin) => {
|
|
835
|
+
scale.domain(domain.asTuple()).range([plotDimensions.height, 0])
|
|
836
|
+
currentDimensions = plotDimensions
|
|
837
|
+
currentMargin = margin
|
|
730
838
|
setAxisRangeFor(axisId, domain)
|
|
839
|
+
cc.requestRedraw()
|
|
840
|
+
},
|
|
841
|
+
// an empty axis has no ticks or label text but does have a colored line -- reuse
|
|
842
|
+
// updateFont's color field to keep that line in sync with the chart's color (e.g., on a
|
|
843
|
+
// theme change), matching the pattern used by the other axis types
|
|
844
|
+
updateFont: (font) => {
|
|
845
|
+
currentColor = font.color
|
|
846
|
+
cc.requestRedraw()
|
|
847
|
+
},
|
|
848
|
+
setHighlighted: (isHighlighted, color, lineWidth) => {
|
|
849
|
+
highlighted = isHighlighted
|
|
850
|
+
if (color !== undefined) highlightColor = color
|
|
851
|
+
if (lineWidth !== undefined) highlightLineWidth = lineWidth
|
|
852
|
+
cc.requestRedraw()
|
|
731
853
|
}
|
|
732
854
|
}
|
|
733
855
|
}
|
|
734
856
|
|
|
735
857
|
/**
|
|
736
|
-
* Adds a new x-axis
|
|
737
|
-
* @param
|
|
738
|
-
* @param axisId
|
|
739
|
-
* @param svg The SVG selection to which to add the axis
|
|
858
|
+
* Adds a new x-axis, registering its draw function with the canvas context.
|
|
859
|
+
* @param cc The canvas context to register the axis' draw function with
|
|
860
|
+
* @param axisId A unique ID for the axis
|
|
740
861
|
* @param plotDimensions The dimensions of the plot
|
|
741
|
-
* @param location The location of the axis
|
|
742
|
-
* @param scaleGenerator The
|
|
743
|
-
* @param domain The axis
|
|
744
|
-
* @param axesLabelFont The font for the axis
|
|
745
|
-
* @param margin The plot margins
|
|
862
|
+
* @param location The location of the axis (top or bottom)
|
|
863
|
+
* @param scaleGenerator The d3 scale to use for the axis (its domain/range get overwritten)
|
|
864
|
+
* @param domain The axis' initial domain (min, max)
|
|
865
|
+
* @param axesLabelFont The font for the axis label and ticks
|
|
866
|
+
* @param margin The plot margins
|
|
746
867
|
* @param axisLabel The label for the axis
|
|
747
|
-
* @param setAxisRangeFor
|
|
748
|
-
* @return A {@link ContinuousNumericAxis}
|
|
868
|
+
* @param setAxisRangeFor Callback used to report the axis' domain when it's updated
|
|
869
|
+
* @return A {@link ContinuousNumericAxis}
|
|
749
870
|
*/
|
|
750
871
|
export function addContinuousNumericXAxis(
|
|
751
|
-
|
|
872
|
+
cc: CanvasContext,
|
|
752
873
|
axisId: string,
|
|
753
|
-
svg: SvgSelection,
|
|
754
874
|
plotDimensions: Dimensions,
|
|
755
|
-
location: AxisLocation.Bottom | AxisLocation.Top,
|
|
875
|
+
location: typeof AxisLocation.Bottom | typeof AxisLocation.Top,
|
|
756
876
|
scaleGenerator: ScaleContinuousNumeric<number, number>,
|
|
757
877
|
domain: [minValue: number, maxValue: number],
|
|
758
878
|
axesLabelFont: AxesFont,
|
|
@@ -762,104 +882,112 @@ export function addContinuousNumericXAxis(
|
|
|
762
882
|
): ContinuousNumericAxis {
|
|
763
883
|
const scale = scaleGenerator.domain(domain).range([0, plotDimensions.width])
|
|
764
884
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
885
|
+
let currentDimensions = plotDimensions
|
|
886
|
+
let currentMargin = margin
|
|
887
|
+
const currentLabel = axisLabel
|
|
888
|
+
let currentFont = axesLabelFont
|
|
889
|
+
let highlighted = false
|
|
890
|
+
let highlightColor = defaultLineStyle().highlightColor
|
|
891
|
+
let highlightLineWidth = defaultLineStyle().highlightWidth
|
|
892
|
+
|
|
893
|
+
const drawHandle: DrawHandle = `x-axis-${cc.chartId}-${axisId}`
|
|
894
|
+
|
|
895
|
+
const draw = (context: CanvasContext) => {
|
|
896
|
+
const {context2D} = context
|
|
897
|
+
context2D.save()
|
|
898
|
+
context2D.translate(currentMargin.left, yTranslation(location, currentDimensions, currentMargin))
|
|
899
|
+
|
|
900
|
+
// NOTE: the original SVG version never applied a clip-path to the continuous numeric
|
|
901
|
+
// axes (only the ordinal/category axes clip), which let the first/last tick labels
|
|
902
|
+
// overflow into the margin without being cut off. Clipping to exactly the plot width
|
|
903
|
+
// here would chop those end labels in half, so ticks/labels are intentionally unclipped.
|
|
904
|
+
const axisColor = highlighted ? highlightColor : currentFont.color
|
|
905
|
+
context2D.strokeStyle = axisColor
|
|
906
|
+
context2D.fillStyle = axisColor
|
|
907
|
+
context2D.lineWidth = highlighted ? highlightLineWidth : 1
|
|
908
|
+
context2D.font = fontStringFor(currentFont.size, currentFont.family, currentFont.weight)
|
|
909
|
+
context2D.textAlign = 'center'
|
|
910
|
+
context2D.textBaseline = location === AxisLocation.Bottom ? 'top' : 'bottom'
|
|
911
|
+
|
|
912
|
+
// domain line
|
|
913
|
+
context2D.beginPath()
|
|
914
|
+
context2D.moveTo(0, 0)
|
|
915
|
+
context2D.lineTo(currentDimensions.width, 0)
|
|
916
|
+
context2D.stroke()
|
|
917
|
+
|
|
918
|
+
// ticks
|
|
919
|
+
const tickDirection = location === AxisLocation.Bottom ? 1 : -1
|
|
920
|
+
scale.ticks().forEach(tickValue => {
|
|
921
|
+
const x = scale(tickValue)
|
|
922
|
+
context2D.beginPath()
|
|
923
|
+
context2D.moveTo(x, 0)
|
|
924
|
+
context2D.lineTo(x, TICK_SIZE * tickDirection)
|
|
925
|
+
context2D.stroke()
|
|
926
|
+
context2D.fillText(
|
|
927
|
+
scale.tickFormat()(tickValue),
|
|
928
|
+
x,
|
|
929
|
+
(TICK_SIZE + TICK_PADDING) * tickDirection
|
|
930
|
+
)
|
|
931
|
+
})
|
|
932
|
+
|
|
933
|
+
// axis label (not clipped, matches the old behavior)
|
|
934
|
+
context2D.fillStyle = axisColor
|
|
935
|
+
context2D.font = fontStringFor(currentFont.size, currentFont.family, currentFont.weight)
|
|
936
|
+
context2D.textAlign = 'center'
|
|
937
|
+
// (a bottom label sits on the canvas' bottom edge, so it's drawn above it, descenders and all)
|
|
938
|
+
context2D.textBaseline = location === AxisLocation.Top ? 'hanging' : 'bottom'
|
|
939
|
+
const labelY = continuousLabelYTranslation(location, currentDimensions, currentMargin) - yTranslation(location, currentDimensions, currentMargin)
|
|
940
|
+
context2D.fillText(currentLabel, currentDimensions.width / 2, labelY)
|
|
941
|
+
|
|
942
|
+
context2D.restore()
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
cc.register(drawHandle, draw, 0)
|
|
946
|
+
|
|
947
|
+
return {
|
|
783
948
|
axisId,
|
|
784
949
|
location,
|
|
785
|
-
|
|
950
|
+
axisType: AxisType.ContinuousNumeric,
|
|
786
951
|
scale,
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
...axis,
|
|
792
|
-
update: (domain: AxisInterval, plotDimensions: Dimensions, margin: Margin) => {
|
|
793
|
-
updateLinearXAxis(domain, axisLabel, chartId, svg, axis, plotDimensions, margin, location)
|
|
952
|
+
update: (domain, plotDimensions, margin) => {
|
|
953
|
+
scale.domain(domain.asTuple()).range([0, plotDimensions.width])
|
|
954
|
+
currentDimensions = plotDimensions
|
|
955
|
+
currentMargin = margin
|
|
794
956
|
setAxisRangeFor(axisId, domain)
|
|
957
|
+
cc.requestRedraw()
|
|
958
|
+
},
|
|
959
|
+
updateFont: (font) => {
|
|
960
|
+
currentFont = font
|
|
961
|
+
cc.requestRedraw()
|
|
962
|
+
},
|
|
963
|
+
setHighlighted: (isHighlighted, color, lineWidth) => {
|
|
964
|
+
highlighted = isHighlighted
|
|
965
|
+
if (color !== undefined) highlightColor = color
|
|
966
|
+
if (lineWidth !== undefined) highlightLineWidth = lineWidth
|
|
967
|
+
cc.requestRedraw()
|
|
795
968
|
}
|
|
796
969
|
}
|
|
797
970
|
}
|
|
798
971
|
|
|
799
972
|
/**
|
|
800
|
-
*
|
|
801
|
-
* @param
|
|
802
|
-
* @param
|
|
803
|
-
* @param chartId The ID of the chart
|
|
804
|
-
* @param svg The SVG selection of which the axis is a child node
|
|
805
|
-
* @param axis The x-axis
|
|
806
|
-
* @param plotDimensions The dimensions of the plot
|
|
807
|
-
* @param margin The plot margins for the border of main SVG group
|
|
808
|
-
* @param location The location of the axis (i.e. top, bottom, left, right)
|
|
809
|
-
*/
|
|
810
|
-
function updateLinearXAxis(
|
|
811
|
-
domain: AxisInterval,
|
|
812
|
-
label: string,
|
|
813
|
-
chartId: number,
|
|
814
|
-
svg: SvgSelection,
|
|
815
|
-
axis: ContinuousNumericAxis,
|
|
816
|
-
plotDimensions: Dimensions,
|
|
817
|
-
margin: Margin,
|
|
818
|
-
location: AxisLocation.Bottom | AxisLocation.Top,
|
|
819
|
-
): void {
|
|
820
|
-
axis.scale.domain(domain.asTuple()).range([0, plotDimensions.width])
|
|
821
|
-
|
|
822
|
-
axis.selection
|
|
823
|
-
.attr('transform', `translate(${margin.left}, ${yTranslation(location, plotDimensions, margin)})`)
|
|
824
|
-
.call(axis.generator)
|
|
825
|
-
svg
|
|
826
|
-
.select(`#${labelIdFor(chartId, location)}`)
|
|
827
|
-
.attr('transform', `translate(${margin.left + plotDimensions.width / 2}, ${continuousLabelYTranslation(location, plotDimensions, margin)})`)
|
|
828
|
-
.text(label)
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
/**
|
|
832
|
-
* The number of pixels to translate the x-axis label to the right
|
|
833
|
-
* @param location The location of the x-axis
|
|
834
|
-
* @param plotDimensions The dimensions of the plot
|
|
835
|
-
* @param margin The plot margins for the border of main SVG group
|
|
836
|
-
* @return The number of pixels to translate the x-axis label to the right
|
|
837
|
-
*/
|
|
838
|
-
function continuousLabelYTranslation(location: AxisLocation.Bottom | AxisLocation.Top, plotDimensions: Dimensions, margin: Margin): number {
|
|
839
|
-
return location === AxisLocation.Bottom ? plotDimensions.height + margin.top + margin.bottom : 0
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
/**
|
|
843
|
-
* Adds a new y-axis to the SVG element at the specified location
|
|
844
|
-
* @param chartId The ID of the chart
|
|
845
|
-
* @param axisId The ID of the axis
|
|
846
|
-
* @param svg The SVG selection to which to add the axis
|
|
973
|
+
* Adds a new y-axis, registering its draw function with the canvas context.
|
|
974
|
+
* @param cc The canvas context to register the axis' draw function with
|
|
975
|
+
* @param axisId A unique ID for the axis
|
|
847
976
|
* @param plotDimensions The dimensions of the plot
|
|
848
|
-
* @param location The location of the axis
|
|
849
|
-
* @param scaleGenerator The
|
|
850
|
-
* @param domain The axis
|
|
851
|
-
* @param axesLabelFont The font for the axis
|
|
852
|
-
* @param margin The plot margins
|
|
977
|
+
* @param location The location of the axis (left or right)
|
|
978
|
+
* @param scaleGenerator The d3 scale to use for the axis (its domain/range get overwritten)
|
|
979
|
+
* @param domain The axis' initial domain (min, max)
|
|
980
|
+
* @param axesLabelFont The font for the axis label and ticks
|
|
981
|
+
* @param margin The plot margins
|
|
853
982
|
* @param axisLabel The label for the axis
|
|
854
|
-
* @param setAxisRangeFor
|
|
855
|
-
* @return A {@link ContinuousNumericAxis}
|
|
983
|
+
* @param setAxisRangeFor Callback used to report the axis' domain when it's updated
|
|
984
|
+
* @return A {@link ContinuousNumericAxis}
|
|
856
985
|
*/
|
|
857
986
|
export function addContinuousNumericYAxis(
|
|
858
|
-
|
|
987
|
+
cc: CanvasContext,
|
|
859
988
|
axisId: string,
|
|
860
|
-
svg: SvgSelection,
|
|
861
989
|
plotDimensions: Dimensions,
|
|
862
|
-
location: AxisLocation.Left | AxisLocation.Right,
|
|
990
|
+
location: typeof AxisLocation.Left | typeof AxisLocation.Right,
|
|
863
991
|
scaleGenerator: ScaleContinuousNumeric<number, number>,
|
|
864
992
|
domain: [minValue: number, maxValue: number],
|
|
865
993
|
axesLabelFont: AxesFont,
|
|
@@ -871,85 +999,113 @@ export function addContinuousNumericYAxis(
|
|
|
871
999
|
.domain(domain)
|
|
872
1000
|
.range([Math.max(margin.bottom, plotDimensions.height), 0])
|
|
873
1001
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1002
|
+
let currentDimensions = plotDimensions
|
|
1003
|
+
let currentMargin = margin
|
|
1004
|
+
const currentLabel = axisLabel
|
|
1005
|
+
let currentFont = axesLabelFont
|
|
1006
|
+
let highlighted = false
|
|
1007
|
+
let highlightColor = defaultLineStyle().highlightColor
|
|
1008
|
+
let highlightLineWidth = defaultLineStyle().highlightWidth
|
|
1009
|
+
|
|
1010
|
+
const drawHandle: DrawHandle = `y-axis-${cc.chartId}-${axisId}`
|
|
1011
|
+
|
|
1012
|
+
const draw = (context: CanvasContext) => {
|
|
1013
|
+
const {context2D} = context
|
|
1014
|
+
context2D.save()
|
|
1015
|
+
context2D.translate(xTranslation(location, currentDimensions, currentMargin), currentMargin.top)
|
|
1016
|
+
|
|
1017
|
+
// NOTE: unclipped, matching the old SVG version's continuous numeric axes (only the
|
|
1018
|
+
// ordinal/category axes clipped there) -- clipping to exactly the plot height would cut
|
|
1019
|
+
// off the top- and bottom-most tick labels, which straddle y=0 and y=height.
|
|
1020
|
+
const axisColor = highlighted ? highlightColor : currentFont.color
|
|
1021
|
+
context2D.strokeStyle = axisColor
|
|
1022
|
+
context2D.fillStyle = axisColor
|
|
1023
|
+
context2D.lineWidth = highlighted ? highlightLineWidth : 1
|
|
1024
|
+
context2D.font = fontStringFor(currentFont.size, currentFont.family, currentFont.weight)
|
|
1025
|
+
context2D.textBaseline = 'middle'
|
|
1026
|
+
context2D.textAlign = location === AxisLocation.Left ? 'right' : 'left'
|
|
1027
|
+
|
|
1028
|
+
// domain line
|
|
1029
|
+
context2D.beginPath()
|
|
1030
|
+
context2D.moveTo(0, 0)
|
|
1031
|
+
context2D.lineTo(0, currentDimensions.height)
|
|
1032
|
+
context2D.stroke()
|
|
1033
|
+
|
|
1034
|
+
// ticks
|
|
1035
|
+
const tickDirection = location === AxisLocation.Left ? -1 : 1
|
|
1036
|
+
scale.ticks().forEach(tickValue => {
|
|
1037
|
+
const y = scale(tickValue)
|
|
1038
|
+
context2D.beginPath()
|
|
1039
|
+
context2D.moveTo(0, y)
|
|
1040
|
+
context2D.lineTo(TICK_SIZE * tickDirection, y)
|
|
1041
|
+
context2D.stroke()
|
|
1042
|
+
context2D.fillText(
|
|
1043
|
+
scale.tickFormat()(tickValue),
|
|
1044
|
+
(TICK_SIZE + TICK_PADDING) * tickDirection,
|
|
1045
|
+
y
|
|
1046
|
+
)
|
|
1047
|
+
})
|
|
1048
|
+
|
|
1049
|
+
// axis label, rotated -90deg, matching the old SVG version
|
|
1050
|
+
context2D.save()
|
|
1051
|
+
const labelX = continuousLabelXTranslation(location, currentDimensions, currentMargin, currentFont) - xTranslation(location, currentDimensions, currentMargin)
|
|
1052
|
+
context2D.translate(labelX, currentDimensions.height / 2)
|
|
1053
|
+
context2D.rotate(-Math.PI / 2)
|
|
1054
|
+
context2D.fillStyle = axisColor
|
|
1055
|
+
context2D.font = fontStringFor(currentFont.size, currentFont.family, currentFont.weight)
|
|
1056
|
+
context2D.textAlign = 'center'
|
|
1057
|
+
context2D.textBaseline = 'alphabetic'
|
|
1058
|
+
context2D.fillText(currentLabel, 0, 0)
|
|
1059
|
+
context2D.restore()
|
|
1060
|
+
|
|
1061
|
+
context2D.restore()
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
cc.register(drawHandle, draw, 0)
|
|
1065
|
+
|
|
1066
|
+
return {
|
|
891
1067
|
axisId,
|
|
892
1068
|
location,
|
|
893
|
-
|
|
1069
|
+
axisType: AxisType.ContinuousNumeric,
|
|
894
1070
|
scale,
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
...axis,
|
|
900
|
-
update: (domain: AxisInterval, plotDimensions: Dimensions, margin: Margin) => {
|
|
901
|
-
updateLinearYAxis(domain, axisLabel, chartId, svg, axis, plotDimensions, margin, axesLabelFont, location)
|
|
1071
|
+
update: (domain, plotDimensions, margin) => {
|
|
1072
|
+
scale.domain(domain.asTuple()).range([Math.max(margin.bottom, plotDimensions.height), 0])
|
|
1073
|
+
currentDimensions = plotDimensions
|
|
1074
|
+
currentMargin = margin
|
|
902
1075
|
setAxisRangeFor(axisId, domain)
|
|
1076
|
+
cc.requestRedraw()
|
|
1077
|
+
},
|
|
1078
|
+
updateFont: (font) => {
|
|
1079
|
+
currentFont = font
|
|
1080
|
+
cc.requestRedraw()
|
|
1081
|
+
},
|
|
1082
|
+
setHighlighted: (isHighlighted, color, lineWidth) => {
|
|
1083
|
+
highlighted = isHighlighted
|
|
1084
|
+
if (color !== undefined) highlightColor = color
|
|
1085
|
+
if (lineWidth !== undefined) highlightLineWidth = lineWidth
|
|
1086
|
+
cc.requestRedraw()
|
|
903
1087
|
}
|
|
904
1088
|
}
|
|
905
1089
|
}
|
|
906
1090
|
|
|
907
1091
|
/**
|
|
908
|
-
*
|
|
909
|
-
*
|
|
910
|
-
* @param
|
|
911
|
-
* @param
|
|
912
|
-
* @param svg The SVG selection of which the axis is a child node
|
|
913
|
-
* @param axis The y-axis
|
|
914
|
-
* @param plotDimensions The dimensions of the plot
|
|
915
|
-
* @param margin The plot margins for the border of main SVG group
|
|
916
|
-
* @param axesLabelFont The font for the axis label
|
|
917
|
-
* @param location The location of the axis (i.e. top, bottom, left, right)
|
|
1092
|
+
* Removes a continuous x-axis' draw function from the canvas context (called on "unmount" to avoid
|
|
1093
|
+
* leaking a draw registration for an axis that's no longer part of the chart).
|
|
1094
|
+
* @param cc The canvas context the axis' draw function is registered with
|
|
1095
|
+
* @param axisId The ID of the axis
|
|
918
1096
|
*/
|
|
919
|
-
function
|
|
920
|
-
|
|
921
|
-
label: string,
|
|
922
|
-
chartId: number,
|
|
923
|
-
svg: SvgSelection,
|
|
924
|
-
axis: ContinuousNumericAxis,
|
|
925
|
-
plotDimensions: Dimensions,
|
|
926
|
-
margin: Margin,
|
|
927
|
-
axesLabelFont: AxesFont,
|
|
928
|
-
location: AxisLocation.Left | AxisLocation.Right,
|
|
929
|
-
): void {
|
|
930
|
-
axis.scale.domain(domain.asTuple()).range([Math.max(margin.bottom, plotDimensions.height), 0])
|
|
931
|
-
axis.selection
|
|
932
|
-
.attr('transform', `translate(${xTranslation(location, plotDimensions, margin)}, ${margin.top})`)
|
|
933
|
-
.call(axis.generator)
|
|
934
|
-
|
|
935
|
-
svg
|
|
936
|
-
.select(`#${labelIdFor(chartId, location)}`)
|
|
937
|
-
.attr('transform', `translate(${continuousLabelXTranslation(location, plotDimensions, margin, axesLabelFont)}, ${margin.top + plotDimensions.height / 2}) rotate(-90)`)
|
|
938
|
-
.text(label)
|
|
1097
|
+
export function removeContinuousXAxis(cc: CanvasContext, axisId: string): void {
|
|
1098
|
+
cc.unregister(`x-axis-${cc.chartId}-${axisId}`)
|
|
939
1099
|
}
|
|
940
1100
|
|
|
941
1101
|
/**
|
|
942
|
-
*
|
|
943
|
-
*
|
|
944
|
-
* @param
|
|
945
|
-
* @param
|
|
946
|
-
* @param axesLabelFont The font for the axis label
|
|
947
|
-
* @return The number of pixels to translate the y-axis label down
|
|
1102
|
+
* Removes a continuous y-axis' draw function from the canvas context (called on "unmount" to avoid
|
|
1103
|
+
* leaking a draw registration for an axis that's no longer part of the chart).
|
|
1104
|
+
* @param cc The canvas context the axis' draw function is registered with
|
|
1105
|
+
* @param axisId The ID of the axis
|
|
948
1106
|
*/
|
|
949
|
-
function
|
|
950
|
-
|
|
951
|
-
axesLabelFont.size :
|
|
952
|
-
margin.left + plotDimensions.width + margin.right - axesLabelFont.size
|
|
1107
|
+
export function removeContinuousYAxis(cc: CanvasContext, axisId: string): void {
|
|
1108
|
+
cc.unregister(`y-axis-${cc.chartId}-${axisId}`)
|
|
953
1109
|
}
|
|
954
1110
|
|
|
955
1111
|
/*
|
|
@@ -957,46 +1113,57 @@ function continuousLabelXTranslation(location: AxisLocation.Left | AxisLocation.
|
|
|
957
1113
|
*/
|
|
958
1114
|
|
|
959
1115
|
/**
|
|
960
|
-
*
|
|
961
|
-
*
|
|
1116
|
+
* The x-coordinate (in canvas pixels) of a y-axis' line: the plot's left edge for a left axis, or
|
|
1117
|
+
* its right edge for a right axis
|
|
1118
|
+
* @param location The location of the axis
|
|
962
1119
|
* @param plotDimensions The dimensions of the plot
|
|
963
|
-
* @param margin The
|
|
964
|
-
* @return The
|
|
1120
|
+
* @param margin The plot margin
|
|
1121
|
+
* @return The x-coordinate of the axis line
|
|
965
1122
|
*/
|
|
966
|
-
function xTranslation(location: AxisLocation.Left | AxisLocation.Right, plotDimensions: Dimensions, margin: Margin): number {
|
|
1123
|
+
function xTranslation(location: typeof AxisLocation.Left | typeof AxisLocation.Right, plotDimensions: Dimensions, margin: Margin): number {
|
|
967
1124
|
return location === AxisLocation.Left ? margin.left : margin.left + plotDimensions.width
|
|
968
1125
|
}
|
|
969
1126
|
|
|
970
1127
|
/**
|
|
971
|
-
* The
|
|
972
|
-
*
|
|
1128
|
+
* The y-coordinate (in canvas pixels) of an x-axis' line: the plot's bottom edge for a bottom
|
|
1129
|
+
* axis, or its top edge for a top axis
|
|
1130
|
+
* @param location The location of the axis
|
|
973
1131
|
* @param plotDimensions The dimensions of the plot
|
|
974
|
-
* @param margin The plot
|
|
975
|
-
* @return The
|
|
1132
|
+
* @param margin The plot margin
|
|
1133
|
+
* @return The y-coordinate of the axis line
|
|
976
1134
|
*/
|
|
977
|
-
function yTranslation(location: AxisLocation.Bottom | AxisLocation.Top, plotDimensions: Dimensions, margin: Margin): number {
|
|
1135
|
+
function yTranslation(location: typeof AxisLocation.Bottom | typeof AxisLocation.Top, plotDimensions: Dimensions, margin: Margin): number {
|
|
978
1136
|
return location === AxisLocation.Bottom ?
|
|
979
1137
|
plotDimensions.height + margin.top :
|
|
980
1138
|
margin.top
|
|
981
1139
|
}
|
|
982
1140
|
|
|
983
1141
|
/**
|
|
984
|
-
*
|
|
985
|
-
*
|
|
986
|
-
* @param location The
|
|
987
|
-
* @
|
|
1142
|
+
* The y-coordinate (in canvas pixels) of a continuous x-axis' label: the canvas' bottom edge for
|
|
1143
|
+
* a bottom axis, or its top edge for a top axis
|
|
1144
|
+
* @param location The location of the axis
|
|
1145
|
+
* @param plotDimensions The dimensions of the plot
|
|
1146
|
+
* @param margin The plot margin
|
|
1147
|
+
* @return The y-coordinate of the axis label
|
|
988
1148
|
*/
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
case AxisLocation.Left:
|
|
992
|
-
case AxisLocation.Right:
|
|
993
|
-
return `stream-chart-y-axis-${location}-label-${chartId}`
|
|
994
|
-
case AxisLocation.Top:
|
|
995
|
-
case AxisLocation.Bottom:
|
|
996
|
-
return `stream-chart-x-axis-${location}-label-${chartId}`
|
|
997
|
-
}
|
|
1149
|
+
function continuousLabelYTranslation(location: typeof AxisLocation.Bottom | typeof AxisLocation.Top, plotDimensions: Dimensions, margin: Margin): number {
|
|
1150
|
+
return location === AxisLocation.Bottom ? plotDimensions.height + margin.top + margin.bottom : 0
|
|
998
1151
|
}
|
|
999
1152
|
|
|
1153
|
+
/**
|
|
1154
|
+
* The x-coordinate (in canvas pixels) of a continuous y-axis' label: the label's font size in from
|
|
1155
|
+
* the canvas' left edge for a left axis, or from its right edge for a right axis
|
|
1156
|
+
* @param location The location of the axis
|
|
1157
|
+
* @param plotDimensions The dimensions of the plot
|
|
1158
|
+
* @param margin The plot margin
|
|
1159
|
+
* @param axesLabelFont The font for the axis label
|
|
1160
|
+
* @return The x-coordinate of the axis label
|
|
1161
|
+
*/
|
|
1162
|
+
function continuousLabelXTranslation(location: typeof AxisLocation.Left | typeof AxisLocation.Right, plotDimensions: Dimensions, margin: Margin, axesLabelFont: AxesFont): number {
|
|
1163
|
+
return location === AxisLocation.Left ?
|
|
1164
|
+
axesLabelFont.size :
|
|
1165
|
+
margin.left + plotDimensions.width + margin.right - axesLabelFont.size
|
|
1166
|
+
}
|
|
1000
1167
|
|
|
1001
1168
|
/*
|
|
1002
1169
|
zooming
|
|
@@ -1007,79 +1174,60 @@ export function labelIdFor(chartId: number, location: AxisLocation): string {
|
|
|
1007
1174
|
*/
|
|
1008
1175
|
export interface ZoomResult<AR extends BaseAxisRange> {
|
|
1009
1176
|
range: AR
|
|
1010
|
-
// range: ContinuousAxisRange
|
|
1011
|
-
zoomFactor: number
|
|
1012
1177
|
}
|
|
1013
1178
|
|
|
1014
|
-
// /**
|
|
1015
|
-
// * todo this can be generalized to any continuous numeric axis, replace x with value
|
|
1016
|
-
// * Called when the user uses the scroll wheel (or scroll gesture) to zoom in or out. Zooms in/out
|
|
1017
|
-
// * at the location of the mouse when the scroll wheel or gesture was applied.
|
|
1018
|
-
// * @param transform The d3 zoom transformation information
|
|
1019
|
-
// * @param x The x-position of the mouse when the scroll wheel or gesture is used
|
|
1020
|
-
// * @param axis The axis being zoomed
|
|
1021
|
-
// * @param range The current range for the axis being zoomed
|
|
1022
|
-
// * @return The updated range and the new zoom factor
|
|
1023
|
-
// */
|
|
1024
|
-
// // export function calculateZoomFor<AR extends BaseAxisRange>(
|
|
1025
|
-
// export function calculateZoomFor(
|
|
1026
|
-
// transform: ZoomTransform,
|
|
1027
|
-
// x: number,
|
|
1028
|
-
// axis: ContinuousNumericAxis,
|
|
1029
|
-
// range: ContinuousAxisRange,
|
|
1030
|
-
// ): ZoomResult<ContinuousAxisRange> {
|
|
1031
|
-
// const time = axis.generator.scale<ScaleLinear<number, number>>().invert(x);
|
|
1032
|
-
// return {
|
|
1033
|
-
// range: range.scale(transform.k, time),
|
|
1034
|
-
// zoomFactor: transform.k
|
|
1035
|
-
// } as ZoomResult<ContinuousAxisRange>
|
|
1036
|
-
// }
|
|
1037
|
-
|
|
1038
1179
|
/**
|
|
1039
1180
|
* Called when the user uses the scroll wheel (or scroll gesture) to zoom in or out. Zooms in/out
|
|
1040
|
-
*
|
|
1041
|
-
* the
|
|
1042
|
-
*
|
|
1043
|
-
*
|
|
1044
|
-
*
|
|
1181
|
+
* around the specified domain value (the pivot), while ensuring that the range (start, end) is
|
|
1182
|
+
* contained within the constraint (min, max). The pivot is a domain value rather than a pixel
|
|
1183
|
+
* position -- while streaming, callers pin it to the axis's current "now" rather than to wherever
|
|
1184
|
+
* the mouse happens to be (see `calcZoomAndUpdate`'s `pivotDomainValueFor`), which is what keeps
|
|
1185
|
+
* "now" fixed on screen as the zoom widens/narrows the window around it.
|
|
1186
|
+
*
|
|
1187
|
+
* `zoomFactor` is *incremental* (relative to the range's `.current`, not its `.original`) -- see
|
|
1188
|
+
* {@link BaseAxisRange.scaledRange}. d3-zoom's own `transform.k` is cumulative from wherever
|
|
1189
|
+
* `__zoom` was last reset to identity, so callers must convert it to an incremental factor (this
|
|
1190
|
+
* event's `k` divided by the previously-applied `k`) before calling this -- see
|
|
1191
|
+
* `useCanvasZoomPan`.
|
|
1192
|
+
* @param zoomFactor The incremental zoom scale factor for this event
|
|
1193
|
+
* @param domainValue The domain value to zoom around (the pivot)
|
|
1045
1194
|
* @param range The current range for the axis being zoomed
|
|
1046
1195
|
* @param constraint The minimum and maximum value the scaled range can have
|
|
1047
|
-
* @return The updated range
|
|
1196
|
+
* @return The updated range
|
|
1048
1197
|
*/
|
|
1049
1198
|
export function calculateConstrainedZoomFor(
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
axis: ContinuousNumericAxis,
|
|
1199
|
+
zoomFactor: number,
|
|
1200
|
+
domainValue: number,
|
|
1053
1201
|
range: ContinuousAxisRange,
|
|
1054
1202
|
constraint: [min: number, max: number],
|
|
1055
1203
|
): ZoomResult<ContinuousAxisRange> {
|
|
1056
|
-
const domainValue = axis.generator.scale<ScaleLinear<number, number>>().invert(x);
|
|
1057
1204
|
return {
|
|
1058
|
-
range: range.constrainedScale(
|
|
1059
|
-
|
|
1060
|
-
} as ZoomResult<ContinuousAxisRange>
|
|
1205
|
+
range: range.constrainedScale(zoomFactor, domainValue, constraint),
|
|
1206
|
+
}
|
|
1061
1207
|
}
|
|
1062
1208
|
|
|
1063
1209
|
/**
|
|
1064
|
-
* Calculates the zoom for an ordinal axis.
|
|
1065
|
-
*
|
|
1210
|
+
* Calculates the zoom for an ordinal axis. Unlike {@link calculateConstrainedZoomFor}'s continuous
|
|
1211
|
+
* axis, `zoomFactor` here is *cumulative* (d3-zoom's own `event.transform.k`, passed through
|
|
1212
|
+
* unmodified) -- see {@link OrdinalAxisRange.scaledCumulative} for why: the range is scaled from
|
|
1213
|
+
* `.current` by the incremental step the cumulative factor implies, except that a factor of
|
|
1214
|
+
* exactly 1 snaps it back to `.original`, so that zooming all the way out always shows every
|
|
1215
|
+
* category, whatever pivots were used along the way.
|
|
1216
|
+
* @param zoomFactor The cumulative zoom scale factor for this event
|
|
1066
1217
|
* @param x The x-position of the mouse when the scroll wheel or gesture is used
|
|
1067
1218
|
* @param range The current range for the axis being zoomed
|
|
1068
1219
|
* @param constraint The minimum and maximum value the scaled range can have
|
|
1069
|
-
* @return A ZoomResult holding the updated range
|
|
1220
|
+
* @return A ZoomResult holding the updated range
|
|
1070
1221
|
*/
|
|
1071
1222
|
export function calculateOrdinalConstrainedZoomFor(
|
|
1072
|
-
|
|
1223
|
+
zoomFactor: number,
|
|
1073
1224
|
x: number,
|
|
1074
1225
|
range: OrdinalAxisRange,
|
|
1075
1226
|
constraint: [min: number, max: number],
|
|
1076
1227
|
): ZoomResult<OrdinalAxisRange> {
|
|
1077
|
-
const updatedRange = range.constrainedScale(transform.k, x, constraint) as OrdinalAxisRange
|
|
1078
|
-
const k = range.original.equals(updatedRange.original) ? 1 : transform.k
|
|
1079
1228
|
return {
|
|
1080
|
-
range:
|
|
1081
|
-
|
|
1082
|
-
} as ZoomResult<OrdinalAxisRange>
|
|
1229
|
+
range: range.constrainedScale(zoomFactor, x, constraint) as OrdinalAxisRange,
|
|
1230
|
+
}
|
|
1083
1231
|
}
|
|
1084
1232
|
|
|
1085
1233
|
/*
|
|
@@ -1087,12 +1235,14 @@ export function calculateOrdinalConstrainedZoomFor(
|
|
|
1087
1235
|
*/
|
|
1088
1236
|
|
|
1089
1237
|
/**
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1092
|
-
* @param
|
|
1093
|
-
* @param
|
|
1238
|
+
* Calculates the axis range after the plot is dragged to the left or right (x-axis) or up or down
|
|
1239
|
+
* (y-axis).
|
|
1240
|
+
* @param delta The amount (in pixels) that the plot is dragged
|
|
1241
|
+
* @param axis The axis being panned
|
|
1242
|
+
* @param range The current range for the axis being panned
|
|
1094
1243
|
* @param [constrainToOriginalRange=false] When set to `true` then the pan requires that the axis
|
|
1095
|
-
* range remains a subset of the
|
|
1244
|
+
* range remains a subset of the original axis range (a pan that would leave it isn't made); when
|
|
1245
|
+
* `false` the pan allows any range
|
|
1096
1246
|
* @return The updated range
|
|
1097
1247
|
*/
|
|
1098
1248
|
export function calculatePanFor(
|
|
@@ -1101,7 +1251,7 @@ export function calculatePanFor(
|
|
|
1101
1251
|
range: ContinuousAxisRange,
|
|
1102
1252
|
constrainToOriginalRange: boolean = false
|
|
1103
1253
|
): ContinuousAxisRange {
|
|
1104
|
-
const scale = axis.
|
|
1254
|
+
const scale = axis.scale as ScaleLinear<number, number>
|
|
1105
1255
|
const value = scale(range.current.start)
|
|
1106
1256
|
if (value !== undefined) {
|
|
1107
1257
|
const deltaValue = scale.invert(value + delta) - range.current.start
|
|
@@ -1113,35 +1263,48 @@ export function calculatePanFor(
|
|
|
1113
1263
|
return range
|
|
1114
1264
|
}
|
|
1115
1265
|
|
|
1266
|
+
/**
|
|
1267
|
+
* Calculates the ordinal axis range after the plot is dragged to the left or right (x-axis) or up
|
|
1268
|
+
* or down (y-axis). A pan that would leave part of the plot without categories (i.e. take the
|
|
1269
|
+
* range's start past 0, or its end short of the plot's width or height) isn't made.
|
|
1270
|
+
* @param delta The amount (in pixels) to pan the axis by.
|
|
1271
|
+
* @param range The current axis range.
|
|
1272
|
+
* @param plotDimensions The dimensions of the plot.
|
|
1273
|
+
* @param location The location of the axis.
|
|
1274
|
+
* @param constrainToOriginalRange Whether to constrain the pan to the original range.
|
|
1275
|
+
* @returns The new axis range.
|
|
1276
|
+
*/
|
|
1116
1277
|
export function calculateOrdinalPanFor(
|
|
1117
1278
|
delta: number,
|
|
1118
1279
|
range: OrdinalAxisRange,
|
|
1119
1280
|
plotDimensions: Dimensions,
|
|
1281
|
+
location: AxisLocation,
|
|
1120
1282
|
constrainToOriginalRange: boolean = false
|
|
1121
1283
|
): OrdinalAxisRange {
|
|
1122
1284
|
const constraint: [start: number, end: number] = constrainToOriginalRange ?
|
|
1123
1285
|
range.original.asTuple() :
|
|
1124
1286
|
[-Infinity, Infinity]
|
|
1125
|
-
//
|
|
1126
|
-
//
|
|
1127
|
-
|
|
1287
|
+
// the axis's pixel extent depends on its location: a top/bottom (x) axis spans the plot's
|
|
1288
|
+
// width, while a left/right (y) axis spans the plot's height
|
|
1289
|
+
const extent = location === AxisLocation.Top || location === AxisLocation.Bottom ?
|
|
1290
|
+
plotDimensions.width :
|
|
1291
|
+
plotDimensions.height
|
|
1292
|
+
// only allow panning if the plot dimensions (i.e. [0, extent]) is in the current axis range
|
|
1293
|
+
if (range.current.start + delta > 0 || range.current.end + delta < extent) {
|
|
1128
1294
|
return range
|
|
1129
1295
|
}
|
|
1130
1296
|
return range.translate(delta, constraint) as OrdinalAxisRange
|
|
1131
1297
|
}
|
|
1132
1298
|
|
|
1133
|
-
/*
|
|
1134
|
-
|
|
1135
|
-
*/
|
|
1136
|
-
|
|
1137
1299
|
/**
|
|
1138
1300
|
* Accepts the series, the assignment of the series to axes, and the current x-axes state, and
|
|
1139
|
-
* returns an array of the distinct axis IDs that cover all the series in the plot
|
|
1301
|
+
* returns an array of the distinct x-axis IDs that cover all the series in the plot (a series
|
|
1302
|
+
* without an x-axis assignment uses the default x-axis).
|
|
1140
1303
|
*
|
|
1141
1304
|
* @param series The array of series
|
|
1142
1305
|
* @param axisAssignments A map association a series name with its axis assignments
|
|
1143
|
-
* @param axesState The current
|
|
1144
|
-
* @return an array of the distinct
|
|
1306
|
+
* @param axesState The current x-axes state
|
|
1307
|
+
* @return an array of the distinct x-axis IDs that cover all the series in the plot
|
|
1145
1308
|
*/
|
|
1146
1309
|
export function axesForSeriesGen<D, A extends BaseAxis>(
|
|
1147
1310
|
series: Array<BaseSeries<D>>,
|
|
@@ -1149,7 +1312,7 @@ export function axesForSeriesGen<D, A extends BaseAxis>(
|
|
|
1149
1312
|
axesState: AxesState<A>
|
|
1150
1313
|
): Array<string> {
|
|
1151
1314
|
return series.map(srs => srs.name)
|
|
1152
|
-
// grab the x-axis assigned to the series
|
|
1315
|
+
// grab the x-axis assigned to the series or use the default x-axis if not
|
|
1153
1316
|
// assignment has been made
|
|
1154
1317
|
.map(name => axisAssignments.get(name)?.xAxis || axesState.axisDefaultId().getOrElse(""))
|
|
1155
1318
|
// de-dup the array of axis IDs so that we don't end up applying the pan or zoom
|
|
@@ -1230,15 +1393,16 @@ function ordinalPanAxes(
|
|
|
1230
1393
|
const currentRange = ranges.get(axisId)
|
|
1231
1394
|
if (currentRange && axis) {
|
|
1232
1395
|
// calculate the change in the axis-range based on the pixel change from the drag event
|
|
1233
|
-
const range = calculateOrdinalPanFor(delta, currentRange, plotDimensions, constrainToOriginalRange)
|
|
1396
|
+
const range = calculateOrdinalPanFor(delta, currentRange, plotDimensions, axis.location, constrainToOriginalRange)
|
|
1234
1397
|
|
|
1235
1398
|
// update the time-range for the axis
|
|
1236
1399
|
ranges.set(axisId, range)
|
|
1237
1400
|
|
|
1238
1401
|
setAxisRange(axisId, range.current)
|
|
1239
1402
|
|
|
1240
|
-
// update the axis' time-range
|
|
1241
|
-
axis
|
|
1403
|
+
// update the axis' time-range -- panning never changes the set of categories, so
|
|
1404
|
+
// pass the axis's own current domain back unchanged
|
|
1405
|
+
axis.update(axis.scale.domain(), range.current, range.original, plotDimensions, margin)
|
|
1242
1406
|
}
|
|
1243
1407
|
})
|
|
1244
1408
|
})
|
|
@@ -1251,7 +1415,7 @@ function ordinalPanAxes(
|
|
|
1251
1415
|
* to the left or right. After calling the handler function, the plot needs to be updated as well, and this is
|
|
1252
1416
|
* left for the caller.
|
|
1253
1417
|
*
|
|
1254
|
-
* Please note that the function generated by this function has side effects -- it updates the
|
|
1418
|
+
* Please note that the function generated by this function has side effects -- it updates the axis ranges.
|
|
1255
1419
|
*
|
|
1256
1420
|
* @param axesForSeries The distinct axes that cover all the series
|
|
1257
1421
|
* @param margin The plot margin
|
|
@@ -1270,18 +1434,15 @@ export function panHandler(
|
|
|
1270
1434
|
): (
|
|
1271
1435
|
x: number,
|
|
1272
1436
|
plotDimensions: Dimensions,
|
|
1273
|
-
// series: Array<string>,
|
|
1274
1437
|
ranges: Map<string, ContinuousAxisRange>,
|
|
1275
1438
|
) => void {
|
|
1276
1439
|
/**
|
|
1277
1440
|
* Adjusts the time-range and updates the plot when the plot is dragged to the left or right
|
|
1278
|
-
* @param
|
|
1441
|
+
* @param delta The amount that the plot is dragged
|
|
1279
1442
|
* @param plotDimensions The dimensions of the plot
|
|
1280
|
-
* @param series An array of series names
|
|
1281
1443
|
* @param ranges A map holding the axis ID and its associated time range
|
|
1282
1444
|
*/
|
|
1283
1445
|
return (delta: number, plotDimensions: Dimensions, ranges: Map<string, ContinuousAxisRange>) => {
|
|
1284
|
-
// return (delta: number, plotDimensions: Dimensions, series: Array<string>, ranges: Map<string, ContinuousAxisRange>) => {
|
|
1285
1446
|
// run through the axis IDs, adjust their domain, and update the time-range set for that axis
|
|
1286
1447
|
panAxes(delta, axesForSeries, axesState, ranges, setAxisRangeFor, plotDimensions, margin, constrainToOriginalRange)
|
|
1287
1448
|
// hey, don't forget to update the plot with the new time-ranges in the code calling this... :)
|
|
@@ -1295,7 +1456,7 @@ export function panHandler(
|
|
|
1295
1456
|
* to the left or right. After calling the handler function, the plot needs to be updated as well, and this is
|
|
1296
1457
|
* left for the caller.
|
|
1297
1458
|
*
|
|
1298
|
-
* Please note that the function generated by this function has side effects -- it updates the axes ranges.
|
|
1459
|
+
* Please note that the function generated by this function has side effects -- it updates the axes' ranges.
|
|
1299
1460
|
*
|
|
1300
1461
|
* @param axesForSeries The distinct axes that cover all the series
|
|
1301
1462
|
* @param margin The plot margin
|
|
@@ -1319,12 +1480,12 @@ export function ordinalPanHandler(
|
|
|
1319
1480
|
) => void {
|
|
1320
1481
|
/**
|
|
1321
1482
|
* Adjusts the time-range and updates the plot when the plot is dragged to the left or right
|
|
1322
|
-
* @param
|
|
1483
|
+
* @param delta The amount that the plot is dragged
|
|
1323
1484
|
* @param plotDimensions The dimensions of the plot
|
|
1324
|
-
* @param
|
|
1485
|
+
* @param _series An array of series names
|
|
1325
1486
|
* @param ranges A map holding the axis ID and its associated time range
|
|
1326
1487
|
*/
|
|
1327
|
-
return (delta: number, plotDimensions: Dimensions,
|
|
1488
|
+
return (delta: number, plotDimensions: Dimensions, _series: Array<string>, ranges: Map<string, OrdinalAxisRange>) => {
|
|
1328
1489
|
// run through the axis IDs, adjust their domain, and update the time-range set for that axis
|
|
1329
1490
|
ordinalPanAxes(delta, axesForSeries, axesState, ranges, setAxisRangeFor, plotDimensions, margin, constrainToOriginalRange)
|
|
1330
1491
|
// hey, don't forget to update the plot with the new ranges in the code calling this... :)
|
|
@@ -1338,7 +1499,7 @@ export function ordinalPanHandler(
|
|
|
1338
1499
|
* to the left or right. After calling the handler function, the plot needs to be updated as well, and this is
|
|
1339
1500
|
* left for the caller.
|
|
1340
1501
|
*
|
|
1341
|
-
* Please note that the function generated by this function has side effects -- it updates the axes ranges.
|
|
1502
|
+
* Please note that the function generated by this function has side effects -- it updates the axes' ranges.
|
|
1342
1503
|
*
|
|
1343
1504
|
* @param xAxesForSeries The distinct x-axes that cover all the series
|
|
1344
1505
|
* @param yAxesForSeries The distinct y-axes that cover all the series
|
|
@@ -1368,12 +1529,14 @@ export function panHandler2D(
|
|
|
1368
1529
|
) => void {
|
|
1369
1530
|
/**
|
|
1370
1531
|
* Adjusts the time-range and updates the plot when the plot is dragged to the left or right
|
|
1371
|
-
* @param deltaX The amount that the plot is dragged
|
|
1532
|
+
* @param deltaX The amount that the plot is dragged in x
|
|
1533
|
+
* @param deltaY The amount that the plot is dragged in y
|
|
1372
1534
|
* @param plotDimensions The dimensions of the plot
|
|
1373
|
-
* @param
|
|
1374
|
-
* @param
|
|
1535
|
+
* @param _series An array of series names
|
|
1536
|
+
* @param xRanges A map holding the x-axis ID and its associated time range
|
|
1537
|
+
* @param yRanges A map holding the y-axis ID and its associated time range
|
|
1375
1538
|
*/
|
|
1376
|
-
return (deltaX, deltaY, plotDimensions,
|
|
1539
|
+
return (deltaX, deltaY, plotDimensions, _series, xRanges, yRanges) => {
|
|
1377
1540
|
// run through the x- and y-axes and update them by delta, within the original bounds
|
|
1378
1541
|
panAxes(deltaX, xAxesForSeries, xAxesState, xRanges, setAxisRange, plotDimensions, margin, constrainToOriginalRange)
|
|
1379
1542
|
panAxes(deltaY, yAxesForSeries, yAxesState, yRanges, setAxisRange, plotDimensions, margin, constrainToOriginalRange)
|
|
@@ -1383,25 +1546,30 @@ export function panHandler2D(
|
|
|
1383
1546
|
|
|
1384
1547
|
/**
|
|
1385
1548
|
* Calculates the zoom for the specified axis and updates the axis and the axis ranges
|
|
1386
|
-
* @param
|
|
1387
|
-
*
|
|
1549
|
+
* @param pivotDomainValueFor Given the axis being zoomed, returns the domain value to pivot the
|
|
1550
|
+
* zoom around. Callers pass a function (rather than a fixed value) because the right pivot
|
|
1551
|
+
* depends on the axis's *own* domain/scale (e.g. inverting a shared mouse pixel position through
|
|
1552
|
+
* each axis's own scale, or reading each axis's own "now" tracking) -- there's no single value
|
|
1553
|
+
* that's correct for every axis when a plot has more than one (e.g. Scatter's dual x-axes).
|
|
1554
|
+
* @param axisId The id of the axis to zoom
|
|
1388
1555
|
* @param margin The plot margin
|
|
1389
|
-
* @param setRangeFor Function for setting the new time-range for a specific axis
|
|
1390
|
-
* @param axesState
|
|
1556
|
+
* @param setRangeFor Function for setting the new time-range for a specific axis
|
|
1557
|
+
* @param axesState The current state of the x- or y-axes
|
|
1391
1558
|
* @param ranges A map associating axis IDs with axis ranges
|
|
1392
1559
|
* @param scaleExtent The smallest and largest scale factors allowed
|
|
1393
|
-
* @param
|
|
1560
|
+
* @param zoomFactor The incremental zoom scale factor for this event -- see
|
|
1561
|
+
* {@link calculateConstrainedZoomFor} for what "incremental" means here
|
|
1394
1562
|
* @param plotDimensions The dimensions of the plot
|
|
1395
1563
|
*/
|
|
1396
1564
|
function calcZoomAndUpdate(
|
|
1397
|
-
|
|
1565
|
+
pivotDomainValueFor: (axis: ContinuousNumericAxis) => number,
|
|
1398
1566
|
axisId: string,
|
|
1399
1567
|
margin: Margin,
|
|
1400
1568
|
setRangeFor: (axisId: string, range: AxisInterval) => void,
|
|
1401
1569
|
axesState: AxesState<ContinuousNumericAxis>,
|
|
1402
1570
|
ranges: Map<string, ContinuousAxisRange>,
|
|
1403
1571
|
scaleExtent: [min: number, max: number],
|
|
1404
|
-
|
|
1572
|
+
zoomFactor: number,
|
|
1405
1573
|
plotDimensions: Dimensions,
|
|
1406
1574
|
): void {
|
|
1407
1575
|
const [, zoomMax] = scaleExtent
|
|
@@ -1415,7 +1583,7 @@ function calcZoomAndUpdate(
|
|
|
1415
1583
|
[range.original.start * zoomMax, range.original.end * zoomMax] :
|
|
1416
1584
|
[0, Infinity]
|
|
1417
1585
|
|
|
1418
|
-
const zoom = calculateConstrainedZoomFor(
|
|
1586
|
+
const zoom = calculateConstrainedZoomFor(zoomFactor, pivotDomainValueFor(axis), range, constraint)
|
|
1419
1587
|
|
|
1420
1588
|
// update the axis range
|
|
1421
1589
|
ranges.set(axisId, zoom.range)
|
|
@@ -1428,6 +1596,21 @@ function calcZoomAndUpdate(
|
|
|
1428
1596
|
})
|
|
1429
1597
|
}
|
|
1430
1598
|
|
|
1599
|
+
/**
|
|
1600
|
+
* Calculates the zoom for the specified ordinal axis and updates the axis and the axis ranges (the
|
|
1601
|
+
* original range is reset to the plot's width or height)
|
|
1602
|
+
* @param value The pixel position to pivot the zoom around (e.g. the mouse position)
|
|
1603
|
+
* @param axisId The id of the axis to zoom
|
|
1604
|
+
* @param margin The plot margin
|
|
1605
|
+
* @param setRangeFor Function for setting the new range for a specific axis
|
|
1606
|
+
* @param setOriginalRangeFor Function for setting the new original range for a specific axis
|
|
1607
|
+
* @param axesState The current state of the x- or y-axes
|
|
1608
|
+
* @param ranges A map associating axis IDs with axis ranges (updated with the zoomed range)
|
|
1609
|
+
* @param scaleExtent The smallest and largest scale factors allowed
|
|
1610
|
+
* @param zoomFactor The *cumulative* zoom scale factor for this event -- see
|
|
1611
|
+
* {@link calculateOrdinalConstrainedZoomFor}
|
|
1612
|
+
* @param plotDimensions The dimensions of the plot
|
|
1613
|
+
*/
|
|
1431
1614
|
function calcOrdinalZoomAndUpdate(
|
|
1432
1615
|
value: number,
|
|
1433
1616
|
axisId: string,
|
|
@@ -1437,7 +1620,7 @@ function calcOrdinalZoomAndUpdate(
|
|
|
1437
1620
|
axesState: AxesState<OrdinalStringAxis>,
|
|
1438
1621
|
ranges: Map<string, OrdinalAxisRange>,
|
|
1439
1622
|
scaleExtent: [min: number, max: number],
|
|
1440
|
-
|
|
1623
|
+
zoomFactor: number,
|
|
1441
1624
|
plotDimensions: Dimensions,
|
|
1442
1625
|
): void {
|
|
1443
1626
|
const [, zoomMax] = scaleExtent
|
|
@@ -1446,22 +1629,27 @@ function calcOrdinalZoomAndUpdate(
|
|
|
1446
1629
|
const range = ranges.get(axisId)
|
|
1447
1630
|
if (range && axis) {
|
|
1448
1631
|
// calculate the constraint for the zoom
|
|
1449
|
-
// const [originalStart, originalEnd] = range.original
|
|
1450
1632
|
const constraint: [number, number] = isFinite(zoomMax) ?
|
|
1451
1633
|
[range.original.start * zoomMax, range.original.end * zoomMax] :
|
|
1452
1634
|
[range.original.start, range.original.end]
|
|
1453
1635
|
|
|
1454
|
-
const zoom = calculateOrdinalConstrainedZoomFor(
|
|
1636
|
+
const zoom = calculateOrdinalConstrainedZoomFor(zoomFactor, value, range, constraint)
|
|
1455
1637
|
|
|
1456
1638
|
// update the axis range
|
|
1457
1639
|
ranges.set(axisId, zoom.range)
|
|
1458
1640
|
|
|
1459
1641
|
setRangeFor(axisId, zoom.range.current)
|
|
1460
|
-
|
|
1642
|
+
// the axis's pixel extent depends on its location: a top/bottom (x) axis spans the
|
|
1643
|
+
// plot's width, while a left/right (y) axis spans the plot's height
|
|
1644
|
+
const extent = axis.location === AxisLocation.Top || axis.location === AxisLocation.Bottom ?
|
|
1645
|
+
plotDimensions.width :
|
|
1646
|
+
plotDimensions.height
|
|
1647
|
+
const origRange = AxisInterval.from(0, extent)
|
|
1461
1648
|
setOriginalRangeFor(axisId, origRange)
|
|
1462
1649
|
|
|
1463
|
-
// update the axis' range
|
|
1464
|
-
axis
|
|
1650
|
+
// update the axis' range -- zooming never changes the set of categories, so pass the
|
|
1651
|
+
// axis's own current domain back unchanged
|
|
1652
|
+
axis.update(axis.scale.domain(), zoom.range.current, origRange, plotDimensions, margin)
|
|
1465
1653
|
}
|
|
1466
1654
|
})
|
|
1467
1655
|
}
|
|
@@ -1472,14 +1660,14 @@ function calcOrdinalZoomAndUpdate(
|
|
|
1472
1660
|
* function returns a handler function. And this handler function adjusts the range when the plot is zoomed.
|
|
1473
1661
|
* After calling the handler function, the plot needs to be updated as well, and this is left for the caller.
|
|
1474
1662
|
*
|
|
1475
|
-
* Please note that the function generated by this function has side effects -- it updates the axes ranges.
|
|
1663
|
+
* Please note that the function generated by this function has side effects -- it updates the axes' ranges.
|
|
1476
1664
|
*
|
|
1477
1665
|
* @param axesForSeries The distinct axes that cover all the series
|
|
1478
1666
|
* @param margin The plot margin
|
|
1479
1667
|
* @param setRangeFor Function for setting the new time-range for a specific axis
|
|
1480
1668
|
* @param axesState The current state of the x- or y-axes
|
|
1481
|
-
* @param scaleExtent The minimum and maximum allowed scale factors
|
|
1482
|
-
* @return A handler function for
|
|
1669
|
+
* @param [scaleExtent=[0, Infinity]] The minimum and maximum allowed scale factors
|
|
1670
|
+
* @return A handler function for zoom events
|
|
1483
1671
|
*/
|
|
1484
1672
|
export function continuousAxisZoomHandler(
|
|
1485
1673
|
axesForSeries: Array<string>,
|
|
@@ -1488,24 +1676,32 @@ export function continuousAxisZoomHandler(
|
|
|
1488
1676
|
axesState: AxesState<ContinuousNumericAxis>,
|
|
1489
1677
|
scaleExtent: [min: number, max: number] = [0, Infinity],
|
|
1490
1678
|
): (
|
|
1491
|
-
|
|
1492
|
-
|
|
1679
|
+
zoomFactor: number,
|
|
1680
|
+
pivotDomainValueFor: (axisId: string, axis: ContinuousNumericAxis) => number,
|
|
1493
1681
|
plotDimensions: Dimensions,
|
|
1494
1682
|
ranges: Map<string, ContinuousAxisRange>,
|
|
1495
1683
|
) => void {
|
|
1496
1684
|
|
|
1497
1685
|
/**
|
|
1498
|
-
* Called when the user uses the scroll wheel (or scroll gesture) to zoom in or out.
|
|
1499
|
-
*
|
|
1500
|
-
*
|
|
1501
|
-
*
|
|
1686
|
+
* Called when the user uses the scroll wheel (or scroll gesture) to zoom in or out.
|
|
1687
|
+
* @param zoomFactor The *incremental* zoom scale factor for this event -- relative to each
|
|
1688
|
+
* axis's `.current`, not its `.original` (see {@link calculateConstrainedZoomFor}). Callers
|
|
1689
|
+
* must convert d3-zoom's cumulative `event.transform.k` to this incremental form themselves
|
|
1690
|
+
* (this event's `k` divided by the previously-applied `k`) before calling this handler.
|
|
1691
|
+
* @param pivotDomainValueFor Given an axis ID and its axis, returns the domain value to pivot
|
|
1692
|
+
* that axis's zoom around -- e.g. the mouse position inverted through that axis's own scale,
|
|
1693
|
+
* or (while streaming) that axis's own "now" tracking, so "now" stays fixed on screen as the
|
|
1694
|
+
* zoom widens/narrows the window around it instead of drifting with wherever the mouse is.
|
|
1502
1695
|
* @param plotDimensions The dimensions of the plot
|
|
1503
1696
|
* @param ranges A map holding the axis ID and its associated time-range
|
|
1504
1697
|
*/
|
|
1505
|
-
return (
|
|
1698
|
+
return (zoomFactor, pivotDomainValueFor, plotDimensions, ranges) => {
|
|
1506
1699
|
// run through the axis IDs, adjust their domain, and update the time-range set for that axis
|
|
1507
1700
|
axesForSeries.forEach(axisId =>
|
|
1508
|
-
calcZoomAndUpdate(
|
|
1701
|
+
calcZoomAndUpdate(
|
|
1702
|
+
axis => pivotDomainValueFor(axisId, axis),
|
|
1703
|
+
axisId, margin, setRangeFor, axesState, ranges, scaleExtent, zoomFactor, plotDimensions
|
|
1704
|
+
)
|
|
1509
1705
|
)
|
|
1510
1706
|
// hey, don't forget to update the plot with the new time-ranges in the code calling this... :)
|
|
1511
1707
|
}
|
|
@@ -1517,15 +1713,15 @@ export function continuousAxisZoomHandler(
|
|
|
1517
1713
|
* function returns a handler function. And this handler function adjusts the range when the plot is zoomed.
|
|
1518
1714
|
* After calling the handler function, the plot needs to be updated as well, and this is left for the caller.
|
|
1519
1715
|
*
|
|
1520
|
-
* Please note that the function generated by this function has side effects -- it updates the axes ranges.
|
|
1716
|
+
* Please note that the function generated by this function has side effects -- it updates the axes' ranges.
|
|
1521
1717
|
*
|
|
1522
1718
|
* @param axesForSeries The distinct axes that cover all the series
|
|
1523
1719
|
* @param margin The plot margin
|
|
1524
1720
|
* @param setRangeFor Function for setting the new time-range for a specific axis
|
|
1525
1721
|
* @param setOriginalRangeFor Function for setting the new original range for a specific axis
|
|
1526
1722
|
* @param axesState The current state of the x- or y-axes
|
|
1527
|
-
* @param scaleExtent The minimum and maximum allowed scale factors
|
|
1528
|
-
* @return A handler function for
|
|
1723
|
+
* @param [scaleExtent=[0, Infinity]] The minimum and maximum allowed scale factors
|
|
1724
|
+
* @return A handler function for zoom events
|
|
1529
1725
|
*/
|
|
1530
1726
|
export function ordinalAxisZoomHandler(
|
|
1531
1727
|
axesForSeries: Array<string>,
|
|
@@ -1535,7 +1731,7 @@ export function ordinalAxisZoomHandler(
|
|
|
1535
1731
|
axesState: AxesState<OrdinalStringAxis>,
|
|
1536
1732
|
scaleExtent: [min: number, max: number] = [0, Infinity],
|
|
1537
1733
|
): (
|
|
1538
|
-
|
|
1734
|
+
zoomFactor: number,
|
|
1539
1735
|
x: number,
|
|
1540
1736
|
plotDimensions: Dimensions,
|
|
1541
1737
|
ranges: Map<string, OrdinalAxisRange>,
|
|
@@ -1544,15 +1740,19 @@ export function ordinalAxisZoomHandler(
|
|
|
1544
1740
|
/**
|
|
1545
1741
|
* Called when the user uses the scroll wheel (or scroll gesture) to zoom in or out. Zooms in/out
|
|
1546
1742
|
* at the location of the mouse when the scroll wheel or gesture was applied.
|
|
1547
|
-
* @param
|
|
1743
|
+
* @param zoomFactor The *cumulative* zoom scale factor for this event -- d3-zoom's own
|
|
1744
|
+
* `event.transform.k`, passed through unmodified. Unlike {@link continuousAxisZoomHandler},
|
|
1745
|
+
* this must NOT be converted to an incremental factor -- see
|
|
1746
|
+
* {@link OrdinalAxisRange.scaledCumulative} for why an ordinal axis's bounded domain needs
|
|
1747
|
+
* the raw cumulative factor instead.
|
|
1548
1748
|
* @param x The x-position of the mouse when the scroll wheel or gesture is used
|
|
1549
1749
|
* @param plotDimensions The dimensions of the plot
|
|
1550
1750
|
* @param ranges A map holding the axis ID and its associated time-range
|
|
1551
1751
|
*/
|
|
1552
|
-
return (
|
|
1752
|
+
return (zoomFactor, x, plotDimensions, ranges) => {
|
|
1553
1753
|
// run through the axis IDs, adjust their domain, and update the time-range set for that axis
|
|
1554
1754
|
axesForSeries.forEach(axisId =>
|
|
1555
|
-
calcOrdinalZoomAndUpdate(x, axisId, margin, setRangeFor, setOriginalRangeFor, axesState, ranges, scaleExtent,
|
|
1755
|
+
calcOrdinalZoomAndUpdate(x, axisId, margin, setRangeFor, setOriginalRangeFor, axesState, ranges, scaleExtent, zoomFactor, plotDimensions)
|
|
1556
1756
|
)
|
|
1557
1757
|
// hey, don't forget to update the plot with the new time-ranges in the code calling this... :)
|
|
1558
1758
|
}
|
|
@@ -1564,7 +1764,7 @@ export function ordinalAxisZoomHandler(
|
|
|
1564
1764
|
* function returns a handler function. And this handler function adjusts the time-range when the plot is zoomed.
|
|
1565
1765
|
* After calling the handler function, the plot needs to be updated as well, and this is left for the caller.
|
|
1566
1766
|
*
|
|
1567
|
-
* Please note that the function generated by this function has side effects -- it updates the axes ranges.
|
|
1767
|
+
* Please note that the function generated by this function has side effects -- it updates the axes' ranges.
|
|
1568
1768
|
*
|
|
1569
1769
|
* @param xAxesForSeries The distinct x-axes that cover all the series
|
|
1570
1770
|
* @param yAxesForSeries The distinct y-axes that cover all the series
|
|
@@ -1573,7 +1773,7 @@ export function ordinalAxisZoomHandler(
|
|
|
1573
1773
|
* @param xAxesState The current state of the x-axes
|
|
1574
1774
|
* @param yAxesState The current state of the y-axes
|
|
1575
1775
|
* @param scaleExtent The smallest and largest scale factors allowed
|
|
1576
|
-
* @return A handler function for
|
|
1776
|
+
* @return A handler function for zoom events
|
|
1577
1777
|
*/
|
|
1578
1778
|
export function axesZoomHandler(
|
|
1579
1779
|
xAxesForSeries: Array<string>,
|
|
@@ -1584,7 +1784,7 @@ export function axesZoomHandler(
|
|
|
1584
1784
|
yAxesState: AxesState<ContinuousNumericAxis>,
|
|
1585
1785
|
scaleExtent: [min: number, max: number],
|
|
1586
1786
|
): (
|
|
1587
|
-
|
|
1787
|
+
zoomFactor: number,
|
|
1588
1788
|
mousePosition: [x: number, y: number],
|
|
1589
1789
|
plotDimensions: Dimensions,
|
|
1590
1790
|
xRanges: Map<string, ContinuousAxisRange>,
|
|
@@ -1594,47 +1794,57 @@ export function axesZoomHandler(
|
|
|
1594
1794
|
/**
|
|
1595
1795
|
* Called when the user uses the scroll wheel (or scroll gesture) to zoom in or out. Zooms in/out
|
|
1596
1796
|
* at the location of the mouse when the scroll wheel or gesture was applied.
|
|
1597
|
-
* @param
|
|
1598
|
-
* @
|
|
1797
|
+
* @param zoomFactor The *incremental* zoom scale factor for this event -- see
|
|
1798
|
+
* {@link continuousAxisZoomHandler} for the full explanation of why this must already be
|
|
1799
|
+
* incremental (not d3-zoom's raw, cumulative `event.transform.k`)
|
|
1800
|
+
* @param mousePosition The position of the mouse when the scroll wheel or gesture is used
|
|
1599
1801
|
* @param plotDimensions The dimensions of the plot
|
|
1600
|
-
* @param
|
|
1802
|
+
* @param xRanges A map holding the x-axis ID and its associated time-range
|
|
1803
|
+
* @param yRanges A map holding the y-axis ID and its associated time-range
|
|
1601
1804
|
*/
|
|
1602
|
-
return (
|
|
1805
|
+
return (zoomFactor, mousePosition, plotDimensions, xRanges, yRanges) => {
|
|
1603
1806
|
// run through the axis IDs, adjust their domain, and update the time-range set for that axis
|
|
1604
1807
|
const [x, y] = mousePosition
|
|
1605
1808
|
xAxesForSeries.forEach(id =>
|
|
1606
|
-
calcZoomAndUpdate(
|
|
1809
|
+
calcZoomAndUpdate(
|
|
1810
|
+
axis => (axis.scale as ScaleLinear<number, number>).invert(x),
|
|
1811
|
+
id, margin, setRangeFor, xAxesState, xRanges, scaleExtent, zoomFactor, plotDimensions
|
|
1812
|
+
)
|
|
1607
1813
|
)
|
|
1608
1814
|
yAxesForSeries.forEach(id =>
|
|
1609
|
-
calcZoomAndUpdate(
|
|
1815
|
+
calcZoomAndUpdate(
|
|
1816
|
+
axis => (axis.scale as ScaleLinear<number, number>).invert(y),
|
|
1817
|
+
id, margin, setRangeFor, yAxesState, yRanges, scaleExtent, zoomFactor, plotDimensions
|
|
1818
|
+
)
|
|
1610
1819
|
)
|
|
1611
1820
|
// hey, don't forget to update the plot with the new time-ranges in the code calling this... :)
|
|
1612
1821
|
}
|
|
1613
1822
|
}
|
|
1614
1823
|
|
|
1615
1824
|
/**
|
|
1616
|
-
* Calculates the axis-ranges for each of the continuous numeric axes in the map
|
|
1825
|
+
* Calculates the axis-ranges for each of the continuous numeric axes in the map, from each axis'
|
|
1826
|
+
* current domain (see {@link continuousRange})
|
|
1617
1827
|
* @param axes The map containing the axes and their associated IDs
|
|
1618
|
-
* @return a map associating the axis IDs
|
|
1828
|
+
* @return a map associating the axis IDs with their continuous axis-range
|
|
1619
1829
|
*/
|
|
1620
1830
|
export function continuousAxisRanges(axes: Map<string, ContinuousNumericAxis>): Map<string, ContinuousAxisRange> {
|
|
1621
1831
|
return continuousRange(axes)
|
|
1622
1832
|
}
|
|
1623
1833
|
|
|
1624
1834
|
/**
|
|
1625
|
-
* Calculates the axis-ranges for each of the ordinal axes in the map
|
|
1835
|
+
* Calculates the axis-ranges for each of the ordinal axes in the map, read from each axis's own
|
|
1836
|
+
* scale range.
|
|
1626
1837
|
* @param axes The map containing the axes and their associated IDs
|
|
1627
|
-
* @
|
|
1628
|
-
* @return a map associating the axis IDs to their ordinal axis-range
|
|
1838
|
+
* @return a map associating the axis IDs with their ordinal axis-range
|
|
1629
1839
|
*/
|
|
1630
|
-
export function ordinalAxisRanges(axes: Map<string, OrdinalStringAxis
|
|
1631
|
-
return ordinalRange(axes
|
|
1840
|
+
export function ordinalAxisRanges(axes: Map<string, OrdinalStringAxis>): Map<string, OrdinalAxisRange> {
|
|
1841
|
+
return ordinalRange(axes)
|
|
1632
1842
|
}
|
|
1633
1843
|
|
|
1634
1844
|
/**
|
|
1635
|
-
* Calculates the axis interval (start, end)
|
|
1636
|
-
* @param axes The axes
|
|
1637
|
-
* @return A map associating each axis with
|
|
1845
|
+
* Calculates the axis interval (start, end) of each axis' current domain
|
|
1846
|
+
* @param axes The axes, by their IDs
|
|
1847
|
+
* @return A map associating each axis ID with its domain's (start, end) interval
|
|
1638
1848
|
*/
|
|
1639
1849
|
export function continuousAxisIntervals(axes: Map<string, ContinuousNumericAxis>): Map<string, AxisInterval> {
|
|
1640
1850
|
return new Map(Array.from(axes.entries())
|
|
@@ -1645,9 +1855,10 @@ export function continuousAxisIntervals(axes: Map<string, ContinuousNumericAxis>
|
|
|
1645
1855
|
}
|
|
1646
1856
|
|
|
1647
1857
|
/**
|
|
1648
|
-
* Calculates the
|
|
1649
|
-
*
|
|
1650
|
-
* @
|
|
1858
|
+
* Calculates the pixel interval (start, end) of each ordinal axis' current range, along with its
|
|
1859
|
+
* categories
|
|
1860
|
+
* @param axes The axes, by their IDs
|
|
1861
|
+
* @return A map associating each axis ID with its range's (start, end) interval and its categories
|
|
1651
1862
|
*/
|
|
1652
1863
|
export function ordinalAxisIntervals(axes: Map<string, OrdinalStringAxis>): Map<string, {interval: AxisInterval, categories: Array<string>}> {
|
|
1653
1864
|
return new Map(Array.from(axes.entries())
|
|
@@ -1658,7 +1869,8 @@ export function ordinalAxisIntervals(axes: Map<string, OrdinalStringAxis>): Map<
|
|
|
1658
1869
|
}
|
|
1659
1870
|
|
|
1660
1871
|
/**
|
|
1661
|
-
* Returns the bounds on the specified continuous numeric axes
|
|
1872
|
+
* Returns the bounds on the specified continuous numeric axes: a new axis range (with its original
|
|
1873
|
+
* range the same as its current range) from each axis' current domain
|
|
1662
1874
|
* @param axes A map associating an axis ID with a {@link ContinuousNumericAxis}
|
|
1663
1875
|
* @return A map associating each specified axis ID with the interval covered (bounds) by the axis
|
|
1664
1876
|
*/
|
|
@@ -1671,15 +1883,22 @@ export function continuousRange(axes: Map<string, ContinuousNumericAxis>): Map<s
|
|
|
1671
1883
|
}
|
|
1672
1884
|
|
|
1673
1885
|
/**
|
|
1674
|
-
* Returns the bounds on the specified
|
|
1886
|
+
* Returns the bounds on the specified ordinal axes, read from each axis's own scale range.
|
|
1887
|
+
*
|
|
1888
|
+
* Previously mapped every axis ID to one shared, caller-supplied `originalRange`, discarding the
|
|
1889
|
+
* axis object entirely (`[id, ]`) -- harmless today only because there's no per-axis margin/offset
|
|
1890
|
+
* mechanism anywhere in this codebase, so every ordinal axis in a chart is architecturally
|
|
1891
|
+
* guaranteed to span the same pixel range already. Reading each axis's own `.scale.range()`
|
|
1892
|
+
* instead (mirroring {@link ordinalAxisIntervals}, which already does this correctly) removes that
|
|
1893
|
+
* latent footgun for a chart with multiple category axes whose ranges genuinely differ.
|
|
1675
1894
|
* @param axes A map associating an axis ID with a {@link OrdinalStringAxis}
|
|
1676
|
-
* @param originalRange The original range of the axis
|
|
1677
1895
|
* @return A map associating each specified axis ID with the interval covered (bounds) by the axis
|
|
1678
1896
|
*/
|
|
1679
|
-
export function ordinalRange(axes: Map<string, OrdinalStringAxis
|
|
1897
|
+
export function ordinalRange(axes: Map<string, OrdinalStringAxis>): Map<string, OrdinalAxisRange> {
|
|
1680
1898
|
return new Map(Array.from(axes.entries())
|
|
1681
|
-
.map(([id,
|
|
1682
|
-
[
|
|
1683
|
-
|
|
1899
|
+
.map(([id, axis]) => {
|
|
1900
|
+
const [start, end] = axis.scale.range()
|
|
1901
|
+
return [id, OrdinalAxisRange.from(start, end)]
|
|
1902
|
+
})
|
|
1684
1903
|
)
|
|
1685
|
-
}
|
|
1904
|
+
}
|