solid-recharts 0.2.0 → 0.2.1

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 (82) hide show
  1. package/package.json +5 -3
  2. package/src/cartesian/Area.tsx +40 -55
  3. package/src/cartesian/Bar.tsx +66 -58
  4. package/src/cartesian/BarStack.tsx +5 -2
  5. package/src/cartesian/Brush.tsx +1 -1
  6. package/src/cartesian/Funnel.tsx +15 -19
  7. package/src/cartesian/Line.tsx +34 -51
  8. package/src/cartesian/LineDrawShape.tsx +8 -6
  9. package/src/cartesian/ReferenceArea.tsx +25 -35
  10. package/src/cartesian/ReferenceDot.tsx +24 -34
  11. package/src/cartesian/ReferenceLine.tsx +25 -34
  12. package/src/cartesian/Scatter.tsx +25 -37
  13. package/src/cartesian/XAxis.tsx +54 -67
  14. package/src/cartesian/YAxis.tsx +53 -66
  15. package/src/cartesian/ZAxis.tsx +27 -40
  16. package/src/chart/SunburstChart.tsx +1 -1
  17. package/src/component/ActivePoints.tsx +0 -9
  18. package/src/component/Dots.tsx +7 -3
  19. package/src/context/ErrorBarContext.tsx +15 -24
  20. package/src/polar/Pie.tsx +36 -48
  21. package/src/polar/PolarAngleAxis.tsx +41 -50
  22. package/src/polar/PolarRadiusAxis.tsx +40 -48
  23. package/src/polar/Radar.tsx +22 -34
  24. package/src/polar/RadialBar.tsx +21 -39
  25. package/src/shape/Curve.tsx +1 -1
  26. package/src/shape/Symbols.tsx +1 -1
  27. package/src/state/SetLegendPayload.ts +19 -27
  28. package/src/state/SetTooltipEntrySettings.tsx +9 -26
  29. package/src/state/cartesianAxisSlice.ts +40 -60
  30. package/src/state/createCollectionActions.ts +51 -0
  31. package/src/state/createSelector.ts +97 -0
  32. package/src/state/externalEventsMiddleware.ts +2 -11
  33. package/src/state/graphicalItemsSlice.ts +27 -40
  34. package/src/state/isEventThrottled.ts +12 -0
  35. package/src/state/keyboardEventsMiddleware.ts +2 -8
  36. package/src/state/layoutSlice.ts +8 -4
  37. package/src/state/legendSlice.ts +24 -26
  38. package/src/state/mouseEventsMiddleware.ts +2 -13
  39. package/src/state/patched.ts +19 -0
  40. package/src/state/referenceElementsSlice.ts +40 -31
  41. package/src/state/registerInStore.ts +68 -0
  42. package/src/state/selectors/areaSelectors.ts +13 -36
  43. package/src/state/selectors/arrayEqualityCheck.ts +32 -7
  44. package/src/state/selectors/axisSelectors.ts +215 -169
  45. package/src/state/selectors/barSelectors.ts +8 -36
  46. package/src/state/selectors/combiners/combineConfiguredScale.ts +1 -1
  47. package/src/state/selectors/combiners/combineRealScaleType.ts +1 -1
  48. package/src/state/selectors/dataSelectors.ts +35 -16
  49. package/src/state/selectors/numberDomainEqualityCheck.ts +16 -8
  50. package/src/state/selectors/selectAxisForGraphicalItem.ts +52 -0
  51. package/src/state/tooltipSlice.ts +93 -64
  52. package/src/util/ActiveShapeUtils.tsx +0 -7
  53. package/src/util/BarUtils.tsx +27 -22
  54. package/src/util/ChartUtils.ts +69 -25
  55. package/src/util/propsAreEqual.ts +3 -33
  56. package/src/util/stacks/stackTypes.ts +1 -1
  57. package/src/util/types.ts +1 -1
  58. package/test/cartesian/CartesianAxis.spec.tsx +1 -1
  59. package/test/cartesian/GraphicalItemClipPath.scale-behavior.spec.tsx +1 -1
  60. package/test/cartesian/ReferenceLine/getEndPoints.spec.ts +1 -1
  61. package/test/cartesian/XAxis/XAxis.timescale.spec.tsx +1 -1
  62. package/test/helper/mockAxes.ts +1 -1
  63. package/test/shape/Curve.spec.tsx +1 -1
  64. package/test/state/createCollectionActions.spec.ts +87 -0
  65. package/test/state/createSelector.spec.ts +158 -0
  66. package/test/state/registerInStore.spec.tsx +101 -0
  67. package/test/state/selectors/combiners/combineConfiguredScale.spec.ts +1 -1
  68. package/test/state/selectors/combiners/combineRealScaleType.spec.ts +1 -1
  69. package/test/state/selectors/selectorMemoization.spec.tsx +136 -0
  70. package/test/util/CartesianUtils/CartesianUtils.spec.ts +1 -1
  71. package/test/util/ChartUtils/ChartUtils.spec.ts +1 -1
  72. package/test/util/ChartUtils/getCateCoordinateOfLine.spec.ts +14 -0
  73. package/test/util/ChartUtils/getStackedData.spec.ts +1 -1
  74. package/test/util/ChartUtils/truncateByDomain.spec.ts +1 -1
  75. package/test/util/scale/createCategoricalInverse.spec.ts +1 -1
  76. package/test-results/.last-run.json +4 -0
  77. package/src/state/reduxDevtoolsJsonStringifyReplacer.ts +0 -15
  78. package/src/util/axisPropsAreEqual.ts +0 -42
  79. package/src/util/resolveDefaultProps.tsx +0 -8
  80. package/src/util/typedDataKey.ts +0 -8
  81. package/test/state/reduxDevtoolsJsonStringifyReplacer.spec.ts +0 -25
  82. package/test/util/axisPropsAreEqual.spec.ts +0 -93
