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