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.
- package/package.json +5 -3
- package/src/cartesian/Area.tsx +40 -55
- package/src/cartesian/Bar.tsx +66 -58
- package/src/cartesian/BarStack.tsx +5 -2
- package/src/cartesian/Brush.tsx +1 -1
- package/src/cartesian/Funnel.tsx +15 -19
- package/src/cartesian/Line.tsx +34 -51
- package/src/cartesian/LineDrawShape.tsx +8 -6
- package/src/cartesian/ReferenceArea.tsx +25 -35
- package/src/cartesian/ReferenceDot.tsx +24 -34
- package/src/cartesian/ReferenceLine.tsx +25 -34
- package/src/cartesian/Scatter.tsx +25 -37
- package/src/cartesian/XAxis.tsx +54 -67
- package/src/cartesian/YAxis.tsx +53 -66
- package/src/cartesian/ZAxis.tsx +27 -40
- package/src/chart/SunburstChart.tsx +1 -1
- package/src/component/ActivePoints.tsx +0 -9
- package/src/component/Dots.tsx +7 -3
- package/src/context/ErrorBarContext.tsx +15 -24
- package/src/polar/Pie.tsx +36 -48
- package/src/polar/PolarAngleAxis.tsx +41 -50
- package/src/polar/PolarRadiusAxis.tsx +40 -48
- package/src/polar/Radar.tsx +22 -34
- package/src/polar/RadialBar.tsx +21 -39
- package/src/shape/Curve.tsx +1 -1
- package/src/shape/Symbols.tsx +1 -1
- package/src/state/SetLegendPayload.ts +19 -27
- package/src/state/SetTooltipEntrySettings.tsx +9 -26
- package/src/state/cartesianAxisSlice.ts +40 -60
- package/src/state/createCollectionActions.ts +51 -0
- package/src/state/createSelector.ts +97 -0
- package/src/state/externalEventsMiddleware.ts +2 -11
- package/src/state/graphicalItemsSlice.ts +27 -40
- package/src/state/isEventThrottled.ts +12 -0
- package/src/state/keyboardEventsMiddleware.ts +2 -8
- package/src/state/layoutSlice.ts +8 -4
- package/src/state/legendSlice.ts +24 -26
- package/src/state/mouseEventsMiddleware.ts +2 -13
- package/src/state/patched.ts +19 -0
- package/src/state/referenceElementsSlice.ts +40 -31
- package/src/state/registerInStore.ts +68 -0
- package/src/state/selectors/areaSelectors.ts +13 -36
- package/src/state/selectors/arrayEqualityCheck.ts +32 -7
- package/src/state/selectors/axisSelectors.ts +215 -169
- package/src/state/selectors/barSelectors.ts +8 -36
- package/src/state/selectors/combiners/combineConfiguredScale.ts +1 -1
- package/src/state/selectors/combiners/combineRealScaleType.ts +1 -1
- package/src/state/selectors/dataSelectors.ts +35 -16
- package/src/state/selectors/numberDomainEqualityCheck.ts +16 -8
- package/src/state/selectors/selectAxisForGraphicalItem.ts +52 -0
- package/src/state/tooltipSlice.ts +93 -64
- package/src/util/ActiveShapeUtils.tsx +0 -7
- package/src/util/BarUtils.tsx +27 -22
- package/src/util/ChartUtils.ts +69 -25
- package/src/util/propsAreEqual.ts +3 -33
- package/src/util/stacks/stackTypes.ts +1 -1
- package/src/util/types.ts +1 -1
- package/test/cartesian/CartesianAxis.spec.tsx +1 -1
- package/test/cartesian/GraphicalItemClipPath.scale-behavior.spec.tsx +1 -1
- package/test/cartesian/ReferenceLine/getEndPoints.spec.ts +1 -1
- package/test/cartesian/XAxis/XAxis.timescale.spec.tsx +1 -1
- package/test/helper/mockAxes.ts +1 -1
- package/test/shape/Curve.spec.tsx +1 -1
- package/test/state/createCollectionActions.spec.ts +87 -0
- package/test/state/createSelector.spec.ts +158 -0
- package/test/state/registerInStore.spec.tsx +101 -0
- package/test/state/selectors/combiners/combineConfiguredScale.spec.ts +1 -1
- package/test/state/selectors/combiners/combineRealScaleType.spec.ts +1 -1
- package/test/state/selectors/selectorMemoization.spec.tsx +136 -0
- package/test/util/CartesianUtils/CartesianUtils.spec.ts +1 -1
- package/test/util/ChartUtils/ChartUtils.spec.ts +1 -1
- package/test/util/ChartUtils/getCateCoordinateOfLine.spec.ts +14 -0
- package/test/util/ChartUtils/getStackedData.spec.ts +1 -1
- package/test/util/ChartUtils/truncateByDomain.spec.ts +1 -1
- package/test/util/scale/createCategoricalInverse.spec.ts +1 -1
- package/test-results/.last-run.json +4 -0
- package/src/state/reduxDevtoolsJsonStringifyReplacer.ts +0 -15
- package/src/util/axisPropsAreEqual.ts +0 -42
- package/src/util/resolveDefaultProps.tsx +0 -8
- package/src/util/typedDataKey.ts +0 -8
- package/test/state/reduxDevtoolsJsonStringifyReplacer.spec.ts +0 -25
- package/test/util/axisPropsAreEqual.spec.ts +0 -93
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solid-recharts",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
".": "./src/index.ts"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"
|
|
12
|
-
"
|
|
11
|
+
"d3-scale": "^4.0.2",
|
|
12
|
+
"d3-shape": "^3.1.0"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@solidjs/testing-library": "^1.0.0-beta.2",
|
|
16
|
+
"@types/d3-scale": "^4.0.2",
|
|
17
|
+
"@types/d3-shape": "^3.1.0",
|
|
16
18
|
"axe-core": "^4.12.0",
|
|
17
19
|
"vitest-axe": "^0.1.0"
|
|
18
20
|
},
|
package/src/cartesian/Area.tsx
CHANGED
|
@@ -11,10 +11,14 @@
|
|
|
11
11
|
// registration components → `createEffect` add/replace + `onCleanup` (Line idiom);
|
|
12
12
|
// React `useRef` prev-frame state → plain `{ current }` MutableRef objects driven
|
|
13
13
|
// by AnimatedItems/useAnimationStartSnapshot.
|
|
14
|
-
import {
|
|
14
|
+
import { createMemo, merge, Show } from "solid-js";
|
|
15
15
|
import type { JSX } from "@solidjs/web";
|
|
16
16
|
import { useAppSelector, useAppStore } from "../state/hooks";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
AnimatedItems,
|
|
19
|
+
useAnimationCallbacks,
|
|
20
|
+
type AnimationInterpolateFn,
|
|
21
|
+
} from "../animation/AnimatedItems";
|
|
18
22
|
import {
|
|
19
23
|
matchAnimationItems,
|
|
20
24
|
matchByIndex,
|
|
@@ -78,7 +82,7 @@ import { interpolate, isNan, isNullish, isNumber } from "../util/DataUtils";
|
|
|
78
82
|
import { BaseAxisWithScale } from "../state/selectors/axisSelectors";
|
|
79
83
|
import { StackId } from "../util/ChartUtils";
|
|
80
84
|
import { StackDataPoint } from "../util/stacks/stackTypes";
|
|
81
|
-
import {
|
|
85
|
+
import { registerInStore } from "../state/registerInStore";
|
|
82
86
|
import { AreaRevealShape, type AreaRevealShapeProps } from "./AreaRevealShape";
|
|
83
87
|
|
|
84
88
|
function getLegendItemColor(
|
|
@@ -417,45 +421,33 @@ export const Area = (props: AreaProps): JSX.Element => {
|
|
|
417
421
|
const layout = useCartesianChartLayout();
|
|
418
422
|
const id = useUniqueId("recharts-area", props.id);
|
|
419
423
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
store.actions.graphicalItems.replaceCartesianGraphicalItem({ prev, next: settings });
|
|
448
|
-
}
|
|
449
|
-
prev = settings;
|
|
450
|
-
},
|
|
451
|
-
);
|
|
452
|
-
|
|
453
|
-
unregisterOnCleanup(() => {
|
|
454
|
-
if (prev && store != null) {
|
|
455
|
-
store.actions.graphicalItems.removeCartesianGraphicalItem(prev);
|
|
456
|
-
prev = null;
|
|
457
|
-
}
|
|
458
|
-
});
|
|
424
|
+
if (store != null) {
|
|
425
|
+
registerInStore<AreaSettings>(
|
|
426
|
+
() => ({
|
|
427
|
+
id,
|
|
428
|
+
type: "area",
|
|
429
|
+
isPanorama,
|
|
430
|
+
data: merged.data,
|
|
431
|
+
dataKey: merged.dataKey ?? "",
|
|
432
|
+
xAxisId: merged.xAxisId,
|
|
433
|
+
yAxisId: merged.yAxisId,
|
|
434
|
+
zAxisId: 0,
|
|
435
|
+
hide: merged.hide,
|
|
436
|
+
// Area has no bar size; the field exists only because AreaSettings shares
|
|
437
|
+
// the MaybeStackedGraphicalItem shape with Bar.
|
|
438
|
+
barSize: undefined,
|
|
439
|
+
stackId: merged.stackId == null ? undefined : String(merged.stackId),
|
|
440
|
+
connectNulls: merged.connectNulls,
|
|
441
|
+
baseValue: merged.baseValue,
|
|
442
|
+
}),
|
|
443
|
+
{
|
|
444
|
+
add: (next) => store.actions.graphicalItems.addCartesianGraphicalItem(next),
|
|
445
|
+
replace: (prev, next) =>
|
|
446
|
+
store.actions.graphicalItems.replaceCartesianGraphicalItem({ prev, next }),
|
|
447
|
+
remove: (prev) => store.actions.graphicalItems.removeCartesianGraphicalItem(prev),
|
|
448
|
+
},
|
|
449
|
+
);
|
|
450
|
+
}
|
|
459
451
|
|
|
460
452
|
const area = useAppSelector((state) => selectArea(state, id, isPanorama));
|
|
461
453
|
|
|
@@ -569,18 +561,11 @@ export const Area = (props: AreaProps): JSX.Element => {
|
|
|
569
561
|
});
|
|
570
562
|
});
|
|
571
563
|
// Labels are hidden while the entrance/update animation runs, shown once it ends.
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const
|
|
577
|
-
setShowLabels(false);
|
|
578
|
-
props.onAnimationStart?.();
|
|
579
|
-
};
|
|
580
|
-
const handleAnimationEnd = (): void => {
|
|
581
|
-
setShowLabels(true);
|
|
582
|
-
props.onAnimationEnd?.();
|
|
583
|
-
};
|
|
564
|
+
const { isAnimating, handleAnimationStart, handleAnimationEnd } = useAnimationCallbacks(
|
|
565
|
+
() => props.onAnimationStart?.(),
|
|
566
|
+
() => props.onAnimationEnd?.(),
|
|
567
|
+
);
|
|
568
|
+
const labelContextEntries = createMemo(() => (isAnimating() ? undefined : labelListEntries()));
|
|
584
569
|
|
|
585
570
|
// Per-frame prev geometry (recharts' previousPointsRef / previousBaselineRef):
|
|
586
571
|
// plain `{ current }` refs (NOT signals) driven by AnimatedItems +
|
|
@@ -761,7 +746,7 @@ export const Area = (props: AreaProps): JSX.Element => {
|
|
|
761
746
|
stepBaseLine,
|
|
762
747
|
target.isRange,
|
|
763
748
|
animationElapsedTime,
|
|
764
|
-
|
|
749
|
+
isAnimating() || animationElapsedTime < 1,
|
|
765
750
|
effectiveIsEntrance,
|
|
766
751
|
);
|
|
767
752
|
}}
|
package/src/cartesian/Bar.tsx
CHANGED
|
@@ -93,7 +93,7 @@ import {
|
|
|
93
93
|
import { BaseAxisWithScale } from "../state/selectors/axisSelectors";
|
|
94
94
|
import { StackId } from "../util/ChartUtils";
|
|
95
95
|
import { BarStackClipLayer, useStackId } from "./BarStack";
|
|
96
|
-
import {
|
|
96
|
+
import { registerInStore } from "../state/registerInStore";
|
|
97
97
|
|
|
98
98
|
const defaultMinPointSize = 0;
|
|
99
99
|
|
|
@@ -419,7 +419,10 @@ function BarBackground(props: {
|
|
|
419
419
|
if (!backgroundFromProps) {
|
|
420
420
|
return undefined;
|
|
421
421
|
}
|
|
422
|
-
|
|
422
|
+
// NOTE: intentionally does NOT read activeIndex() here — that would rebuild
|
|
423
|
+
// the whole rects array (and recreate every background-rect DOM node) on
|
|
424
|
+
// every hover move. `isActive` is derived per-rect in the JSX below, so the
|
|
425
|
+
// array stays stable across hover and only the active flag updates.
|
|
423
426
|
const dataKey = props.dataKey;
|
|
424
427
|
const backgroundProps = svgPropertiesNoEventsFromUnknown(backgroundFromProps);
|
|
425
428
|
const backgroundEventProps = svgPropertiesAndEventsFromUnknown({
|
|
@@ -441,7 +444,7 @@ function BarBackground(props: {
|
|
|
441
444
|
return [
|
|
442
445
|
{
|
|
443
446
|
option: backgroundFromProps,
|
|
444
|
-
isActive:
|
|
447
|
+
isActive: false, // placeholder; derived reactively per-rect in the JSX below
|
|
445
448
|
...rest,
|
|
446
449
|
fill: "#eee",
|
|
447
450
|
...bg,
|
|
@@ -465,7 +468,12 @@ function BarBackground(props: {
|
|
|
465
468
|
<ZIndexLayer zIndex={getZIndexFromUnknown(props.background, DefaultZIndexes.barBackground)}>
|
|
466
469
|
<Layer>
|
|
467
470
|
<For each={rects()}>
|
|
468
|
-
{(barRectangleProps) =>
|
|
471
|
+
{(barRectangleProps) => (
|
|
472
|
+
<BarRectangle
|
|
473
|
+
{...barRectangleProps}
|
|
474
|
+
isActive={String(barRectangleProps.originalDataIndex) === activeIndex()}
|
|
475
|
+
/>
|
|
476
|
+
)}
|
|
469
477
|
</For>
|
|
470
478
|
</Layer>
|
|
471
479
|
</ZIndexLayer>
|
|
@@ -850,44 +858,32 @@ export const Bar = (props: BarProps): JSX.Element => {
|
|
|
850
858
|
const activeBar = merged.activeBar;
|
|
851
859
|
const hasCustomShape = shape !== defaultBarShape;
|
|
852
860
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
store.actions.graphicalItems.replaceCartesianGraphicalItem({ prev, next: settings });
|
|
880
|
-
}
|
|
881
|
-
prev = settings;
|
|
882
|
-
},
|
|
883
|
-
);
|
|
884
|
-
|
|
885
|
-
unregisterOnCleanup(() => {
|
|
886
|
-
if (prev && store != null) {
|
|
887
|
-
store.actions.graphicalItems.removeCartesianGraphicalItem(prev);
|
|
888
|
-
prev = null;
|
|
889
|
-
}
|
|
890
|
-
});
|
|
861
|
+
if (store != null) {
|
|
862
|
+
registerInStore<BarSettings>(
|
|
863
|
+
() => ({
|
|
864
|
+
id,
|
|
865
|
+
type: "bar",
|
|
866
|
+
isPanorama,
|
|
867
|
+
data: merged.data,
|
|
868
|
+
dataKey: merged.dataKey,
|
|
869
|
+
xAxisId: merged.xAxisId,
|
|
870
|
+
yAxisId: merged.yAxisId,
|
|
871
|
+
zAxisId: 0,
|
|
872
|
+
hide: merged.hide,
|
|
873
|
+
stackId,
|
|
874
|
+
barSize: merged.barSize,
|
|
875
|
+
maxBarSize: merged.maxBarSize,
|
|
876
|
+
minPointSize: merged.minPointSize,
|
|
877
|
+
hasCustomShape,
|
|
878
|
+
}),
|
|
879
|
+
{
|
|
880
|
+
add: (next) => store.actions.graphicalItems.addCartesianGraphicalItem(next),
|
|
881
|
+
replace: (prev, next) =>
|
|
882
|
+
store.actions.graphicalItems.replaceCartesianGraphicalItem({ prev, next }),
|
|
883
|
+
remove: (prev) => store.actions.graphicalItems.removeCartesianGraphicalItem(prev),
|
|
884
|
+
},
|
|
885
|
+
);
|
|
886
|
+
}
|
|
891
887
|
|
|
892
888
|
// Per-bar <Cell> overrides (collected from children, in data order).
|
|
893
889
|
const cells = collectCells(() => props.children);
|
|
@@ -1112,7 +1108,15 @@ export const Bar = (props: BarProps): JSX.Element => {
|
|
|
1112
1108
|
>
|
|
1113
1109
|
{(frame, animationElapsedTime, isEntrance) => (
|
|
1114
1110
|
<Layer>
|
|
1115
|
-
|
|
1111
|
+
{/* keyed={false} (index-keyed): `frame` is a fresh array of
|
|
1112
|
+
fresh rect objects every animation tick; reference-keyed
|
|
1113
|
+
<For> would dispose+recreate every bar's DOM node and
|
|
1114
|
+
rebuild its ~10 event closures per frame. Index-keying
|
|
1115
|
+
reuses the rows (geometry updates reactively via the
|
|
1116
|
+
getter-wrapped props) and builds the handlers once per
|
|
1117
|
+
slot. `r` is now an accessor — event handlers read `r()`
|
|
1118
|
+
at dispatch time so they see the current frame's rect. */}
|
|
1119
|
+
<For each={frame} keyed={false}>
|
|
1116
1120
|
{(r, i) => (
|
|
1117
1121
|
// Render every selector-output rectangle, matching recharts
|
|
1118
1122
|
// (zero-dimension genuinely-empty bars are already dropped at
|
|
@@ -1124,12 +1128,12 @@ export const Bar = (props: BarProps): JSX.Element => {
|
|
|
1124
1128
|
// here suppressed all animated bars until the animation was
|
|
1125
1129
|
// driven to a non-zero frame.
|
|
1126
1130
|
<BarRectangleLayer
|
|
1127
|
-
entry={r}
|
|
1128
|
-
displayIndex={i
|
|
1131
|
+
entry={r()}
|
|
1132
|
+
displayIndex={i}
|
|
1129
1133
|
dataKey={merged.dataKey}
|
|
1130
1134
|
activeBar={activeBar}
|
|
1131
|
-
fill={r.fill ?? r.cell?.fill ?? merged.fill}
|
|
1132
|
-
stroke={r.stroke ?? r.cell?.stroke ?? merged.stroke}
|
|
1135
|
+
fill={r().fill ?? r().cell?.fill ?? merged.fill}
|
|
1136
|
+
stroke={r().stroke ?? r().cell?.stroke ?? merged.stroke}
|
|
1133
1137
|
radius={merged.radius}
|
|
1134
1138
|
shape={shape}
|
|
1135
1139
|
animationElapsedTime={animationElapsedTime}
|
|
@@ -1137,43 +1141,47 @@ export const Bar = (props: BarProps): JSX.Element => {
|
|
|
1137
1141
|
isAnimating={isAnimating() || animationElapsedTime < 1}
|
|
1138
1142
|
isEntrance={isEntrance}
|
|
1139
1143
|
isEntryActive={isEntryActive}
|
|
1140
|
-
cellClass={typeof r.class === "string" ? r.class : undefined}
|
|
1141
|
-
onMouseEnter={
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
+
cellClass={typeof r().class === "string" ? r().class : undefined}
|
|
1145
|
+
onMouseEnter={(event) =>
|
|
1146
|
+
onItemEnter(r(), r().originalDataIndex)(event)
|
|
1147
|
+
}
|
|
1148
|
+
onMouseLeave={(event) =>
|
|
1149
|
+
onItemLeave(r(), r().originalDataIndex)(event)
|
|
1150
|
+
}
|
|
1151
|
+
onClick={(event) => onItemClick(r(), r().originalDataIndex)(event)}
|
|
1144
1152
|
onMouseOver={
|
|
1145
1153
|
onMouseOverHandler != null
|
|
1146
|
-
? (event) => onMouseOverHandler(r, r.originalDataIndex, event)
|
|
1154
|
+
? (event) => onMouseOverHandler(r(), r().originalDataIndex, event)
|
|
1147
1155
|
: undefined
|
|
1148
1156
|
}
|
|
1149
1157
|
onMouseOut={
|
|
1150
1158
|
onMouseOutHandler != null
|
|
1151
|
-
? (event) => onMouseOutHandler(r, r.originalDataIndex, event)
|
|
1159
|
+
? (event) => onMouseOutHandler(r(), r().originalDataIndex, event)
|
|
1152
1160
|
: undefined
|
|
1153
1161
|
}
|
|
1154
1162
|
onMouseMove={
|
|
1155
1163
|
onMouseMoveHandler != null
|
|
1156
|
-
? (event) => onMouseMoveHandler(r, r.originalDataIndex, event)
|
|
1164
|
+
? (event) => onMouseMoveHandler(r(), r().originalDataIndex, event)
|
|
1157
1165
|
: undefined
|
|
1158
1166
|
}
|
|
1159
1167
|
onMouseDown={
|
|
1160
1168
|
onMouseDownHandler != null
|
|
1161
|
-
? (event) => onMouseDownHandler(r, r.originalDataIndex, event)
|
|
1169
|
+
? (event) => onMouseDownHandler(r(), r().originalDataIndex, event)
|
|
1162
1170
|
: undefined
|
|
1163
1171
|
}
|
|
1164
1172
|
onMouseUp={
|
|
1165
1173
|
onMouseUpHandler != null
|
|
1166
|
-
? (event) => onMouseUpHandler(r, r.originalDataIndex, event)
|
|
1174
|
+
? (event) => onMouseUpHandler(r(), r().originalDataIndex, event)
|
|
1167
1175
|
: undefined
|
|
1168
1176
|
}
|
|
1169
1177
|
onTouchMove={
|
|
1170
1178
|
onTouchMoveHandler != null
|
|
1171
|
-
? (event) => onTouchMoveHandler(r, i
|
|
1179
|
+
? (event) => onTouchMoveHandler(r(), i, event)
|
|
1172
1180
|
: undefined
|
|
1173
1181
|
}
|
|
1174
1182
|
onTouchEnd={
|
|
1175
1183
|
onTouchEndHandler != null
|
|
1176
|
-
? (event) => onTouchEndHandler(r, i
|
|
1184
|
+
? (event) => onTouchEndHandler(r(), i, event)
|
|
1177
1185
|
: undefined
|
|
1178
1186
|
}
|
|
1179
1187
|
/>
|
|
@@ -73,11 +73,14 @@ export const useBarStackClipPathUrl = (index: number): string | undefined => {
|
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
export const BarStackClipLayer = (props: LayerProps & { index: number }): JSX.Element => {
|
|
76
|
-
|
|
76
|
+
// Memoized so `props.index` is read in a tracking scope: when an index-keyed
|
|
77
|
+
// <For> feeds a reactive `entry.originalDataIndex` here, a bare hook call would
|
|
78
|
+
// read it untracked (STRICT_READ_UNTRACKED) and never update.
|
|
79
|
+
const clipPathUrl = createMemo(() => useBarStackClipPathUrl(props.index));
|
|
77
80
|
return (
|
|
78
81
|
<Layer
|
|
79
82
|
class={["recharts-bar-stack-layer", props.class].filter(Boolean).join(" ")}
|
|
80
|
-
clipPath={clipPathUrl}
|
|
83
|
+
clipPath={clipPathUrl()}
|
|
81
84
|
onMouseEnter={props.onMouseEnter}
|
|
82
85
|
onMouseLeave={props.onMouseLeave}
|
|
83
86
|
onClick={props.onClick}
|
package/src/cartesian/Brush.tsx
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
// "with controlled startIndex/endIndex" Brush.spec block.
|
|
26
26
|
import { createEffect, createMemo, createSignal, merge, onCleanup, Show } from "solid-js";
|
|
27
27
|
import type { JSX } from "@solidjs/web";
|
|
28
|
-
import { scalePoint } from "
|
|
28
|
+
import { scalePoint } from "d3-scale";
|
|
29
29
|
import { useAppSelector, useAppStore } from "../state/hooks";
|
|
30
30
|
import { Layer } from "../container/Layer";
|
|
31
31
|
import { Text } from "../component/Text";
|
package/src/cartesian/Funnel.tsx
CHANGED
|
@@ -17,11 +17,15 @@
|
|
|
17
17
|
// Enter/Leave/onTouchStart/Move/End, props-first `(data, index, event)`) ARE wired.
|
|
18
18
|
// No legend registration and no ZIndexLayer — matching recharts (upstream Funnel
|
|
19
19
|
// registers no legend payload and renders inline without a zIndex portal).
|
|
20
|
-
import { createMemo,
|
|
20
|
+
import { createMemo, For, merge, Show } from "solid-js";
|
|
21
21
|
import type { JSX } from "@solidjs/web";
|
|
22
22
|
import { useAppSelector } from "../state/hooks";
|
|
23
23
|
import { useUniqueId } from "../util/useUniqueId";
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
AnimatedItems,
|
|
26
|
+
useAnimationCallbacks,
|
|
27
|
+
type AnimationInterpolateFn,
|
|
28
|
+
} from "../animation/AnimatedItems";
|
|
25
29
|
import { matchAppend, type AnimationMatchByProp } from "../animation/matchBy";
|
|
26
30
|
import type { MutableRef } from "../animation/useAnimationStartSnapshot";
|
|
27
31
|
import { useCartesianChartLayout } from "../context/chartLayoutContext";
|
|
@@ -452,18 +456,11 @@ export function Funnel<DataPointType = Record<string, unknown>, DataValueType =
|
|
|
452
456
|
current: null,
|
|
453
457
|
};
|
|
454
458
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const
|
|
460
|
-
props.onAnimationStart?.();
|
|
461
|
-
setShowLabels(false);
|
|
462
|
-
};
|
|
463
|
-
const handleAnimationEnd = (): void => {
|
|
464
|
-
props.onAnimationEnd?.();
|
|
465
|
-
setShowLabels(true);
|
|
466
|
-
};
|
|
459
|
+
const { isAnimating, handleAnimationStart, handleAnimationEnd } = useAnimationCallbacks(
|
|
460
|
+
() => props.onAnimationStart?.(),
|
|
461
|
+
() => props.onAnimationEnd?.(),
|
|
462
|
+
);
|
|
463
|
+
const labelContextEntries = createMemo(() => (isAnimating() ? undefined : labelListEntries()));
|
|
467
464
|
|
|
468
465
|
const layerClass = (): string =>
|
|
469
466
|
["recharts-trapezoids", merged.class].filter((c): c is string => Boolean(c)).join(" ");
|
|
@@ -514,13 +511,12 @@ export function Funnel<DataPointType = Record<string, unknown>, DataValueType =
|
|
|
514
511
|
const isActive = (): boolean =>
|
|
515
512
|
Boolean(activeShape) && activeItemIndex() === String(i());
|
|
516
513
|
// v3.9 ShapeAnimationProps forwarded into the custom-shape
|
|
517
|
-
// invocation, mirroring upstream FunnelTrapezoids.
|
|
518
|
-
//
|
|
519
|
-
//
|
|
520
|
-
// `isAnimating` through the whole entrance frame.
|
|
514
|
+
// invocation, mirroring upstream FunnelTrapezoids. Combine
|
|
515
|
+
// `< 1` like upstream so the shape sees `isAnimating` through
|
|
516
|
+
// the whole entrance frame.
|
|
521
517
|
const shapeAnimationProps = (): ShapeAnimationProps => ({
|
|
522
518
|
animationElapsedTime,
|
|
523
|
-
isAnimating:
|
|
519
|
+
isAnimating: isAnimating() || animationElapsedTime < 1,
|
|
524
520
|
isEntrance,
|
|
525
521
|
});
|
|
526
522
|
return (
|
package/src/cartesian/Line.tsx
CHANGED
|
@@ -25,7 +25,11 @@ import { useActiveTooltipDataPoints } from "../hooks";
|
|
|
25
25
|
import { selectActiveTooltipIndex } from "../state/selectors/tooltipSelectors";
|
|
26
26
|
import type { JSX } from "@solidjs/web";
|
|
27
27
|
import { useAppSelector, useAppStore } from "../state/hooks";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
AnimatedItems,
|
|
30
|
+
useAnimationCallbacks,
|
|
31
|
+
type AnimationInterpolateFn,
|
|
32
|
+
} from "../animation/AnimatedItems";
|
|
29
33
|
import { matchByIndex, type AnimationItem, type AnimationMatchByProp } from "../animation/matchBy";
|
|
30
34
|
import type { MutableRef } from "../animation/useAnimationStartSnapshot";
|
|
31
35
|
import { LineDrawShape, type LineDrawShapeProps } from "./LineDrawShape";
|
|
@@ -80,7 +84,7 @@ import { BaseAxisWithScale } from "../state/selectors/axisSelectors";
|
|
|
80
84
|
import { GraphicalItemClipPath, useNeedsClip } from "./GraphicalItemClipPath";
|
|
81
85
|
import { DefaultZIndexes } from "../zIndex/DefaultZIndexes";
|
|
82
86
|
import { ZIndexLayer } from "../zIndex/ZIndexLayer";
|
|
83
|
-
import {
|
|
87
|
+
import { registerInStore } from "../state/registerInStore";
|
|
84
88
|
|
|
85
89
|
export interface LinePointItem {
|
|
86
90
|
readonly value: unknown;
|
|
@@ -342,39 +346,27 @@ export const Line = (props: LineProps): JSX.Element => {
|
|
|
342
346
|
const layout = useCartesianChartLayout();
|
|
343
347
|
const id = useUniqueId("recharts-line", props.id);
|
|
344
348
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
store.actions.graphicalItems.replaceCartesianGraphicalItem({ prev, next: settings });
|
|
367
|
-
}
|
|
368
|
-
prev = settings;
|
|
369
|
-
},
|
|
370
|
-
);
|
|
371
|
-
|
|
372
|
-
unregisterOnCleanup(() => {
|
|
373
|
-
if (prev && store != null) {
|
|
374
|
-
store.actions.graphicalItems.removeCartesianGraphicalItem(prev);
|
|
375
|
-
prev = null;
|
|
376
|
-
}
|
|
377
|
-
});
|
|
349
|
+
if (store != null) {
|
|
350
|
+
registerInStore<LineSettings>(
|
|
351
|
+
() => ({
|
|
352
|
+
id,
|
|
353
|
+
type: "line",
|
|
354
|
+
isPanorama,
|
|
355
|
+
data: merged.data,
|
|
356
|
+
dataKey: merged.dataKey,
|
|
357
|
+
xAxisId: merged.xAxisId,
|
|
358
|
+
yAxisId: merged.yAxisId,
|
|
359
|
+
zAxisId: 0,
|
|
360
|
+
hide: merged.hide,
|
|
361
|
+
}),
|
|
362
|
+
{
|
|
363
|
+
add: (next) => store.actions.graphicalItems.addCartesianGraphicalItem(next),
|
|
364
|
+
replace: (prev, next) =>
|
|
365
|
+
store.actions.graphicalItems.replaceCartesianGraphicalItem({ prev, next }),
|
|
366
|
+
remove: (prev) => store.actions.graphicalItems.removeCartesianGraphicalItem(prev),
|
|
367
|
+
},
|
|
368
|
+
);
|
|
369
|
+
}
|
|
378
370
|
|
|
379
371
|
const points = useAppSelector((state) =>
|
|
380
372
|
selectLinePoints(state, merged.xAxisId, merged.yAxisId, isPanorama, id),
|
|
@@ -494,19 +486,11 @@ export const Line = (props: LineProps): JSX.Element => {
|
|
|
494
486
|
});
|
|
495
487
|
// Labels are hidden while the entrance animation runs and shown once it ends
|
|
496
488
|
// (matching recharts: showLabels = !isAnimating).
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
const labelContextEntries = createMemo(() => (
|
|
502
|
-
const handleAnimationStart = (): void => {
|
|
503
|
-
props.onAnimationStart?.();
|
|
504
|
-
setShowLabels(false);
|
|
505
|
-
};
|
|
506
|
-
const handleAnimationEnd = (): void => {
|
|
507
|
-
props.onAnimationEnd?.();
|
|
508
|
-
setShowLabels(true);
|
|
509
|
-
};
|
|
489
|
+
const { isAnimating, handleAnimationStart, handleAnimationEnd } = useAnimationCallbacks(
|
|
490
|
+
() => props.onAnimationStart?.(),
|
|
491
|
+
() => props.onAnimationEnd?.(),
|
|
492
|
+
);
|
|
493
|
+
const labelContextEntries = createMemo(() => (isAnimating() ? undefined : labelListEntries()));
|
|
510
494
|
|
|
511
495
|
// Measure on data change AND on animation start/end (showLabels flips): a
|
|
512
496
|
// data-change morph alters the path geometry per frame, so the length taken
|
|
@@ -515,7 +499,7 @@ export const Line = (props: LineProps): JSX.Element => {
|
|
|
515
499
|
// settled dasharray the new curve's true length — mirroring recharts, which
|
|
516
500
|
// recomputes totalLength on the post-animation re-render.
|
|
517
501
|
createEffect(
|
|
518
|
-
() => ({ points: points(), showLabels:
|
|
502
|
+
() => ({ points: points(), showLabels: !isAnimating() }),
|
|
519
503
|
() => {
|
|
520
504
|
if (curveEl?.getTotalLength == null) {
|
|
521
505
|
return;
|
|
@@ -627,8 +611,7 @@ export const Line = (props: LineProps): JSX.Element => {
|
|
|
627
611
|
// recharts: visibleLength is computed only while the animation is
|
|
628
612
|
// active; once settled it is null so no entrance dasharray remains.
|
|
629
613
|
const len = untrack(totalLength);
|
|
630
|
-
const animationActive =
|
|
631
|
-
untrack(showLabels) === false || animationElapsedTime < 1;
|
|
614
|
+
const animationActive = untrack(isAnimating) || animationElapsedTime < 1;
|
|
632
615
|
const visibleLength = animationActive
|
|
633
616
|
? animatedLineLength.getVisibleLength(animationElapsedTime, len)
|
|
634
617
|
: null;
|
|
@@ -138,14 +138,16 @@ export function LineDrawShape(props: LineDrawShapeProps): JSX.Element {
|
|
|
138
138
|
props.forwardPathRef?.(el);
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
-
// Re-measure
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
141
|
+
// Re-measure the path length only while the entrance dash is actually being
|
|
142
|
+
// drawn (`visibleLength != null`). `totalLength` feeds `strokeDasharray` ONLY on
|
|
143
|
+
// that branch (see below), so once the reveal is done — including every
|
|
144
|
+
// subsequent data-change morph, where `frame` changes per frame — measuring
|
|
145
|
+
// would force a synchronous SVG layout (reflow) each frame for a value nothing
|
|
146
|
+
// reads. Guarding on visibleLength drops those per-frame reflows.
|
|
145
147
|
createEffect(
|
|
146
148
|
() => ({ points: props.points, visibleLength: props.visibleLength }),
|
|
147
|
-
() => {
|
|
148
|
-
if (pathEl?.getTotalLength == null) {
|
|
149
|
+
(deps) => {
|
|
150
|
+
if (deps.visibleLength == null || pathEl?.getTotalLength == null) {
|
|
149
151
|
return;
|
|
150
152
|
}
|
|
151
153
|
const length = getTotalLength(pathEl);
|