@@ -88,7 +88,21 @@ import {
88
88
  import { selectChartHeight, selectChartWidth } from "./containerSelectors";
89
89
  import { selectAllXAxes, selectAllYAxes } from "./selectAllAxes";
90
90
  import { pickAxisType } from "./pickAxisType";
91
+ import { pickAxisId } from "./pickAxisId";
92
+ import { createSelector } from "../createSelector";
93
+ import { arrayContentsAreEqualCheck, emptyArraysAreEqualCheck } from "./arrayEqualityCheck";
94
+ import { numberDomainEqualityCheck } from "./numberDomainEqualityCheck";
91
95
  import { isWellBehavedNumber } from "../../util/isWellBehavedNumber";
96
+
97
+ // `pickAxisType` is generic (`<T>(state, axisType: T) => T`), so as a bare input
98
+ // selector its return infers to `unknown`. These typed picks pin it to the axis
99
+ // arg shape each combiner expects — used wherever it feeds a wrapped selector.
100
+ const pickAxisTypeArg = (_state: RechartsRootState, axisType: AllAxisTypes): AllAxisTypes =>
101
+ axisType;
102
+ const pickRenderableAxisType = (
103
+ _state: RechartsRootState,
104
+ axisType: RenderableAxisType,
105
+ ): RenderableAxisType => axisType;
92
106
  import { getNiceTickValues, getTickValuesFixedDomain } from "../../util/scale";
93
107
  import { selectChartOffsetInternal } from "./selectChartOffsetInternal";
94
108
  import { selectBrushDimensions } from "./brushSelectors";
@@ -387,11 +401,17 @@ export function itemAxisPredicate(axisType: AllAxisTypes, axisId: AxisId) {
387
401
  };
388
402
  }
389
403
 
390
- const selectAxisPredicate = (
391
- _state: RechartsRootState,
404
+ // Wrapped (ADR-0002) so the predicate reference is stable per (axisType, axisId)
405
+ // — an unwrapped fresh closure per call would defeat downstream item-settings
406
+ // caching. Mirrors upstream `createSelector([pickAxisType, pickAxisId], itemAxisPredicate)`.
407
+ const selectAxisPredicate: (
408
+ state: RechartsRootState,
392
409
  axisType: AllAxisTypes,
393
410
  axisId: AxisId,
394
- ): ((item: CartesianGraphicalItemSettings) => boolean) => itemAxisPredicate(axisType, axisId);
411
+ ) => (item: CartesianGraphicalItemSettings) => boolean = createSelector(
412
+ [pickAxisTypeArg, pickAxisId],
413
+ itemAxisPredicate,
414
+ );
395
415
 
396
416
  export const combineGraphicalItemsSettings = <T extends GraphicalItemSettings>(
397
417
  graphicalItems: ReadonlyArray<T>,
@@ -405,28 +425,29 @@ export const combineGraphicalItemsSettings = <T extends GraphicalItemSettings>(
405
425
  return !item.hide;
406
426
  });
407
427
 
408
- export const selectCartesianItemsSettings = (
428
+ export const selectCartesianItemsSettings: (
409
429
  state: RechartsRootState,
410
430
  axisType: AllAxisTypes,
411
431
  axisId: AxisId,
412
- ): ReadonlyArray<CartesianGraphicalItemSettings> =>
413
- combineGraphicalItemsSettings(
414
- selectUnfilteredCartesianItems(state),
415
- selectBaseAxis(state, axisType, axisId),
416
- selectAxisPredicate(state, axisType, axisId),
417
- );
432
+ ) => ReadonlyArray<CartesianGraphicalItemSettings> = createSelector(
433
+ [selectUnfilteredCartesianItems, selectBaseAxis, selectAxisPredicate],
434
+ combineGraphicalItemsSettings,
435
+ { resultEqualityCheck: emptyArraysAreEqualCheck },
436
+ );
418
437
 
