@octanejs/recharts 0.1.41 → 0.1.42
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 → LICENSE.recharts} +3 -2
- package/README.md +12 -0
- package/package.json +10 -9
- package/src/animation/AnimatedItems.tsrx +36 -3
- package/src/animation/AnimationController.ts +17 -65
- package/src/animation/{AnimationControllerImpl.js → AnimationControllerImpl.ts} +19 -12
- package/src/animation/{AnimationHandle.js → AnimationHandle.ts} +82 -65
- package/src/animation/CSSTransitionAnimate.tsrx +3 -2
- package/src/animation/JavascriptAnimate.tsrx +18 -2
- package/src/animation/easing.ts +235 -0
- package/src/animation/{matchBy.js → matchBy.ts} +76 -120
- package/src/animation/timeoutController.ts +71 -0
- package/src/animation/useAnimationController.ts +4 -1
- package/src/animation/useAnimationStartSnapshot.ts +3 -3
- package/src/animation/util.ts +12 -0
- package/src/cartesian/Area.tsrx +33 -31
- package/src/cartesian/AreaRevealShape.tsrx +4 -3
- package/src/cartesian/Bar.ts +1 -0
- package/src/cartesian/Bar.tsrx +293 -39
- package/src/cartesian/BarStack.tsrx +31 -9
- package/src/cartesian/CartesianAxis.tsrx +154 -22
- package/src/cartesian/CartesianGrid.tsrx +5 -6
- package/src/cartesian/ErrorBar.tsrx +9 -8
- package/src/cartesian/Funnel.tsrx +40 -37
- package/src/cartesian/GraphicalItemClipPath.tsrx +11 -4
- package/src/cartesian/Line.ts +1 -0
- package/src/cartesian/Line.tsrx +206 -28
- package/src/cartesian/LineDrawShape.tsrx +23 -10
- package/src/cartesian/ReferenceLine.tsrx +8 -8
- package/src/cartesian/Scatter.tsrx +61 -66
- package/src/cartesian/XAxis.tsrx +78 -9
- package/src/cartesian/YAxis.tsrx +85 -12
- package/src/cartesian/getCartesianPosition.ts +265 -0
- package/src/cartesian/getEquidistantTicks.ts +144 -0
- package/src/cartesian/getTicks.ts +221 -0
- package/src/chart/AreaChart.tsrx +5 -1
- package/src/chart/BarChart.tsrx +8 -2
- package/src/chart/CartesianChart.tsrx +17 -4
- package/src/chart/CategoricalChart.tsrx +4 -1
- package/src/chart/ComposedChart.tsrx +5 -1
- package/src/chart/FunnelChart.tsrx +5 -1
- package/src/chart/LineChart.tsrx +8 -2
- package/src/chart/PieChart.tsrx +6 -2
- package/src/chart/PolarChart.tsrx +5 -4
- package/src/chart/RadarChart.tsrx +6 -2
- package/src/chart/RadialBarChart.tsrx +6 -2
- package/src/chart/RechartsWrapper.tsrx +57 -21
- package/src/chart/Sankey.tsrx +89 -91
- package/src/chart/ScatterChart.tsrx +5 -1
- package/src/chart/SunburstChart.tsrx +21 -14
- package/src/chart/types.ts +0 -1
- package/src/component/ActivePoints.tsrx +30 -5
- package/src/component/Cell.ts +19 -1
- package/src/component/Cursor.tsrx +11 -10
- package/src/component/DefaultLegendContent.tsrx +15 -10
- package/src/component/DefaultTooltipContent.tsrx +11 -9
- package/src/component/Dots.tsrx +37 -6
- package/src/component/Label.tsrx +99 -19
- package/src/component/LabelList.tsrx +54 -5
- package/src/component/Legend.tsrx +9 -5
- package/src/component/ResponsiveContainer.tsrx +13 -10
- package/src/component/Text.tsrx +95 -32
- package/src/component/Tooltip.tsrx +11 -10
- package/src/component/TooltipBoundingBox.tsrx +4 -5
- package/src/component/{responsiveContainerUtils.js → responsiveContainerUtils.ts} +59 -36
- package/src/container/ClipPathProvider.tsrx +6 -2
- package/src/container/Layer.tsrx +8 -1
- package/src/container/RootSurface.tsrx +15 -4
- package/src/container/Surface.tsrx +15 -1
- package/src/context/CellsContext.ts +6 -5
- package/src/context/ErrorBarContext.tsrx +23 -4
- package/src/context/RegisterGraphicalItemId.tsrx +10 -2
- package/src/context/accessibilityContext.ts +5 -0
- package/src/context/brushUpdateContext.ts +11 -0
- package/src/context/chartDataContext.ts +5 -4
- package/src/context/chartLayoutContext.ts +22 -8
- package/src/context/legendPayloadContext.tsrx +1 -1
- package/src/context/tooltipContext.ts +83 -0
- package/src/{hooks.js → hooks.ts} +88 -66
- package/src/index.ts +37 -37
- package/src/legacy-props.ts +129 -0
- package/src/polar/Pie.tsrx +91 -69
- package/src/polar/PolarAngleAxis.tsrx +47 -47
- package/src/polar/PolarGrid.tsrx +7 -6
- package/src/polar/PolarRadiusAxis.tsrx +29 -21
- package/src/polar/Radar.tsrx +26 -19
- package/src/polar/RadialBar.tsrx +59 -42
- package/src/polar/{defaultPolarAngleAxisProps.js → defaultPolarAngleAxisProps.ts} +6 -6
- package/src/polar/{defaultPolarRadiusAxisProps.js → defaultPolarRadiusAxisProps.ts} +5 -4
- package/src/shape/Cross.tsrx +14 -2
- package/src/shape/Curve.tsrx +111 -23
- package/src/shape/Dot.tsrx +12 -1
- package/src/shape/Polygon.tsrx +28 -6
- package/src/shape/Rectangle.tsrx +30 -3
- package/src/shape/Sector.tsrx +48 -6
- package/src/shape/Symbols.tsrx +24 -6
- package/src/shape/Trapezoid.tsrx +33 -3
- package/src/state/RechartsStoreProvider.tsrx +11 -2
- package/src/state/ReportChartProps.ts +2 -2
- package/src/state/ReportMainChartProps.ts +2 -1
- package/src/state/SetGraphicalItem.ts +6 -4
- package/src/state/SetLegendPayload.ts +7 -4
- package/src/state/SetTooltipEntrySettings.ts +5 -2
- package/src/state/brushSlice.ts +40 -0
- package/src/state/cartesianAxisSlice.ts +291 -0
- package/src/state/chartDataSlice.ts +87 -0
- package/src/state/errorBarSlice.ts +78 -0
- package/src/state/eventSettingsSlice.ts +30 -0
- package/src/state/{externalEventsMiddleware.js → externalEventsMiddleware.ts} +55 -34
- package/src/state/graphicalItemsSlice.ts +180 -0
- package/src/state/{keyboardEventsMiddleware.js → keyboardEventsMiddleware.ts} +72 -57
- package/src/state/layoutSlice.ts +50 -0
- package/src/state/legendSlice.ts +109 -0
- package/src/state/{mouseEventsMiddleware.js → mouseEventsMiddleware.ts} +45 -35
- package/src/state/optionsSlice.ts +65 -0
- package/src/state/polarAxisSlice.ts +42 -0
- package/src/state/polarOptionsSlice.ts +43 -0
- package/src/state/reduxDevtoolsJsonStringifyReplacer.ts +13 -0
- package/src/state/referenceElementsSlice.ts +100 -0
- package/src/state/renderedTicksSlice.ts +60 -0
- package/src/state/rootPropsSlice.ts +72 -0
- package/src/state/selectors/areaSelectors.ts +1 -2
- package/src/state/selectors/{arrayEqualityCheck.js → arrayEqualityCheck.ts} +4 -5
- package/src/state/selectors/axisSelectors.ts +2502 -0
- package/src/state/selectors/barSelectors.ts +373 -0
- package/src/state/selectors/barStackSelectors.ts +99 -0
- package/src/state/selectors/brushSelectors.ts +28 -0
- package/src/state/selectors/combiners/combineActiveLabel.ts +16 -0
- package/src/state/selectors/combiners/{combineActiveTooltipIndex.js → combineActiveTooltipIndex.ts} +45 -23
- package/src/state/selectors/combiners/combineAllBarPositions.ts +130 -0
- package/src/state/selectors/combiners/combineAxisRangeWithReverse.ts +16 -0
- package/src/state/selectors/combiners/combineBarPosition.ts +23 -0
- package/src/state/selectors/combiners/combineBarSizeList.ts +79 -0
- package/src/state/selectors/combiners/{combineCheckedDomain.js → combineCheckedDomain.ts} +9 -6
- package/src/state/selectors/combiners/combineConfiguredScale.ts +100 -0
- package/src/state/selectors/combiners/combineCoordinateForDefaultIndex.ts +46 -0
- package/src/state/selectors/combiners/{combineDisplayedStackedData.js → combineDisplayedStackedData.ts} +23 -19
- package/src/state/selectors/combiners/combineInverseScaleFunction.ts +16 -0
- package/src/state/selectors/combiners/{combineRealScaleType.js → combineRealScaleType.ts} +16 -8
- package/src/state/selectors/combiners/combineStackedData.ts +27 -0
- package/src/state/selectors/combiners/combineTooltipInteractionState.ts +86 -0
- package/src/state/selectors/combiners/combineTooltipPayload.ts +201 -0
- package/src/state/selectors/combiners/{combineTooltipPayloadConfigurations.js → combineTooltipPayloadConfigurations.ts} +17 -18
- package/src/state/selectors/containerSelectors.ts +12 -0
- package/src/state/selectors/{dataSelectors.js → dataSelectors.ts} +43 -41
- package/src/state/selectors/funnelSelectors.ts +5 -5
- package/src/state/selectors/graphicalItemSelectors.ts +22 -0
- package/src/state/selectors/legendSelectors.ts +25 -0
- package/src/state/selectors/lineSelectors.ts +159 -0
- package/src/state/selectors/numberDomainEqualityCheck.ts +15 -0
- package/src/state/selectors/pickAxisId.ts +6 -0
- package/src/state/selectors/pickAxisType.ts +4 -0
- package/src/state/selectors/pieSelectors.ts +14 -13
- package/src/state/selectors/polarAxisSelectors.ts +223 -0
- package/src/state/selectors/polarSelectors.ts +1 -1
- package/src/state/selectors/radarSelectors.ts +1 -1
- package/src/state/selectors/radialBarSelectors.ts +17 -16
- package/src/state/selectors/rootPropsSelectors.ts +26 -0
- package/src/state/selectors/scatterSelectors.ts +11 -10
- package/src/state/selectors/{selectActivePropsFromChartPointer.js → selectActivePropsFromChartPointer.ts} +11 -4
- package/src/state/selectors/selectAllAxes.ts +20 -0
- package/src/state/selectors/selectChartOffset.ts +17 -0
- package/src/state/selectors/selectChartOffsetInternal.ts +147 -0
- package/src/state/selectors/{selectPlotArea.js → selectPlotArea.ts} +4 -3
- package/src/state/selectors/selectTooltipAxisId.ts +6 -0
- package/src/state/selectors/{selectTooltipAxisType.js → selectTooltipAxisType.ts} +10 -4
- package/src/state/selectors/selectTooltipEventType.ts +39 -0
- package/src/state/selectors/selectTooltipPayloadSearcher.ts +6 -0
- package/src/state/selectors/selectTooltipSettings.ts +6 -0
- package/src/state/selectors/selectTooltipState.ts +5 -0
- package/src/state/selectors/selectors.ts +315 -0
- package/src/state/selectors/tooltipSelectors.ts +611 -0
- package/src/state/selectors/touchSelectors.ts +52 -0
- package/src/state/{store.js → store.ts} +46 -36
- package/src/state/tooltipSlice.ts +507 -0
- package/src/state/{touchEventsMiddleware.js → touchEventsMiddleware.ts} +54 -42
- package/src/state/types/AreaSettings.ts +15 -0
- package/src/state/types/BarSettings.ts +15 -0
- package/src/state/types/LineSettings.ts +10 -0
- package/src/state/types/PieSettings.ts +32 -0
- package/src/state/types/RadarSettings.ts +6 -0
- package/src/state/types/RadialBarSettings.ts +10 -0
- package/src/state/types/ScatterSettings.ts +13 -0
- package/src/state/types/StackedGraphicalItem.ts +34 -0
- package/src/state/zIndexSlice.ts +137 -0
- package/src/synchronisation/syncSelectors.ts +7 -0
- package/src/synchronisation/types.ts +42 -0
- package/src/synchronisation/useChartSynchronisation.tsrx +26 -11
- package/src/types.ts +55 -0
- package/src/util/ActiveShapeUtils.tsrx +34 -10
- package/src/util/BarUtils.tsrx +43 -3
- package/src/util/{CartesianUtils.js → CartesianUtils.ts} +22 -39
- package/src/util/ChartUtils.ts +759 -0
- package/src/util/{Constants.js → Constants.ts} +6 -6
- package/src/util/DOMUtils.ts +135 -0
- package/src/util/{DataUtils.js → DataUtils.ts} +120 -47
- package/src/util/Events.ts +22 -0
- package/src/util/FunnelUtils.tsrx +3 -3
- package/src/util/Global.ts +13 -0
- package/src/util/IfOverflow.ts +16 -0
- package/src/util/LRUCache.ts +42 -0
- package/src/util/{LogUtils.js → LogUtils.ts} +7 -12
- package/src/util/OctaneTypes.ts +51 -0
- package/src/util/PolarUtils.ts +132 -0
- package/src/util/RadialBarUtils.tsrx +3 -3
- package/src/util/ReduceCSSCalc.ts +186 -0
- package/src/util/ScatterUtils.tsrx +9 -7
- package/src/util/{TickUtils.js → TickUtils.ts} +30 -13
- package/src/util/{YAxisUtils.js → YAxisUtils.ts} +27 -17
- package/src/util/axisPropsAreEqual.ts +49 -0
- package/src/util/createEventProxy.ts +20 -0
- package/src/util/{excludeEventProps.js → excludeEventProps.ts} +8 -6
- package/src/util/getActiveCoordinate.ts +210 -0
- package/src/util/{getAxisTypeBasedOnLayout.js → getAxisTypeBasedOnLayout.ts} +7 -3
- package/src/util/getClassNameFromUnknown.ts +7 -0
- package/src/util/{getEveryNth.js → getEveryNth.ts} +5 -6
- package/src/util/getRadiusAndStrokeWidthFromDot.ts +31 -0
- package/src/util/{getRelativeCoordinate.js → getRelativeCoordinate.ts} +26 -9
- package/src/util/getSliced.ts +14 -0
- package/src/util/{isDomainSpecifiedByUser.js → isDomainSpecifiedByUser.ts} +46 -100
- package/src/util/isWellBehavedNumber.ts +8 -0
- package/src/util/payload/getUniqPayload.ts +1 -1
- package/src/util/{propsAreEqual.js → propsAreEqual.ts} +10 -8
- package/src/util/{resolveDefaultProps.js → resolveDefaultProps.ts} +35 -57
- package/src/util/round.ts +33 -0
- package/src/util/scale/CustomScaleDefinition.ts +82 -0
- package/src/util/scale/RechartsScale.ts +131 -0
- package/src/util/scale/{createCategoricalInverse.js → createCategoricalInverse.ts} +24 -29
- package/src/util/scale/{getNiceTickValues.js → getNiceTickValues.ts} +125 -146
- package/src/util/scale/index.ts +2 -0
- package/src/util/scale/util/{arithmetic.js → arithmetic.ts} +12 -8
- package/src/util/stacks/getStackSeriesIdentifier.ts +21 -0
- package/src/util/stacks/stackTypes.ts +58 -0
- package/src/util/{svgPropertiesNoEvents.js → svgPropertiesNoEvents.ts} +52 -27
- package/src/util/tooltip/translate.ts +2 -1
- package/src/util/typedDataKey.ts +49 -0
- package/src/util/types.ts +1265 -7
- package/src/util/{useUniqueId.js → useUniqueId.ts} +9 -5
- package/src/zIndex/{DefaultZIndexes.js → DefaultZIndexes.ts} +13 -4
- package/src/zIndex/ZIndexLayer.tsrx +13 -4
- package/src/zIndex/ZIndexPortal.tsrx +14 -4
- package/src/zIndex/{getZIndexFromUnknown.js → getZIndexFromUnknown.ts} +3 -3
- package/src/zIndex/{zIndexSelectors.js → zIndexSelectors.ts} +17 -11
- package/src/animation/easing.js +0 -193
- package/src/animation/timeoutController.js +0 -49
- package/src/animation/util.js +0 -11
- package/src/cartesian/Area.tsrx.d.ts +0 -340
- package/src/cartesian/Bar.tsrx.d.ts +0 -4
- package/src/cartesian/BarStack.tsrx.d.ts +0 -6
- package/src/cartesian/CartesianAxis.tsrx.d.ts +0 -3
- package/src/cartesian/CartesianGrid.tsrx.d.ts +0 -219
- package/src/cartesian/ErrorBar.tsrx.d.ts +0 -131
- package/src/cartesian/Funnel.tsrx.d.ts +0 -274
- package/src/cartesian/Line.tsrx.d.ts +0 -4
- package/src/cartesian/LineDrawShape.tsrx.d.ts +0 -2
- package/src/cartesian/ReferenceLine.tsrx.d.ts +0 -178
- package/src/cartesian/Scatter.tsrx.d.ts +0 -351
- package/src/cartesian/XAxis.tsrx.d.ts +0 -3
- package/src/cartesian/YAxis.tsrx.d.ts +0 -3
- package/src/cartesian/ZAxis.tsrx.d.ts +0 -102
- package/src/cartesian/getCartesianPosition.js +0 -277
- package/src/cartesian/getEquidistantTicks.js +0 -133
- package/src/cartesian/getTicks.js +0 -280
- package/src/chart/AreaChart.tsrx.d.ts +0 -12
- package/src/chart/BarChart.tsrx.d.ts +0 -2
- package/src/chart/CartesianChart.tsrx.d.ts +0 -10
- package/src/chart/ComposedChart.tsrx.d.ts +0 -12
- package/src/chart/FunnelChart.tsrx.d.ts +0 -12
- package/src/chart/LineChart.tsrx.d.ts +0 -2
- package/src/chart/PieChart.tsrx.d.ts +0 -31
- package/src/chart/RadarChart.tsrx.d.ts +0 -31
- package/src/chart/RadialBarChart.tsrx.d.ts +0 -31
- package/src/chart/Sankey.tsrx.d.ts +0 -253
- package/src/chart/ScatterChart.tsrx.d.ts +0 -12
- package/src/chart/SunburstChart.tsrx.d.ts +0 -146
- package/src/component/DefaultLegendContent.tsrx.d.ts +0 -125
- package/src/component/DefaultTooltipContent.tsrx.d.ts +0 -87
- package/src/component/Label.tsrx.d.ts +0 -12
- package/src/component/LabelList.tsrx.d.ts +0 -8
- package/src/component/Legend.tsrx.d.ts +0 -88
- package/src/component/ResponsiveContainer.tsrx.d.ts +0 -81
- package/src/component/Text.tsrx.d.ts +0 -9
- package/src/component/Tooltip.tsrx.d.ts +0 -246
- package/src/container/Layer.tsrx.d.ts +0 -6
- package/src/container/Surface.tsrx.d.ts +0 -12
- package/src/context/accessibilityContext.js +0 -10
- package/src/context/tooltipContext.js +0 -55
- package/src/index.d.ts +0 -281
- package/src/polar/Pie.tsrx.d.ts +0 -436
- package/src/polar/PolarAngleAxis.tsrx.d.ts +0 -215
- package/src/polar/PolarGrid.tsrx.d.ts +0 -117
- package/src/polar/PolarRadiusAxis.tsrx.d.ts +0 -209
- package/src/polar/Radar.tsrx.d.ts +0 -215
- package/src/polar/RadialBar.tsrx.d.ts +0 -340
- package/src/shape/Cross.tsrx.d.ts +0 -12
- package/src/shape/Curve.tsrx.d.ts +0 -42
- package/src/shape/Dot.tsrx.d.ts +0 -9
- package/src/shape/Polygon.tsrx.d.ts +0 -13
- package/src/shape/Rectangle.tsrx.d.ts +0 -31
- package/src/shape/Sector.tsrx.d.ts +0 -29
- package/src/shape/Symbols.tsrx.d.ts +0 -15
- package/src/shape/Trapezoid.tsrx.d.ts +0 -29
- package/src/state/brushSlice.js +0 -36
- package/src/state/cartesianAxisSlice.js +0 -256
- package/src/state/chartDataSlice.js +0 -67
- package/src/state/errorBarSlice.js +0 -53
- package/src/state/eventSettingsSlice.js +0 -25
- package/src/state/graphicalItemsSlice.js +0 -91
- package/src/state/layoutSlice.js +0 -57
- package/src/state/legendSlice.js +0 -83
- package/src/state/optionsSlice.js +0 -45
- package/src/state/polarAxisSlice.js +0 -33
- package/src/state/polarOptionsSlice.js +0 -26
- package/src/state/reduxDevtoolsJsonStringifyReplacer.js +0 -14
- package/src/state/referenceElementsSlice.js +0 -51
- package/src/state/renderedTicksSlice.js +0 -41
- package/src/state/rootPropsSlice.js +0 -48
- package/src/state/selectors/axisSelectors.js +0 -2113
- package/src/state/selectors/barSelectors.js +0 -263
- package/src/state/selectors/barStackSelectors.js +0 -65
- package/src/state/selectors/brushSelectors.js +0 -21
- package/src/state/selectors/combiners/combineActiveLabel.js +0 -18
- package/src/state/selectors/combiners/combineAllBarPositions.js +0 -169
- package/src/state/selectors/combiners/combineAxisRangeWithReverse.js +0 -11
- package/src/state/selectors/combiners/combineBarPosition.js +0 -17
- package/src/state/selectors/combiners/combineBarSizeList.js +0 -115
- package/src/state/selectors/combiners/combineConfiguredScale.js +0 -46
- package/src/state/selectors/combiners/combineCoordinateForDefaultIndex.js +0 -48
- package/src/state/selectors/combiners/combineInverseScaleFunction.js +0 -12
- package/src/state/selectors/combiners/combineStackedData.js +0 -22
- package/src/state/selectors/combiners/combineTooltipInteractionState.js +0 -121
- package/src/state/selectors/combiners/combineTooltipPayload.js +0 -248
- package/src/state/selectors/containerSelectors.js +0 -6
- package/src/state/selectors/graphicalItemSelectors.js +0 -25
- package/src/state/selectors/legendSelectors.js +0 -18
- package/src/state/selectors/lineSelectors.js +0 -106
- package/src/state/selectors/numberDomainEqualityCheck.js +0 -11
- package/src/state/selectors/pickAxisId.js +0 -3
- package/src/state/selectors/pickAxisType.js +0 -3
- package/src/state/selectors/polarAxisSelectors.js +0 -237
- package/src/state/selectors/rootPropsSelectors.js +0 -13
- package/src/state/selectors/selectAllAxes.js +0 -15
- package/src/state/selectors/selectChartOffset.js +0 -12
- package/src/state/selectors/selectChartOffsetInternal.js +0 -174
- package/src/state/selectors/selectTooltipAxisId.js +0 -3
- package/src/state/selectors/selectTooltipEventType.js +0 -27
- package/src/state/selectors/selectTooltipPayloadSearcher.js +0 -3
- package/src/state/selectors/selectTooltipSettings.js +0 -3
- package/src/state/selectors/selectTooltipState.js +0 -3
- package/src/state/selectors/selectors.js +0 -222
- package/src/state/selectors/tooltipSelectors.js +0 -471
- package/src/state/selectors/touchSelectors.js +0 -31
- package/src/state/tooltipSlice.js +0 -237
- package/src/state/types/StackedGraphicalItem.js +0 -18
- package/src/state/zIndexSlice.js +0 -167
- package/src/synchronisation/syncSelectors.js +0 -5
- package/src/util/ChartUtils.js +0 -630
- package/src/util/DOMUtils.js +0 -180
- package/src/util/Events.js +0 -7
- package/src/util/Global.js +0 -13
- package/src/util/LRUCache.js +0 -58
- package/src/util/PolarUtils.js +0 -190
- package/src/util/ReduceCSSCalc.js +0 -260
- package/src/util/axisPropsAreEqual.js +0 -63
- package/src/util/createEventProxy.js +0 -18
- package/src/util/getActiveCoordinate.js +0 -253
- package/src/util/getClassNameFromUnknown.js +0 -8
- package/src/util/getRadiusAndStrokeWidthFromDot.js +0 -28
- package/src/util/getSliced.js +0 -11
- package/src/util/isWellBehavedNumber.js +0 -8
- package/src/util/round.js +0 -39
- package/src/util/scale/RechartsScale.js +0 -68
- package/src/util/scale/index.js +0 -3
- package/src/util/stacks/getStackSeriesIdentifier.js +0 -10
- package/src/zIndex/ZIndexLayer.tsrx.d.ts +0 -2
|
@@ -0,0 +1,2502 @@
|
|
|
1
|
+
// Adapted from recharts@3.9.2, commit b3451050c027a23957ffa50a2665c9119df21e47.
|
|
2
|
+
import { createSelector } from 'reselect';
|
|
3
|
+
import { range } from 'es-toolkit/compat';
|
|
4
|
+
import { selectChartLayout } from '../../context/chartLayoutContext';
|
|
5
|
+
import {
|
|
6
|
+
getDomainOfStackGroups,
|
|
7
|
+
getStackedData,
|
|
8
|
+
getValueByDataKey,
|
|
9
|
+
isCategoricalAxis,
|
|
10
|
+
StackId,
|
|
11
|
+
} from '../../util/ChartUtils';
|
|
12
|
+
import {
|
|
13
|
+
AxisDomain,
|
|
14
|
+
AxisTick,
|
|
15
|
+
AxisType,
|
|
16
|
+
CartesianTickItem,
|
|
17
|
+
CategoricalDomain,
|
|
18
|
+
CategoricalDomainItem,
|
|
19
|
+
ChartOffsetInternal,
|
|
20
|
+
Coordinate,
|
|
21
|
+
RechartsScaleType,
|
|
22
|
+
DataKey,
|
|
23
|
+
LayoutType,
|
|
24
|
+
NumberDomain,
|
|
25
|
+
Size,
|
|
26
|
+
StackOffsetType,
|
|
27
|
+
TickItem,
|
|
28
|
+
} from '../../util/types';
|
|
29
|
+
import {
|
|
30
|
+
AxisId,
|
|
31
|
+
BaseCartesianAxis,
|
|
32
|
+
CartesianAxisSettings,
|
|
33
|
+
XAxisOrientation,
|
|
34
|
+
XAxisSettings,
|
|
35
|
+
YAxisOrientation,
|
|
36
|
+
YAxisSettings,
|
|
37
|
+
ZAxisSettings,
|
|
38
|
+
} from '../cartesianAxisSlice';
|
|
39
|
+
import { RechartsRootState } from '../store';
|
|
40
|
+
import {
|
|
41
|
+
selectChartDataWithIndexes,
|
|
42
|
+
selectChartDataWithIndexesIfNotInPanoramaPosition4,
|
|
43
|
+
selectChartDataSliceIfNotInPanorama,
|
|
44
|
+
} from './dataSelectors';
|
|
45
|
+
import {
|
|
46
|
+
isWellFormedNumberDomain,
|
|
47
|
+
numericalDomainSpecifiedWithoutRequiringData,
|
|
48
|
+
parseNumericalUserDomain,
|
|
49
|
+
} from '../../util/isDomainSpecifiedByUser';
|
|
50
|
+
import { AppliedChartData, ChartData, ChartDataState } from '../chartDataSlice';
|
|
51
|
+
import {
|
|
52
|
+
getPercentValue,
|
|
53
|
+
hasDuplicate,
|
|
54
|
+
isNan,
|
|
55
|
+
isNotNil,
|
|
56
|
+
isNumOrStr,
|
|
57
|
+
mathSign,
|
|
58
|
+
} from '../../util/DataUtils';
|
|
59
|
+
import {
|
|
60
|
+
BaseCartesianGraphicalItemSettings,
|
|
61
|
+
BasePolarGraphicalItemSettings,
|
|
62
|
+
CartesianGraphicalItemSettings,
|
|
63
|
+
GraphicalItemSettings,
|
|
64
|
+
} from '../graphicalItemsSlice';
|
|
65
|
+
import { isWellBehavedNumber } from '../../util/isWellBehavedNumber';
|
|
66
|
+
import { getNiceTickValues, getTickValuesFixedDomain } from '../../util/scale';
|
|
67
|
+
import {
|
|
68
|
+
ReferenceAreaSettings,
|
|
69
|
+
ReferenceDotSettings,
|
|
70
|
+
ReferenceElementSettings,
|
|
71
|
+
ReferenceLineSettings,
|
|
72
|
+
} from '../referenceElementsSlice';
|
|
73
|
+
import { selectChartHeight, selectChartWidth } from './containerSelectors';
|
|
74
|
+
import { selectAllXAxes, selectAllYAxes } from './selectAllAxes';
|
|
75
|
+
import { selectChartOffsetInternal } from './selectChartOffsetInternal';
|
|
76
|
+
import { AxisPropsForCartesianGridTicksGeneration } from '../../cartesian/CartesianGrid.tsrx';
|
|
77
|
+
import { BrushDimensions, selectBrushDimensions, selectBrushSettings } from './brushSelectors';
|
|
78
|
+
import {
|
|
79
|
+
selectBarCategoryGap,
|
|
80
|
+
selectChartName,
|
|
81
|
+
selectReverseStackOrder,
|
|
82
|
+
selectStackOffsetType,
|
|
83
|
+
} from './rootPropsSelectors';
|
|
84
|
+
import {
|
|
85
|
+
selectAngleAxis,
|
|
86
|
+
selectAngleAxisRange,
|
|
87
|
+
selectRadiusAxis,
|
|
88
|
+
selectRadiusAxisRange,
|
|
89
|
+
} from './polarAxisSelectors';
|
|
90
|
+
import { AngleAxisSettings, RadiusAxisSettings } from '../polarAxisSlice';
|
|
91
|
+
import { pickAxisType } from './pickAxisType';
|
|
92
|
+
import { pickAxisId } from './pickAxisId';
|
|
93
|
+
import { combineAxisRangeWithReverse } from './combiners/combineAxisRangeWithReverse';
|
|
94
|
+
import { DEFAULT_Y_AXIS_WIDTH } from '../../util/Constants';
|
|
95
|
+
import { getStackSeriesIdentifier } from '../../util/stacks/getStackSeriesIdentifier';
|
|
96
|
+
import { AllStackGroups, StackGroup } from '../../util/stacks/stackTypes';
|
|
97
|
+
import {
|
|
98
|
+
combineDisplayedStackedData,
|
|
99
|
+
DisplayedStackedData,
|
|
100
|
+
} from './combiners/combineDisplayedStackedData';
|
|
101
|
+
import { DefinitelyStackedGraphicalItem, isStacked } from '../types/StackedGraphicalItem';
|
|
102
|
+
import { ErrorBarsSettings, ErrorBarsState } from '../errorBarSlice';
|
|
103
|
+
import { numberDomainEqualityCheck } from './numberDomainEqualityCheck';
|
|
104
|
+
import { emptyArraysAreEqualCheck } from './arrayEqualityCheck';
|
|
105
|
+
import { AllAxisTypes, RenderableAxisType, selectTooltipAxisType } from './selectTooltipAxisType';
|
|
106
|
+
import { selectTooltipAxisId } from './selectTooltipAxisId';
|
|
107
|
+
import { RechartsScale, rechartsScaleFactory } from '../../util/scale/RechartsScale';
|
|
108
|
+
import { combineCheckedDomain } from './combiners/combineCheckedDomain';
|
|
109
|
+
import { CustomScaleDefinition } from '../../util/scale/CustomScaleDefinition';
|
|
110
|
+
import { combineConfiguredScale } from './combiners/combineConfiguredScale';
|
|
111
|
+
import { combineRealScaleType } from './combiners/combineRealScaleType';
|
|
112
|
+
import { InverseScaleFunction } from '../../hooks';
|
|
113
|
+
import { createCategoricalInverse } from '../../util/scale/createCategoricalInverse';
|
|
114
|
+
import { combineInverseScaleFunction } from './combiners/combineInverseScaleFunction';
|
|
115
|
+
|
|
116
|
+
export const defaultNumericDomain: AxisDomain = [0, 'auto'];
|
|
117
|
+
|
|
118
|
+
export type RenderableAxisSettings =
|
|
119
|
+
XAxisSettings | YAxisSettings | AngleAxisSettings | RadiusAxisSettings;
|
|
120
|
+
|
|
121
|
+
export type AllAxisSettings =
|
|
122
|
+
XAxisSettings | YAxisSettings | ZAxisSettings | AngleAxisSettings | RadiusAxisSettings;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* If an axis is not explicitly defined as an element,
|
|
126
|
+
* we still need to render something in the chart and we need
|
|
127
|
+
* some object to hold the domain and default settings.
|
|
128
|
+
*/
|
|
129
|
+
export const implicitXAxis: XAxisSettings = {
|
|
130
|
+
allowDataOverflow: false,
|
|
131
|
+
allowDecimals: true,
|
|
132
|
+
allowDuplicatedCategory: true,
|
|
133
|
+
angle: 0,
|
|
134
|
+
dataKey: undefined,
|
|
135
|
+
domain: undefined,
|
|
136
|
+
height: 30,
|
|
137
|
+
hide: true,
|
|
138
|
+
id: 0,
|
|
139
|
+
includeHidden: false,
|
|
140
|
+
interval: 'preserveEnd',
|
|
141
|
+
minTickGap: 5,
|
|
142
|
+
mirror: false,
|
|
143
|
+
name: undefined,
|
|
144
|
+
orientation: 'bottom',
|
|
145
|
+
padding: { left: 0, right: 0 },
|
|
146
|
+
reversed: false,
|
|
147
|
+
scale: 'auto',
|
|
148
|
+
tick: true,
|
|
149
|
+
tickCount: 5,
|
|
150
|
+
tickFormatter: undefined,
|
|
151
|
+
ticks: undefined,
|
|
152
|
+
type: 'category',
|
|
153
|
+
unit: undefined,
|
|
154
|
+
niceTicks: 'auto',
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const selectXAxisSettingsNoDefaults = (
|
|
158
|
+
state: RechartsRootState,
|
|
159
|
+
axisId: AxisId,
|
|
160
|
+
): XAxisSettings | undefined => {
|
|
161
|
+
return state.cartesianAxis.xAxis[axisId];
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const selectXAxisSettings = (state: RechartsRootState, axisId: AxisId): XAxisSettings => {
|
|
165
|
+
const axis = selectXAxisSettingsNoDefaults(state, axisId);
|
|
166
|
+
if (axis == null) {
|
|
167
|
+
return implicitXAxis;
|
|
168
|
+
}
|
|
169
|
+
return axis;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* If an axis is not explicitly defined as an element,
|
|
174
|
+
* we still need to render something in the chart and we need
|
|
175
|
+
* some object to hold the domain and default settings.
|
|
176
|
+
*/
|
|
177
|
+
export const implicitYAxis: YAxisSettings = {
|
|
178
|
+
allowDataOverflow: false,
|
|
179
|
+
allowDecimals: true,
|
|
180
|
+
allowDuplicatedCategory: true,
|
|
181
|
+
angle: 0,
|
|
182
|
+
dataKey: undefined,
|
|
183
|
+
domain: defaultNumericDomain,
|
|
184
|
+
hide: true,
|
|
185
|
+
id: 0,
|
|
186
|
+
includeHidden: false,
|
|
187
|
+
interval: 'preserveEnd',
|
|
188
|
+
minTickGap: 5,
|
|
189
|
+
mirror: false,
|
|
190
|
+
name: undefined,
|
|
191
|
+
orientation: 'left',
|
|
192
|
+
padding: { top: 0, bottom: 0 },
|
|
193
|
+
reversed: false,
|
|
194
|
+
scale: 'auto',
|
|
195
|
+
tick: true,
|
|
196
|
+
tickCount: 5,
|
|
197
|
+
tickFormatter: undefined,
|
|
198
|
+
ticks: undefined,
|
|
199
|
+
type: 'number',
|
|
200
|
+
unit: undefined,
|
|
201
|
+
niceTicks: 'auto',
|
|
202
|
+
width: DEFAULT_Y_AXIS_WIDTH,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export const selectYAxisSettingsNoDefaults = (
|
|
206
|
+
state: RechartsRootState,
|
|
207
|
+
axisId: AxisId,
|
|
208
|
+
): YAxisSettings | undefined => {
|
|
209
|
+
return state.cartesianAxis.yAxis[axisId];
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export const selectYAxisSettings = (state: RechartsRootState, axisId: AxisId): YAxisSettings => {
|
|
213
|
+
const axis = selectYAxisSettingsNoDefaults(state, axisId);
|
|
214
|
+
if (axis == null) {
|
|
215
|
+
return implicitYAxis;
|
|
216
|
+
}
|
|
217
|
+
return axis;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export const implicitZAxis: ZAxisSettings = {
|
|
221
|
+
domain: [0, 'auto'],
|
|
222
|
+
includeHidden: false,
|
|
223
|
+
reversed: false,
|
|
224
|
+
allowDataOverflow: false,
|
|
225
|
+
allowDuplicatedCategory: false,
|
|
226
|
+
dataKey: undefined,
|
|
227
|
+
id: 0,
|
|
228
|
+
name: '',
|
|
229
|
+
range: [64, 64],
|
|
230
|
+
scale: 'auto',
|
|
231
|
+
type: 'number',
|
|
232
|
+
unit: '',
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export const selectZAxisSettings = (state: RechartsRootState, axisId: AxisId): ZAxisSettings => {
|
|
236
|
+
const axis = state.cartesianAxis.zAxis[axisId];
|
|
237
|
+
if (axis == null) {
|
|
238
|
+
return implicitZAxis;
|
|
239
|
+
}
|
|
240
|
+
return axis;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export const selectBaseAxis = (
|
|
244
|
+
state: RechartsRootState,
|
|
245
|
+
axisType: AllAxisTypes,
|
|
246
|
+
axisId: AxisId,
|
|
247
|
+
): AllAxisSettings => {
|
|
248
|
+
switch (axisType) {
|
|
249
|
+
case 'xAxis': {
|
|
250
|
+
return selectXAxisSettings(state, axisId);
|
|
251
|
+
}
|
|
252
|
+
case 'yAxis': {
|
|
253
|
+
return selectYAxisSettings(state, axisId);
|
|
254
|
+
}
|
|
255
|
+
case 'zAxis': {
|
|
256
|
+
return selectZAxisSettings(state, axisId);
|
|
257
|
+
}
|
|
258
|
+
case 'angleAxis': {
|
|
259
|
+
return selectAngleAxis(state, axisId);
|
|
260
|
+
}
|
|
261
|
+
case 'radiusAxis': {
|
|
262
|
+
return selectRadiusAxis(state, axisId);
|
|
263
|
+
}
|
|
264
|
+
default:
|
|
265
|
+
throw new Error(`Unexpected axis type: ${axisType}`);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const selectCartesianAxisSettings = (
|
|
270
|
+
state: RechartsRootState,
|
|
271
|
+
axisType: 'xAxis' | 'yAxis',
|
|
272
|
+
axisId: AxisId,
|
|
273
|
+
): XAxisSettings | YAxisSettings => {
|
|
274
|
+
switch (axisType) {
|
|
275
|
+
case 'xAxis': {
|
|
276
|
+
return selectXAxisSettings(state, axisId);
|
|
277
|
+
}
|
|
278
|
+
case 'yAxis': {
|
|
279
|
+
return selectYAxisSettings(state, axisId);
|
|
280
|
+
}
|
|
281
|
+
default:
|
|
282
|
+
throw new Error(`Unexpected axis type: ${axisType}`);
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Selects either an X or Y axis. Doesn't work with Z axis - for that, instead use selectBaseAxis.
|
|
288
|
+
* @param state Root state
|
|
289
|
+
* @param axisType xAxis | yAxis
|
|
290
|
+
* @param axisId xAxisId | yAxisId
|
|
291
|
+
* @returns axis settings object
|
|
292
|
+
*/
|
|
293
|
+
export const selectRenderableAxisSettings = (
|
|
294
|
+
state: RechartsRootState,
|
|
295
|
+
axisType: RenderableAxisType,
|
|
296
|
+
axisId: AxisId,
|
|
297
|
+
): RenderableAxisSettings => {
|
|
298
|
+
switch (axisType) {
|
|
299
|
+
case 'xAxis': {
|
|
300
|
+
return selectXAxisSettings(state, axisId);
|
|
301
|
+
}
|
|
302
|
+
case 'yAxis': {
|
|
303
|
+
return selectYAxisSettings(state, axisId);
|
|
304
|
+
}
|
|
305
|
+
case 'angleAxis': {
|
|
306
|
+
return selectAngleAxis(state, axisId);
|
|
307
|
+
}
|
|
308
|
+
case 'radiusAxis': {
|
|
309
|
+
return selectRadiusAxis(state, axisId);
|
|
310
|
+
}
|
|
311
|
+
default:
|
|
312
|
+
throw new Error(`Unexpected axis type: ${axisType}`);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @param state RechartsRootState
|
|
318
|
+
* @return boolean true if there is at least one Bar or RadialBar
|
|
319
|
+
*/
|
|
320
|
+
export const selectHasBar = (state: RechartsRootState): boolean =>
|
|
321
|
+
state.graphicalItems.cartesianItems.some((item) => item.type === 'bar') ||
|
|
322
|
+
state.graphicalItems.polarItems.some((item) => item.type === 'radialBar');
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Filters CartesianGraphicalItemSettings by the relevant axis ID
|
|
326
|
+
* @param axisType 'xAxis' | 'yAxis' | 'zAxis' | 'radiusAxis' | 'angleAxis'
|
|
327
|
+
* @param axisId from props, defaults to 0
|
|
328
|
+
*
|
|
329
|
+
* @returns Predicate function that return true for CartesianGraphicalItemSettings that are relevant to the specified axis
|
|
330
|
+
*/
|
|
331
|
+
export function itemAxisPredicate(axisType: AllAxisTypes, axisId: AxisId) {
|
|
332
|
+
return (item: BaseCartesianGraphicalItemSettings | BasePolarGraphicalItemSettings) => {
|
|
333
|
+
switch (axisType) {
|
|
334
|
+
case 'xAxis':
|
|
335
|
+
// This is sensitive to the data type, as 0 !== '0'. I wonder if we should be more flexible. How does 2.x branch behave? TODO write test for that
|
|
336
|
+
return 'xAxisId' in item && item.xAxisId === axisId;
|
|
337
|
+
case 'yAxis':
|
|
338
|
+
return 'yAxisId' in item && item.yAxisId === axisId;
|
|
339
|
+
case 'zAxis':
|
|
340
|
+
return 'zAxisId' in item && item.zAxisId === axisId;
|
|
341
|
+
case 'angleAxis':
|
|
342
|
+
return 'angleAxisId' in item && item.angleAxisId === axisId;
|
|
343
|
+
case 'radiusAxis':
|
|
344
|
+
return 'radiusAxisId' in item && item.radiusAxisId === axisId;
|
|
345
|
+
default:
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// TODO appears there is a bug where this selector is called from polar context, find and fix it.
|
|
352
|
+
export const selectUnfilteredCartesianItems = (
|
|
353
|
+
state: RechartsRootState,
|
|
354
|
+
): ReadonlyArray<CartesianGraphicalItemSettings> => state.graphicalItems.cartesianItems;
|
|
355
|
+
|
|
356
|
+
const selectAxisPredicate: (
|
|
357
|
+
_state: RechartsRootState,
|
|
358
|
+
axisType: AllAxisTypes,
|
|
359
|
+
axisId: AxisId,
|
|
360
|
+
) => (item: CartesianGraphicalItemSettings) => boolean = createSelector(
|
|
361
|
+
[pickAxisType, pickAxisId],
|
|
362
|
+
itemAxisPredicate,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
export const combineGraphicalItemsSettings = <T extends GraphicalItemSettings>(
|
|
366
|
+
graphicalItems: ReadonlyArray<T>,
|
|
367
|
+
axisSettings: BaseCartesianAxis,
|
|
368
|
+
axisPredicate: (item: T) => boolean,
|
|
369
|
+
) =>
|
|
370
|
+
graphicalItems.filter(axisPredicate).filter((item) => {
|
|
371
|
+
if (axisSettings?.includeHidden === true) {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
return !item.hide;
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
export const selectCartesianItemsSettings: (
|
|
378
|
+
state: RechartsRootState,
|
|
379
|
+
axisType: AllAxisTypes,
|
|
380
|
+
axisId: AxisId,
|
|
381
|
+
) => ReadonlyArray<CartesianGraphicalItemSettings> = createSelector(
|
|
382
|
+
[selectUnfilteredCartesianItems, selectBaseAxis, selectAxisPredicate],
|
|
383
|
+
combineGraphicalItemsSettings,
|
|
384
|
+
{
|
|
385
|
+
memoizeOptions: {
|
|
386
|
+
resultEqualityCheck: emptyArraysAreEqualCheck,
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
export const selectStackedCartesianItemsSettings: (
|
|
392
|
+
state: RechartsRootState,
|
|
393
|
+
axisType: AllAxisTypes,
|
|
394
|
+
axisId: AxisId,
|
|
395
|
+
) => ReadonlyArray<DefinitelyStackedGraphicalItem> = createSelector(
|
|
396
|
+
[selectCartesianItemsSettings],
|
|
397
|
+
(
|
|
398
|
+
cartesianItems: ReadonlyArray<CartesianGraphicalItemSettings>,
|
|
399
|
+
): ReadonlyArray<DefinitelyStackedGraphicalItem> => {
|
|
400
|
+
return cartesianItems
|
|
401
|
+
.filter((item) => item.type === 'area' || item.type === 'bar')
|
|
402
|
+
.filter(isStacked);
|
|
403
|
+
},
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
export const filterGraphicalNotStackedItems = (
|
|
407
|
+
cartesianItems: ReadonlyArray<GraphicalItemSettings>,
|
|
408
|
+
): ReadonlyArray<GraphicalItemSettings> =>
|
|
409
|
+
cartesianItems.filter((item) => !('stackId' in item) || item.stackId === undefined);
|
|
410
|
+
|
|
411
|
+
const selectCartesianItemsSettingsExceptStacked: (
|
|
412
|
+
state: RechartsRootState,
|
|
413
|
+
axisType: AllAxisTypes,
|
|
414
|
+
axisId: AxisId,
|
|
415
|
+
) => ReadonlyArray<GraphicalItemSettings> = createSelector(
|
|
416
|
+
[selectCartesianItemsSettings],
|
|
417
|
+
filterGraphicalNotStackedItems,
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
export const combineGraphicalItemsData = (cartesianItems: ReadonlyArray<GraphicalItemSettings>) =>
|
|
421
|
+
cartesianItems
|
|
422
|
+
.map((item) => item.data)
|
|
423
|
+
.filter(Boolean)
|
|
424
|
+
.flat(1);
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Returns true if at least one graphical item does not define its own `data` prop
|
|
428
|
+
* and therefore relies on the chart-level data.
|
|
429
|
+
* This is used to decide whether to merge chart-level data into the axis domain calculation.
|
|
430
|
+
*/
|
|
431
|
+
export const selectAnyCartesianItemsUsesChartData: (
|
|
432
|
+
state: RechartsRootState,
|
|
433
|
+
axisType: AllAxisTypes,
|
|
434
|
+
axisId: AxisId,
|
|
435
|
+
) => boolean = createSelector([selectCartesianItemsSettings], (items) =>
|
|
436
|
+
items.some((item) => !item.data),
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* This is a "cheap" selector - it returns the data but doesn't iterate them, so it is not sensitive on the array length.
|
|
441
|
+
* Also does not apply dataKey yet.
|
|
442
|
+
* @param state RechartsRootState
|
|
443
|
+
* @returns data defined on the chart graphical items, such as Line or Scatter or Pie, and filtered with appropriate dataKey
|
|
444
|
+
*/
|
|
445
|
+
export const selectCartesianGraphicalItemsData: (
|
|
446
|
+
state: RechartsRootState,
|
|
447
|
+
axisType: AllAxisTypes,
|
|
448
|
+
axisId: AxisId,
|
|
449
|
+
) => ChartData = createSelector([selectCartesianItemsSettings], combineGraphicalItemsData, {
|
|
450
|
+
memoizeOptions: {
|
|
451
|
+
resultEqualityCheck: emptyArraysAreEqualCheck,
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
export const combineDisplayedData = (
|
|
456
|
+
graphicalItemsData: ChartData,
|
|
457
|
+
{ chartData = [], dataStartIndex, dataEndIndex }: ChartDataState,
|
|
458
|
+
): ChartData => {
|
|
459
|
+
if (graphicalItemsData.length > 0) {
|
|
460
|
+
/*
|
|
461
|
+
* There is no slicing when data is defined on graphical items. Why?
|
|
462
|
+
* Because Brush ignores data defined on graphical items,
|
|
463
|
+
* and does not render.
|
|
464
|
+
* So Brush will never show up in a Scatter chart for example.
|
|
465
|
+
* This is something we will need to fix.
|
|
466
|
+
*
|
|
467
|
+
* Now, when the root chart data is not defined, the dataEndIndex is 0,
|
|
468
|
+
* which means the itemsData will be sliced to an empty array anyway.
|
|
469
|
+
* But that's an implementation detail, and we can fix that too.
|
|
470
|
+
*
|
|
471
|
+
* Also, in absence of Axis dataKey, we use the dataKey from each item, respectively.
|
|
472
|
+
* This is the usual pattern for numerical axis, that is the one where bars go up:
|
|
473
|
+
* users don't specify any dataKey by default and expect the axis to "just match the data".
|
|
474
|
+
*/
|
|
475
|
+
return graphicalItemsData;
|
|
476
|
+
}
|
|
477
|
+
return chartData.slice(dataStartIndex, dataEndIndex + 1);
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* This selector will return all data there is in the chart: graphical items, chart root, all together.
|
|
482
|
+
* Useful for figuring out an axis domain (because that needs to know of everything),
|
|
483
|
+
* not useful for rendering individual graphical elements (because they need to know which data is theirs and which is not).
|
|
484
|
+
*
|
|
485
|
+
* This function will discard the original indexes, so it is also not useful for anything that depends on ordering.
|
|
486
|
+
*/
|
|
487
|
+
export const selectDisplayedData: (
|
|
488
|
+
state: RechartsRootState,
|
|
489
|
+
axisType: AllAxisTypes,
|
|
490
|
+
axisId: AxisId,
|
|
491
|
+
isPanorama: boolean,
|
|
492
|
+
) => ChartData = createSelector(
|
|
493
|
+
[selectCartesianGraphicalItemsData, selectChartDataWithIndexesIfNotInPanoramaPosition4],
|
|
494
|
+
combineDisplayedData,
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
export const combineAppliedValues = (
|
|
498
|
+
data: ChartData,
|
|
499
|
+
axisSettings: BaseCartesianAxis,
|
|
500
|
+
items: ReadonlyArray<GraphicalItemSettings>,
|
|
501
|
+
): AppliedChartData => {
|
|
502
|
+
if (axisSettings?.dataKey != null) {
|
|
503
|
+
return data.map((item) => ({ value: getValueByDataKey(item, axisSettings.dataKey) }));
|
|
504
|
+
}
|
|
505
|
+
if (items.length > 0) {
|
|
506
|
+
return items
|
|
507
|
+
.map((item) => item.dataKey)
|
|
508
|
+
.flatMap((dataKey) => data.map((entry) => ({ value: getValueByDataKey(entry, dataKey) })));
|
|
509
|
+
}
|
|
510
|
+
return data.map((entry) => ({ value: entry }));
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Computes applied values from graphical items data plus, when needed, chart-level data.
|
|
515
|
+
*
|
|
516
|
+
* When at least one graphical item has no own `data` (it relies on chart root data) AND the axis has a `dataKey`,
|
|
517
|
+
* AND there are other items that do have their own data (meaning `displayedData` only contains graphical items data,
|
|
518
|
+
* not chart root data), we also include chart root data values so the axis domain covers all categories,
|
|
519
|
+
* including those only present in the chart root data but not in any graphical item's own data.
|
|
520
|
+
*
|
|
521
|
+
* Values from chart root data that don't match the axis dataKey (undefined) are excluded.
|
|
522
|
+
*/
|
|
523
|
+
export const combineAllAppliedValues = (
|
|
524
|
+
displayedData: ChartData,
|
|
525
|
+
axisSettings: BaseCartesianAxis,
|
|
526
|
+
items: ReadonlyArray<GraphicalItemSettings>,
|
|
527
|
+
{ chartData = [], dataStartIndex, dataEndIndex }: ChartDataState,
|
|
528
|
+
anyItemUsesChartData: boolean,
|
|
529
|
+
graphicalItemsData: ChartData,
|
|
530
|
+
): AppliedChartData => {
|
|
531
|
+
const appliedValues = combineAppliedValues(displayedData, axisSettings, items);
|
|
532
|
+
if (anyItemUsesChartData && axisSettings?.dataKey != null && graphicalItemsData.length > 0) {
|
|
533
|
+
const chartDataSlice = chartData.slice(dataStartIndex, dataEndIndex + 1);
|
|
534
|
+
const chartAppliedValues: AppliedChartData = chartDataSlice
|
|
535
|
+
.map((item) => ({ value: getValueByDataKey(item, axisSettings.dataKey) }))
|
|
536
|
+
.filter((av) => av.value != null);
|
|
537
|
+
return [...chartAppliedValues, ...appliedValues];
|
|
538
|
+
}
|
|
539
|
+
return appliedValues;
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* This selector will return all values with the appropriate dataKey applied on them.
|
|
544
|
+
* Which dataKey is appropriate depends on where it is defined.
|
|
545
|
+
*
|
|
546
|
+
* This is an expensive selector - it will iterate all data and compute their value using the provided dataKey.
|
|
547
|
+
*/
|
|
548
|
+
export const selectAllAppliedValues: (
|
|
549
|
+
state: RechartsRootState,
|
|
550
|
+
axisType: AllAxisTypes,
|
|
551
|
+
axisId: AxisId,
|
|
552
|
+
isPanorama: boolean,
|
|
553
|
+
) => AppliedChartData = createSelector(
|
|
554
|
+
[
|
|
555
|
+
selectDisplayedData,
|
|
556
|
+
selectBaseAxis,
|
|
557
|
+
selectCartesianItemsSettings,
|
|
558
|
+
selectChartDataWithIndexesIfNotInPanoramaPosition4,
|
|
559
|
+
selectAnyCartesianItemsUsesChartData,
|
|
560
|
+
selectCartesianGraphicalItemsData,
|
|
561
|
+
],
|
|
562
|
+
combineAllAppliedValues,
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
function makeNumber(val: unknown): number | undefined {
|
|
566
|
+
if (isNumOrStr(val) || val instanceof Date) {
|
|
567
|
+
const n = Number(val);
|
|
568
|
+
if (isWellBehavedNumber(n)) {
|
|
569
|
+
return n;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return undefined;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function makeDomain(val: unknown): NumberDomain | undefined {
|
|
576
|
+
if (Array.isArray(val)) {
|
|
577
|
+
const attempt = [makeNumber(val[0]), makeNumber(val[1])];
|
|
578
|
+
if (isWellFormedNumberDomain(attempt)) {
|
|
579
|
+
return attempt;
|
|
580
|
+
}
|
|
581
|
+
return undefined;
|
|
582
|
+
}
|
|
583
|
+
const n = makeNumber(val);
|
|
584
|
+
if (n == null) {
|
|
585
|
+
return undefined;
|
|
586
|
+
}
|
|
587
|
+
return [n, n];
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function onlyAllowNumbers(data: ReadonlyArray<unknown>): ReadonlyArray<number> {
|
|
591
|
+
return data.map(makeNumber).filter(isNotNil);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function sortBy(a: unknown, b: unknown): number {
|
|
595
|
+
const aNum = makeNumber(a);
|
|
596
|
+
const bNum = makeNumber(b);
|
|
597
|
+
if (aNum == null && bNum == null) {
|
|
598
|
+
return 0;
|
|
599
|
+
}
|
|
600
|
+
if (aNum == null) {
|
|
601
|
+
return -1;
|
|
602
|
+
}
|
|
603
|
+
if (bNum == null) {
|
|
604
|
+
return 1;
|
|
605
|
+
}
|
|
606
|
+
return aNum - bNum;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
export const selectSortedDataPoints: (
|
|
610
|
+
state: RechartsRootState,
|
|
611
|
+
axisType: AllAxisTypes,
|
|
612
|
+
axisId: AxisId,
|
|
613
|
+
isPanorama: boolean,
|
|
614
|
+
) => ReadonlyArray<unknown> | undefined = createSelector(
|
|
615
|
+
[selectAllAppliedValues],
|
|
616
|
+
(appliedData) => {
|
|
617
|
+
return appliedData?.map((item) => item.value).sort(sortBy);
|
|
618
|
+
},
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
export function isErrorBarRelevantForAxisType(
|
|
622
|
+
axisType: AllAxisTypes,
|
|
623
|
+
errorBar: ErrorBarsSettings,
|
|
624
|
+
): boolean {
|
|
625
|
+
switch (axisType) {
|
|
626
|
+
case 'xAxis':
|
|
627
|
+
return errorBar.direction === 'x';
|
|
628
|
+
case 'yAxis':
|
|
629
|
+
return errorBar.direction === 'y';
|
|
630
|
+
default:
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
export type AppliedChartDataWithErrorDomain = {
|
|
636
|
+
/**
|
|
637
|
+
* This is the value after the dataKey has been applied. Presumably a number? But no guarantees.
|
|
638
|
+
*/
|
|
639
|
+
value: unknown;
|
|
640
|
+
/**
|
|
641
|
+
* This is the error domain, if any, for the current value.
|
|
642
|
+
* This may be either x or y direction, whatever is applicable.
|
|
643
|
+
* Assumption is that we're looking at this data from the point of view of a single axis,
|
|
644
|
+
* and that axis dictates the relevant direction.
|
|
645
|
+
*/
|
|
646
|
+
errorDomain: ReadonlyArray<number> | undefined;
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* @param entry One item in the 'data' array. Could be anything really - this is defined externally. This is the raw, before dataKey application
|
|
651
|
+
* @param appliedValue This is the result of applying the 'main' dataKey on the `entry`.
|
|
652
|
+
* @param relevantErrorBars Error bars that are relevant for the current axis and layout and all that.
|
|
653
|
+
* @return either undefined or an array of ErrorValue
|
|
654
|
+
*/
|
|
655
|
+
export function getErrorDomainByDataKey(
|
|
656
|
+
entry: unknown,
|
|
657
|
+
appliedValue: unknown,
|
|
658
|
+
relevantErrorBars: ReadonlyArray<ErrorBarsSettings> | undefined,
|
|
659
|
+
): ReadonlyArray<number> {
|
|
660
|
+
if (!relevantErrorBars) {
|
|
661
|
+
return [];
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (!relevantErrorBars.length) {
|
|
665
|
+
return [];
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
let appliedNumericValue: number | undefined;
|
|
669
|
+
if (typeof appliedValue === 'number' && !isNan(appliedValue)) {
|
|
670
|
+
appliedNumericValue = appliedValue;
|
|
671
|
+
} else if (Array.isArray(appliedValue)) {
|
|
672
|
+
const numericRangeValues = onlyAllowNumbers(appliedValue);
|
|
673
|
+
if (numericRangeValues.length > 0) {
|
|
674
|
+
appliedNumericValue = Math.max(...numericRangeValues);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (appliedNumericValue == null) {
|
|
679
|
+
return [];
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return onlyAllowNumbers(
|
|
683
|
+
relevantErrorBars.flatMap((eb) => {
|
|
684
|
+
const errorValue = getValueByDataKey(entry, eb.dataKey);
|
|
685
|
+
let lowBound, highBound: unknown;
|
|
686
|
+
|
|
687
|
+
if (Array.isArray(errorValue)) {
|
|
688
|
+
[lowBound, highBound] = errorValue;
|
|
689
|
+
} else {
|
|
690
|
+
lowBound = highBound = errorValue;
|
|
691
|
+
}
|
|
692
|
+
if (!isWellBehavedNumber(lowBound) || !isWellBehavedNumber(highBound)) {
|
|
693
|
+
return undefined;
|
|
694
|
+
}
|
|
695
|
+
return [appliedNumericValue - lowBound, appliedNumericValue + highBound];
|
|
696
|
+
}),
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
export const selectTooltipAxis = (state: RechartsRootState): RenderableAxisSettings => {
|
|
701
|
+
const axisType = selectTooltipAxisType(state);
|
|
702
|
+
const axisId = selectTooltipAxisId(state);
|
|
703
|
+
return selectRenderableAxisSettings(state, axisType, axisId);
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
export const selectTooltipAxisDataKey: (state: RechartsRootState) => DataKey<any> | undefined =
|
|
707
|
+
createSelector(
|
|
708
|
+
[selectTooltipAxis],
|
|
709
|
+
(axis: RenderableAxisSettings | undefined): DataKey<any> | undefined => axis?.dataKey,
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
export const selectDisplayedStackedData: (
|
|
713
|
+
state: RechartsRootState,
|
|
714
|
+
axisType: AllAxisTypes,
|
|
715
|
+
axisId: AxisId,
|
|
716
|
+
isPanorama: boolean,
|
|
717
|
+
) => DisplayedStackedData = createSelector(
|
|
718
|
+
[
|
|
719
|
+
selectStackedCartesianItemsSettings,
|
|
720
|
+
selectChartDataWithIndexesIfNotInPanoramaPosition4,
|
|
721
|
+
selectTooltipAxis,
|
|
722
|
+
],
|
|
723
|
+
combineDisplayedStackedData,
|
|
724
|
+
);
|
|
725
|
+
|
|
726
|
+
export const combineStackGroups = (
|
|
727
|
+
displayedData: DisplayedStackedData,
|
|
728
|
+
items: ReadonlyArray<DefinitelyStackedGraphicalItem>,
|
|
729
|
+
stackOffsetType: StackOffsetType,
|
|
730
|
+
reverseStackOrder: boolean,
|
|
731
|
+
): AllStackGroups => {
|
|
732
|
+
const initialItemsGroups: Record<StackId, Array<DefinitelyStackedGraphicalItem>> = {};
|
|
733
|
+
const itemsGroup: Record<StackId, ReadonlyArray<DefinitelyStackedGraphicalItem>> = items.reduce(
|
|
734
|
+
(
|
|
735
|
+
acc: Record<StackId, Array<DefinitelyStackedGraphicalItem>>,
|
|
736
|
+
item: DefinitelyStackedGraphicalItem,
|
|
737
|
+
) => {
|
|
738
|
+
if (item.stackId == null) {
|
|
739
|
+
return acc;
|
|
740
|
+
}
|
|
741
|
+
let stack = acc[item.stackId];
|
|
742
|
+
if (stack == null) {
|
|
743
|
+
stack = [];
|
|
744
|
+
}
|
|
745
|
+
stack.push(item);
|
|
746
|
+
acc[item.stackId] = stack;
|
|
747
|
+
return acc;
|
|
748
|
+
},
|
|
749
|
+
initialItemsGroups,
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
return Object.fromEntries(
|
|
753
|
+
Object.entries(itemsGroup).map(([stackId, graphicalItems]): [StackId, StackGroup] => {
|
|
754
|
+
const orderedGraphicalItems = reverseStackOrder
|
|
755
|
+
? [...graphicalItems].reverse()
|
|
756
|
+
: graphicalItems;
|
|
757
|
+
const dataKeys = orderedGraphicalItems.map(getStackSeriesIdentifier);
|
|
758
|
+
return [
|
|
759
|
+
stackId,
|
|
760
|
+
{
|
|
761
|
+
// @ts-expect-error getStackedData requires that the input is array of objects, Recharts does not test for that
|
|
762
|
+
stackedData: getStackedData(displayedData, dataKeys, stackOffsetType),
|
|
763
|
+
graphicalItems: orderedGraphicalItems,
|
|
764
|
+
},
|
|
765
|
+
];
|
|
766
|
+
}),
|
|
767
|
+
);
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Stack groups are groups of graphical items that stack on each other.
|
|
772
|
+
* Stack is a function of axis type (X, Y), axis ID, and stack ID.
|
|
773
|
+
* Graphical items that do not have a stack ID are not going to be present in stack groups.
|
|
774
|
+
*/
|
|
775
|
+
export const selectStackGroups: (
|
|
776
|
+
state: RechartsRootState,
|
|
777
|
+
axisType: AllAxisTypes,
|
|
778
|
+
axisId: AxisId,
|
|
779
|
+
isPanorama: boolean,
|
|
780
|
+
) => AllStackGroups | undefined = createSelector(
|
|
781
|
+
[
|
|
782
|
+
selectDisplayedStackedData,
|
|
783
|
+
selectStackedCartesianItemsSettings,
|
|
784
|
+
selectStackOffsetType,
|
|
785
|
+
selectReverseStackOrder,
|
|
786
|
+
],
|
|
787
|
+
combineStackGroups,
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
export const combineDomainOfStackGroups = (
|
|
791
|
+
stackGroups: AllStackGroups | undefined,
|
|
792
|
+
{ dataStartIndex, dataEndIndex }: ChartDataState,
|
|
793
|
+
axisType: AllAxisTypes,
|
|
794
|
+
domainFromUserPreference: NumberDomain | undefined,
|
|
795
|
+
): NumberDomain | undefined => {
|
|
796
|
+
if (domainFromUserPreference != null) {
|
|
797
|
+
// User has specified a domain, so we respect that and we can skip computing anything else
|
|
798
|
+
return undefined;
|
|
799
|
+
}
|
|
800
|
+
if (axisType === 'zAxis') {
|
|
801
|
+
// ZAxis ignores stacks
|
|
802
|
+
return undefined;
|
|
803
|
+
}
|
|
804
|
+
return getDomainOfStackGroups(stackGroups, dataStartIndex, dataEndIndex);
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
const selectAllowsDataOverflow: (
|
|
808
|
+
state: RechartsRootState,
|
|
809
|
+
axisType: AllAxisTypes,
|
|
810
|
+
axisId: AxisId,
|
|
811
|
+
) => boolean = createSelector([selectBaseAxis], (axisSettings) => axisSettings.allowDataOverflow);
|
|
812
|
+
|
|
813
|
+
export const getDomainDefinition = (axisSettings: AllAxisSettings): AxisDomain => {
|
|
814
|
+
if (axisSettings == null || !('domain' in axisSettings)) {
|
|
815
|
+
return defaultNumericDomain;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (axisSettings.domain != null) {
|
|
819
|
+
return axisSettings.domain;
|
|
820
|
+
}
|
|
821
|
+
if ('ticks' in axisSettings && axisSettings.ticks != null) {
|
|
822
|
+
if (axisSettings.type === 'number') {
|
|
823
|
+
const allValues = onlyAllowNumbers(axisSettings.ticks);
|
|
824
|
+
return [Math.min(...allValues), Math.max(...allValues)];
|
|
825
|
+
}
|
|
826
|
+
if (axisSettings.type === 'category') {
|
|
827
|
+
return axisSettings.ticks.map(String);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return axisSettings?.domain ?? defaultNumericDomain;
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
export const selectDomainDefinition: (
|
|
834
|
+
state: RechartsRootState,
|
|
835
|
+
axisType: AllAxisTypes,
|
|
836
|
+
axisId: AxisId,
|
|
837
|
+
) => AxisDomain = createSelector([selectBaseAxis], getDomainDefinition);
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Under certain circumstances, we can determine the domain without looking at the data at all.
|
|
841
|
+
* This is the case when the domain is explicitly specified as numbers, or when it is specified
|
|
842
|
+
* as 'auto' or 'dataMin'/'dataMax' and data overflow is not allowed.
|
|
843
|
+
*
|
|
844
|
+
* In that case, this function will return the domain, otherwise it returns undefined.
|
|
845
|
+
*
|
|
846
|
+
* This is an optimization to avoid unnecessary data processing.
|
|
847
|
+
* @param state
|
|
848
|
+
* @param axisType
|
|
849
|
+
* @param axisId
|
|
850
|
+
* @param isPanorama
|
|
851
|
+
*/
|
|
852
|
+
export const selectDomainFromUserPreference: (
|
|
853
|
+
state: RechartsRootState,
|
|
854
|
+
axisType: AllAxisTypes,
|
|
855
|
+
axisId: AxisId,
|
|
856
|
+
) => NumberDomain | undefined = createSelector(
|
|
857
|
+
[selectDomainDefinition, selectAllowsDataOverflow],
|
|
858
|
+
numericalDomainSpecifiedWithoutRequiringData,
|
|
859
|
+
);
|
|
860
|
+
|
|
861
|
+
export const selectDomainOfStackGroups: (
|
|
862
|
+
state: RechartsRootState,
|
|
863
|
+
axisType: AllAxisTypes,
|
|
864
|
+
axisId: AxisId,
|
|
865
|
+
isPanorama: boolean,
|
|
866
|
+
) => NumberDomain | undefined = createSelector(
|
|
867
|
+
[selectStackGroups, selectChartDataWithIndexes, pickAxisType, selectDomainFromUserPreference],
|
|
868
|
+
combineDomainOfStackGroups,
|
|
869
|
+
{
|
|
870
|
+
memoizeOptions: {
|
|
871
|
+
resultEqualityCheck: numberDomainEqualityCheck,
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
);
|
|
875
|
+
|
|
876
|
+
export const selectAllErrorBarSettings = (state: RechartsRootState): ErrorBarsState =>
|
|
877
|
+
state.errorBars;
|
|
878
|
+
|
|
879
|
+
const combineRelevantErrorBarSettings = (
|
|
880
|
+
cartesianItemsSettings: ReadonlyArray<CartesianGraphicalItemSettings>,
|
|
881
|
+
allErrorBarSettings: ErrorBarsState,
|
|
882
|
+
axisType: RenderableAxisType,
|
|
883
|
+
): ReadonlyArray<ErrorBarsSettings> => {
|
|
884
|
+
return cartesianItemsSettings
|
|
885
|
+
.flatMap((item) => {
|
|
886
|
+
return allErrorBarSettings[item.id];
|
|
887
|
+
})
|
|
888
|
+
.filter(Boolean)
|
|
889
|
+
.filter((e) => {
|
|
890
|
+
return isErrorBarRelevantForAxisType(axisType, e);
|
|
891
|
+
});
|
|
892
|
+
};
|
|
893
|
+
|
|
894
|
+
export const mergeDomains = (
|
|
895
|
+
...domains: ReadonlyArray<ReadonlyArray<number> | undefined>
|
|
896
|
+
): NumberDomain | undefined => {
|
|
897
|
+
const allDomains = domains.filter(
|
|
898
|
+
Boolean as BooleanConstructor &
|
|
899
|
+
((domain: ReadonlyArray<number> | undefined) => domain is ReadonlyArray<number>),
|
|
900
|
+
);
|
|
901
|
+
if (allDomains.length === 0) {
|
|
902
|
+
return undefined;
|
|
903
|
+
}
|
|
904
|
+
const allValues = allDomains.flat();
|
|
905
|
+
const min = Math.min(...allValues);
|
|
906
|
+
const max = Math.max(...allValues);
|
|
907
|
+
return [min, max];
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
export const combineDomainOfAllAppliedNumericalValuesIncludingErrorValues = (
|
|
911
|
+
displayedData: ChartData,
|
|
912
|
+
axisSettings: BaseCartesianAxis,
|
|
913
|
+
items: ReadonlyArray<GraphicalItemSettings>,
|
|
914
|
+
errorBars: ErrorBarsState,
|
|
915
|
+
axisType: AllAxisTypes,
|
|
916
|
+
chartDataSlice: ChartData = [],
|
|
917
|
+
): NumberDomain | undefined => {
|
|
918
|
+
let lowerEnd: number | undefined, upperEnd: number | undefined;
|
|
919
|
+
if (items.length > 0) {
|
|
920
|
+
/*
|
|
921
|
+
* Iterate item-first so each item can use its own `data` prop when present,
|
|
922
|
+
* falling back to the chart-level data slice for items that rely on it.
|
|
923
|
+
* This ensures that mixing items with and without own data (e.g. Bar reading
|
|
924
|
+
* chart-level data alongside a Scatter with its own outlier data) produces a
|
|
925
|
+
* domain that covers all values from all sources.
|
|
926
|
+
*/
|
|
927
|
+
items.forEach((item) => {
|
|
928
|
+
const itemData: ChartData = item.data != null ? [...item.data] : chartDataSlice;
|
|
929
|
+
const relevantErrorBars = errorBars[item.id]?.filter((errorBar) =>
|
|
930
|
+
isErrorBarRelevantForAxisType(axisType, errorBar),
|
|
931
|
+
);
|
|
932
|
+
itemData.forEach((entry) => {
|
|
933
|
+
const valueByDataKey = getValueByDataKey(entry, axisSettings.dataKey ?? item.dataKey);
|
|
934
|
+
const errorDomain = getErrorDomainByDataKey(entry, valueByDataKey, relevantErrorBars);
|
|
935
|
+
if (errorDomain.length >= 2) {
|
|
936
|
+
const localLower = Math.min(...errorDomain);
|
|
937
|
+
const localUpper = Math.max(...errorDomain);
|
|
938
|
+
if (lowerEnd == null || localLower < lowerEnd) {
|
|
939
|
+
lowerEnd = localLower;
|
|
940
|
+
}
|
|
941
|
+
if (upperEnd == null || localUpper > upperEnd) {
|
|
942
|
+
upperEnd = localUpper;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
const dataValueDomain: NumberDomain | undefined = makeDomain(valueByDataKey);
|
|
946
|
+
if (dataValueDomain != null) {
|
|
947
|
+
lowerEnd = lowerEnd == null ? dataValueDomain[0] : Math.min(lowerEnd, dataValueDomain[0]);
|
|
948
|
+
upperEnd = upperEnd == null ? dataValueDomain[1] : Math.max(upperEnd, dataValueDomain[1]);
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
if (axisSettings?.dataKey != null && items.length === 0) {
|
|
954
|
+
// When there are no items, fall back to the displayed data (chart-level or graphical item data).
|
|
955
|
+
displayedData.forEach((item) => {
|
|
956
|
+
const dataValueDomain: NumberDomain | undefined = makeDomain(
|
|
957
|
+
getValueByDataKey(item, axisSettings.dataKey),
|
|
958
|
+
);
|
|
959
|
+
if (dataValueDomain != null) {
|
|
960
|
+
lowerEnd = lowerEnd == null ? dataValueDomain[0] : Math.min(lowerEnd, dataValueDomain[0]);
|
|
961
|
+
upperEnd = upperEnd == null ? dataValueDomain[1] : Math.max(upperEnd, dataValueDomain[1]);
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
if (isWellBehavedNumber(lowerEnd) && isWellBehavedNumber(upperEnd)) {
|
|
967
|
+
return [lowerEnd, upperEnd];
|
|
968
|
+
}
|
|
969
|
+
return undefined;
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
const selectDomainOfAllAppliedNumericalValuesIncludingErrorValues: (
|
|
973
|
+
state: RechartsRootState,
|
|
974
|
+
axisType: AllAxisTypes,
|
|
975
|
+
axisId: AxisId,
|
|
976
|
+
isPanorama: boolean,
|
|
977
|
+
) => NumberDomain | undefined = createSelector(
|
|
978
|
+
[
|
|
979
|
+
selectDisplayedData,
|
|
980
|
+
selectBaseAxis,
|
|
981
|
+
selectCartesianItemsSettingsExceptStacked,
|
|
982
|
+
selectAllErrorBarSettings,
|
|
983
|
+
pickAxisType,
|
|
984
|
+
selectChartDataSliceIfNotInPanorama,
|
|
985
|
+
],
|
|
986
|
+
combineDomainOfAllAppliedNumericalValuesIncludingErrorValues,
|
|
987
|
+
{
|
|
988
|
+
memoizeOptions: {
|
|
989
|
+
resultEqualityCheck: numberDomainEqualityCheck,
|
|
990
|
+
},
|
|
991
|
+
},
|
|
992
|
+
);
|
|
993
|
+
|
|
994
|
+
function onlyAllowNumbersAndStringsAndDates(item: {
|
|
995
|
+
value: unknown;
|
|
996
|
+
}): string | number | Date | undefined {
|
|
997
|
+
const { value } = item;
|
|
998
|
+
if (isNumOrStr(value) || value instanceof Date) {
|
|
999
|
+
return value;
|
|
1000
|
+
}
|
|
1001
|
+
return undefined;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
const computeDomainOfTypeCategory = (
|
|
1005
|
+
allDataSquished: AppliedChartData,
|
|
1006
|
+
axisSettings: BaseCartesianAxis,
|
|
1007
|
+
isCategorical: boolean,
|
|
1008
|
+
): CategoricalDomain => {
|
|
1009
|
+
const categoricalDomain = allDataSquished
|
|
1010
|
+
.map(onlyAllowNumbersAndStringsAndDates)
|
|
1011
|
+
.filter((v) => v != null);
|
|
1012
|
+
if (
|
|
1013
|
+
isCategorical &&
|
|
1014
|
+
(axisSettings.dataKey == null ||
|
|
1015
|
+
(axisSettings.allowDuplicatedCategory && hasDuplicate(categoricalDomain)))
|
|
1016
|
+
) {
|
|
1017
|
+
/*
|
|
1018
|
+
* 1. In an absence of dataKey, Recharts will use array indexes as its categorical domain
|
|
1019
|
+
* 2. When category axis has duplicated text, serial numbers are used to generate scale
|
|
1020
|
+
*/
|
|
1021
|
+
return range(0, allDataSquished.length);
|
|
1022
|
+
}
|
|
1023
|
+
if (axisSettings.allowDuplicatedCategory) {
|
|
1024
|
+
return categoricalDomain;
|
|
1025
|
+
}
|
|
1026
|
+
return Array.from(new Set(categoricalDomain));
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1029
|
+
export const selectReferenceDots = (
|
|
1030
|
+
state: RechartsRootState,
|
|
1031
|
+
): ReadonlyArray<ReferenceDotSettings> => state.referenceElements.dots;
|
|
1032
|
+
|
|
1033
|
+
export const filterReferenceElements = <T extends ReferenceElementSettings>(
|
|
1034
|
+
elements: ReadonlyArray<T>,
|
|
1035
|
+
axisType: AllAxisTypes,
|
|
1036
|
+
axisId: AxisId,
|
|
1037
|
+
): ReadonlyArray<T> => {
|
|
1038
|
+
return elements
|
|
1039
|
+
.filter((el) => el.ifOverflow === 'extendDomain')
|
|
1040
|
+
.filter((el) => {
|
|
1041
|
+
if (axisType === 'xAxis') {
|
|
1042
|
+
return el.xAxisId === axisId;
|
|
1043
|
+
}
|
|
1044
|
+
return el.yAxisId === axisId;
|
|
1045
|
+
});
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
export const selectReferenceDotsByAxis: (
|
|
1049
|
+
state: RechartsRootState,
|
|
1050
|
+
axisType: AllAxisTypes,
|
|
1051
|
+
axisId: AxisId,
|
|
1052
|
+
) => ReadonlyArray<ReferenceDotSettings> = createSelector(
|
|
1053
|
+
[selectReferenceDots, pickAxisType, pickAxisId],
|
|
1054
|
+
filterReferenceElements,
|
|
1055
|
+
);
|
|
1056
|
+
|
|
1057
|
+
export const selectReferenceAreas = (
|
|
1058
|
+
state: RechartsRootState,
|
|
1059
|
+
): ReadonlyArray<ReferenceAreaSettings> => state.referenceElements.areas;
|
|
1060
|
+
|
|
1061
|
+
export const selectReferenceAreasByAxis: (
|
|
1062
|
+
state: RechartsRootState,
|
|
1063
|
+
axisType: AllAxisTypes,
|
|
1064
|
+
axisId: AxisId,
|
|
1065
|
+
) => ReadonlyArray<ReferenceAreaSettings> = createSelector(
|
|
1066
|
+
[selectReferenceAreas, pickAxisType, pickAxisId],
|
|
1067
|
+
filterReferenceElements,
|
|
1068
|
+
);
|
|
1069
|
+
|
|
1070
|
+
export const selectReferenceLines = (
|
|
1071
|
+
state: RechartsRootState,
|
|
1072
|
+
): ReadonlyArray<ReferenceLineSettings> => state.referenceElements.lines;
|
|
1073
|
+
|
|
1074
|
+
export const selectReferenceLinesByAxis: (
|
|
1075
|
+
state: RechartsRootState,
|
|
1076
|
+
axisType: AllAxisTypes,
|
|
1077
|
+
axisId: AxisId,
|
|
1078
|
+
) => ReadonlyArray<ReferenceLineSettings> = createSelector(
|
|
1079
|
+
[selectReferenceLines, pickAxisType, pickAxisId],
|
|
1080
|
+
filterReferenceElements,
|
|
1081
|
+
);
|
|
1082
|
+
|
|
1083
|
+
export const combineDotsDomain = (
|
|
1084
|
+
dots: ReadonlyArray<ReferenceDotSettings> | undefined,
|
|
1085
|
+
axisType: RenderableAxisType,
|
|
1086
|
+
): NumberDomain | undefined => {
|
|
1087
|
+
if (dots == null) {
|
|
1088
|
+
return undefined;
|
|
1089
|
+
}
|
|
1090
|
+
const allCoords = onlyAllowNumbers(dots.map((dot) => (axisType === 'xAxis' ? dot.x : dot.y)));
|
|
1091
|
+
if (allCoords.length === 0) {
|
|
1092
|
+
return undefined;
|
|
1093
|
+
}
|
|
1094
|
+
return [Math.min(...allCoords), Math.max(...allCoords)];
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
const selectReferenceDotsDomain = createSelector(
|
|
1098
|
+
selectReferenceDotsByAxis,
|
|
1099
|
+
pickAxisType,
|
|
1100
|
+
combineDotsDomain,
|
|
1101
|
+
);
|
|
1102
|
+
|
|
1103
|
+
export const combineAreasDomain = (
|
|
1104
|
+
areas: ReadonlyArray<ReferenceAreaSettings> | undefined,
|
|
1105
|
+
axisType: RenderableAxisType,
|
|
1106
|
+
): NumberDomain | undefined => {
|
|
1107
|
+
if (areas == null) {
|
|
1108
|
+
return undefined;
|
|
1109
|
+
}
|
|
1110
|
+
const allCoords = onlyAllowNumbers(
|
|
1111
|
+
areas.flatMap((area) => [
|
|
1112
|
+
axisType === 'xAxis' ? area.x1 : area.y1,
|
|
1113
|
+
axisType === 'xAxis' ? area.x2 : area.y2,
|
|
1114
|
+
]),
|
|
1115
|
+
);
|
|
1116
|
+
if (allCoords.length === 0) {
|
|
1117
|
+
return undefined;
|
|
1118
|
+
}
|
|
1119
|
+
return [Math.min(...allCoords), Math.max(...allCoords)];
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
const selectReferenceAreasDomain = createSelector(
|
|
1123
|
+
[selectReferenceAreasByAxis, pickAxisType],
|
|
1124
|
+
combineAreasDomain,
|
|
1125
|
+
);
|
|
1126
|
+
|
|
1127
|
+
function extractXCoordinates(line: ReferenceLineSettings): ReadonlyArray<number> {
|
|
1128
|
+
if (line.x != null) {
|
|
1129
|
+
return onlyAllowNumbers([line.x]);
|
|
1130
|
+
}
|
|
1131
|
+
const segmentCoordinates: ReadonlyArray<string | number | undefined> | undefined =
|
|
1132
|
+
line.segment?.map((s) => s.x);
|
|
1133
|
+
if (segmentCoordinates == null || segmentCoordinates.length === 0) {
|
|
1134
|
+
return [];
|
|
1135
|
+
}
|
|
1136
|
+
return onlyAllowNumbers(segmentCoordinates);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
function extractYCoordinates(line: ReferenceLineSettings): ReadonlyArray<number> {
|
|
1140
|
+
if (line.y != null) {
|
|
1141
|
+
return onlyAllowNumbers([line.y]);
|
|
1142
|
+
}
|
|
1143
|
+
const segmentCoordinates: ReadonlyArray<string | number | undefined> | undefined =
|
|
1144
|
+
line.segment?.map((s) => s.y);
|
|
1145
|
+
if (segmentCoordinates == null || segmentCoordinates.length === 0) {
|
|
1146
|
+
return [];
|
|
1147
|
+
}
|
|
1148
|
+
return onlyAllowNumbers(segmentCoordinates);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
export const combineLinesDomain = (
|
|
1152
|
+
lines: ReadonlyArray<ReferenceLineSettings> | undefined,
|
|
1153
|
+
axisType: RenderableAxisType,
|
|
1154
|
+
): NumberDomain | undefined => {
|
|
1155
|
+
if (lines == null) {
|
|
1156
|
+
return undefined;
|
|
1157
|
+
}
|
|
1158
|
+
const allCoords: ReadonlyArray<number> = lines.flatMap((line) =>
|
|
1159
|
+
axisType === 'xAxis' ? extractXCoordinates(line) : extractYCoordinates(line),
|
|
1160
|
+
);
|
|
1161
|
+
if (allCoords.length === 0) {
|
|
1162
|
+
return undefined;
|
|
1163
|
+
}
|
|
1164
|
+
return [Math.min(...allCoords), Math.max(...allCoords)];
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
const selectReferenceLinesDomain = createSelector(
|
|
1168
|
+
[selectReferenceLinesByAxis, pickAxisType],
|
|
1169
|
+
combineLinesDomain,
|
|
1170
|
+
);
|
|
1171
|
+
|
|
1172
|
+
const selectReferenceElementsDomain = createSelector(
|
|
1173
|
+
selectReferenceDotsDomain,
|
|
1174
|
+
selectReferenceLinesDomain,
|
|
1175
|
+
selectReferenceAreasDomain,
|
|
1176
|
+
(dotsDomain, linesDomain, areasDomain): NumberDomain | undefined => {
|
|
1177
|
+
return mergeDomains(dotsDomain, areasDomain, linesDomain);
|
|
1178
|
+
},
|
|
1179
|
+
);
|
|
1180
|
+
|
|
1181
|
+
export const combineNumericalDomain = (
|
|
1182
|
+
axisSettings: BaseCartesianAxis,
|
|
1183
|
+
domainDefinition: AxisDomain | undefined,
|
|
1184
|
+
domainFromUserPreference: NumberDomain | undefined,
|
|
1185
|
+
domainOfStackGroups: NumberDomain | undefined,
|
|
1186
|
+
dataAndErrorBarsDomain: NumberDomain | undefined,
|
|
1187
|
+
referenceElementsDomain: NumberDomain | undefined,
|
|
1188
|
+
layout: LayoutType,
|
|
1189
|
+
axisType: AllAxisTypes,
|
|
1190
|
+
numericTicksDomain?: NumberDomain | undefined,
|
|
1191
|
+
): NumberDomain | undefined => {
|
|
1192
|
+
if (domainFromUserPreference != null) {
|
|
1193
|
+
// We're done! No need to compute anything else.
|
|
1194
|
+
return domainFromUserPreference;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
const shouldIncludeDomainOfStackGroups =
|
|
1198
|
+
(layout === 'vertical' && axisType === 'xAxis') ||
|
|
1199
|
+
(layout === 'horizontal' && axisType === 'yAxis');
|
|
1200
|
+
|
|
1201
|
+
const mergedDomains = shouldIncludeDomainOfStackGroups
|
|
1202
|
+
? mergeDomains(domainOfStackGroups, referenceElementsDomain, dataAndErrorBarsDomain)
|
|
1203
|
+
: mergeDomains(referenceElementsDomain, dataAndErrorBarsDomain);
|
|
1204
|
+
|
|
1205
|
+
const parsedDomain = parseNumericalUserDomain(
|
|
1206
|
+
domainDefinition,
|
|
1207
|
+
mergedDomains,
|
|
1208
|
+
axisSettings.allowDataOverflow,
|
|
1209
|
+
);
|
|
1210
|
+
if (parsedDomain != null) {
|
|
1211
|
+
return parsedDomain;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/*
|
|
1215
|
+
* https://github.com/recharts/recharts/issues/7362
|
|
1216
|
+
* The user-provided domain could not be resolved without data - it's a function
|
|
1217
|
+
* bound or one of 'auto'/'dataMin'/'dataMax' - and there is no data domain to
|
|
1218
|
+
* resolve it from: empty or all-null data, and no domain-extending reference
|
|
1219
|
+
* elements (only reference dots/lines/areas with ifOverflow="extendDomain"
|
|
1220
|
+
* contribute to mergedDomains). If the
|
|
1221
|
+
* axis opted into data overflow and supplied an explicit numeric `ticks` array,
|
|
1222
|
+
* fall back to the ticks' [min, max] extent so the explicit tick ladder still
|
|
1223
|
+
* renders, mirroring what a literal numeric domain already does.
|
|
1224
|
+
*/
|
|
1225
|
+
if (axisSettings.allowDataOverflow && mergedDomains == null && numericTicksDomain != null) {
|
|
1226
|
+
return numericTicksDomain;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
return parsedDomain;
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* https://github.com/recharts/recharts/issues/7362
|
|
1234
|
+
* Derives the [min, max] extent of an axis's explicit numeric `ticks`. Used by
|
|
1235
|
+
* {@link combineNumericalDomain} as a last-resort domain when a non-literal domain
|
|
1236
|
+
* cannot be resolved without data. Returns undefined for non-number axes, or when
|
|
1237
|
+
* no numeric ticks were supplied.
|
|
1238
|
+
*/
|
|
1239
|
+
export const combineNumericTicksDomain = (
|
|
1240
|
+
axisSettings: AllAxisSettings,
|
|
1241
|
+
): NumberDomain | undefined => {
|
|
1242
|
+
if (
|
|
1243
|
+
axisSettings == null ||
|
|
1244
|
+
axisSettings.type !== 'number' ||
|
|
1245
|
+
!('ticks' in axisSettings) ||
|
|
1246
|
+
axisSettings.ticks == null
|
|
1247
|
+
) {
|
|
1248
|
+
return undefined;
|
|
1249
|
+
}
|
|
1250
|
+
const numericTicks = onlyAllowNumbers(axisSettings.ticks);
|
|
1251
|
+
if (numericTicks.length === 0) {
|
|
1252
|
+
return undefined;
|
|
1253
|
+
}
|
|
1254
|
+
return [Math.min(...numericTicks), Math.max(...numericTicks)];
|
|
1255
|
+
};
|
|
1256
|
+
|
|
1257
|
+
export const selectNumericTicksDomain: (
|
|
1258
|
+
state: RechartsRootState,
|
|
1259
|
+
axisType: AllAxisTypes,
|
|
1260
|
+
axisId: AxisId,
|
|
1261
|
+
) => NumberDomain | undefined = createSelector([selectBaseAxis], combineNumericTicksDomain, {
|
|
1262
|
+
memoizeOptions: {
|
|
1263
|
+
resultEqualityCheck: numberDomainEqualityCheck,
|
|
1264
|
+
},
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
export const selectNumericalDomain: (
|
|
1268
|
+
state: RechartsRootState,
|
|
1269
|
+
axisType: AllAxisTypes,
|
|
1270
|
+
axisId: AxisId,
|
|
1271
|
+
isPanorama: boolean,
|
|
1272
|
+
) => NumberDomain | undefined = createSelector(
|
|
1273
|
+
[
|
|
1274
|
+
selectBaseAxis,
|
|
1275
|
+
selectDomainDefinition,
|
|
1276
|
+
selectDomainFromUserPreference,
|
|
1277
|
+
selectDomainOfStackGroups,
|
|
1278
|
+
selectDomainOfAllAppliedNumericalValuesIncludingErrorValues,
|
|
1279
|
+
selectReferenceElementsDomain,
|
|
1280
|
+
selectChartLayout,
|
|
1281
|
+
pickAxisType,
|
|
1282
|
+
selectNumericTicksDomain,
|
|
1283
|
+
],
|
|
1284
|
+
combineNumericalDomain,
|
|
1285
|
+
{
|
|
1286
|
+
memoizeOptions: {
|
|
1287
|
+
resultEqualityCheck: numberDomainEqualityCheck,
|
|
1288
|
+
},
|
|
1289
|
+
},
|
|
1290
|
+
);
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Expand by design maps everything between 0 and 1,
|
|
1294
|
+
* there is nothing to compute.
|
|
1295
|
+
* See https://d3js.org/d3-shape/stack#stack-offsets
|
|
1296
|
+
*/
|
|
1297
|
+
const expandDomain: NumberDomain = [0, 1];
|
|
1298
|
+
|
|
1299
|
+
export const combineAxisDomain = (
|
|
1300
|
+
axisSettings: BaseCartesianAxis,
|
|
1301
|
+
layout: LayoutType,
|
|
1302
|
+
displayedData: ChartData | undefined,
|
|
1303
|
+
allAppliedValues: AppliedChartData,
|
|
1304
|
+
stackOffsetType: StackOffsetType,
|
|
1305
|
+
axisType: AllAxisTypes,
|
|
1306
|
+
numericalDomain: NumberDomain | undefined,
|
|
1307
|
+
): NumberDomain | CategoricalDomain | undefined => {
|
|
1308
|
+
if (
|
|
1309
|
+
(axisSettings == null || displayedData == null || displayedData.length === 0) &&
|
|
1310
|
+
numericalDomain === undefined
|
|
1311
|
+
) {
|
|
1312
|
+
return undefined;
|
|
1313
|
+
}
|
|
1314
|
+
const { dataKey, type } = axisSettings;
|
|
1315
|
+
const isCategorical = isCategoricalAxis(layout, axisType);
|
|
1316
|
+
|
|
1317
|
+
if (isCategorical && dataKey == null) {
|
|
1318
|
+
return range(0, displayedData?.length ?? 0);
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
if (type === 'category') {
|
|
1322
|
+
return computeDomainOfTypeCategory(allAppliedValues, axisSettings, isCategorical);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
if (stackOffsetType === 'expand' && !isCategorical) {
|
|
1326
|
+
return expandDomain;
|
|
1327
|
+
}
|
|
1328
|
+
return numericalDomain;
|
|
1329
|
+
};
|
|
1330
|
+
|
|
1331
|
+
export const selectAxisDomain: (
|
|
1332
|
+
state: RechartsRootState,
|
|
1333
|
+
axisType: AllAxisTypes,
|
|
1334
|
+
axisId: AxisId,
|
|
1335
|
+
isPanorama: boolean,
|
|
1336
|
+
) => NumberDomain | CategoricalDomain | undefined = createSelector(
|
|
1337
|
+
[
|
|
1338
|
+
selectBaseAxis,
|
|
1339
|
+
selectChartLayout,
|
|
1340
|
+
selectDisplayedData,
|
|
1341
|
+
selectAllAppliedValues,
|
|
1342
|
+
selectStackOffsetType,
|
|
1343
|
+
pickAxisType,
|
|
1344
|
+
selectNumericalDomain,
|
|
1345
|
+
],
|
|
1346
|
+
combineAxisDomain,
|
|
1347
|
+
);
|
|
1348
|
+
|
|
1349
|
+
export const selectRealScaleType: (
|
|
1350
|
+
state: RechartsRootState,
|
|
1351
|
+
axisType: AllAxisTypes,
|
|
1352
|
+
axisId: AxisId,
|
|
1353
|
+
) => RechartsScaleType | undefined = createSelector(
|
|
1354
|
+
[selectBaseAxis, selectHasBar, selectChartName],
|
|
1355
|
+
combineRealScaleType,
|
|
1356
|
+
);
|
|
1357
|
+
|
|
1358
|
+
export const combineNiceTicks = (
|
|
1359
|
+
axisDomain: NumberDomain | CategoricalDomain | undefined,
|
|
1360
|
+
axisSettings: RenderableAxisSettings,
|
|
1361
|
+
realScaleType: string | undefined,
|
|
1362
|
+
): ReadonlyArray<number> | undefined => {
|
|
1363
|
+
const { niceTicks } = axisSettings;
|
|
1364
|
+
|
|
1365
|
+
if (niceTicks === 'none') {
|
|
1366
|
+
return undefined;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const domainDefinition: AxisDomain = getDomainDefinition(axisSettings);
|
|
1370
|
+
const hasDomainAutoKeyword =
|
|
1371
|
+
Array.isArray(domainDefinition) &&
|
|
1372
|
+
(domainDefinition[0] === 'auto' || domainDefinition[1] === 'auto');
|
|
1373
|
+
|
|
1374
|
+
if (
|
|
1375
|
+
(niceTicks === 'snap125' || niceTicks === 'adaptive') &&
|
|
1376
|
+
axisSettings != null &&
|
|
1377
|
+
axisSettings.tickCount &&
|
|
1378
|
+
isWellFormedNumberDomain(axisDomain)
|
|
1379
|
+
) {
|
|
1380
|
+
if (hasDomainAutoKeyword) {
|
|
1381
|
+
return getNiceTickValues(
|
|
1382
|
+
axisDomain,
|
|
1383
|
+
axisSettings.tickCount,
|
|
1384
|
+
axisSettings.allowDecimals,
|
|
1385
|
+
niceTicks,
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
if (axisSettings.type === 'number') {
|
|
1389
|
+
return getTickValuesFixedDomain(
|
|
1390
|
+
axisDomain as NumberDomain,
|
|
1391
|
+
axisSettings.tickCount,
|
|
1392
|
+
axisSettings.allowDecimals,
|
|
1393
|
+
niceTicks,
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
if (
|
|
1399
|
+
niceTicks === 'auto' &&
|
|
1400
|
+
realScaleType === 'linear' &&
|
|
1401
|
+
axisSettings != null &&
|
|
1402
|
+
axisSettings.tickCount
|
|
1403
|
+
) {
|
|
1404
|
+
// Current magic-selector behaviour: apply nice ticks when the domain contains
|
|
1405
|
+
// an 'auto' keyword (may extend the domain), or for any fixed number-type axis.
|
|
1406
|
+
// Always uses the space-efficient algorithm (adaptive).
|
|
1407
|
+
if (hasDomainAutoKeyword && isWellFormedNumberDomain(axisDomain)) {
|
|
1408
|
+
return getNiceTickValues(
|
|
1409
|
+
axisDomain,
|
|
1410
|
+
axisSettings.tickCount,
|
|
1411
|
+
axisSettings.allowDecimals,
|
|
1412
|
+
'adaptive',
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
if (axisSettings.type === 'number' && isWellFormedNumberDomain(axisDomain)) {
|
|
1417
|
+
return getTickValuesFixedDomain(
|
|
1418
|
+
axisDomain as NumberDomain,
|
|
1419
|
+
axisSettings.tickCount,
|
|
1420
|
+
axisSettings.allowDecimals,
|
|
1421
|
+
'adaptive',
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
return undefined;
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
export const selectNiceTicks: (
|
|
1430
|
+
state: RechartsRootState,
|
|
1431
|
+
axisType: RenderableAxisType,
|
|
1432
|
+
axisId: AxisId,
|
|
1433
|
+
isPanorama: boolean,
|
|
1434
|
+
) => ReadonlyArray<number> | undefined = createSelector(
|
|
1435
|
+
[selectAxisDomain, selectRenderableAxisSettings, selectRealScaleType],
|
|
1436
|
+
combineNiceTicks,
|
|
1437
|
+
);
|
|
1438
|
+
|
|
1439
|
+
export const combineAxisDomainWithNiceTicks = (
|
|
1440
|
+
axisSettings: BaseCartesianAxis,
|
|
1441
|
+
domain: NumberDomain | CategoricalDomain | undefined,
|
|
1442
|
+
niceTicks: ReadonlyArray<number> | undefined,
|
|
1443
|
+
axisType: RenderableAxisType,
|
|
1444
|
+
): NumberDomain | CategoricalDomain | undefined => {
|
|
1445
|
+
if (
|
|
1446
|
+
/*
|
|
1447
|
+
* Angle axis for some reason uses nice ticks when rendering axis tick labels,
|
|
1448
|
+
* but doesn't use nice ticks for extending domain like all the other axes do.
|
|
1449
|
+
* Not really sure why? Is there a good reason,
|
|
1450
|
+
* or is it just because someone added support for nice ticks to the other axes and forgot this one?
|
|
1451
|
+
*/
|
|
1452
|
+
axisType !== 'angleAxis' &&
|
|
1453
|
+
axisSettings?.type === 'number' &&
|
|
1454
|
+
isWellFormedNumberDomain(domain) &&
|
|
1455
|
+
Array.isArray(niceTicks) &&
|
|
1456
|
+
niceTicks.length > 0
|
|
1457
|
+
) {
|
|
1458
|
+
const minFromDomain = domain[0];
|
|
1459
|
+
const minFromTicks = niceTicks[0] ?? 0;
|
|
1460
|
+
const maxFromDomain = domain[1];
|
|
1461
|
+
const maxFromTicks = niceTicks[niceTicks.length - 1] ?? 0;
|
|
1462
|
+
return [Math.min(minFromDomain, minFromTicks), Math.max(maxFromDomain, maxFromTicks)];
|
|
1463
|
+
}
|
|
1464
|
+
return domain;
|
|
1465
|
+
};
|
|
1466
|
+
|
|
1467
|
+
export const selectAxisDomainIncludingNiceTicks: (
|
|
1468
|
+
state: RechartsRootState,
|
|
1469
|
+
axisType: RenderableAxisType,
|
|
1470
|
+
axisId: AxisId,
|
|
1471
|
+
isPanorama: boolean,
|
|
1472
|
+
) => NumberDomain | CategoricalDomain | undefined = createSelector(
|
|
1473
|
+
[selectBaseAxis, selectAxisDomain, selectNiceTicks, pickAxisType],
|
|
1474
|
+
combineAxisDomainWithNiceTicks,
|
|
1475
|
+
);
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* Returns the smallest gap, between two numbers in the data, as a ratio of the whole range (max - min).
|
|
1479
|
+
* Ignores domain provided by user and only considers domain from data.
|
|
1480
|
+
*
|
|
1481
|
+
* The result is a number between 0 and 1.
|
|
1482
|
+
*/
|
|
1483
|
+
export const selectSmallestDistanceBetweenValues: (
|
|
1484
|
+
state: RechartsRootState,
|
|
1485
|
+
axisType: RenderableAxisType,
|
|
1486
|
+
axisId: AxisId,
|
|
1487
|
+
isPanorama: boolean,
|
|
1488
|
+
) => number | undefined = createSelector(
|
|
1489
|
+
selectAllAppliedValues,
|
|
1490
|
+
selectBaseAxis,
|
|
1491
|
+
(allDataSquished: AppliedChartData, axisSettings: BaseCartesianAxis): number | undefined => {
|
|
1492
|
+
if (!axisSettings || axisSettings.type !== 'number') {
|
|
1493
|
+
return undefined;
|
|
1494
|
+
}
|
|
1495
|
+
let smallestDistanceBetweenValues = Infinity;
|
|
1496
|
+
const sortedValues = Array.from(onlyAllowNumbers(allDataSquished.map((d) => d.value))).sort(
|
|
1497
|
+
(a, b) => a - b,
|
|
1498
|
+
);
|
|
1499
|
+
const first = sortedValues[0];
|
|
1500
|
+
const last = sortedValues[sortedValues.length - 1];
|
|
1501
|
+
if (first == null || last == null) {
|
|
1502
|
+
return Infinity;
|
|
1503
|
+
}
|
|
1504
|
+
const diff = last - first;
|
|
1505
|
+
if (diff === 0) {
|
|
1506
|
+
return Infinity;
|
|
1507
|
+
}
|
|
1508
|
+
// Only do n - 1 distance calculations because there's only n - 1 distances between n values.
|
|
1509
|
+
for (let i = 0; i < sortedValues.length - 1; i++) {
|
|
1510
|
+
const curr = sortedValues[i];
|
|
1511
|
+
const next = sortedValues[i + 1];
|
|
1512
|
+
if (curr == null || next == null) {
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
const distance = next - curr;
|
|
1516
|
+
smallestDistanceBetweenValues = Math.min(smallestDistanceBetweenValues, distance);
|
|
1517
|
+
}
|
|
1518
|
+
return smallestDistanceBetweenValues / diff;
|
|
1519
|
+
},
|
|
1520
|
+
);
|
|
1521
|
+
|
|
1522
|
+
const selectCalculatedPadding: (
|
|
1523
|
+
state: RechartsRootState,
|
|
1524
|
+
axisType: RenderableAxisType,
|
|
1525
|
+
axisId: AxisId,
|
|
1526
|
+
isPanorama: boolean,
|
|
1527
|
+
padding: string,
|
|
1528
|
+
) => number = createSelector(
|
|
1529
|
+
selectSmallestDistanceBetweenValues,
|
|
1530
|
+
selectChartLayout,
|
|
1531
|
+
selectBarCategoryGap,
|
|
1532
|
+
selectChartOffsetInternal,
|
|
1533
|
+
(_1: unknown, _2: unknown, _3: unknown, _4: unknown, padding: string): string => padding,
|
|
1534
|
+
(
|
|
1535
|
+
smallestDistanceInPercent: number | undefined,
|
|
1536
|
+
layout: LayoutType,
|
|
1537
|
+
barCategoryGap: number | string,
|
|
1538
|
+
offset: ChartOffsetInternal,
|
|
1539
|
+
padding: string,
|
|
1540
|
+
): number => {
|
|
1541
|
+
if (!isWellBehavedNumber(smallestDistanceInPercent)) {
|
|
1542
|
+
return 0;
|
|
1543
|
+
}
|
|
1544
|
+
const rangeWidth = layout === 'vertical' ? offset.height : offset.width;
|
|
1545
|
+
|
|
1546
|
+
if (padding === 'gap') {
|
|
1547
|
+
return (smallestDistanceInPercent * rangeWidth) / 2;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
if (padding === 'no-gap') {
|
|
1551
|
+
const gap = getPercentValue(barCategoryGap, smallestDistanceInPercent * rangeWidth);
|
|
1552
|
+
const halfBand = (smallestDistanceInPercent * rangeWidth) / 2;
|
|
1553
|
+
return halfBand - gap - ((halfBand - gap) / rangeWidth) * gap;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
return 0;
|
|
1557
|
+
},
|
|
1558
|
+
);
|
|
1559
|
+
|
|
1560
|
+
export const selectCalculatedXAxisPadding: (
|
|
1561
|
+
state: RechartsRootState,
|
|
1562
|
+
axisId: AxisId,
|
|
1563
|
+
isPanorama: boolean,
|
|
1564
|
+
) => number = (state, axisId, isPanorama) => {
|
|
1565
|
+
const xAxisSettings = selectXAxisSettings(state, axisId);
|
|
1566
|
+
if (xAxisSettings == null || typeof xAxisSettings.padding !== 'string') {
|
|
1567
|
+
return 0;
|
|
1568
|
+
}
|
|
1569
|
+
return selectCalculatedPadding(state, 'xAxis', axisId, isPanorama, xAxisSettings.padding);
|
|
1570
|
+
};
|
|
1571
|
+
|
|
1572
|
+
export const selectCalculatedYAxisPadding: (
|
|
1573
|
+
state: RechartsRootState,
|
|
1574
|
+
axisId: AxisId,
|
|
1575
|
+
isPanorama: boolean,
|
|
1576
|
+
) => number = (state, axisId, isPanorama) => {
|
|
1577
|
+
const yAxisSettings = selectYAxisSettings(state, axisId);
|
|
1578
|
+
if (yAxisSettings == null || typeof yAxisSettings.padding !== 'string') {
|
|
1579
|
+
return 0;
|
|
1580
|
+
}
|
|
1581
|
+
return selectCalculatedPadding(state, 'yAxis', axisId, isPanorama, yAxisSettings.padding);
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1584
|
+
const selectXAxisPadding: (
|
|
1585
|
+
state: RechartsRootState,
|
|
1586
|
+
axisId: AxisId,
|
|
1587
|
+
isPanorama: boolean,
|
|
1588
|
+
) => { left: number; right: number } = createSelector(
|
|
1589
|
+
selectXAxisSettings,
|
|
1590
|
+
selectCalculatedXAxisPadding,
|
|
1591
|
+
(xAxisSettings: XAxisSettings, calculated: number) => {
|
|
1592
|
+
if (xAxisSettings == null) {
|
|
1593
|
+
return { left: 0, right: 0 };
|
|
1594
|
+
}
|
|
1595
|
+
const { padding } = xAxisSettings;
|
|
1596
|
+
if (typeof padding === 'string') {
|
|
1597
|
+
return { left: calculated, right: calculated };
|
|
1598
|
+
}
|
|
1599
|
+
return {
|
|
1600
|
+
left: (padding.left ?? 0) + calculated,
|
|
1601
|
+
right: (padding.right ?? 0) + calculated,
|
|
1602
|
+
};
|
|
1603
|
+
},
|
|
1604
|
+
);
|
|
1605
|
+
|
|
1606
|
+
const selectYAxisPadding: (
|
|
1607
|
+
state: RechartsRootState,
|
|
1608
|
+
axisId: AxisId,
|
|
1609
|
+
isPanorama: boolean,
|
|
1610
|
+
) => { top: number; bottom: number } = createSelector(
|
|
1611
|
+
selectYAxisSettings,
|
|
1612
|
+
selectCalculatedYAxisPadding,
|
|
1613
|
+
(yAxisSettings: YAxisSettings, calculated: number) => {
|
|
1614
|
+
if (yAxisSettings == null) {
|
|
1615
|
+
return { top: 0, bottom: 0 };
|
|
1616
|
+
}
|
|
1617
|
+
const { padding } = yAxisSettings;
|
|
1618
|
+
if (typeof padding === 'string') {
|
|
1619
|
+
return { top: calculated, bottom: calculated };
|
|
1620
|
+
}
|
|
1621
|
+
return {
|
|
1622
|
+
top: (padding.top ?? 0) + calculated,
|
|
1623
|
+
bottom: (padding.bottom ?? 0) + calculated,
|
|
1624
|
+
};
|
|
1625
|
+
},
|
|
1626
|
+
);
|
|
1627
|
+
|
|
1628
|
+
export const selectXAxisRange: (
|
|
1629
|
+
state: RechartsRootState,
|
|
1630
|
+
axisId: AxisId,
|
|
1631
|
+
isPanorama: boolean,
|
|
1632
|
+
) => AxisRange | undefined = createSelector(
|
|
1633
|
+
[
|
|
1634
|
+
selectChartOffsetInternal,
|
|
1635
|
+
selectXAxisPadding,
|
|
1636
|
+
selectBrushDimensions,
|
|
1637
|
+
selectBrushSettings,
|
|
1638
|
+
(_state: RechartsRootState, _axisId: AxisId, isPanorama) => isPanorama,
|
|
1639
|
+
],
|
|
1640
|
+
(
|
|
1641
|
+
offset: ChartOffsetInternal,
|
|
1642
|
+
padding,
|
|
1643
|
+
brushDimensions: BrushDimensions,
|
|
1644
|
+
{ padding: brushPadding },
|
|
1645
|
+
isPanorama: boolean,
|
|
1646
|
+
): AxisRange | undefined => {
|
|
1647
|
+
if (isPanorama) {
|
|
1648
|
+
return [brushPadding.left, brushDimensions.width - brushPadding.right];
|
|
1649
|
+
}
|
|
1650
|
+
return [offset.left + padding.left, offset.left + offset.width - padding.right];
|
|
1651
|
+
},
|
|
1652
|
+
);
|
|
1653
|
+
|
|
1654
|
+
export type AxisRange = readonly [number, number];
|
|
1655
|
+
|
|
1656
|
+
export const selectYAxisRange: (
|
|
1657
|
+
state: RechartsRootState,
|
|
1658
|
+
axisId: AxisId,
|
|
1659
|
+
isPanorama: boolean,
|
|
1660
|
+
) => AxisRange | undefined = createSelector(
|
|
1661
|
+
[
|
|
1662
|
+
selectChartOffsetInternal,
|
|
1663
|
+
selectChartLayout,
|
|
1664
|
+
selectYAxisPadding,
|
|
1665
|
+
selectBrushDimensions,
|
|
1666
|
+
selectBrushSettings,
|
|
1667
|
+
(_state: RechartsRootState, _axisId: AxisId, isPanorama) => isPanorama,
|
|
1668
|
+
],
|
|
1669
|
+
(
|
|
1670
|
+
offset: ChartOffsetInternal,
|
|
1671
|
+
layout: LayoutType,
|
|
1672
|
+
padding: { top: number; bottom: number },
|
|
1673
|
+
brushDimensions: BrushDimensions,
|
|
1674
|
+
{ padding: brushPadding },
|
|
1675
|
+
isPanorama: boolean,
|
|
1676
|
+
): AxisRange | undefined => {
|
|
1677
|
+
if (isPanorama) {
|
|
1678
|
+
return [brushDimensions.height - brushPadding.bottom, brushPadding.top];
|
|
1679
|
+
}
|
|
1680
|
+
if (layout === 'horizontal') {
|
|
1681
|
+
return [offset.top + offset.height - padding.bottom, offset.top + padding.top];
|
|
1682
|
+
}
|
|
1683
|
+
return [offset.top + padding.top, offset.top + offset.height - padding.bottom];
|
|
1684
|
+
},
|
|
1685
|
+
);
|
|
1686
|
+
|
|
1687
|
+
export const selectAxisRange = (
|
|
1688
|
+
state: RechartsRootState,
|
|
1689
|
+
axisType: AllAxisTypes,
|
|
1690
|
+
axisId: AxisId,
|
|
1691
|
+
isPanorama: boolean,
|
|
1692
|
+
): AxisRange | undefined => {
|
|
1693
|
+
switch (axisType) {
|
|
1694
|
+
case 'xAxis':
|
|
1695
|
+
return selectXAxisRange(state, axisId, isPanorama);
|
|
1696
|
+
case 'yAxis':
|
|
1697
|
+
return selectYAxisRange(state, axisId, isPanorama);
|
|
1698
|
+
case 'zAxis':
|
|
1699
|
+
return selectZAxisSettings(state, axisId)?.range;
|
|
1700
|
+
case 'angleAxis':
|
|
1701
|
+
return selectAngleAxisRange(state);
|
|
1702
|
+
case 'radiusAxis':
|
|
1703
|
+
return selectRadiusAxisRange(state, axisId);
|
|
1704
|
+
default:
|
|
1705
|
+
return undefined;
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1708
|
+
|
|
1709
|
+
export const selectAxisRangeWithReverse: (
|
|
1710
|
+
state: RechartsRootState,
|
|
1711
|
+
axisType: AllAxisTypes,
|
|
1712
|
+
axisId: AxisId,
|
|
1713
|
+
isPanorama: boolean,
|
|
1714
|
+
) => AxisRange | undefined = createSelector(
|
|
1715
|
+
[selectBaseAxis, selectAxisRange],
|
|
1716
|
+
combineAxisRangeWithReverse,
|
|
1717
|
+
);
|
|
1718
|
+
|
|
1719
|
+
export const selectCheckedAxisDomain: (
|
|
1720
|
+
state: RechartsRootState,
|
|
1721
|
+
axisType: RenderableAxisType,
|
|
1722
|
+
axisId: AxisId,
|
|
1723
|
+
isPanorama: boolean,
|
|
1724
|
+
) => NumberDomain | CategoricalDomain | undefined = createSelector(
|
|
1725
|
+
[selectRealScaleType, selectAxisDomainIncludingNiceTicks],
|
|
1726
|
+
combineCheckedDomain,
|
|
1727
|
+
);
|
|
1728
|
+
|
|
1729
|
+
const selectConfiguredScale: (
|
|
1730
|
+
state: RechartsRootState,
|
|
1731
|
+
axisType: RenderableAxisType,
|
|
1732
|
+
axisId: AxisId,
|
|
1733
|
+
isPanorama: boolean,
|
|
1734
|
+
) => CustomScaleDefinition | undefined = createSelector(
|
|
1735
|
+
[selectBaseAxis, selectRealScaleType, selectCheckedAxisDomain, selectAxisRangeWithReverse],
|
|
1736
|
+
combineConfiguredScale,
|
|
1737
|
+
);
|
|
1738
|
+
|
|
1739
|
+
export const combineCategoricalDomain = (
|
|
1740
|
+
layout: LayoutType,
|
|
1741
|
+
appliedValues: AppliedChartData,
|
|
1742
|
+
axis: RenderableAxisSettings,
|
|
1743
|
+
axisType: RenderableAxisType,
|
|
1744
|
+
): ReadonlyArray<unknown> | undefined => {
|
|
1745
|
+
if (axis == null || axis.dataKey == null) {
|
|
1746
|
+
return undefined;
|
|
1747
|
+
}
|
|
1748
|
+
const { type, scale } = axis;
|
|
1749
|
+
const isCategorical = isCategoricalAxis(layout, axisType);
|
|
1750
|
+
if (isCategorical && (type === 'number' || scale !== 'auto')) {
|
|
1751
|
+
return appliedValues.map((d) => d.value);
|
|
1752
|
+
}
|
|
1753
|
+
return undefined;
|
|
1754
|
+
};
|
|
1755
|
+
|
|
1756
|
+
export const selectCategoricalDomain: (
|
|
1757
|
+
state: RechartsRootState,
|
|
1758
|
+
axisType: RenderableAxisType,
|
|
1759
|
+
axisId: AxisId,
|
|
1760
|
+
isPanorama: boolean,
|
|
1761
|
+
) => ReadonlyArray<unknown> | undefined = createSelector(
|
|
1762
|
+
[selectChartLayout, selectAllAppliedValues, selectRenderableAxisSettings, pickAxisType],
|
|
1763
|
+
combineCategoricalDomain,
|
|
1764
|
+
);
|
|
1765
|
+
|
|
1766
|
+
export const selectAxisScale: (
|
|
1767
|
+
state: RechartsRootState,
|
|
1768
|
+
axisType: RenderableAxisType,
|
|
1769
|
+
axisId: AxisId,
|
|
1770
|
+
isPanorama: boolean,
|
|
1771
|
+
) => RechartsScale | undefined = createSelector([selectConfiguredScale], rechartsScaleFactory);
|
|
1772
|
+
|
|
1773
|
+
export const selectAxisInverseScale: (
|
|
1774
|
+
state: RechartsRootState,
|
|
1775
|
+
axisType: RenderableAxisType,
|
|
1776
|
+
axisId: AxisId,
|
|
1777
|
+
isPanorama: boolean,
|
|
1778
|
+
) => InverseScaleFunction | undefined = createSelector(
|
|
1779
|
+
[selectConfiguredScale],
|
|
1780
|
+
combineInverseScaleFunction,
|
|
1781
|
+
);
|
|
1782
|
+
|
|
1783
|
+
export const selectAxisInverseDataSnapScale: (
|
|
1784
|
+
state: RechartsRootState,
|
|
1785
|
+
axisType: RenderableAxisType,
|
|
1786
|
+
axisId: AxisId,
|
|
1787
|
+
isPanorama: boolean,
|
|
1788
|
+
) => InverseScaleFunction | undefined = createSelector(
|
|
1789
|
+
[selectConfiguredScale, selectSortedDataPoints],
|
|
1790
|
+
createCategoricalInverse,
|
|
1791
|
+
);
|
|
1792
|
+
|
|
1793
|
+
export const selectErrorBarsSettings: (
|
|
1794
|
+
state: RechartsRootState,
|
|
1795
|
+
axisType: AllAxisTypes,
|
|
1796
|
+
axisId: AxisId,
|
|
1797
|
+
) => ReadonlyArray<ErrorBarsSettings> = createSelector(
|
|
1798
|
+
[selectCartesianItemsSettings, selectAllErrorBarSettings, pickAxisType],
|
|
1799
|
+
combineRelevantErrorBarSettings,
|
|
1800
|
+
);
|
|
1801
|
+
|
|
1802
|
+
function compareIds(a: CartesianAxisSettings, b: CartesianAxisSettings) {
|
|
1803
|
+
if (a.id < b.id) {
|
|
1804
|
+
return -1;
|
|
1805
|
+
}
|
|
1806
|
+
if (a.id > b.id) {
|
|
1807
|
+
return 1;
|
|
1808
|
+
}
|
|
1809
|
+
return 0;
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
const pickAxisOrientation = <T>(_state: RechartsRootState, orientation: T): T => orientation;
|
|
1813
|
+
|
|
1814
|
+
const pickMirror = (_state: RechartsRootState, _orientation: unknown, mirror: boolean): boolean =>
|
|
1815
|
+
mirror;
|
|
1816
|
+
|
|
1817
|
+
const selectAllXAxesWithOffsetType: (
|
|
1818
|
+
state: RechartsRootState,
|
|
1819
|
+
orientation: XAxisOrientation,
|
|
1820
|
+
mirror: boolean,
|
|
1821
|
+
) => ReadonlyArray<XAxisSettings> = createSelector(
|
|
1822
|
+
selectAllXAxes,
|
|
1823
|
+
pickAxisOrientation,
|
|
1824
|
+
pickMirror,
|
|
1825
|
+
(allAxes: ReadonlyArray<XAxisSettings>, orientation: XAxisOrientation, mirror: boolean) =>
|
|
1826
|
+
allAxes
|
|
1827
|
+
.filter((axis) => axis.orientation === orientation)
|
|
1828
|
+
.filter((axis) => axis.mirror === mirror)
|
|
1829
|
+
.sort(compareIds),
|
|
1830
|
+
);
|
|
1831
|
+
|
|
1832
|
+
const selectAllYAxesWithOffsetType: (
|
|
1833
|
+
state: RechartsRootState,
|
|
1834
|
+
orientation: YAxisOrientation,
|
|
1835
|
+
mirror: boolean,
|
|
1836
|
+
) => ReadonlyArray<YAxisSettings> = createSelector(
|
|
1837
|
+
selectAllYAxes,
|
|
1838
|
+
pickAxisOrientation,
|
|
1839
|
+
pickMirror,
|
|
1840
|
+
(allAxes: ReadonlyArray<YAxisSettings>, orientation: YAxisOrientation, mirror: boolean) =>
|
|
1841
|
+
allAxes
|
|
1842
|
+
.filter((axis) => axis.orientation === orientation)
|
|
1843
|
+
.filter((axis) => axis.mirror === mirror)
|
|
1844
|
+
.sort(compareIds),
|
|
1845
|
+
);
|
|
1846
|
+
|
|
1847
|
+
const getXAxisSize = (offset: ChartOffsetInternal, axisSettings: XAxisSettings): Size => {
|
|
1848
|
+
return {
|
|
1849
|
+
width: offset.width,
|
|
1850
|
+
height: axisSettings.height,
|
|
1851
|
+
};
|
|
1852
|
+
};
|
|
1853
|
+
|
|
1854
|
+
const getYAxisSize = (offset: ChartOffsetInternal, axisSettings: YAxisSettings): Size => {
|
|
1855
|
+
const width = typeof axisSettings.width === 'number' ? axisSettings.width : DEFAULT_Y_AXIS_WIDTH;
|
|
1856
|
+
return {
|
|
1857
|
+
width,
|
|
1858
|
+
height: offset.height,
|
|
1859
|
+
};
|
|
1860
|
+
};
|
|
1861
|
+
|
|
1862
|
+
export const selectXAxisSize: (state: RechartsRootState, xAxisId: AxisId) => Size = createSelector(
|
|
1863
|
+
selectChartOffsetInternal,
|
|
1864
|
+
selectXAxisSettings,
|
|
1865
|
+
getXAxisSize,
|
|
1866
|
+
);
|
|
1867
|
+
|
|
1868
|
+
type AxisOffsetSteps = Record<AxisId, number>;
|
|
1869
|
+
|
|
1870
|
+
const combineXAxisPositionStartingPoint = (
|
|
1871
|
+
offset: ChartOffsetInternal,
|
|
1872
|
+
orientation: XAxisOrientation,
|
|
1873
|
+
chartHeight: number,
|
|
1874
|
+
) => {
|
|
1875
|
+
switch (orientation) {
|
|
1876
|
+
case 'top':
|
|
1877
|
+
return offset.top;
|
|
1878
|
+
case 'bottom':
|
|
1879
|
+
return chartHeight - offset.bottom;
|
|
1880
|
+
default:
|
|
1881
|
+
return 0;
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1884
|
+
|
|
1885
|
+
const combineYAxisPositionStartingPoint = (
|
|
1886
|
+
offset: ChartOffsetInternal,
|
|
1887
|
+
orientation: YAxisOrientation,
|
|
1888
|
+
chartWidth: number,
|
|
1889
|
+
) => {
|
|
1890
|
+
switch (orientation) {
|
|
1891
|
+
case 'left':
|
|
1892
|
+
return offset.left;
|
|
1893
|
+
case 'right':
|
|
1894
|
+
return chartWidth - offset.right;
|
|
1895
|
+
default:
|
|
1896
|
+
return 0;
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
|
|
1900
|
+
export const selectAllXAxesOffsetSteps: (
|
|
1901
|
+
state: RechartsRootState,
|
|
1902
|
+
orientation: XAxisOrientation,
|
|
1903
|
+
mirror: boolean,
|
|
1904
|
+
) => AxisOffsetSteps = createSelector(
|
|
1905
|
+
selectChartHeight,
|
|
1906
|
+
selectChartOffsetInternal,
|
|
1907
|
+
selectAllXAxesWithOffsetType,
|
|
1908
|
+
pickAxisOrientation,
|
|
1909
|
+
pickMirror,
|
|
1910
|
+
(chartHeight, offset, allAxesWithSameOffsetType, orientation: XAxisOrientation, mirror) => {
|
|
1911
|
+
const steps: AxisOffsetSteps = {};
|
|
1912
|
+
let position: number;
|
|
1913
|
+
allAxesWithSameOffsetType.forEach((axis) => {
|
|
1914
|
+
const axisSize = getXAxisSize(offset, axis);
|
|
1915
|
+
if (position == null) {
|
|
1916
|
+
position = combineXAxisPositionStartingPoint(offset, orientation, chartHeight);
|
|
1917
|
+
}
|
|
1918
|
+
const needSpace = (orientation === 'top' && !mirror) || (orientation === 'bottom' && mirror);
|
|
1919
|
+
steps[axis.id] = position - Number(needSpace) * axisSize.height;
|
|
1920
|
+
position += (needSpace ? -1 : 1) * axisSize.height;
|
|
1921
|
+
});
|
|
1922
|
+
return steps;
|
|
1923
|
+
},
|
|
1924
|
+
);
|
|
1925
|
+
|
|
1926
|
+
export const selectAllYAxesOffsetSteps: (
|
|
1927
|
+
state: RechartsRootState,
|
|
1928
|
+
orientation: YAxisOrientation,
|
|
1929
|
+
mirror: boolean,
|
|
1930
|
+
) => AxisOffsetSteps = createSelector(
|
|
1931
|
+
selectChartWidth,
|
|
1932
|
+
selectChartOffsetInternal,
|
|
1933
|
+
selectAllYAxesWithOffsetType,
|
|
1934
|
+
pickAxisOrientation,
|
|
1935
|
+
pickMirror,
|
|
1936
|
+
(
|
|
1937
|
+
chartWidth,
|
|
1938
|
+
offset: ChartOffsetInternal,
|
|
1939
|
+
allAxesWithSameOffsetType,
|
|
1940
|
+
orientation: YAxisOrientation,
|
|
1941
|
+
mirror,
|
|
1942
|
+
) => {
|
|
1943
|
+
const steps: AxisOffsetSteps = {};
|
|
1944
|
+
let position: number;
|
|
1945
|
+
allAxesWithSameOffsetType.forEach((axis) => {
|
|
1946
|
+
const axisSize = getYAxisSize(offset, axis);
|
|
1947
|
+
if (position == null) {
|
|
1948
|
+
position = combineYAxisPositionStartingPoint(offset, orientation, chartWidth);
|
|
1949
|
+
}
|
|
1950
|
+
const needSpace = (orientation === 'left' && !mirror) || (orientation === 'right' && mirror);
|
|
1951
|
+
steps[axis.id] = position - Number(needSpace) * axisSize.width;
|
|
1952
|
+
position += (needSpace ? -1 : 1) * axisSize.width;
|
|
1953
|
+
});
|
|
1954
|
+
return steps;
|
|
1955
|
+
},
|
|
1956
|
+
);
|
|
1957
|
+
|
|
1958
|
+
const selectXAxisOffsetSteps = (state: RechartsRootState, axisId: AxisId) => {
|
|
1959
|
+
const axisSettings = selectXAxisSettings(state, axisId);
|
|
1960
|
+
if (axisSettings == null) {
|
|
1961
|
+
return undefined;
|
|
1962
|
+
}
|
|
1963
|
+
return selectAllXAxesOffsetSteps(state, axisSettings.orientation, axisSettings.mirror);
|
|
1964
|
+
};
|
|
1965
|
+
|
|
1966
|
+
export const selectXAxisPosition: (
|
|
1967
|
+
state: RechartsRootState,
|
|
1968
|
+
axisId: AxisId,
|
|
1969
|
+
) => Coordinate | undefined = createSelector(
|
|
1970
|
+
[
|
|
1971
|
+
selectChartOffsetInternal,
|
|
1972
|
+
selectXAxisSettings,
|
|
1973
|
+
selectXAxisOffsetSteps,
|
|
1974
|
+
(_: unknown, axisId: AxisId) => axisId,
|
|
1975
|
+
],
|
|
1976
|
+
(offset, axisSettings, allSteps, axisId) => {
|
|
1977
|
+
if (axisSettings == null) {
|
|
1978
|
+
return undefined;
|
|
1979
|
+
}
|
|
1980
|
+
const stepOfThisAxis = allSteps?.[axisId];
|
|
1981
|
+
if (stepOfThisAxis == null) {
|
|
1982
|
+
return { x: offset.left, y: 0 };
|
|
1983
|
+
}
|
|
1984
|
+
return { x: offset.left, y: stepOfThisAxis };
|
|
1985
|
+
},
|
|
1986
|
+
);
|
|
1987
|
+
|
|
1988
|
+
const selectYAxisOffsetSteps = (state: RechartsRootState, axisId: AxisId) => {
|
|
1989
|
+
const axisSettings = selectYAxisSettings(state, axisId);
|
|
1990
|
+
if (axisSettings == null) {
|
|
1991
|
+
return undefined;
|
|
1992
|
+
}
|
|
1993
|
+
return selectAllYAxesOffsetSteps(state, axisSettings.orientation, axisSettings.mirror);
|
|
1994
|
+
};
|
|
1995
|
+
export const selectYAxisPosition: (
|
|
1996
|
+
state: RechartsRootState,
|
|
1997
|
+
axisId: AxisId,
|
|
1998
|
+
) => Coordinate | undefined = createSelector(
|
|
1999
|
+
[
|
|
2000
|
+
selectChartOffsetInternal,
|
|
2001
|
+
selectYAxisSettings,
|
|
2002
|
+
selectYAxisOffsetSteps,
|
|
2003
|
+
(_: unknown, axisId: AxisId) => axisId,
|
|
2004
|
+
],
|
|
2005
|
+
(offset, axisSettings, allSteps, axisId) => {
|
|
2006
|
+
if (axisSettings == null) {
|
|
2007
|
+
return undefined;
|
|
2008
|
+
}
|
|
2009
|
+
const stepOfThisAxis = allSteps?.[axisId];
|
|
2010
|
+
if (stepOfThisAxis == null) {
|
|
2011
|
+
return { x: 0, y: offset.top };
|
|
2012
|
+
}
|
|
2013
|
+
return { x: stepOfThisAxis, y: offset.top };
|
|
2014
|
+
},
|
|
2015
|
+
);
|
|
2016
|
+
|
|
2017
|
+
export const selectYAxisSize: (state: RechartsRootState, yAxisId: AxisId) => Size = createSelector(
|
|
2018
|
+
selectChartOffsetInternal,
|
|
2019
|
+
selectYAxisSettings,
|
|
2020
|
+
(offset: ChartOffsetInternal, axisSettings: YAxisSettings): Size => {
|
|
2021
|
+
const width =
|
|
2022
|
+
typeof axisSettings.width === 'number' ? axisSettings.width : DEFAULT_Y_AXIS_WIDTH;
|
|
2023
|
+
|
|
2024
|
+
return {
|
|
2025
|
+
width,
|
|
2026
|
+
height: offset.height,
|
|
2027
|
+
};
|
|
2028
|
+
},
|
|
2029
|
+
);
|
|
2030
|
+
|
|
2031
|
+
export const selectCartesianAxisSize = (
|
|
2032
|
+
state: RechartsRootState,
|
|
2033
|
+
axisType: RenderableAxisType,
|
|
2034
|
+
axisId: AxisId,
|
|
2035
|
+
): number | undefined => {
|
|
2036
|
+
switch (axisType) {
|
|
2037
|
+
case 'xAxis': {
|
|
2038
|
+
return selectXAxisSize(state, axisId).width;
|
|
2039
|
+
}
|
|
2040
|
+
case 'yAxis': {
|
|
2041
|
+
return selectYAxisSize(state, axisId).height;
|
|
2042
|
+
}
|
|
2043
|
+
default: {
|
|
2044
|
+
return undefined;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
|
|
2049
|
+
export const combineDuplicateDomain = (
|
|
2050
|
+
chartLayout: LayoutType,
|
|
2051
|
+
appliedValues: AppliedChartData,
|
|
2052
|
+
axis: BaseCartesianAxis,
|
|
2053
|
+
axisType: AllAxisTypes,
|
|
2054
|
+
): ReadonlyArray<unknown> | undefined => {
|
|
2055
|
+
if (axis == null) {
|
|
2056
|
+
return undefined;
|
|
2057
|
+
}
|
|
2058
|
+
const { allowDuplicatedCategory, type, dataKey } = axis;
|
|
2059
|
+
const isCategorical = isCategoricalAxis(chartLayout, axisType);
|
|
2060
|
+
const allData = appliedValues.map((av) => av.value);
|
|
2061
|
+
const validData = allData.filter((v) => v != null);
|
|
2062
|
+
if (
|
|
2063
|
+
dataKey &&
|
|
2064
|
+
isCategorical &&
|
|
2065
|
+
type === 'category' &&
|
|
2066
|
+
allowDuplicatedCategory &&
|
|
2067
|
+
hasDuplicate(validData)
|
|
2068
|
+
) {
|
|
2069
|
+
return allData;
|
|
2070
|
+
}
|
|
2071
|
+
return undefined;
|
|
2072
|
+
};
|
|
2073
|
+
|
|
2074
|
+
export const selectDuplicateDomain: (
|
|
2075
|
+
state: RechartsRootState,
|
|
2076
|
+
axisType: AllAxisTypes,
|
|
2077
|
+
axisId: AxisId,
|
|
2078
|
+
isPanorama: boolean,
|
|
2079
|
+
) => ReadonlyArray<unknown> | undefined = createSelector(
|
|
2080
|
+
[selectChartLayout, selectAllAppliedValues, selectBaseAxis, pickAxisType],
|
|
2081
|
+
combineDuplicateDomain,
|
|
2082
|
+
);
|
|
2083
|
+
|
|
2084
|
+
export const selectAxisPropsNeededForCartesianGridTicksGenerator: (
|
|
2085
|
+
state: RechartsRootState,
|
|
2086
|
+
axisType: 'xAxis' | 'yAxis',
|
|
2087
|
+
axisId: AxisId,
|
|
2088
|
+
isPanorama: boolean,
|
|
2089
|
+
) => AxisPropsForCartesianGridTicksGeneration | undefined = createSelector(
|
|
2090
|
+
[
|
|
2091
|
+
selectChartLayout,
|
|
2092
|
+
selectCartesianAxisSettings,
|
|
2093
|
+
selectRealScaleType,
|
|
2094
|
+
selectAxisScale,
|
|
2095
|
+
selectDuplicateDomain,
|
|
2096
|
+
selectCategoricalDomain,
|
|
2097
|
+
selectAxisRange,
|
|
2098
|
+
selectNiceTicks,
|
|
2099
|
+
pickAxisType,
|
|
2100
|
+
],
|
|
2101
|
+
(
|
|
2102
|
+
layout: LayoutType,
|
|
2103
|
+
axis: XAxisSettings | YAxisSettings,
|
|
2104
|
+
realScaleType: string | undefined,
|
|
2105
|
+
scale: RechartsScale | undefined,
|
|
2106
|
+
duplicateDomain,
|
|
2107
|
+
categoricalDomain,
|
|
2108
|
+
axisRange,
|
|
2109
|
+
niceTicks,
|
|
2110
|
+
axisType: AxisType,
|
|
2111
|
+
): AxisPropsForCartesianGridTicksGeneration | undefined => {
|
|
2112
|
+
if (axis == null) {
|
|
2113
|
+
return undefined;
|
|
2114
|
+
}
|
|
2115
|
+
const isCategorical = isCategoricalAxis(layout, axisType);
|
|
2116
|
+
return {
|
|
2117
|
+
angle: axis.angle,
|
|
2118
|
+
interval: axis.interval,
|
|
2119
|
+
minTickGap: axis.minTickGap,
|
|
2120
|
+
orientation: axis.orientation,
|
|
2121
|
+
tick: axis.tick,
|
|
2122
|
+
tickCount: axis.tickCount,
|
|
2123
|
+
tickFormatter: axis.tickFormatter,
|
|
2124
|
+
ticks: axis.ticks,
|
|
2125
|
+
type: axis.type,
|
|
2126
|
+
unit: axis.unit,
|
|
2127
|
+
axisType,
|
|
2128
|
+
categoricalDomain,
|
|
2129
|
+
duplicateDomain,
|
|
2130
|
+
isCategorical,
|
|
2131
|
+
niceTicks,
|
|
2132
|
+
range: axisRange,
|
|
2133
|
+
realScaleType,
|
|
2134
|
+
scale,
|
|
2135
|
+
};
|
|
2136
|
+
},
|
|
2137
|
+
);
|
|
2138
|
+
|
|
2139
|
+
/**
|
|
2140
|
+
* Of on four almost identical implementations of tick generation.
|
|
2141
|
+
* The four horsemen of tick generation are:
|
|
2142
|
+
* - {@link selectTooltipAxisTicks}
|
|
2143
|
+
* - {@link combineAxisTicks}
|
|
2144
|
+
* - {@link getTicksOfAxis}.
|
|
2145
|
+
* - {@link combineGraphicalItemTicks}
|
|
2146
|
+
*/
|
|
2147
|
+
export const combineAxisTicks = (
|
|
2148
|
+
layout: LayoutType,
|
|
2149
|
+
axis: RenderableAxisSettings,
|
|
2150
|
+
realScaleType: RechartsScaleType | undefined,
|
|
2151
|
+
scale: RechartsScale | undefined,
|
|
2152
|
+
niceTicks: ReadonlyArray<number> | undefined,
|
|
2153
|
+
axisRange: AxisRange | undefined,
|
|
2154
|
+
duplicateDomain: ReadonlyArray<unknown> | undefined,
|
|
2155
|
+
categoricalDomain: ReadonlyArray<unknown> | undefined,
|
|
2156
|
+
axisType: RenderableAxisType,
|
|
2157
|
+
): ReadonlyArray<TickItem> | undefined => {
|
|
2158
|
+
if (axis == null || scale == null) {
|
|
2159
|
+
return undefined;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
const isCategorical = isCategoricalAxis(layout, axisType);
|
|
2163
|
+
|
|
2164
|
+
const { type, ticks, tickCount } = axis;
|
|
2165
|
+
|
|
2166
|
+
const offsetForBand =
|
|
2167
|
+
// @ts-expect-error This is testing for `scaleBand` but for band axis the type is reported as `band` so this looks like a dead code with a workaround elsewhere?
|
|
2168
|
+
realScaleType === 'scaleBand' && typeof scale.bandwidth === 'function'
|
|
2169
|
+
? scale.bandwidth() / 2
|
|
2170
|
+
: 2;
|
|
2171
|
+
|
|
2172
|
+
let offset = type === 'category' && scale.bandwidth ? scale.bandwidth() / offsetForBand : 0;
|
|
2173
|
+
|
|
2174
|
+
offset =
|
|
2175
|
+
axisType === 'angleAxis' && axisRange != null && axisRange.length >= 2
|
|
2176
|
+
? mathSign(axisRange[0] - axisRange[1]) * 2 * offset
|
|
2177
|
+
: offset;
|
|
2178
|
+
|
|
2179
|
+
// The ticks set by user should only affect the ticks adjacent to axis line
|
|
2180
|
+
const ticksOrNiceTicks = ticks || niceTicks;
|
|
2181
|
+
if (ticksOrNiceTicks) {
|
|
2182
|
+
return ticksOrNiceTicks
|
|
2183
|
+
.map((entry: AxisTick, index: number): TickItem | null => {
|
|
2184
|
+
const scaleContent = duplicateDomain ? duplicateDomain.indexOf(entry) : entry;
|
|
2185
|
+
|
|
2186
|
+
const scaled = scale.map(scaleContent);
|
|
2187
|
+
if (!isWellBehavedNumber(scaled)) {
|
|
2188
|
+
return null;
|
|
2189
|
+
}
|
|
2190
|
+
return {
|
|
2191
|
+
index,
|
|
2192
|
+
coordinate: scaled + offset,
|
|
2193
|
+
value: entry,
|
|
2194
|
+
offset,
|
|
2195
|
+
};
|
|
2196
|
+
})
|
|
2197
|
+
.filter(isNotNil);
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
// When axis is a categorical axis, but the type of axis is not number and scale is not "auto"
|
|
2201
|
+
// For type='number' with linear scale (where niceTicks are available), skip this branch
|
|
2202
|
+
// so ticks are evenly spaced instead of placed at data positions (GitHub issue #4271)
|
|
2203
|
+
if (isCategorical && categoricalDomain) {
|
|
2204
|
+
return categoricalDomain
|
|
2205
|
+
.map((entry: unknown, index: number): TickItem | null => {
|
|
2206
|
+
const scaled = scale.map(entry);
|
|
2207
|
+
if (!isWellBehavedNumber(scaled)) {
|
|
2208
|
+
return null;
|
|
2209
|
+
}
|
|
2210
|
+
return {
|
|
2211
|
+
coordinate: scaled + offset,
|
|
2212
|
+
value: entry,
|
|
2213
|
+
index,
|
|
2214
|
+
offset,
|
|
2215
|
+
};
|
|
2216
|
+
})
|
|
2217
|
+
.filter(isNotNil);
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
if (scale.ticks) {
|
|
2221
|
+
return scale
|
|
2222
|
+
.ticks(tickCount)
|
|
2223
|
+
.map((entry: number, index: number): TickItem | null => {
|
|
2224
|
+
const scaled = scale.map(entry);
|
|
2225
|
+
if (!isWellBehavedNumber(scaled)) {
|
|
2226
|
+
return null;
|
|
2227
|
+
}
|
|
2228
|
+
return { coordinate: scaled + offset, value: entry, index, offset };
|
|
2229
|
+
})
|
|
2230
|
+
.filter(isNotNil);
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
// When axis has duplicated text, serial numbers are used to generate scale
|
|
2234
|
+
return scale
|
|
2235
|
+
.domain()
|
|
2236
|
+
.map((entry: CategoricalDomainItem, index: number): TickItem | null => {
|
|
2237
|
+
const scaled = scale.map(entry);
|
|
2238
|
+
if (!isWellBehavedNumber(scaled)) {
|
|
2239
|
+
return null;
|
|
2240
|
+
}
|
|
2241
|
+
return {
|
|
2242
|
+
coordinate: scaled + offset,
|
|
2243
|
+
// @ts-expect-error can't use Date as index
|
|
2244
|
+
value: duplicateDomain ? duplicateDomain[entry] : entry,
|
|
2245
|
+
index,
|
|
2246
|
+
offset,
|
|
2247
|
+
};
|
|
2248
|
+
})
|
|
2249
|
+
.filter(isNotNil);
|
|
2250
|
+
};
|
|
2251
|
+
export const selectTicksOfAxis: (
|
|
2252
|
+
state: RechartsRootState,
|
|
2253
|
+
axisType: RenderableAxisType,
|
|
2254
|
+
axisId: AxisId,
|
|
2255
|
+
isPanorama: boolean,
|
|
2256
|
+
) => ReadonlyArray<CartesianTickItem> | undefined = createSelector(
|
|
2257
|
+
[
|
|
2258
|
+
selectChartLayout,
|
|
2259
|
+
selectRenderableAxisSettings,
|
|
2260
|
+
selectRealScaleType,
|
|
2261
|
+
selectAxisScale,
|
|
2262
|
+
selectNiceTicks,
|
|
2263
|
+
selectAxisRange,
|
|
2264
|
+
selectDuplicateDomain,
|
|
2265
|
+
selectCategoricalDomain,
|
|
2266
|
+
pickAxisType,
|
|
2267
|
+
],
|
|
2268
|
+
combineAxisTicks,
|
|
2269
|
+
);
|
|
2270
|
+
|
|
2271
|
+
/**
|
|
2272
|
+
* Of on four almost identical implementations of tick generation.
|
|
2273
|
+
* The four horsemen of tick generation are:
|
|
2274
|
+
* - {@link selectTooltipAxisTicks}
|
|
2275
|
+
* - {@link combineAxisTicks}
|
|
2276
|
+
* - {@link getTicksOfAxis}.
|
|
2277
|
+
* - {@link combineGraphicalItemTicks}
|
|
2278
|
+
*/
|
|
2279
|
+
export const combineGraphicalItemTicks = (
|
|
2280
|
+
layout: LayoutType,
|
|
2281
|
+
axis: RenderableAxisSettings,
|
|
2282
|
+
scale: RechartsScale | undefined,
|
|
2283
|
+
axisRange: AxisRange | undefined,
|
|
2284
|
+
duplicateDomain: ReadonlyArray<unknown> | undefined,
|
|
2285
|
+
categoricalDomain: ReadonlyArray<unknown> | undefined,
|
|
2286
|
+
axisType: RenderableAxisType,
|
|
2287
|
+
): TickItem[] | undefined => {
|
|
2288
|
+
if (axis == null || scale == null || axisRange == null || axisRange[0] === axisRange[1]) {
|
|
2289
|
+
return undefined;
|
|
2290
|
+
}
|
|
2291
|
+
const isCategorical = isCategoricalAxis(layout, axisType);
|
|
2292
|
+
|
|
2293
|
+
const { tickCount } = axis;
|
|
2294
|
+
|
|
2295
|
+
let offset = 0;
|
|
2296
|
+
|
|
2297
|
+
offset =
|
|
2298
|
+
axisType === 'angleAxis' && axisRange?.length >= 2
|
|
2299
|
+
? mathSign(axisRange[0] - axisRange[1]) * 2 * offset
|
|
2300
|
+
: offset;
|
|
2301
|
+
|
|
2302
|
+
// When axis is a categorical axis, but the type of axis is number or the scale of axis is not "auto"
|
|
2303
|
+
if (isCategorical && categoricalDomain) {
|
|
2304
|
+
return categoricalDomain
|
|
2305
|
+
.map((entry: unknown, index: number): TickItem | null => {
|
|
2306
|
+
const scaled = scale.map(entry);
|
|
2307
|
+
if (!isWellBehavedNumber(scaled)) {
|
|
2308
|
+
return null;
|
|
2309
|
+
}
|
|
2310
|
+
return {
|
|
2311
|
+
coordinate: scaled + offset,
|
|
2312
|
+
value: entry,
|
|
2313
|
+
index,
|
|
2314
|
+
offset,
|
|
2315
|
+
};
|
|
2316
|
+
})
|
|
2317
|
+
.filter(isNotNil);
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
if (scale.ticks) {
|
|
2321
|
+
return scale
|
|
2322
|
+
.ticks(tickCount)
|
|
2323
|
+
.map((entry: number, index: number): TickItem | null => {
|
|
2324
|
+
const scaled = scale.map(entry);
|
|
2325
|
+
if (!isWellBehavedNumber(scaled)) {
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
return { coordinate: scaled + offset, value: entry, index, offset };
|
|
2329
|
+
})
|
|
2330
|
+
.filter(isNotNil);
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
// When axis has duplicated text, serial numbers are used to generate scale
|
|
2334
|
+
return scale
|
|
2335
|
+
.domain()
|
|
2336
|
+
.map((entry: CategoricalDomainItem, index: number): TickItem | null => {
|
|
2337
|
+
const scaled = scale.map(entry);
|
|
2338
|
+
if (!isWellBehavedNumber(scaled)) {
|
|
2339
|
+
return null;
|
|
2340
|
+
}
|
|
2341
|
+
return {
|
|
2342
|
+
coordinate: scaled + offset,
|
|
2343
|
+
// @ts-expect-error can't use unknown as index
|
|
2344
|
+
value: duplicateDomain ? duplicateDomain[entry] : entry,
|
|
2345
|
+
index,
|
|
2346
|
+
offset,
|
|
2347
|
+
};
|
|
2348
|
+
})
|
|
2349
|
+
.filter(isNotNil);
|
|
2350
|
+
};
|
|
2351
|
+
|
|
2352
|
+
export const selectTicksOfGraphicalItem: (
|
|
2353
|
+
state: RechartsRootState,
|
|
2354
|
+
axisType: RenderableAxisType,
|
|
2355
|
+
axisId: AxisId,
|
|
2356
|
+
isPanorama: boolean,
|
|
2357
|
+
) => TickItem[] | undefined = createSelector(
|
|
2358
|
+
[
|
|
2359
|
+
selectChartLayout,
|
|
2360
|
+
selectRenderableAxisSettings,
|
|
2361
|
+
selectAxisScale,
|
|
2362
|
+
selectAxisRange,
|
|
2363
|
+
selectDuplicateDomain,
|
|
2364
|
+
selectCategoricalDomain,
|
|
2365
|
+
pickAxisType,
|
|
2366
|
+
],
|
|
2367
|
+
combineGraphicalItemTicks,
|
|
2368
|
+
);
|
|
2369
|
+
|
|
2370
|
+
/**
|
|
2371
|
+
* This is the internal representation of an axis along with its scale function.
|
|
2372
|
+
* Here we have already computed the scale function for the axis,
|
|
2373
|
+
* and replaced the union type of scale (string | function) with just the function type.
|
|
2374
|
+
*/
|
|
2375
|
+
export type BaseAxisWithScale = Omit<BaseCartesianAxis, 'scale'> & { scale: RechartsScale };
|
|
2376
|
+
|
|
2377
|
+
export const selectAxisWithScale: (
|
|
2378
|
+
state: RechartsRootState,
|
|
2379
|
+
axisType: RenderableAxisType,
|
|
2380
|
+
axisId: AxisId,
|
|
2381
|
+
isPanorama: boolean,
|
|
2382
|
+
) => BaseAxisWithScale | undefined = createSelector(
|
|
2383
|
+
selectBaseAxis,
|
|
2384
|
+
selectAxisScale,
|
|
2385
|
+
(axis, scale): BaseAxisWithScale | undefined => {
|
|
2386
|
+
if (axis == null || scale == null) {
|
|
2387
|
+
return undefined;
|
|
2388
|
+
}
|
|
2389
|
+
return {
|
|
2390
|
+
...axis,
|
|
2391
|
+
scale,
|
|
2392
|
+
};
|
|
2393
|
+
},
|
|
2394
|
+
);
|
|
2395
|
+
|
|
2396
|
+
const selectZAxisConfiguredScale: (
|
|
2397
|
+
state: RechartsRootState,
|
|
2398
|
+
axisType: 'zAxis',
|
|
2399
|
+
axisId: AxisId,
|
|
2400
|
+
isPanorama: false,
|
|
2401
|
+
) => CustomScaleDefinition | undefined = createSelector(
|
|
2402
|
+
[selectBaseAxis, selectRealScaleType, selectAxisDomain, selectAxisRangeWithReverse],
|
|
2403
|
+
combineConfiguredScale,
|
|
2404
|
+
);
|
|
2405
|
+
|
|
2406
|
+
const selectZAxisScale: (
|
|
2407
|
+
state: RechartsRootState,
|
|
2408
|
+
axisType: 'zAxis',
|
|
2409
|
+
axisId: AxisId,
|
|
2410
|
+
isPanorama: false,
|
|
2411
|
+
) => RechartsScale | undefined = createSelector([selectZAxisConfiguredScale], rechartsScaleFactory);
|
|
2412
|
+
|
|
2413
|
+
export type ZAxisWithScale = Omit<ZAxisSettings, 'scale'> & { scale: RechartsScale };
|
|
2414
|
+
|
|
2415
|
+
export const selectZAxisWithScale: (
|
|
2416
|
+
state: RechartsRootState,
|
|
2417
|
+
_axisType: 'zAxis',
|
|
2418
|
+
axisId: AxisId,
|
|
2419
|
+
isPanorama: false,
|
|
2420
|
+
) => ZAxisWithScale | undefined = createSelector(
|
|
2421
|
+
(state: RechartsRootState, _axisType: 'zAxis', axisId: AxisId) =>
|
|
2422
|
+
selectZAxisSettings(state, axisId),
|
|
2423
|
+
selectZAxisScale,
|
|
2424
|
+
(axis: ZAxisSettings, scale: RechartsScale | undefined): ZAxisWithScale | undefined => {
|
|
2425
|
+
if (axis == null || scale == null) {
|
|
2426
|
+
return undefined;
|
|
2427
|
+
}
|
|
2428
|
+
return {
|
|
2429
|
+
...axis,
|
|
2430
|
+
scale,
|
|
2431
|
+
};
|
|
2432
|
+
},
|
|
2433
|
+
);
|
|
2434
|
+
|
|
2435
|
+
/**
|
|
2436
|
+
* We are also going to need to implement polar chart directions if we want to support keyboard controls for those.
|
|
2437
|
+
*/
|
|
2438
|
+
export type AxisDirection = 'left-to-right' | 'right-to-left' | 'top-to-bottom' | 'bottom-to-top';
|
|
2439
|
+
|
|
2440
|
+
export const selectChartDirection: (state: RechartsRootState) => AxisDirection | undefined =
|
|
2441
|
+
createSelector(
|
|
2442
|
+
[selectChartLayout, selectAllXAxes, selectAllYAxes],
|
|
2443
|
+
(
|
|
2444
|
+
layout: LayoutType,
|
|
2445
|
+
allXAxes: ReadonlyArray<XAxisSettings>,
|
|
2446
|
+
allYAxes: ReadonlyArray<YAxisSettings>,
|
|
2447
|
+
): AxisDirection | undefined => {
|
|
2448
|
+
switch (layout) {
|
|
2449
|
+
case 'horizontal': {
|
|
2450
|
+
return allXAxes.some((axis) => axis.reversed) ? 'right-to-left' : 'left-to-right';
|
|
2451
|
+
}
|
|
2452
|
+
case 'vertical': {
|
|
2453
|
+
return allYAxes.some((axis) => axis.reversed) ? 'bottom-to-top' : 'top-to-bottom';
|
|
2454
|
+
}
|
|
2455
|
+
// TODO: make this better. For now, right arrow triggers "forward", left arrow "back"
|
|
2456
|
+
// however, the tooltip moves an unintuitive direction because of how the indices are rendered
|
|
2457
|
+
case 'centric':
|
|
2458
|
+
case 'radial': {
|
|
2459
|
+
return 'left-to-right';
|
|
2460
|
+
}
|
|
2461
|
+
default: {
|
|
2462
|
+
return undefined;
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
},
|
|
2466
|
+
);
|
|
2467
|
+
|
|
2468
|
+
export const selectRenderedTicksOfAxis: (
|
|
2469
|
+
state: RechartsRootState,
|
|
2470
|
+
axisType: 'xAxis' | 'yAxis',
|
|
2471
|
+
axisId: AxisId,
|
|
2472
|
+
) => ReadonlyArray<TickItem> | undefined = (state, axisType, axisId) => {
|
|
2473
|
+
return state.renderedTicks[axisType]?.[axisId];
|
|
2474
|
+
};
|
|
2475
|
+
|
|
2476
|
+
export const selectAxisInverseTickSnapScale: (
|
|
2477
|
+
state: RechartsRootState,
|
|
2478
|
+
axisType: 'xAxis' | 'yAxis',
|
|
2479
|
+
axisId: AxisId,
|
|
2480
|
+
) => InverseScaleFunction | undefined = createSelector(
|
|
2481
|
+
[selectRenderedTicksOfAxis],
|
|
2482
|
+
(ticks: ReadonlyArray<TickItem> | undefined) => {
|
|
2483
|
+
if (!ticks || ticks.length === 0) {
|
|
2484
|
+
return undefined;
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
return (pixelValue: number) => {
|
|
2488
|
+
// Find the tick with the closest coordinate to pixelValue
|
|
2489
|
+
let minDistance = Infinity;
|
|
2490
|
+
let closestTick = ticks[0];
|
|
2491
|
+
|
|
2492
|
+
for (const tick of ticks) {
|
|
2493
|
+
const distance = Math.abs(tick.coordinate - pixelValue);
|
|
2494
|
+
if (distance < minDistance) {
|
|
2495
|
+
minDistance = distance;
|
|
2496
|
+
closestTick = tick;
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
return closestTick?.value;
|
|
2500
|
+
};
|
|
2501
|
+
},
|
|
2502
|
+
);
|