@octanejs/recharts 0.1.16 → 0.1.17
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 +4 -4
- package/src/animation/CSSTransitionAnimate.tsrx +142 -0
- package/src/cartesian/Area.tsrx +1096 -0
- package/src/cartesian/Area.tsrx.d.ts +340 -0
- package/src/cartesian/AreaRevealShape.tsrx +202 -0
- package/src/cartesian/CartesianGrid.tsrx +643 -0
- package/src/cartesian/CartesianGrid.tsrx.d.ts +219 -0
- package/src/cartesian/ErrorBar.tsrx +322 -0
- package/src/cartesian/ErrorBar.tsrx.d.ts +131 -0
- package/src/cartesian/Funnel.tsrx +833 -0
- package/src/cartesian/Funnel.tsrx.d.ts +274 -0
- package/src/cartesian/ReferenceLine.tsrx +452 -0
- package/src/cartesian/ReferenceLine.tsrx.d.ts +178 -0
- package/src/cartesian/Scatter.tsrx +1076 -0
- package/src/cartesian/Scatter.tsrx.d.ts +351 -0
- package/src/cartesian/ZAxis.tsrx +148 -0
- package/src/cartesian/ZAxis.tsrx.d.ts +102 -0
- package/src/chart/AreaChart.tsrx +22 -0
- package/src/chart/AreaChart.tsrx.d.ts +12 -0
- package/src/chart/ComposedChart.tsrx +22 -0
- package/src/chart/ComposedChart.tsrx.d.ts +12 -0
- package/src/chart/FunnelChart.tsrx +22 -0
- package/src/chart/FunnelChart.tsrx.d.ts +12 -0
- package/src/chart/PieChart.tsrx +31 -0
- package/src/chart/PieChart.tsrx.d.ts +31 -0
- package/src/chart/PolarChart.tsrx +121 -0
- package/src/chart/RadarChart.tsrx +53 -0
- package/src/chart/RadarChart.tsrx.d.ts +31 -0
- package/src/chart/RadialBarChart.tsrx +31 -0
- package/src/chart/RadialBarChart.tsrx.d.ts +31 -0
- package/src/chart/Sankey.tsrx +1542 -0
- package/src/chart/Sankey.tsrx.d.ts +253 -0
- package/src/chart/ScatterChart.tsrx +22 -0
- package/src/chart/ScatterChart.tsrx.d.ts +12 -0
- package/src/chart/SunburstChart.tsrx +495 -0
- package/src/chart/SunburstChart.tsrx.d.ts +146 -0
- package/src/chart/types.ts +19 -0
- package/src/component/Cursor.tsrx +157 -0
- package/src/component/DefaultLegendContent.tsrx +288 -0
- package/src/component/DefaultLegendContent.tsrx.d.ts +125 -0
- package/src/component/DefaultTooltipContent.tsrx +215 -0
- package/src/component/DefaultTooltipContent.tsrx.d.ts +87 -0
- package/src/component/Legend.tsrx +302 -0
- package/src/component/Legend.tsrx.d.ts +88 -0
- package/src/component/ResponsiveContainer.ts +1 -22
- package/src/component/ResponsiveContainer.tsrx +171 -0
- package/src/component/ResponsiveContainer.tsrx.d.ts +81 -0
- package/src/component/Tooltip.tsrx +425 -0
- package/src/component/Tooltip.tsrx.d.ts +246 -0
- package/src/component/TooltipBoundingBox.tsrx +135 -0
- package/src/context/CellsContext.ts +12 -0
- package/src/context/legendPayloadContext.tsrx +11 -0
- package/src/context/useTooltipAxis.ts +20 -0
- package/src/index.d.ts +236 -0
- package/src/index.ts +67 -0
- package/src/polar/Pie.tsrx +1224 -0
- package/src/polar/Pie.tsrx.d.ts +436 -0
- package/src/polar/PolarAngleAxis.tsrx +554 -0
- package/src/polar/PolarAngleAxis.tsrx.d.ts +215 -0
- package/src/polar/PolarGrid.tsrx +317 -0
- package/src/polar/PolarGrid.tsrx.d.ts +117 -0
- package/src/polar/PolarRadiusAxis.tsrx +463 -0
- package/src/polar/PolarRadiusAxis.tsrx.d.ts +209 -0
- package/src/polar/Radar.tsrx +696 -0
- package/src/polar/Radar.tsrx.d.ts +215 -0
- package/src/polar/RadialBar.tsrx +887 -0
- package/src/polar/RadialBar.tsrx.d.ts +340 -0
- package/src/state/ReportPolarOptions.tsrx +11 -0
- package/src/state/selectors/areaSelectors.ts +257 -0
- package/src/state/selectors/funnelSelectors.ts +86 -0
- package/src/state/selectors/pieSelectors.ts +134 -0
- package/src/state/selectors/polarGridSelectors.ts +43 -0
- package/src/state/selectors/polarScaleSelectors.ts +159 -0
- package/src/state/selectors/polarSelectors.ts +198 -0
- package/src/state/selectors/radarSelectors.ts +228 -0
- package/src/state/selectors/radialBarSelectors.ts +511 -0
- package/src/state/selectors/scatterSelectors.ts +168 -0
- package/src/util/FunnelUtils.tsrx +19 -0
- package/src/util/RadialBarUtils.tsrx +29 -0
- package/src/util/ScatterUtils.tsrx +34 -0
- package/src/util/cursor/getCursorPoints.ts +45 -0
- package/src/util/cursor/getCursorRectangle.ts +28 -0
- package/src/util/cursor/getRadialCursorPoints.ts +31 -0
- package/src/util/payload/getUniqPayload.ts +28 -0
- package/src/util/scale/CartesianScaleHelper.ts +71 -0
- package/src/util/tooltip/translate.ts +168 -0
- package/src/util/types.ts +3 -0
- package/src/util/useElementOffset.ts +147 -0
|
@@ -0,0 +1,1076 @@
|
|
|
1
|
+
import * as React from 'octane';
|
|
2
|
+
import { MutableRefObject, ReactElement, ReactNode, useMemo, useRef } from 'octane';
|
|
3
|
+
|
|
4
|
+
import { clsx } from 'clsx';
|
|
5
|
+
import { Layer } from '../container/Layer';
|
|
6
|
+
import {
|
|
7
|
+
CartesianLabelListContextProvider,
|
|
8
|
+
CartesianLabelListEntry,
|
|
9
|
+
ImplicitLabelListType,
|
|
10
|
+
LabelListFromLabelProp,
|
|
11
|
+
} from '../component/LabelList';
|
|
12
|
+
import { Curve, CurveType, Props as CurveProps } from '../shape/Curve';
|
|
13
|
+
import { ErrorBarDataItem, ErrorBarDataPointFormatter, ErrorBarDirection } from './ErrorBar';
|
|
14
|
+
import { getLinearRegression, interpolate, isNullish } from '../util/DataUtils';
|
|
15
|
+
import { getCateCoordinateOfLine, getTooltipNameProp, getValueByDataKey } from '../util/ChartUtils';
|
|
16
|
+
import {
|
|
17
|
+
ActiveShape,
|
|
18
|
+
adaptEventsOfChild,
|
|
19
|
+
AnimationDuration,
|
|
20
|
+
EasingInput,
|
|
21
|
+
Coordinate,
|
|
22
|
+
DataConsumer,
|
|
23
|
+
DataKey,
|
|
24
|
+
DataProvider,
|
|
25
|
+
isNonEmptyArray,
|
|
26
|
+
LegendType,
|
|
27
|
+
NonEmptyArray,
|
|
28
|
+
NullableCoordinate,
|
|
29
|
+
PresentationAttributesAdaptChildEvent,
|
|
30
|
+
ShapeAnimationProps,
|
|
31
|
+
SymbolType,
|
|
32
|
+
TickItem,
|
|
33
|
+
TrapezoidViewBox,
|
|
34
|
+
CartesianLayout,
|
|
35
|
+
} from '../util/types';
|
|
36
|
+
import { Formatter, TooltipType } from '../component/DefaultTooltipContent';
|
|
37
|
+
import { ScatterShapeProps, ScatterSymbol } from '../util/ScatterUtils';
|
|
38
|
+
import { InnerSymbolsProp } from '../shape/Symbols';
|
|
39
|
+
import type { LegendPayload } from '../component/DefaultLegendContent';
|
|
40
|
+
import {
|
|
41
|
+
useMouseClickItemDispatch,
|
|
42
|
+
useMouseEnterItemDispatch,
|
|
43
|
+
useMouseLeaveItemDispatch,
|
|
44
|
+
} from '../context/tooltipContext';
|
|
45
|
+
import {
|
|
46
|
+
TooltipPayload,
|
|
47
|
+
TooltipPayloadConfiguration,
|
|
48
|
+
TooltipPayloadEntry,
|
|
49
|
+
} from '../state/tooltipSlice';
|
|
50
|
+
import { SetTooltipEntrySettings } from '../state/SetTooltipEntrySettings';
|
|
51
|
+
import { SetErrorBarContext } from '../context/ErrorBarContext';
|
|
52
|
+
import { AxisId } from '../state/cartesianAxisSlice';
|
|
53
|
+
import { GraphicalItemClipPath, useNeedsClip } from './GraphicalItemClipPath';
|
|
54
|
+
import { selectScatterPoints } from '../state/selectors/scatterSelectors';
|
|
55
|
+
import { useAppSelector } from '../state/hooks';
|
|
56
|
+
import { RechartsRootState } from '../state/store';
|
|
57
|
+
import { BaseAxisWithScale, implicitZAxis, ZAxisWithScale } from '../state/selectors/axisSelectors';
|
|
58
|
+
import { useIsPanorama } from '../context/PanoramaContext';
|
|
59
|
+
import { selectActiveTooltipIndex } from '../state/selectors/tooltipSelectors';
|
|
60
|
+
import { SetLegendPayload } from '../state/SetLegendPayload';
|
|
61
|
+
import { DATA_ITEM_GRAPHICAL_ITEM_ID_ATTRIBUTE_NAME } from '../util/Constants';
|
|
62
|
+
import { resolveDefaultProps } from '../util/resolveDefaultProps';
|
|
63
|
+
import { RegisterGraphicalItemId } from '../context/RegisterGraphicalItemId';
|
|
64
|
+
import type { ScatterSettings } from '../state/types/ScatterSettings';
|
|
65
|
+
import { SetCartesianGraphicalItem } from '../state/SetGraphicalItem';
|
|
66
|
+
import {
|
|
67
|
+
svgPropertiesNoEvents,
|
|
68
|
+
svgPropertiesNoEventsFromUnknown,
|
|
69
|
+
} from '../util/svgPropertiesNoEvents';
|
|
70
|
+
import { useCartesianChartLayout, useViewBox } from '../context/chartLayoutContext';
|
|
71
|
+
import {
|
|
72
|
+
AnimatedItems,
|
|
73
|
+
AnimationInterpolateFn,
|
|
74
|
+
useAnimationCallbacks,
|
|
75
|
+
} from '../animation/AnimatedItems';
|
|
76
|
+
import { AnimationMatchByProp, matchAppend } from '../animation/matchBy';
|
|
77
|
+
import { WithIdRequired, WithoutId } from '../util/useUniqueId';
|
|
78
|
+
import { GraphicalItemId } from '../state/graphicalItemsSlice';
|
|
79
|
+
import { ZIndexable, ZIndexLayer } from '../zIndex/ZIndexLayer';
|
|
80
|
+
import { DefaultZIndexes } from '../zIndex/DefaultZIndexes';
|
|
81
|
+
import { propsAreEqual } from '../util/propsAreEqual';
|
|
82
|
+
import { ChartData } from '../state/chartDataSlice';
|
|
83
|
+
import { CellsContext, useCellRegistry } from '../context/CellsContext';
|
|
84
|
+
|
|
85
|
+
export interface ScatterPointNode {
|
|
86
|
+
x?: number | string;
|
|
87
|
+
y?: number | string;
|
|
88
|
+
z?: number | string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Scatter coordinates are nullable because sometimes the point value is out of the domain,
|
|
93
|
+
* and we can't compute a valid coordinate for it.
|
|
94
|
+
*
|
|
95
|
+
* Scatter -> Symbol ignores points with null cx or cy so those won't render if using the default shapes.
|
|
96
|
+
* However: the points are exposed via various props and can be used in custom shapes so we keep them around.
|
|
97
|
+
*/
|
|
98
|
+
export interface ScatterPointItem {
|
|
99
|
+
/**
|
|
100
|
+
* The x coordinate of the point center in pixels.
|
|
101
|
+
*/
|
|
102
|
+
cx: number | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* The y coordinate of the point center in pixels.
|
|
105
|
+
*/
|
|
106
|
+
cy: number | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* The x coordinate (in pixels) of the top-left corner of the rectangle that wraps the point.
|
|
109
|
+
*/
|
|
110
|
+
x: number | undefined;
|
|
111
|
+
/**
|
|
112
|
+
* The y coordinate (in pixels) of the top-left corner of the rectangle that wraps the point.
|
|
113
|
+
*/
|
|
114
|
+
y: number | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* ScatterPointItem size is an abstract number that is used to calculate the radius of the point.
|
|
117
|
+
* It's not the radius itself, but rather a value that is used to calculate the radius.
|
|
118
|
+
* Interacts with the zAxis range.
|
|
119
|
+
*/
|
|
120
|
+
size: number;
|
|
121
|
+
/**
|
|
122
|
+
* Width of the point in pixels.
|
|
123
|
+
*/
|
|
124
|
+
width: number;
|
|
125
|
+
/**
|
|
126
|
+
* Height of the point in pixels.
|
|
127
|
+
*/
|
|
128
|
+
height: number;
|
|
129
|
+
node: ScatterPointNode;
|
|
130
|
+
payload?: any;
|
|
131
|
+
tooltipPayload?: TooltipPayload;
|
|
132
|
+
tooltipPosition: Coordinate;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export type ScatterCustomizedShape =
|
|
136
|
+
ActiveShape<ScatterShapeProps & InnerSymbolsProp, SVGPathElement> | SymbolType;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Internal props, combination of external props + defaultProps + private Recharts state
|
|
140
|
+
*/
|
|
141
|
+
interface ScatterInternalProps extends ZIndexable {
|
|
142
|
+
data?: ChartData;
|
|
143
|
+
xAxisId: string | number;
|
|
144
|
+
yAxisId: string | number;
|
|
145
|
+
zAxisId: string | number;
|
|
146
|
+
dataKey?: DataKey<any>;
|
|
147
|
+
line?: | ReactElement<SVGElement>
|
|
148
|
+
| ((props: any) => ReactElement<SVGElement>)
|
|
149
|
+
| CurveProps
|
|
150
|
+
| boolean;
|
|
151
|
+
lineType: 'fitting' | 'joint';
|
|
152
|
+
lineJointType: CurveType;
|
|
153
|
+
legendType: LegendType;
|
|
154
|
+
tooltipType?: TooltipType;
|
|
155
|
+
formatter?: Formatter;
|
|
156
|
+
className?: string;
|
|
157
|
+
name?: string;
|
|
158
|
+
activeShape?: ScatterCustomizedShape;
|
|
159
|
+
shape: ScatterCustomizedShape;
|
|
160
|
+
points: ReadonlyArray<ScatterPointItem>;
|
|
161
|
+
hide: boolean;
|
|
162
|
+
label?: ImplicitLabelListType;
|
|
163
|
+
isAnimationActive: boolean | 'auto';
|
|
164
|
+
animationBegin: number;
|
|
165
|
+
animationDuration: AnimationDuration;
|
|
166
|
+
animationEasing: EasingInput;
|
|
167
|
+
animationInterpolateFn: AnimationInterpolateFn<ScatterPointItem, CartesianLayout>;
|
|
168
|
+
animationMatchBy: AnimationMatchByProp<ScatterPointItem>;
|
|
169
|
+
onAnimationStart?: () => void;
|
|
170
|
+
onAnimationEnd?: () => void;
|
|
171
|
+
needClip: boolean;
|
|
172
|
+
id: GraphicalItemId;
|
|
173
|
+
children?: ReactNode;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* External props, intended for end users to fill in
|
|
178
|
+
*/
|
|
179
|
+
interface ScatterProps<DataPointType = any, DataValueType = any> extends DataProvider<
|
|
180
|
+
DataPointType
|
|
181
|
+
>, DataConsumer<DataPointType, DataValueType>, ZIndexable {
|
|
182
|
+
/**
|
|
183
|
+
* Unique identifier of this component.
|
|
184
|
+
* Used as an HTML attribute `id`, and also to identify this element internally.
|
|
185
|
+
*
|
|
186
|
+
* If undefined, Recharts will generate a unique ID automatically.
|
|
187
|
+
*/
|
|
188
|
+
id?: string;
|
|
189
|
+
/**
|
|
190
|
+
* The id of XAxis which is corresponding to the data. Required when there are multiple XAxes.
|
|
191
|
+
* @defaultValue 0
|
|
192
|
+
*/
|
|
193
|
+
xAxisId?: AxisId;
|
|
194
|
+
/**
|
|
195
|
+
* The id of YAxis which is corresponding to the data. Required when there are multiple YAxes.
|
|
196
|
+
* @defaultValue 0
|
|
197
|
+
*/
|
|
198
|
+
yAxisId?: AxisId;
|
|
199
|
+
/**
|
|
200
|
+
* The id of ZAxis which is corresponding to the data. Required when there are multiple ZAxes.
|
|
201
|
+
*
|
|
202
|
+
* ZAxis does not render directly, has no ticks and no tick line.
|
|
203
|
+
* It is used to control the size of each scatter point.
|
|
204
|
+
*
|
|
205
|
+
* @defaultValue 0
|
|
206
|
+
* @see {@link https://recharts.github.io/en-US/examples/ThreeDimScatterChart/ Scatter chart with Z axis}
|
|
207
|
+
*/
|
|
208
|
+
zAxisId?: AxisId;
|
|
209
|
+
/**
|
|
210
|
+
* Renders line connecting individual points.
|
|
211
|
+
* Options:
|
|
212
|
+
* - `false`: no line is drawn.
|
|
213
|
+
* - `true`: a default line is drawn.
|
|
214
|
+
* - `ReactElement`: the option is the custom line element.
|
|
215
|
+
* - `function`: the function will be called to render customized line.
|
|
216
|
+
* - `object`: the option is the props of Curve element.
|
|
217
|
+
*
|
|
218
|
+
* Also see the `lineType` prop which controls how is this line calculated.
|
|
219
|
+
*
|
|
220
|
+
* @defaultValue false
|
|
221
|
+
* @example <Scatter line />
|
|
222
|
+
* @example <Scatter line={CustomizedLineComponent} />
|
|
223
|
+
* @example <Scatter line={{ strokeDasharray: '5 5' }} />
|
|
224
|
+
*
|
|
225
|
+
* @see {@link https://recharts.github.io/en-US/examples/JointLineScatterChart/ Scatter chart with joint line}
|
|
226
|
+
*/
|
|
227
|
+
line?: | ReactElement<SVGElement>
|
|
228
|
+
| ((props: any) => ReactElement<SVGElement>)
|
|
229
|
+
| CurveProps
|
|
230
|
+
| boolean;
|
|
231
|
+
/**
|
|
232
|
+
* Determines calculation method of the line if the `line` prop is set.
|
|
233
|
+
* Options:
|
|
234
|
+
* - `'joint'`: line will be generated by connecting all the points.
|
|
235
|
+
* - `'fitting'`: line will be generated by fitting algorithm (linear regression).
|
|
236
|
+
*
|
|
237
|
+
* Has no effect if `line` prop is not set or is false.
|
|
238
|
+
*
|
|
239
|
+
* @defaultValue joint
|
|
240
|
+
* @example <Scatter line lineType="fitting" />
|
|
241
|
+
* @see {@link https://recharts.github.io/en-US/examples/JointLineScatterChart/ Scatter chart with joint line}
|
|
242
|
+
*/
|
|
243
|
+
lineType?: 'fitting' | 'joint';
|
|
244
|
+
/**
|
|
245
|
+
* Determines the shape of joint line.
|
|
246
|
+
* Same as `type` prop on Curve, Line and Area components.
|
|
247
|
+
*
|
|
248
|
+
* Has no effect if `line` prop is not set or is false or if `lineType` is 'fitting'.
|
|
249
|
+
*
|
|
250
|
+
* @defaultValue linear
|
|
251
|
+
* @see {@link https://recharts.github.io/en-US/examples/JointLineScatterChart/ Scatter chart with joint line}
|
|
252
|
+
*/
|
|
253
|
+
lineJointType?: CurveType;
|
|
254
|
+
/**
|
|
255
|
+
* The type of icon in legend.
|
|
256
|
+
* If set to 'none', no legend item will be rendered.
|
|
257
|
+
*
|
|
258
|
+
* @defaultValue 'circle'
|
|
259
|
+
* @example <Scatter legendType="diamond" />
|
|
260
|
+
*/
|
|
261
|
+
legendType?: LegendType;
|
|
262
|
+
tooltipType?: TooltipType;
|
|
263
|
+
/**
|
|
264
|
+
* Formats the value displayed in the tooltip for this Scatter.
|
|
265
|
+
* When set, takes precedence over the `formatter` prop on the Tooltip component.
|
|
266
|
+
*/
|
|
267
|
+
formatter?: Formatter;
|
|
268
|
+
className?: string;
|
|
269
|
+
/**
|
|
270
|
+
* The name of data.
|
|
271
|
+
* This option will be used in tooltip and legend to represent this graphical item.
|
|
272
|
+
* If no value was set to this option, the value of dataKey will be used alternatively.
|
|
273
|
+
*/
|
|
274
|
+
name?: string;
|
|
275
|
+
/**
|
|
276
|
+
* This component is rendered when this graphical item is activated
|
|
277
|
+
* (could be by mouse hover, touch, keyboard, programmatically).
|
|
278
|
+
*
|
|
279
|
+
* @see {@link https://recharts.github.io/en-US/examples/SimpleScatterChart/ Scatter chart with custom active shape}
|
|
280
|
+
* @example <Scatter activeShape={{ fill: 'red' }} />
|
|
281
|
+
*/
|
|
282
|
+
activeShape?: ScatterCustomizedShape;
|
|
283
|
+
/**
|
|
284
|
+
* Determines the shape of individual data points.
|
|
285
|
+
* - Can be one of the predefined shapes as a string, which will be passed to {@link Symbols} component.
|
|
286
|
+
* - If set a ReactElement, the shape of point can be customized.
|
|
287
|
+
* - If set a function, the function will be called to render customized shape.
|
|
288
|
+
* - During animations, the function shape also receives `animationElapsedTime`, `isAnimating`, and `isEntrance`.
|
|
289
|
+
* @defaultValue circle
|
|
290
|
+
*
|
|
291
|
+
* @example <Scatter shape={CustomizedShapeComponent} />
|
|
292
|
+
* @example <Scatter shape="diamond" />
|
|
293
|
+
*
|
|
294
|
+
* @see {@link https://recharts.github.io/en-US/examples/JointLineScatterChart/ Scatter chart with custom shapes}
|
|
295
|
+
*/
|
|
296
|
+
shape?: ScatterCustomizedShape;
|
|
297
|
+
/**
|
|
298
|
+
* Hides the whole graphical element when true.
|
|
299
|
+
*
|
|
300
|
+
* Hiding an element is different from removing it from the chart:
|
|
301
|
+
* Hidden graphical elements are still visible in Legend,
|
|
302
|
+
* and can be included in axis domain calculations,
|
|
303
|
+
* depending on `includeHidden` props of your XAxis/YAxis.
|
|
304
|
+
*
|
|
305
|
+
* @defaultValue false
|
|
306
|
+
*/
|
|
307
|
+
hide?: boolean;
|
|
308
|
+
/**
|
|
309
|
+
* Renders one label for each data point. Options:
|
|
310
|
+
* - `true`: renders default labels;
|
|
311
|
+
* - `false`: no labels are rendered;
|
|
312
|
+
* - `object`: the props of LabelList component;
|
|
313
|
+
* - `ReactElement`: a custom SVG label element, such as `<text>` or `<g>`.
|
|
314
|
+
* HTML elements such as `<div>` are not valid inside the chart SVG and may trigger React DOM warnings.
|
|
315
|
+
* - `function`: a render function of custom label.
|
|
316
|
+
*
|
|
317
|
+
* @defaultValue false
|
|
318
|
+
*/
|
|
319
|
+
label?: ImplicitLabelListType;
|
|
320
|
+
/**
|
|
321
|
+
* If set false, animation of Scatter points will be disabled.
|
|
322
|
+
* If set "auto", the animation will be disabled in SSR and will respect the user's prefers-reduced-motion system preference for accessibility.
|
|
323
|
+
* @defaultValue 'auto'
|
|
324
|
+
*/
|
|
325
|
+
isAnimationActive?: boolean | 'auto';
|
|
326
|
+
/**
|
|
327
|
+
* Specifies when the animation should begin, the unit of this option is ms.
|
|
328
|
+
* @defaultValue 0
|
|
329
|
+
*/
|
|
330
|
+
animationBegin?: number;
|
|
331
|
+
/**
|
|
332
|
+
* Specifies the duration of animation, the unit of this option is ms.
|
|
333
|
+
* @defaultValue 400
|
|
334
|
+
*/
|
|
335
|
+
animationDuration?: AnimationDuration;
|
|
336
|
+
/**
|
|
337
|
+
* The type of easing function.
|
|
338
|
+
* @defaultValue 'linear'
|
|
339
|
+
*/
|
|
340
|
+
animationEasing?: EasingInput;
|
|
341
|
+
/**
|
|
342
|
+
* Custom animation function for interpolating data items.
|
|
343
|
+
* When provided, this replaces the default animation interpolation.
|
|
344
|
+
*
|
|
345
|
+
* @param prevItems The items from the previous animation frame, or null on first render
|
|
346
|
+
* @param nextItems The target items to animate towards
|
|
347
|
+
* @param animationElapsedTime A normalized time value (0 = start, 1 = end)
|
|
348
|
+
* @returns The interpolated items at time animationElapsedTime
|
|
349
|
+
*
|
|
350
|
+
* @since 3.9
|
|
351
|
+
* @see {@link https://recharts.github.io/en-US/guide/animations/ Animations guide}
|
|
352
|
+
*/
|
|
353
|
+
animationInterpolateFn?: AnimationInterpolateFn<ScatterPointItem, CartesianLayout>;
|
|
354
|
+
/**
|
|
355
|
+
* Strategy for matching previous items to next items during animation.
|
|
356
|
+
* Determines how Recharts pairs old data points with new data points
|
|
357
|
+
* to create smooth transitions.
|
|
358
|
+
*
|
|
359
|
+
* - `matchAppend` (default): match sequentially by index and treat newly appended items as new
|
|
360
|
+
* - `matchByIndex`: match by array position with proportional stretching
|
|
361
|
+
* - `matchByDataKey('someKey')`: match by a data key from the payload
|
|
362
|
+
* - Custom function `(item, index) => key`: match by the returned key
|
|
363
|
+
*
|
|
364
|
+
* @defaultValue append
|
|
365
|
+
* @see matchByIndex
|
|
366
|
+
* @see matchByDataKey
|
|
367
|
+
* @see matchAppend
|
|
368
|
+
*
|
|
369
|
+
* @since 3.9
|
|
370
|
+
* @see {@link https://recharts.github.io/en-US/guide/animations/ Animations guide}
|
|
371
|
+
*/
|
|
372
|
+
animationMatchBy?: AnimationMatchByProp<ScatterPointItem>;
|
|
373
|
+
/**
|
|
374
|
+
* Called when the Scatter animation starts.
|
|
375
|
+
*/
|
|
376
|
+
onAnimationStart?: () => void;
|
|
377
|
+
/**
|
|
378
|
+
* Called when the Scatter animation finishes.
|
|
379
|
+
*/
|
|
380
|
+
onAnimationEnd?: () => void;
|
|
381
|
+
/**
|
|
382
|
+
* Z-Index of this component and its children. The higher the value,
|
|
383
|
+
* the more on top it will be rendered.
|
|
384
|
+
* Components with higher zIndex will appear in front of components with lower zIndex.
|
|
385
|
+
* If undefined or 0, the content is rendered in the default layer without portals.
|
|
386
|
+
*
|
|
387
|
+
* @since 3.4
|
|
388
|
+
* @defaultValue 600
|
|
389
|
+
* @see {@link https://recharts.github.io/en-US/guide/zIndex/ Z-Index and layers guide}
|
|
390
|
+
*/
|
|
391
|
+
zIndex?: number;
|
|
392
|
+
children?: ReactNode;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Because of naming conflict, we are forced to ignore certain (valid) SVG attributes.
|
|
397
|
+
*/
|
|
398
|
+
type BaseScatterSvgProps =
|
|
399
|
+
Omit<
|
|
400
|
+
PresentationAttributesAdaptChildEvent<ScatterPointItem, SVGGraphicsElement>,
|
|
401
|
+
'points' | 'ref' | 'children' | 'dangerouslySetInnerHTML'
|
|
402
|
+
>;
|
|
403
|
+
|
|
404
|
+
type InternalProps = BaseScatterSvgProps & ScatterInternalProps;
|
|
405
|
+
|
|
406
|
+
export type Props<DataPointType = any, ValueAxisType = any> =
|
|
407
|
+
BaseScatterSvgProps & ScatterProps<DataPointType, ValueAxisType>;
|
|
408
|
+
|
|
409
|
+
const computeLegendPayloadFromScatterProps = (props: Props): ReadonlyArray<LegendPayload> => {
|
|
410
|
+
const { dataKey, name, fill, legendType, hide } = props;
|
|
411
|
+
return [
|
|
412
|
+
{
|
|
413
|
+
inactive: hide,
|
|
414
|
+
dataKey,
|
|
415
|
+
type: legendType,
|
|
416
|
+
color: fill,
|
|
417
|
+
value: getTooltipNameProp(name, dataKey),
|
|
418
|
+
payload: props,
|
|
419
|
+
},
|
|
420
|
+
];
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
type InputRequiredToComputeTooltipEntrySettings = {
|
|
424
|
+
dataKey?: DataKey<any> | undefined;
|
|
425
|
+
points?: ReadonlyArray<ScatterPointItem>;
|
|
426
|
+
stroke?: string;
|
|
427
|
+
strokeWidth?: number | string;
|
|
428
|
+
fill?: string;
|
|
429
|
+
name?: string;
|
|
430
|
+
hide?: boolean;
|
|
431
|
+
formatter?: Formatter;
|
|
432
|
+
tooltipType?: TooltipType;
|
|
433
|
+
id: GraphicalItemId;
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const SetScatterTooltipEntrySettings = React.memo(({
|
|
437
|
+
dataKey,
|
|
438
|
+
points,
|
|
439
|
+
stroke,
|
|
440
|
+
strokeWidth,
|
|
441
|
+
fill,
|
|
442
|
+
name,
|
|
443
|
+
hide,
|
|
444
|
+
formatter,
|
|
445
|
+
tooltipType,
|
|
446
|
+
id,
|
|
447
|
+
}: InputRequiredToComputeTooltipEntrySettings) => {
|
|
448
|
+
const tooltipEntrySettings: TooltipPayloadConfiguration = {
|
|
449
|
+
dataDefinedOnItem: points?.map((p: ScatterPointItem) => p.tooltipPayload),
|
|
450
|
+
getPosition: (index) => points?.[Number(index)]?.tooltipPosition,
|
|
451
|
+
settings: {
|
|
452
|
+
stroke,
|
|
453
|
+
strokeWidth,
|
|
454
|
+
fill,
|
|
455
|
+
nameKey: undefined,
|
|
456
|
+
dataKey,
|
|
457
|
+
name: getTooltipNameProp(name, dataKey),
|
|
458
|
+
hide,
|
|
459
|
+
type: tooltipType,
|
|
460
|
+
color: fill,
|
|
461
|
+
unit: '', // why doesn't Scatter support unit?
|
|
462
|
+
formatter,
|
|
463
|
+
graphicalItemId: id,
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
return <SetTooltipEntrySettings tooltipEntrySettings={tooltipEntrySettings} />;
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
type ScatterSymbolsProps =
|
|
470
|
+
ShapeAnimationProps & {
|
|
471
|
+
points: ReadonlyArray<ScatterPointItem>;
|
|
472
|
+
showLabels: boolean;
|
|
473
|
+
allOtherScatterProps: InternalProps;
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
function ScatterLine({
|
|
477
|
+
points,
|
|
478
|
+
props,
|
|
479
|
+
}: {
|
|
480
|
+
points: NonEmptyArray<ScatterPointItem>;
|
|
481
|
+
props: WithoutId<InternalProps>;
|
|
482
|
+
}) {
|
|
483
|
+
const { line, lineType, lineJointType } = props;
|
|
484
|
+
|
|
485
|
+
if (!line) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const scatterProps = svgPropertiesNoEvents(props);
|
|
490
|
+
const customLineProps = svgPropertiesNoEventsFromUnknown(line);
|
|
491
|
+
let linePoints: ReadonlyArray<NullableCoordinate>, lineItem: ReactNode;
|
|
492
|
+
|
|
493
|
+
if (lineType === 'joint') {
|
|
494
|
+
linePoints = points.map((entry) => ({ x: entry.cx ?? null, y: entry.cy ?? null }));
|
|
495
|
+
} else if (lineType === 'fitting') {
|
|
496
|
+
const { xmin, xmax, a, b } = getLinearRegression(points);
|
|
497
|
+
const linearExp = (x: number) => a * x + b;
|
|
498
|
+
linePoints = [
|
|
499
|
+
{ x: xmin, y: linearExp(xmin) },
|
|
500
|
+
{ x: xmax, y: linearExp(xmax) },
|
|
501
|
+
];
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const lineProps: CurveProps = {
|
|
505
|
+
...scatterProps,
|
|
506
|
+
// @ts-expect-error customLineProps is contributing unknown props
|
|
507
|
+
fill: 'none',
|
|
508
|
+
// @ts-expect-error customLineProps is contributing unknown props
|
|
509
|
+
stroke: scatterProps && scatterProps.fill,
|
|
510
|
+
...customLineProps,
|
|
511
|
+
// @ts-expect-error linePoints is used before it is assigned (???)
|
|
512
|
+
points: linePoints,
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
if (React.isValidElement(line)) {
|
|
516
|
+
lineItem = React.cloneElement(line as any, lineProps);
|
|
517
|
+
} else if (typeof line === 'function') {
|
|
518
|
+
lineItem = line(lineProps);
|
|
519
|
+
} else {
|
|
520
|
+
lineItem = <Curve {...lineProps} type={lineJointType} />;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return <Layer className="recharts-scatter-line" key="recharts-scatter-line">{lineItem}</Layer>;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function ScatterLabelListProvider({
|
|
527
|
+
showLabels,
|
|
528
|
+
points,
|
|
529
|
+
children,
|
|
530
|
+
}: {
|
|
531
|
+
showLabels: boolean;
|
|
532
|
+
points: ReadonlyArray<ScatterPointItem>;
|
|
533
|
+
children: ReactNode;
|
|
534
|
+
}) {
|
|
535
|
+
const chartViewBox = useViewBox();
|
|
536
|
+
const labelListEntries: ReadonlyArray<CartesianLabelListEntry> = useMemo(() => {
|
|
537
|
+
return points?.map((point): CartesianLabelListEntry => {
|
|
538
|
+
const viewBox: TrapezoidViewBox = {
|
|
539
|
+
/*
|
|
540
|
+
* Scatter label uses x and y as the reference point for the label,
|
|
541
|
+
* not cx and cy.
|
|
542
|
+
*/
|
|
543
|
+
x: point.x ?? 0,
|
|
544
|
+
/*
|
|
545
|
+
* Scatter label uses x and y as the reference point for the label,
|
|
546
|
+
* not cx and cy.
|
|
547
|
+
*/
|
|
548
|
+
y: point.y ?? 0,
|
|
549
|
+
width: point.width,
|
|
550
|
+
height: point.height,
|
|
551
|
+
lowerWidth: point.width,
|
|
552
|
+
upperWidth: point.width,
|
|
553
|
+
};
|
|
554
|
+
return {
|
|
555
|
+
...viewBox,
|
|
556
|
+
/*
|
|
557
|
+
* Here we put undefined because Scatter shows two values usually, one for X and one for Y.
|
|
558
|
+
* LabelList will see this undefined and will use its own `dataKey` prop to determine which value to show,
|
|
559
|
+
* using the payload below.
|
|
560
|
+
*/
|
|
561
|
+
value: undefined,
|
|
562
|
+
payload: point.payload,
|
|
563
|
+
viewBox,
|
|
564
|
+
parentViewBox: chartViewBox,
|
|
565
|
+
fill: undefined,
|
|
566
|
+
};
|
|
567
|
+
});
|
|
568
|
+
}, [chartViewBox, points]);
|
|
569
|
+
|
|
570
|
+
return <CartesianLabelListContextProvider
|
|
571
|
+
value={showLabels ? labelListEntries : undefined}
|
|
572
|
+
>{children}</CartesianLabelListContextProvider>;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Individual scatter point component that subscribes to its own isActive state.
|
|
577
|
+
* This avoids re-rendering all points when the active index changes —
|
|
578
|
+
* only the point becoming active and the point becoming inactive re-render.
|
|
579
|
+
*
|
|
580
|
+
* @param entry The scatter point data including coordinates, size, and tooltip payload
|
|
581
|
+
* @param index The index of this point in the points array
|
|
582
|
+
* @param shape The default shape to render for inactive points
|
|
583
|
+
* @param activeShape The shape to render when this point is active, or undefined if no active shape
|
|
584
|
+
* @param baseProps SVG presentation attributes (fill, stroke, etc.) shared across all points
|
|
585
|
+
* @param id The graphical item ID of the parent Scatter component
|
|
586
|
+
* @param restOfAllOtherProps Remaining Scatter props for user-provided event handlers via adaptEventsOfChild
|
|
587
|
+
* @param onMouseEnterFromContext Curried mouse enter handler that dispatches tooltip activation
|
|
588
|
+
* @param onMouseLeaveFromContext Curried mouse leave handler that dispatches tooltip deactivation
|
|
589
|
+
* @param onClickFromContext Curried click handler that dispatches tooltip click activation
|
|
590
|
+
*/
|
|
591
|
+
function ScatterPoint({
|
|
592
|
+
entry,
|
|
593
|
+
index,
|
|
594
|
+
shape,
|
|
595
|
+
activeShape,
|
|
596
|
+
baseProps,
|
|
597
|
+
id,
|
|
598
|
+
restOfAllOtherProps,
|
|
599
|
+
animationElapsedTime,
|
|
600
|
+
isAnimating,
|
|
601
|
+
isEntrance,
|
|
602
|
+
onMouseEnterFromContext,
|
|
603
|
+
onMouseLeaveFromContext,
|
|
604
|
+
onClickFromContext,
|
|
605
|
+
}: ShapeAnimationProps & {
|
|
606
|
+
entry: ScatterPointItem;
|
|
607
|
+
index: number;
|
|
608
|
+
shape: ScatterCustomizedShape;
|
|
609
|
+
activeShape: ScatterCustomizedShape | undefined;
|
|
610
|
+
baseProps: ReturnType<typeof svgPropertiesNoEvents>;
|
|
611
|
+
id: GraphicalItemId;
|
|
612
|
+
restOfAllOtherProps: InternalProps;
|
|
613
|
+
onMouseEnterFromContext: (data: ScatterPointItem, index: number) => (event: React.MouseEvent<
|
|
614
|
+
SVGGraphicsElement
|
|
615
|
+
>) => void;
|
|
616
|
+
onMouseLeaveFromContext: (data: ScatterPointItem, index: number) => (event: React.MouseEvent<
|
|
617
|
+
SVGGraphicsElement
|
|
618
|
+
>) => void;
|
|
619
|
+
onClickFromContext: (data: ScatterPointItem, index: number) => (event: React.MouseEvent<
|
|
620
|
+
SVGGraphicsElement
|
|
621
|
+
>) => void;
|
|
622
|
+
}): React.ReactElement {
|
|
623
|
+
const hasActiveShape = activeShape != null && activeShape !== false;
|
|
624
|
+
const selectIsActive = useMemo(() => {
|
|
625
|
+
const strIndex = String(index);
|
|
626
|
+
return (state: RechartsRootState): boolean =>
|
|
627
|
+
hasActiveShape && selectActiveTooltipIndex(state) === strIndex;
|
|
628
|
+
}, [hasActiveShape, index]);
|
|
629
|
+
const isActive: boolean = useAppSelector(selectIsActive) ?? false;
|
|
630
|
+
|
|
631
|
+
// isActive is only true when hasActiveShape is true, so activeShape is defined here
|
|
632
|
+
const option =
|
|
633
|
+
isActive && activeShape != null && activeShape !== false ? activeShape : shape;
|
|
634
|
+
const symbolProps: ScatterShapeProps = {
|
|
635
|
+
...baseProps,
|
|
636
|
+
...entry,
|
|
637
|
+
isActive,
|
|
638
|
+
index,
|
|
639
|
+
animationElapsedTime,
|
|
640
|
+
isAnimating,
|
|
641
|
+
isEntrance,
|
|
642
|
+
[DATA_ITEM_GRAPHICAL_ITEM_ID_ATTRIBUTE_NAME]: String(id),
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
return <ZIndexLayer
|
|
646
|
+
/*
|
|
647
|
+
* inactive Scatters use the parent zIndex, which is represented by undefined here.
|
|
648
|
+
* ZIndexLayer will render undefined zIndex as-is, as regular children, without portals.
|
|
649
|
+
* Active Scatters use the activeDot zIndex so they render above other elements.
|
|
650
|
+
*/
|
|
651
|
+
zIndex={isActive ? DefaultZIndexes.activeDot : undefined}
|
|
652
|
+
>
|
|
653
|
+
<Layer
|
|
654
|
+
className="recharts-scatter-symbol"
|
|
655
|
+
{...adaptEventsOfChild(restOfAllOtherProps, entry, index)}
|
|
656
|
+
onMouseEnter={onMouseEnterFromContext(entry, index)}
|
|
657
|
+
onMouseLeave={onMouseLeaveFromContext(entry, index)}
|
|
658
|
+
onClick={onClickFromContext(entry, index)}
|
|
659
|
+
>
|
|
660
|
+
<ScatterSymbol option={option} {...symbolProps} />
|
|
661
|
+
</Layer>
|
|
662
|
+
</ZIndexLayer>;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function ScatterSymbols(props: ScatterSymbolsProps) {
|
|
666
|
+
const { points, allOtherScatterProps, animationElapsedTime, isAnimating, isEntrance } = props;
|
|
667
|
+
const { shape, activeShape, dataKey } = allOtherScatterProps;
|
|
668
|
+
const { id, ...allOtherPropsWithoutId } = allOtherScatterProps;
|
|
669
|
+
|
|
670
|
+
const {
|
|
671
|
+
onMouseEnter: onMouseEnterFromProps,
|
|
672
|
+
onClick: onItemClickFromProps,
|
|
673
|
+
onMouseLeave: onMouseLeaveFromProps,
|
|
674
|
+
...restOfAllOtherProps
|
|
675
|
+
} = allOtherScatterProps;
|
|
676
|
+
|
|
677
|
+
const onMouseEnterFromContext = useMouseEnterItemDispatch(onMouseEnterFromProps, dataKey, id);
|
|
678
|
+
const onMouseLeaveFromContext = useMouseLeaveItemDispatch(onMouseLeaveFromProps);
|
|
679
|
+
const onClickFromContext = useMouseClickItemDispatch(onItemClickFromProps, dataKey, id);
|
|
680
|
+
if (!isNonEmptyArray(points)) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const baseProps = svgPropertiesNoEvents(allOtherPropsWithoutId);
|
|
685
|
+
|
|
686
|
+
return <>
|
|
687
|
+
<ScatterLine points={points} props={allOtherPropsWithoutId} />
|
|
688
|
+
{points.map(
|
|
689
|
+
(entry: ScatterPointItem, i: number) =>
|
|
690
|
+
<ScatterPoint
|
|
691
|
+
key={`symbol-${entry?.cx}-${entry?.cy}-${entry?.size}-${i}`}
|
|
692
|
+
entry={entry}
|
|
693
|
+
index={i}
|
|
694
|
+
shape={shape}
|
|
695
|
+
activeShape={activeShape}
|
|
696
|
+
baseProps={baseProps}
|
|
697
|
+
id={id}
|
|
698
|
+
restOfAllOtherProps={restOfAllOtherProps}
|
|
699
|
+
animationElapsedTime={animationElapsedTime}
|
|
700
|
+
isAnimating={isAnimating}
|
|
701
|
+
isEntrance={isEntrance}
|
|
702
|
+
onMouseEnterFromContext={onMouseEnterFromContext}
|
|
703
|
+
onMouseLeaveFromContext={onMouseLeaveFromContext}
|
|
704
|
+
onClickFromContext={onClickFromContext}
|
|
705
|
+
/>,
|
|
706
|
+
)}
|
|
707
|
+
</>;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const defaultScatterAnimateItems: AnimationInterpolateFn<ScatterPointItem, CartesianLayout> = (
|
|
711
|
+
items,
|
|
712
|
+
animationElapsedTime,
|
|
713
|
+
) => {
|
|
714
|
+
if (items == null) return [];
|
|
715
|
+
if (animationElapsedTime === 1) {
|
|
716
|
+
return items.flatMap((item) => (item.status === 'removed' ? [] : [item.next]));
|
|
717
|
+
}
|
|
718
|
+
return items.flatMap((item) => {
|
|
719
|
+
if (item.status === 'removed') return [];
|
|
720
|
+
if (item.status === 'matched') {
|
|
721
|
+
return [
|
|
722
|
+
{
|
|
723
|
+
...item.next,
|
|
724
|
+
cx: item.next.cx == null
|
|
725
|
+
? undefined
|
|
726
|
+
: interpolate(item.prev.cx, item.next.cx, animationElapsedTime),
|
|
727
|
+
cy: item.next.cy == null
|
|
728
|
+
? undefined
|
|
729
|
+
: interpolate(item.prev.cy, item.next.cy, animationElapsedTime),
|
|
730
|
+
size: interpolate(item.prev.size, item.next.size, animationElapsedTime),
|
|
731
|
+
},
|
|
732
|
+
];
|
|
733
|
+
}
|
|
734
|
+
// added
|
|
735
|
+
return [
|
|
736
|
+
{ ...item.next, size: interpolate(0, item.next.size, animationElapsedTime) },
|
|
737
|
+
];
|
|
738
|
+
});
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
function SymbolsWithAnimation({
|
|
742
|
+
previousPointsRef,
|
|
743
|
+
props,
|
|
744
|
+
}: {
|
|
745
|
+
previousPointsRef: MutableRefObject<ReadonlyArray<ScatterPointItem> | null>;
|
|
746
|
+
props: InternalProps;
|
|
747
|
+
}) {
|
|
748
|
+
const {
|
|
749
|
+
points,
|
|
750
|
+
isAnimationActive,
|
|
751
|
+
animationBegin,
|
|
752
|
+
animationDuration,
|
|
753
|
+
animationEasing,
|
|
754
|
+
animationInterpolateFn,
|
|
755
|
+
onAnimationStart,
|
|
756
|
+
onAnimationEnd,
|
|
757
|
+
} = props;
|
|
758
|
+
|
|
759
|
+
const { isAnimating, handleAnimationStart, handleAnimationEnd } = useAnimationCallbacks(
|
|
760
|
+
onAnimationStart,
|
|
761
|
+
onAnimationEnd,
|
|
762
|
+
);
|
|
763
|
+
const layout = useCartesianChartLayout();
|
|
764
|
+
|
|
765
|
+
if (layout == null) return null;
|
|
766
|
+
|
|
767
|
+
return <ScatterLabelListProvider showLabels={!isAnimating} points={points}>
|
|
768
|
+
<AnimatedItems
|
|
769
|
+
animationInput={points}
|
|
770
|
+
animationIdPrefix="recharts-scatter-"
|
|
771
|
+
items={points}
|
|
772
|
+
previousItemsRef={previousPointsRef}
|
|
773
|
+
isAnimationActive={isAnimationActive}
|
|
774
|
+
animationBegin={animationBegin}
|
|
775
|
+
animationDuration={animationDuration}
|
|
776
|
+
animationEasing={animationEasing}
|
|
777
|
+
onAnimationStart={handleAnimationStart}
|
|
778
|
+
onAnimationEnd={handleAnimationEnd}
|
|
779
|
+
animationInterpolateFn={animationInterpolateFn}
|
|
780
|
+
animationMatchBy={props.animationMatchBy}
|
|
781
|
+
layout={layout}
|
|
782
|
+
>
|
|
783
|
+
{(stepData, animationElapsedTime, isEntrance) => <Layer>
|
|
784
|
+
<ScatterSymbols
|
|
785
|
+
points={stepData}
|
|
786
|
+
allOtherScatterProps={props}
|
|
787
|
+
showLabels={!isAnimating}
|
|
788
|
+
animationElapsedTime={animationElapsedTime}
|
|
789
|
+
isAnimating={isAnimating || animationElapsedTime < 1}
|
|
790
|
+
isEntrance={isEntrance}
|
|
791
|
+
/>
|
|
792
|
+
</Layer>}
|
|
793
|
+
</AnimatedItems>
|
|
794
|
+
{props.children}
|
|
795
|
+
<LabelListFromLabelProp label={props.label} />
|
|
796
|
+
</ScatterLabelListProvider>;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
export function computeScatterPoints({
|
|
800
|
+
displayedData,
|
|
801
|
+
xAxis,
|
|
802
|
+
yAxis,
|
|
803
|
+
zAxis,
|
|
804
|
+
scatterSettings,
|
|
805
|
+
xAxisTicks,
|
|
806
|
+
yAxisTicks,
|
|
807
|
+
cells,
|
|
808
|
+
}: {
|
|
809
|
+
displayedData: ReadonlyArray<unknown>;
|
|
810
|
+
xAxis: BaseAxisWithScale;
|
|
811
|
+
yAxis: BaseAxisWithScale;
|
|
812
|
+
zAxis: ZAxisWithScale | undefined;
|
|
813
|
+
scatterSettings: ScatterSettings;
|
|
814
|
+
xAxisTicks: ReadonlyArray<TickItem> | undefined;
|
|
815
|
+
yAxisTicks: ReadonlyArray<TickItem> | undefined;
|
|
816
|
+
cells: ReadonlyArray<ReactElement> | undefined;
|
|
817
|
+
}): ReadonlyArray<ScatterPointItem> {
|
|
818
|
+
const xAxisDataKey = isNullish(xAxis.dataKey) ? scatterSettings.dataKey : xAxis.dataKey;
|
|
819
|
+
const yAxisDataKey = isNullish(yAxis.dataKey) ? scatterSettings.dataKey : yAxis.dataKey;
|
|
820
|
+
const zAxisDataKey = zAxis && zAxis.dataKey;
|
|
821
|
+
const defaultRangeZ = zAxis ? zAxis.range : implicitZAxis.range;
|
|
822
|
+
const defaultZ = defaultRangeZ && defaultRangeZ[0];
|
|
823
|
+
const xBandSize =
|
|
824
|
+
xAxis.scale.bandwidth ? xAxis.scale.bandwidth() : 0;
|
|
825
|
+
const yBandSize =
|
|
826
|
+
yAxis.scale.bandwidth ? yAxis.scale.bandwidth() : 0;
|
|
827
|
+
return displayedData.map((entry: unknown, index): ScatterPointItem => {
|
|
828
|
+
const x: unknown = getValueByDataKey(entry, xAxisDataKey);
|
|
829
|
+
const y: unknown = getValueByDataKey(entry, yAxisDataKey);
|
|
830
|
+
const z: unknown = isNullish(zAxisDataKey)
|
|
831
|
+
? '-'
|
|
832
|
+
: getValueByDataKey(entry, zAxisDataKey) ?? '-';
|
|
833
|
+
|
|
834
|
+
const tooltipPayload: Array<TooltipPayloadEntry> = [
|
|
835
|
+
{
|
|
836
|
+
name: isNullish(xAxis.dataKey) ? scatterSettings.name : xAxis.name || String(xAxis.dataKey),
|
|
837
|
+
unit: xAxis.unit || '',
|
|
838
|
+
// @ts-expect-error getValueByDataKey does not validate the output type
|
|
839
|
+
value: x,
|
|
840
|
+
payload: entry,
|
|
841
|
+
dataKey: xAxisDataKey,
|
|
842
|
+
type: scatterSettings.tooltipType,
|
|
843
|
+
graphicalItemId: scatterSettings.id,
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
name: isNullish(yAxis.dataKey) ? scatterSettings.name : yAxis.name || String(yAxis.dataKey),
|
|
847
|
+
unit: yAxis.unit || '',
|
|
848
|
+
// @ts-expect-error getValueByDataKey does not validate the output type
|
|
849
|
+
value: y,
|
|
850
|
+
payload: entry,
|
|
851
|
+
dataKey: yAxisDataKey,
|
|
852
|
+
type: scatterSettings.tooltipType,
|
|
853
|
+
graphicalItemId: scatterSettings.id,
|
|
854
|
+
},
|
|
855
|
+
];
|
|
856
|
+
|
|
857
|
+
if (z !== '-' && zAxis != null) {
|
|
858
|
+
tooltipPayload.push({
|
|
859
|
+
// @ts-expect-error name prop should not have dataKey in it
|
|
860
|
+
name: zAxis.name || zAxis.dataKey,
|
|
861
|
+
unit: zAxis.unit || '',
|
|
862
|
+
// @ts-expect-error getValueByDataKey does not validate the output type
|
|
863
|
+
value: z,
|
|
864
|
+
payload: entry,
|
|
865
|
+
dataKey: zAxisDataKey,
|
|
866
|
+
type: scatterSettings.tooltipType,
|
|
867
|
+
graphicalItemId: scatterSettings.id,
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const cx: number | null = getCateCoordinateOfLine({
|
|
872
|
+
axis: xAxis,
|
|
873
|
+
ticks: xAxisTicks,
|
|
874
|
+
bandSize: xBandSize,
|
|
875
|
+
entry,
|
|
876
|
+
index,
|
|
877
|
+
dataKey: xAxisDataKey,
|
|
878
|
+
});
|
|
879
|
+
const cy = getCateCoordinateOfLine({
|
|
880
|
+
axis: yAxis,
|
|
881
|
+
ticks: yAxisTicks,
|
|
882
|
+
bandSize: yBandSize,
|
|
883
|
+
entry,
|
|
884
|
+
index,
|
|
885
|
+
dataKey: yAxisDataKey,
|
|
886
|
+
});
|
|
887
|
+
const size =
|
|
888
|
+
z !== '-' && zAxis != null ? zAxis.scale.map(z) : defaultZ;
|
|
889
|
+
const radius =
|
|
890
|
+
size == null ? 0 : Math.sqrt(Math.max(size, 0) / Math.PI);
|
|
891
|
+
|
|
892
|
+
return {
|
|
893
|
+
// @ts-expect-error can't spread unknown
|
|
894
|
+
...entry,
|
|
895
|
+
cx,
|
|
896
|
+
cy,
|
|
897
|
+
x: cx == null ? undefined : cx - radius,
|
|
898
|
+
y: cy == null ? undefined : cy - radius,
|
|
899
|
+
width: 2 * radius,
|
|
900
|
+
height: 2 * radius,
|
|
901
|
+
size,
|
|
902
|
+
node: { x, y, z },
|
|
903
|
+
tooltipPayload,
|
|
904
|
+
tooltipPosition: { x: cx, y: cy },
|
|
905
|
+
payload: entry,
|
|
906
|
+
...cells && cells[index] && cells[index].props,
|
|
907
|
+
};
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
const errorBarDataPointFormatter: ErrorBarDataPointFormatter<ScatterPointItem> = (
|
|
912
|
+
dataPoint: ScatterPointItem,
|
|
913
|
+
dataKey: Props['dataKey'],
|
|
914
|
+
direction: ErrorBarDirection,
|
|
915
|
+
): ErrorBarDataItem => {
|
|
916
|
+
return {
|
|
917
|
+
x: dataPoint.cx,
|
|
918
|
+
y: dataPoint.cy,
|
|
919
|
+
value: direction === 'x' ? Number(dataPoint.node.x) : Number(dataPoint.node.y),
|
|
920
|
+
// @ts-expect-error getValueByDataKey does not validate the output type
|
|
921
|
+
errorVal: getValueByDataKey(dataPoint, dataKey),
|
|
922
|
+
};
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
function ScatterWithId(props: InternalProps) {
|
|
926
|
+
const { hide, points, className, needClip, xAxisId, yAxisId, id } = props;
|
|
927
|
+
const previousPointsRef = useRef<ReadonlyArray<ScatterPointItem> | null>(null);
|
|
928
|
+
if (hide) {
|
|
929
|
+
return props.children;
|
|
930
|
+
}
|
|
931
|
+
const layerClass = clsx('recharts-scatter', className);
|
|
932
|
+
const clipPathId = id;
|
|
933
|
+
|
|
934
|
+
return <ZIndexLayer zIndex={props.zIndex}>
|
|
935
|
+
<Layer
|
|
936
|
+
className={layerClass}
|
|
937
|
+
clipPath={needClip ? `url(#clipPath-${clipPathId})` : undefined}
|
|
938
|
+
id={id}
|
|
939
|
+
>
|
|
940
|
+
{needClip &&
|
|
941
|
+
<defs>
|
|
942
|
+
<GraphicalItemClipPath clipPathId={clipPathId} xAxisId={xAxisId} yAxisId={yAxisId} />
|
|
943
|
+
</defs>}
|
|
944
|
+
<SetErrorBarContext
|
|
945
|
+
xAxisId={xAxisId}
|
|
946
|
+
yAxisId={yAxisId}
|
|
947
|
+
data={points}
|
|
948
|
+
dataPointFormatter={errorBarDataPointFormatter}
|
|
949
|
+
errorBarOffset={0}
|
|
950
|
+
>
|
|
951
|
+
<Layer key="recharts-scatter-symbols">
|
|
952
|
+
<SymbolsWithAnimation props={props} previousPointsRef={previousPointsRef} />
|
|
953
|
+
</Layer>
|
|
954
|
+
</SetErrorBarContext>
|
|
955
|
+
</Layer>
|
|
956
|
+
</ZIndexLayer>;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
export const defaultScatterProps = {
|
|
960
|
+
xAxisId: 0,
|
|
961
|
+
yAxisId: 0,
|
|
962
|
+
zAxisId: 0,
|
|
963
|
+
label: false,
|
|
964
|
+
line: false,
|
|
965
|
+
legendType: 'circle',
|
|
966
|
+
lineType: 'joint',
|
|
967
|
+
lineJointType: 'linear',
|
|
968
|
+
shape: 'circle',
|
|
969
|
+
hide: false,
|
|
970
|
+
|
|
971
|
+
isAnimationActive: 'auto',
|
|
972
|
+
animationBegin: 0,
|
|
973
|
+
animationDuration: 400,
|
|
974
|
+
animationEasing: 'linear',
|
|
975
|
+
animationMatchBy: matchAppend,
|
|
976
|
+
animationInterpolateFn: defaultScatterAnimateItems,
|
|
977
|
+
zIndex: DefaultZIndexes.scatter,
|
|
978
|
+
} as const satisfies Partial<Props>;
|
|
979
|
+
|
|
980
|
+
function ScatterImpl(props: WithIdRequired<Props>) {
|
|
981
|
+
const {
|
|
982
|
+
animationBegin,
|
|
983
|
+
animationDuration,
|
|
984
|
+
animationEasing,
|
|
985
|
+
hide,
|
|
986
|
+
isAnimationActive,
|
|
987
|
+
legendType,
|
|
988
|
+
lineJointType,
|
|
989
|
+
lineType,
|
|
990
|
+
shape,
|
|
991
|
+
xAxisId,
|
|
992
|
+
yAxisId,
|
|
993
|
+
zAxisId,
|
|
994
|
+
...everythingElse
|
|
995
|
+
} = resolveDefaultProps(props, defaultScatterProps);
|
|
996
|
+
|
|
997
|
+
const { needClip } = useNeedsClip(xAxisId, yAxisId);
|
|
998
|
+
const [cells, cellRegistry] = useCellRegistry();
|
|
999
|
+
const isPanorama = useIsPanorama();
|
|
1000
|
+
|
|
1001
|
+
const points = useAppSelector((state) => {
|
|
1002
|
+
return selectScatterPoints(state, xAxisId, yAxisId, zAxisId, props.id, cells, isPanorama);
|
|
1003
|
+
});
|
|
1004
|
+
if (needClip == null || points == null) {
|
|
1005
|
+
return <CellsContext.Provider value={cellRegistry}>{props.children}</CellsContext.Provider>;
|
|
1006
|
+
}
|
|
1007
|
+
return <CellsContext.Provider value={cellRegistry}>
|
|
1008
|
+
<SetScatterTooltipEntrySettings
|
|
1009
|
+
dataKey={props.dataKey}
|
|
1010
|
+
points={points}
|
|
1011
|
+
stroke={props.stroke}
|
|
1012
|
+
strokeWidth={props.strokeWidth}
|
|
1013
|
+
fill={props.fill}
|
|
1014
|
+
name={props.name}
|
|
1015
|
+
hide={props.hide}
|
|
1016
|
+
formatter={props.formatter}
|
|
1017
|
+
tooltipType={props.tooltipType}
|
|
1018
|
+
id={props.id}
|
|
1019
|
+
/>
|
|
1020
|
+
<ScatterWithId
|
|
1021
|
+
{...everythingElse}
|
|
1022
|
+
xAxisId={xAxisId}
|
|
1023
|
+
yAxisId={yAxisId}
|
|
1024
|
+
zAxisId={zAxisId}
|
|
1025
|
+
lineType={lineType}
|
|
1026
|
+
lineJointType={lineJointType}
|
|
1027
|
+
legendType={legendType}
|
|
1028
|
+
shape={shape}
|
|
1029
|
+
hide={hide}
|
|
1030
|
+
isAnimationActive={isAnimationActive}
|
|
1031
|
+
animationBegin={animationBegin}
|
|
1032
|
+
animationDuration={animationDuration}
|
|
1033
|
+
animationEasing={animationEasing}
|
|
1034
|
+
points={points}
|
|
1035
|
+
needClip={needClip}
|
|
1036
|
+
/>
|
|
1037
|
+
</CellsContext.Provider>;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
function ScatterFn(outsideProps: Props) {
|
|
1041
|
+
const props = resolveDefaultProps(outsideProps, defaultScatterProps);
|
|
1042
|
+
const isPanorama = useIsPanorama();
|
|
1043
|
+
return <RegisterGraphicalItemId id={props.id} type="scatter">
|
|
1044
|
+
{(id) => <>
|
|
1045
|
+
<SetLegendPayload legendPayload={computeLegendPayloadFromScatterProps(props)} />
|
|
1046
|
+
<SetCartesianGraphicalItem
|
|
1047
|
+
type="scatter"
|
|
1048
|
+
id={id}
|
|
1049
|
+
data={props.data}
|
|
1050
|
+
xAxisId={props.xAxisId}
|
|
1051
|
+
yAxisId={props.yAxisId}
|
|
1052
|
+
zAxisId={props.zAxisId}
|
|
1053
|
+
dataKey={props.dataKey}
|
|
1054
|
+
hide={props.hide}
|
|
1055
|
+
name={props.name}
|
|
1056
|
+
tooltipType={props.tooltipType}
|
|
1057
|
+
isPanorama={isPanorama}
|
|
1058
|
+
/>
|
|
1059
|
+
<ScatterImpl {...props} id={id} />
|
|
1060
|
+
</>}
|
|
1061
|
+
</RegisterGraphicalItemId>;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* @provides LabelListContext
|
|
1066
|
+
* @provides ErrorBarContext
|
|
1067
|
+
* @provides CellReader
|
|
1068
|
+
* @consumes CartesianChartContext
|
|
1069
|
+
*/
|
|
1070
|
+
export const Scatter = React.memo(ScatterFn, propsAreEqual) as (props: Props<
|
|
1071
|
+
DataPointType,
|
|
1072
|
+
ValueAxisType
|
|
1073
|
+
>) => ReactElement;
|
|
1074
|
+
// @ts-expect-error we need to set the displayName for debugging purposes
|
|
1075
|
+
|
|
1076
|
+
Scatter.displayName = 'Scatter';
|