419
438
  export const filterGraphicalNotStackedItems = (
420
439
  cartesianItems: ReadonlyArray<GraphicalItemSettings>,
421
440
  ): ReadonlyArray<GraphicalItemSettings> =>
422
441
  cartesianItems.filter((item) => !("stackId" in item) || item.stackId === undefined);
423
442
 
424
- const selectCartesianItemsSettingsExceptStacked = (
443
+ const selectCartesianItemsSettingsExceptStacked: (
425
444
  state: RechartsRootState,
426
445
  axisType: AllAxisTypes,
427
446
  axisId: AxisId,
428
- ): ReadonlyArray<GraphicalItemSettings> =>
429
- filterGraphicalNotStackedItems(selectCartesianItemsSettings(state, axisType, axisId));
447
+ ) => ReadonlyArray<GraphicalItemSettings> = createSelector(
448
+ [selectCartesianItemsSettings],
449
+ filterGraphicalNotStackedItems,
450
+ );
430
451
 
431
452
  export const combineGraphicalItemsData = (
432
453
  cartesianItems: ReadonlyArray<GraphicalItemSettings>,
@@ -440,20 +461,24 @@ export const combineGraphicalItemsData = (
440
461
  * True if at least one graphical item relies on the chart-level data (no own
441
462
  * `data` prop).
442
463
  */
443
- export const selectAnyCartesianItemsUsesChartData = (
464
+ export const selectAnyCartesianItemsUsesChartData: (
444
465
  state: RechartsRootState,
445
466
  axisType: AllAxisTypes,
446
467
  axisId: AxisId,
447
- ): boolean => selectCartesianItemsSettings(state, axisType, axisId).some((item) => !item.data);
468
+ ) => boolean = createSelector([selectCartesianItemsSettings], (items) =>
469
+ items.some((item) => !item.data),
470
+ );
448
471
 
449
472
  /**
450
473
  * Data defined on the chart graphical items (Line/Scatter/etc.), filtered by axis.
451
474
  */
452
- export const selectCartesianGraphicalItemsData = (
475
+ export const selectCartesianGraphicalItemsData: (
453
476
  state: RechartsRootState,
454
477
  axisType: AllAxisTypes,
455
478
  axisId: AxisId,
456
- ): ChartData => combineGraphicalItemsData(selectCartesianItemsSettings(state, axisType, axisId));
479
+ ) => ChartData = createSelector([selectCartesianItemsSettings], combineGraphicalItemsData, {
480
+ resultEqualityCheck: emptyArraysAreEqualCheck,
481
+ });
457
482
 
458
483
  export const combineDisplayedData = (
459
484
  graphicalItemsData: ChartData,
@@ -469,16 +494,15 @@ export const combineDisplayedData = (
469
494
  * All data in the chart (graphical items + chart root), useful for figuring out
470
495
  * an axis domain. Discards original indexes, so not useful for ordering.
471
496
  */
472
- export const selectDisplayedData = (
497
+ export const selectDisplayedData: (
473
498
  state: RechartsRootState,
474
499
  axisType: AllAxisTypes,
475
500
  axisId: AxisId,
476
501
  isPanorama: boolean,
477
- ): ChartData =>
478
- combineDisplayedData(
479
- selectCartesianGraphicalItemsData(state, axisType, axisId),
480
- selectChartDataWithIndexesIfNotInPanoramaPosition4(state, axisType, axisId, isPanorama),
481
- );
502
+ ) => ChartData = createSelector(
503
+ [selectCartesianGraphicalItemsData, selectChartDataWithIndexesIfNotInPanoramaPosition4],
504
+ combineDisplayedData,
505
+ );
482
506
 
483
507
  export const combineAppliedValues = (
484
508
  data: ChartData,
@@ -518,20 +542,22 @@ export const combineAllAppliedValues = (
518
542
  /**
519
543
  * All values with the appropriate dataKey applied. Expensive — iterates all data.
520
544
  */
521
- export const selectAllAppliedValues = (
545
+ export const selectAllAppliedValues: (
522
546
  state: RechartsRootState,
523
547
  axisType: AllAxisTypes,
524
548
  axisId: AxisId,
525
549
  isPanorama: boolean,
526
- ): AppliedChartData =>
527
- combineAllAppliedValues(
528
- selectDisplayedData(state, axisType, axisId, isPanorama),
529
- selectBaseAxis(state, axisType, axisId),
530
- selectCartesianItemsSettings(state, axisType, axisId),
531
- selectChartDataWithIndexesIfNotInPanoramaPosition4(state, axisType, axisId, isPanorama),
532
- selectAnyCartesianItemsUsesChartData(state, axisType, axisId),
533
- selectCartesianGraphicalItemsData(state, axisType, axisId),
534
- );
550
+ ) => AppliedChartData = createSelector(
551
+ [
552
+ selectDisplayedData,
553
+ selectBaseAxis,
554
+ selectCartesianItemsSettings,
555
+ selectChartDataWithIndexesIfNotInPanoramaPosition4,
556
+ selectAnyCartesianItemsUsesChartData,
557
+ selectCartesianGraphicalItemsData,
558
+ ],
559
+ combineAllAppliedValues,
560
+ );
535
561
 
536
562
  function makeNumber(val: unknown): number | undefined {
537
563
  if (isNumOrStr(val) || val instanceof Date) {
@@ -713,6 +739,14 @@ export const combineDomainOfAllAppliedNumericalValuesIncludingErrorValues = (
713
739
  return undefined;
714
740
  };
715
741
 
742
+ // NOT wrapped (ADR-0002 leaf-input constraint): `selectAllErrorBarSettings`
743
+ // returns the `errorBars` slice root and the combiner iterates its nested,
744
+ // dynamically-keyed entries. Under a wrapped selector the combiner runs
745
+ // untracked and the input reads only the root, so an ErrorBar registering after
746
+ // first compute is never tracked → the domain stays stale (missed the error-bar
747
+ // extension). Kept as a plain function so its combiner runs tracked inside its
748
+ // wrapped consumer `selectNumericalDomain`, whose numberDomainEqualityCheck
749
+ // still stabilizes the domain reference downstream.
716
750
  export const selectDomainOfAllAppliedNumericalValuesIncludingErrorValues = (
717
751
  state: RechartsRootState,
718
752
  axisType: AllAxisTypes,
@@ -945,29 +979,32 @@ export const selectDomainFromUserPreference = (
945
979
  // (reselect createSelectors → plain functions; memoization from useAppSelector).
946
980
 
947
981
  /** Cartesian items (Bar/Area) that are actually stacked (have stackId + dataKey). */
948
- export const selectStackedCartesianItemsSettings = (
982
+ export const selectStackedCartesianItemsSettings: (
949
983
  state: RechartsRootState,
950
984
  axisType: AllAxisTypes,
951
985
  axisId: AxisId,
952
- ): ReadonlyArray<DefinitelyStackedGraphicalItem> =>
953
- selectCartesianItemsSettings(state, axisType, axisId)
954
- .filter((item) => item.type === "area" || item.type === "bar")
955
- .filter(isStacked);
986
+ ) => ReadonlyArray<DefinitelyStackedGraphicalItem> = createSelector(
987
+ [selectCartesianItemsSettings],
988
+ (cartesianItems): ReadonlyArray<DefinitelyStackedGraphicalItem> =>
989
+ cartesianItems.filter((item) => item.type === "area" || item.type === "bar").filter(isStacked),
990
+ );
956
991
 
957
992
  const selectTooltipAxisForStacks = (state: RechartsRootState): BaseCartesianAxis =>
958
993
  selectRenderableAxisSettings(state, selectTooltipAxisType(state), selectTooltipAxisId(state));
959
994
 
960
- export const selectDisplayedStackedData = (
995
+ export const selectDisplayedStackedData: (
961
996
  state: RechartsRootState,
962
997
  axisType: AllAxisTypes,
963
998
  axisId: AxisId,
964
999
  isPanorama: boolean,
965
- ): DisplayedStackedData =>
966
- combineDisplayedStackedData(
967
- selectStackedCartesianItemsSettings(state, axisType, axisId),
968
- selectChartDataWithIndexesIfNotInPanoramaPosition4(state, axisType, axisId, isPanorama),
969
- selectTooltipAxisForStacks(state),
970
- );
1000
+ ) => DisplayedStackedData = createSelector(
1001
+ [
1002
+ selectStackedCartesianItemsSettings,
1003
+ selectChartDataWithIndexesIfNotInPanoramaPosition4,
1004
+ selectTooltipAxisForStacks,
1005
+ ],
1006
+ combineDisplayedStackedData,
1007
+ );
971
1008
 
972
1009
  export const combineStackGroups = (
973
1010
  displayedData: DisplayedStackedData,
@@ -1006,18 +1043,20 @@ export const combineStackGroups = (
1006
1043
  * Stack groups are groups of graphical items that stack on each other, keyed by
1007
1044
  * stack ID. Items without a stack ID do not appear.
1008
1045
  */
1009
- export const selectStackGroups = (
1046
+ export const selectStackGroups: (
1010
1047
  state: RechartsRootState,
1011
1048
  axisType: AllAxisTypes,
1012
1049
  axisId: AxisId,
1013
1050
  isPanorama: boolean,
1014
- ): AllStackGroups =>
1015
- combineStackGroups(
1016
- selectDisplayedStackedData(state, axisType, axisId, isPanorama),
1017
- selectStackedCartesianItemsSettings(state, axisType, axisId),
1018
- selectStackOffsetType(state),
1019
- selectReverseStackOrder(state),
1020
- );
1051
+ ) => AllStackGroups = createSelector(
1052
+ [
1053
+ selectDisplayedStackedData,
1054
+ selectStackedCartesianItemsSettings,
1055
+ selectStackOffsetType,
1056
+ selectReverseStackOrder,
1057
+ ],
1058
+ combineStackGroups,
1059
+ );
1021
1060
 
1022
1061
  export const combineDomainOfStackGroups = (
1023
1062
  stackGroups: AllStackGroups | undefined,
@@ -1034,18 +1073,16 @@ export const combineDomainOfStackGroups = (
1034
1073
  return getDomainOfStackGroups(stackGroups, dataStartIndex, dataEndIndex);
1035
1074
  };
1036
1075
 
1037
- export const selectDomainOfStackGroups = (
1076
+ export const selectDomainOfStackGroups: (
1038
1077
  state: RechartsRootState,
1039
1078
  axisType: AllAxisTypes,
1040
1079
  axisId: AxisId,
1041
1080
  isPanorama: boolean,
1042
- ): NumberDomain | undefined =>
1043
- combineDomainOfStackGroups(
1044
- selectStackGroups(state, axisType, axisId, isPanorama),
1045
- selectChartDataWithIndexes(state),
1046
- axisType,
1047
- selectDomainFromUserPreference(state, axisType, axisId),
1048
- );
1081
+ ) => NumberDomain | undefined = createSelector(
1082
+ [selectStackGroups, selectChartDataWithIndexes, pickAxisTypeArg, selectDomainFromUserPreference],
1083
+ combineDomainOfStackGroups,
1084
+ { resultEqualityCheck: numberDomainEqualityCheck },
1085
+ );
1049
1086
 
1050
1087
  export const combineNumericalDomain = (
1051
1088
  axisSettings: BaseCartesianAxis,
@@ -1127,34 +1164,34 @@ export const combineNumericTicksDomain = (
1127
1164
  return [Math.min(...numericTicks), Math.max(...numericTicks)];
1128
1165
  };
1129
1166
 
1130
- export const selectNumericTicksDomain = (
1167
+ export const selectNumericTicksDomain: (
1131
1168
  state: RechartsRootState,
1132
1169
  axisType: AllAxisTypes,
1133
1170
  axisId: AxisId,
1134
- ): NumberDomain | undefined => combineNumericTicksDomain(selectBaseAxis(state, axisType, axisId));
1171
+ ) => NumberDomain | undefined = createSelector([selectBaseAxis], combineNumericTicksDomain, {
1172
+ resultEqualityCheck: numberDomainEqualityCheck,
1173
+ });
1135
1174
 
1136
- export const selectNumericalDomain = (
1175
+ export const selectNumericalDomain: (
1137
1176
  state: RechartsRootState,
1138
1177
  axisType: AllAxisTypes,
1139
1178
  axisId: AxisId,
1140
1179
  isPanorama: boolean,
1141
- ): NumberDomain | undefined =>
1142
- combineNumericalDomain(
1143
- selectBaseAxis(state, axisType, axisId),
1144
- selectDomainDefinition(state, axisType, axisId),
1145
- selectDomainFromUserPreference(state, axisType, axisId),
1146
- selectDomainOfStackGroups(state, axisType, axisId, isPanorama),
1147
- selectDomainOfAllAppliedNumericalValuesIncludingErrorValues(
1148
- state,
1149
- axisType,
1150
- axisId,
1151
- isPanorama,
1152
- ),
1153
- selectReferenceElementsDomain(state, axisType, axisId),
1154
- selectChartLayout(state),
1155
- axisType,
1156
- selectNumericTicksDomain(state, axisType, axisId),
1157
- );
1180
+ ) => NumberDomain | undefined = createSelector(
1181
+ [
1182
+ selectBaseAxis,
1183
+ selectDomainDefinition,
1184
+ selectDomainFromUserPreference,
1185
+ selectDomainOfStackGroups,
1186
+ selectDomainOfAllAppliedNumericalValuesIncludingErrorValues,
1187
+ selectReferenceElementsDomain,
1188
+ selectChartLayout,
1189
+ pickAxisTypeArg,
1190
+ selectNumericTicksDomain,
1191
+ ],
1192
+ combineNumericalDomain,
1193
+ { resultEqualityCheck: numberDomainEqualityCheck },
1194
+ );
1158
1195
 
1159
1196
  /**
1160
1197
  * Expand maps everything between 0 and 1; nothing to compute.
@@ -1194,32 +1231,32 @@ export const combineAxisDomain = (
1194
1231
  return numericalDomain;
1195
1232
  };
1196
1233
 
1197
- export const selectAxisDomain = (
1234
+ export const selectAxisDomain: (
1198
1235
  state: RechartsRootState,
1199
1236
  axisType: AllAxisTypes,
1200
1237
  axisId: AxisId,
1201
1238
  isPanorama: boolean,
1202
- ): NumberDomain | CategoricalDomain | undefined =>
1203
- combineAxisDomain(
1204
- selectBaseAxis(state, axisType, axisId),
1205
- selectChartLayout(state),
1206
- selectDisplayedData(state, axisType, axisId, isPanorama),
1207
- selectAllAppliedValues(state, axisType, axisId, isPanorama),
1208
- selectStackOffsetType(state),
1209
- axisType,
1210
- selectNumericalDomain(state, axisType, axisId, isPanorama),
1211
- );
1212
-
1213
- export const selectRealScaleType = (
1239
+ ) => NumberDomain | CategoricalDomain | undefined = createSelector(
1240
+ [
1241
+ selectBaseAxis,
1242
+ selectChartLayout,
1243
+ selectDisplayedData,
1244
+ selectAllAppliedValues,
1245
+ selectStackOffsetType,
1246
+ pickAxisTypeArg,
1247
+ selectNumericalDomain,
1248
+ ],
1249
+ combineAxisDomain,
1250
+ );
1251
+
1252
+ export const selectRealScaleType: (
1214
1253
  state: RechartsRootState,
1215
1254
  axisType: AllAxisTypes,
1216
1255
  axisId: AxisId,
1217
- ): RechartsScaleType | undefined =>
1218
- combineRealScaleType(
1219
- selectBaseAxis(state, axisType, axisId),
1220
- selectHasBar(state),
1221
- selectChartName(state),
1222
- );
1256
+ ) => RechartsScaleType | undefined = createSelector(
1257
+ [selectBaseAxis, selectHasBar, selectChartName],
1258
+ combineRealScaleType,
1259
+ );
1223
1260
 
1224
1261
  export const combineNiceTicks = (
1225
1262
  axisDomain: NumberDomain | CategoricalDomain | undefined,
@@ -1289,17 +1326,15 @@ export const combineNiceTicks = (
1289
1326
  return undefined;
1290
1327
  };
1291
1328
 
1292
- export const selectNiceTicks = (
1329
+ export const selectNiceTicks: (
1293
1330
  state: RechartsRootState,
1294
1331
  axisType: RenderableAxisType,
1295
1332
  axisId: AxisId,
1296
1333
  isPanorama: boolean,
1297
- ): ReadonlyArray<number> | undefined =>
1298
- combineNiceTicks(
1299
- selectAxisDomain(state, axisType, axisId, isPanorama),
1300
- selectRenderableAxisSettings(state, axisType, axisId),
1301
- selectRealScaleType(state, axisType, axisId),
1302
- );
1334
+ ) => ReadonlyArray<number> | undefined = createSelector(
1335
+ [selectAxisDomain, selectRenderableAxisSettings, selectRealScaleType],
1336
+ combineNiceTicks,
1337
+ );
1303
1338
 
1304
1339
  export const combineAxisDomainWithNiceTicks = (
1305
1340
  axisSettings: BaseCartesianAxis,
@@ -1325,18 +1360,15 @@ export const combineAxisDomainWithNiceTicks = (
1325
1360
  return domain;
1326
1361
  };
1327
1362
 
1328
- export const selectAxisDomainIncludingNiceTicks = (
1363
+ export const selectAxisDomainIncludingNiceTicks: (
1329
1364
  state: RechartsRootState,
1330
1365
  axisType: RenderableAxisType,
1331
1366
  axisId: AxisId,
1332
1367
  isPanorama: boolean,
1333
- ): NumberDomain | CategoricalDomain | undefined =>
1334
- combineAxisDomainWithNiceTicks(
1335
- selectBaseAxis(state, axisType, axisId),
1336
- selectAxisDomain(state, axisType, axisId, isPanorama),
1337
- selectNiceTicks(state, axisType, axisId, isPanorama),
1338
- axisType,
1339
- );
1368
+ ) => NumberDomain | CategoricalDomain | undefined = createSelector(
1369
+ [selectBaseAxis, selectAxisDomain, selectNiceTicks, pickRenderableAxisType],
1370
+ combineAxisDomainWithNiceTicks,
1371
+ );
1340
1372
 
1341
1373
  /**
1342
1374
  * Smallest gap between two data numbers as a ratio of the whole range (0..1).
@@ -1515,7 +1547,7 @@ export const selectYAxisRange = (
1515
1547
  return [offset.top + padding.top, offset.top + offset.height - padding.bottom];
1516
1548
  };
1517
1549
 
1518
- export const selectAxisRange = (
1550
+ const computeAxisRange = (
1519
1551
  state: RechartsRootState,
1520
1552
  axisType: AllAxisTypes,
1521
1553
  axisId: AxisId,
@@ -1537,40 +1569,57 @@ export const selectAxisRange = (
1537
1569
  }
1538
1570
  };
1539
1571
 
1540
- export const selectAxisRangeWithReverse = (
1572
+ // The range tuple is rebuilt fresh from offset/padding on every call; this
1573
+ // identity wrapper (ADR-0002) applies content-equality so callers get a stable
1574
+ // reference when the plot range is unchanged — otherwise the tick/scale chain
1575
+ // above rebuilds per consumer per mousemove frame.
1576
+ export const selectAxisRange: (
1541
1577
  state: RechartsRootState,
1542
1578
  axisType: AllAxisTypes,
1543
1579
  axisId: AxisId,
1544
1580
  isPanorama: boolean,
1545
- ): AxisRange | undefined =>
1546
- combineAxisRangeWithReverse(
1547
- selectBaseAxis(state, axisType, axisId),
1548
- selectAxisRange(state, axisType, axisId, isPanorama),
1549
- );
1581
+ ) => AxisRange | undefined = createSelector([computeAxisRange], (range) => range, {
1582
+ resultEqualityCheck: (a, b) =>
1583
+ a == null || b == null ? a === b : arrayContentsAreEqualCheck(a, b),
1584
+ });
1550
1585
 
1551
- export const selectCheckedAxisDomain = (
1586
+ export const selectAxisRangeWithReverse: (
1587
+ state: RechartsRootState,
1588
+ axisType: AllAxisTypes,
1589
+ axisId: AxisId,
1590
+ isPanorama: boolean,
1591
+ ) => AxisRange | undefined = createSelector(
1592
+ [selectBaseAxis, selectAxisRange],
1593
+ combineAxisRangeWithReverse,
1594
+ // The range tuple is recomputed fresh each call (from offset/padding) but only
1595
+ // its VALUES matter; content-equality returns the prior reference when the plot
1596
+ // range is unchanged (e.g. across a mousemove), so the scale/ticks chain above
1597
+ // stays cache-stable instead of rebuilding a d3 scale per consumer per frame.
1598
+ {
1599
+ resultEqualityCheck: (a, b) =>
1600
+ a == null || b == null ? a === b : arrayContentsAreEqualCheck(a, b),
1601
+ },
1602
+ );
1603
+
1604
+ export const selectCheckedAxisDomain: (
1552
1605
  state: RechartsRootState,
1553
1606
  axisType: RenderableAxisType,
1554
1607
  axisId: AxisId,
1555
1608
  isPanorama: boolean,
1556
- ): NumberDomain | CategoricalDomain | undefined =>
1557
- combineCheckedDomain(
1558
- selectRealScaleType(state, axisType, axisId),
1559
- selectAxisDomainIncludingNiceTicks(state, axisType, axisId, isPanorama),
1560
- );
1609
+ ) => NumberDomain | CategoricalDomain | undefined = createSelector(
1610
+ [selectRealScaleType, selectAxisDomainIncludingNiceTicks],
1611
+ combineCheckedDomain,
1612
+ );
1561
1613
 
1562
- const selectConfiguredScale = (
1614
+ const selectConfiguredScale: (
1563
1615
  state: RechartsRootState,
1564
1616
  axisType: RenderableAxisType,
1565
1617
  axisId: AxisId,
1566
1618
  isPanorama: boolean,
1567
- ): CustomScaleDefinition | undefined =>
1568
- combineConfiguredScale(
1569
- selectBaseAxis(state, axisType, axisId),
1570
- selectRealScaleType(state, axisType, axisId),
1571
- selectCheckedAxisDomain(state, axisType, axisId, isPanorama),
1572
- selectAxisRangeWithReverse(state, axisType, axisId, isPanorama),
1573
- );
1619
+ ) => CustomScaleDefinition | undefined = createSelector(
1620
+ [selectBaseAxis, selectRealScaleType, selectCheckedAxisDomain, selectAxisRangeWithReverse],
1621
+ combineConfiguredScale,
1622
+ );
1574
1623
 
1575
1624
  export const combineCategoricalDomain = (
1576
1625
  layout: LayoutType,
@@ -1589,26 +1638,22 @@ export const combineCategoricalDomain = (
1589
1638
  return undefined;
1590
1639
  };
1591
1640
 
1592
- export const selectCategoricalDomain = (
1641
+ export const selectCategoricalDomain: (
1593
1642
  state: RechartsRootState,
1594
1643
  axisType: RenderableAxisType,
1595
1644
  axisId: AxisId,
1596
1645
  isPanorama: boolean,
1597
- ): ReadonlyArray<unknown> | undefined =>
1598
- combineCategoricalDomain(
1599
- selectChartLayout(state),
1600
- selectAllAppliedValues(state, axisType, axisId, isPanorama),
1601
- selectRenderableAxisSettings(state, axisType, axisId),
1602
- axisType,
1603
- );
1646
+ ) => ReadonlyArray<unknown> | undefined = createSelector(
1647
+ [selectChartLayout, selectAllAppliedValues, selectRenderableAxisSettings, pickRenderableAxisType],
1648
+ combineCategoricalDomain,
1649
+ );
1604
1650
 
1605
- export const selectAxisScale = (
1651
+ export const selectAxisScale: (
1606
1652
  state: RechartsRootState,
1607
1653
  axisType: RenderableAxisType,
1608
1654
  axisId: AxisId,
1609
1655
  isPanorama: boolean,
1610
- ): RechartsScale | undefined =>
1611
- rechartsScaleFactory(selectConfiguredScale(state, axisType, axisId, isPanorama));
1656
+ ) => RechartsScale | undefined = createSelector([selectConfiguredScale], rechartsScaleFactory);
1612
1657
 
1613
1658
  /** The ticks the axis component actually rendered (from the renderedTicks slice). */
1614
1659
  export const selectRenderedTicksOfAxis = (
@@ -1621,13 +1666,15 @@ export const selectRenderedTicksOfAxis = (
1621
1666
  * Maps a pixel coordinate back to a data value (the inverse of the axis scale).
1622
1667
  * Continuous scales invert; categorical scales snap to the nearest category.
1623
1668
  */
1624
- export const selectAxisInverseScale = (
1669
+ export const selectAxisInverseScale: (
1625
1670
  state: RechartsRootState,
1626
1671
  axisType: RenderableAxisType,
1627
1672
  axisId: AxisId,
1628
1673
  isPanorama: boolean,
1629
- ): InverseScaleFunction | undefined =>
1630
- combineInverseScaleFunction(selectConfiguredScale(state, axisType, axisId, isPanorama));
1674
+ ) => InverseScaleFunction | undefined = createSelector(
1675
+ [selectConfiguredScale],
1676
+ combineInverseScaleFunction,
1677
+ );
1631
1678
 
1632
1679
  /** Inverse scale that snaps a pixel coordinate to the nearest rendered tick value. */
1633
1680
  export const selectAxisInverseTickSnapScale = (
@@ -1678,18 +1725,15 @@ export const combineDuplicateDomain = (
1678
1725
  return undefined;
1679
1726
  };
1680
1727
 
1681
- export const selectDuplicateDomain = (
1728
+ export const selectDuplicateDomain: (
1682
1729
  state: RechartsRootState,
1683
1730
  axisType: AllAxisTypes,
1684
1731
  axisId: AxisId,
1685
1732
  isPanorama: boolean,
1686
- ): ReadonlyArray<unknown> | undefined =>
1687
- combineDuplicateDomain(
1688
- selectChartLayout(state),
1689
- selectAllAppliedValues(state, axisType, axisId, isPanorama),
1690
- selectBaseAxis(state, axisType, axisId),
1691
- axisType,
1692
- );
1733
+ ) => ReadonlyArray<unknown> | undefined = createSelector(
1734
+ [selectChartLayout, selectAllAppliedValues, selectBaseAxis, pickAxisTypeArg],
1735
+ combineDuplicateDomain,
1736
+ );
1693
1737
 
1694
1738
  /**
1695
1739
  * One of four near-identical tick generators. This one feeds graphical items
@@ -1767,21 +1811,23 @@ export const combineGraphicalItemTicks = (
1767
1811
  .filter(isNotNil);
1768
1812
  };
1769
1813
 
1770
- export const selectTicksOfGraphicalItem = (
1814
+ export const selectTicksOfGraphicalItem: (
1771
1815
  state: RechartsRootState,
1772
1816
  axisType: RenderableAxisType,
1773
1817
  axisId: AxisId,
1774
1818
  isPanorama: boolean,
1775
- ): TickItem[] | undefined =>
1776
- combineGraphicalItemTicks(
1777
- selectChartLayout(state),
1778
- selectRenderableAxisSettings(state, axisType, axisId),
1779
- selectAxisScale(state, axisType, axisId, isPanorama),
1780
- selectAxisRange(state, axisType, axisId, isPanorama),
1781
- selectDuplicateDomain(state, axisType, axisId, isPanorama),
1782
- selectCategoricalDomain(state, axisType, axisId, isPanorama),
1783
- axisType,
1784
- );
1819
+ ) => TickItem[] | undefined = createSelector(
1820
+ [
1821
+ selectChartLayout,
1822
+ selectRenderableAxisSettings,
1823
+ selectAxisScale,
1824
+ selectAxisRange,
1825
+ selectDuplicateDomain,
1826
+ selectCategoricalDomain,
1827
+ pickRenderableAxisType,
1828
+ ],
1829
+ combineGraphicalItemTicks,
1830
+ );
1785
1831
 
1786
1832
  export const selectAxisWithScale = (
1787
1833
  state: RechartsRootState,