@octanejs/recharts 0.1.41 → 0.1.43

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