@octanejs/recharts 0.1.16 → 0.1.18

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 (88) hide show
  1. package/package.json +4 -4
  2. package/src/animation/CSSTransitionAnimate.tsrx +142 -0
  3. package/src/cartesian/Area.tsrx +1096 -0
  4. package/src/cartesian/Area.tsrx.d.ts +340 -0
  5. package/src/cartesian/AreaRevealShape.tsrx +202 -0
  6. package/src/cartesian/CartesianGrid.tsrx +643 -0
  7. package/src/cartesian/CartesianGrid.tsrx.d.ts +219 -0
  8. package/src/cartesian/ErrorBar.tsrx +322 -0
  9. package/src/cartesian/ErrorBar.tsrx.d.ts +131 -0
  10. package/src/cartesian/Funnel.tsrx +833 -0
  11. package/src/cartesian/Funnel.tsrx.d.ts +274 -0
  12. package/src/cartesian/ReferenceLine.tsrx +452 -0
  13. package/src/cartesian/ReferenceLine.tsrx.d.ts +178 -0
  14. package/src/cartesian/Scatter.tsrx +1076 -0
  15. package/src/cartesian/Scatter.tsrx.d.ts +351 -0
  16. package/src/cartesian/ZAxis.tsrx +148 -0
  17. package/src/cartesian/ZAxis.tsrx.d.ts +102 -0
  18. package/src/chart/AreaChart.tsrx +22 -0
  19. package/src/chart/AreaChart.tsrx.d.ts +12 -0
  20. package/src/chart/ComposedChart.tsrx +22 -0
  21. package/src/chart/ComposedChart.tsrx.d.ts +12 -0
  22. package/src/chart/FunnelChart.tsrx +22 -0
  23. package/src/chart/FunnelChart.tsrx.d.ts +12 -0
  24. package/src/chart/PieChart.tsrx +31 -0
  25. package/src/chart/PieChart.tsrx.d.ts +31 -0
  26. package/src/chart/PolarChart.tsrx +121 -0
  27. package/src/chart/RadarChart.tsrx +53 -0
  28. package/src/chart/RadarChart.tsrx.d.ts +31 -0
  29. package/src/chart/RadialBarChart.tsrx +31 -0
  30. package/src/chart/RadialBarChart.tsrx.d.ts +31 -0
  31. package/src/chart/Sankey.tsrx +1542 -0
  32. package/src/chart/Sankey.tsrx.d.ts +253 -0
  33. package/src/chart/ScatterChart.tsrx +22 -0
  34. package/src/chart/ScatterChart.tsrx.d.ts +12 -0
  35. package/src/chart/SunburstChart.tsrx +495 -0
  36. package/src/chart/SunburstChart.tsrx.d.ts +146 -0
  37. package/src/chart/types.ts +19 -0
  38. package/src/component/Cursor.tsrx +157 -0
  39. package/src/component/DefaultLegendContent.tsrx +288 -0
  40. package/src/component/DefaultLegendContent.tsrx.d.ts +125 -0
  41. package/src/component/DefaultTooltipContent.tsrx +215 -0
  42. package/src/component/DefaultTooltipContent.tsrx.d.ts +87 -0
  43. package/src/component/Legend.tsrx +302 -0
  44. package/src/component/Legend.tsrx.d.ts +88 -0
  45. package/src/component/ResponsiveContainer.ts +1 -22
  46. package/src/component/ResponsiveContainer.tsrx +171 -0
  47. package/src/component/ResponsiveContainer.tsrx.d.ts +81 -0
  48. package/src/component/Tooltip.tsrx +425 -0
  49. package/src/component/Tooltip.tsrx.d.ts +246 -0
  50. package/src/component/TooltipBoundingBox.tsrx +135 -0
  51. package/src/context/CellsContext.ts +12 -0
  52. package/src/context/legendPayloadContext.tsrx +11 -0
  53. package/src/context/useTooltipAxis.ts +20 -0
  54. package/src/index.d.ts +236 -0
  55. package/src/index.ts +67 -0
  56. package/src/polar/Pie.tsrx +1224 -0
  57. package/src/polar/Pie.tsrx.d.ts +436 -0
  58. package/src/polar/PolarAngleAxis.tsrx +554 -0
  59. package/src/polar/PolarAngleAxis.tsrx.d.ts +215 -0
  60. package/src/polar/PolarGrid.tsrx +317 -0
  61. package/src/polar/PolarGrid.tsrx.d.ts +117 -0
  62. package/src/polar/PolarRadiusAxis.tsrx +463 -0
  63. package/src/polar/PolarRadiusAxis.tsrx.d.ts +209 -0
  64. package/src/polar/Radar.tsrx +696 -0
  65. package/src/polar/Radar.tsrx.d.ts +215 -0
  66. package/src/polar/RadialBar.tsrx +887 -0
  67. package/src/polar/RadialBar.tsrx.d.ts +340 -0
  68. package/src/state/ReportPolarOptions.tsrx +11 -0
  69. package/src/state/selectors/areaSelectors.ts +257 -0
  70. package/src/state/selectors/funnelSelectors.ts +86 -0
  71. package/src/state/selectors/pieSelectors.ts +134 -0
  72. package/src/state/selectors/polarGridSelectors.ts +43 -0
  73. package/src/state/selectors/polarScaleSelectors.ts +159 -0
  74. package/src/state/selectors/polarSelectors.ts +198 -0
  75. package/src/state/selectors/radarSelectors.ts +228 -0
  76. package/src/state/selectors/radialBarSelectors.ts +511 -0
  77. package/src/state/selectors/scatterSelectors.ts +168 -0
  78. package/src/util/FunnelUtils.tsrx +19 -0
  79. package/src/util/RadialBarUtils.tsrx +29 -0
  80. package/src/util/ScatterUtils.tsrx +34 -0
  81. package/src/util/cursor/getCursorPoints.ts +45 -0
  82. package/src/util/cursor/getCursorRectangle.ts +28 -0
  83. package/src/util/cursor/getRadialCursorPoints.ts +31 -0
  84. package/src/util/payload/getUniqPayload.ts +28 -0
  85. package/src/util/scale/CartesianScaleHelper.ts +71 -0
  86. package/src/util/tooltip/translate.ts +168 -0
  87. package/src/util/types.ts +3 -0
  88. package/src/util/useElementOffset.ts +147 -0
@@ -0,0 +1,1224 @@
1
+ import * as React from 'octane';
2
+ import { MutableRefObject, ReactElement, ReactNode, SVGProps, useMemo, useRef } from 'octane';
3
+ import get from 'es-toolkit/compat/get';
4
+
5
+ import { clsx } from 'clsx';
6
+ import { selectPieLegend, selectPieSectors } from '../state/selectors/pieSelectors';
7
+ import { useAppSelector } from '../state/hooks';
8
+ import { Layer } from '../container/Layer';
9
+ import { Curve, Props as CurveProps } from '../shape/Curve';
10
+ import { Sector } from '../shape/Sector';
11
+ import { Text } from '../component/Text';
12
+ import { getMaxRadius, polarToCartesian } from '../util/PolarUtils';
13
+ import { getPercentValue, interpolate, isNumber, mathSign } from '../util/DataUtils';
14
+ import { getTooltipNameProp, getValueByDataKey } from '../util/ChartUtils';
15
+ import {
16
+ ActiveShape,
17
+ adaptEventsOfChild,
18
+ AnimationDuration,
19
+ ChartOffsetInternal,
20
+ Coordinate,
21
+ DataConsumer,
22
+ DataKey,
23
+ DataProvider,
24
+ EasingInput,
25
+ GeometrySector,
26
+ LegendType,
27
+ PolarLayout,
28
+ PresentationAttributesAdaptChildEvent,
29
+ ShapeAnimationProps,
30
+ TooltipType,
31
+ } from '../util/types';
32
+ import { Shape } from '../util/ActiveShapeUtils';
33
+ import {
34
+ TooltipTriggerInfo,
35
+ useMouseClickItemDispatch,
36
+ useMouseEnterItemDispatch,
37
+ useMouseLeaveItemDispatch,
38
+ } from '../context/tooltipContext';
39
+ import { TooltipPayload, TooltipPayloadConfiguration } from '../state/tooltipSlice';
40
+ import { SetTooltipEntrySettings } from '../state/SetTooltipEntrySettings';
41
+ import { Formatter } from '../component/DefaultTooltipContent';
42
+ import {
43
+ selectActiveTooltipDataKey,
44
+ selectActiveTooltipGraphicalItemId,
45
+ selectActiveTooltipIndex,
46
+ } from '../state/selectors/tooltipSelectors';
47
+ import { SetPolarLegendPayload } from '../state/SetLegendPayload';
48
+ import {
49
+ DATA_ITEM_GRAPHICAL_ITEM_ID_ATTRIBUTE_NAME,
50
+ DATA_ITEM_INDEX_ATTRIBUTE_NAME,
51
+ } from '../util/Constants';
52
+ import {
53
+ AnimatedItems,
54
+ AnimationInterpolateFn,
55
+ useAnimationCallbacks,
56
+ } from '../animation/AnimatedItems';
57
+ import { AnimationMatchByProp, matchAppend } from '../animation/matchBy';
58
+ import { RequiresDefaultProps, resolveDefaultProps } from '../util/resolveDefaultProps';
59
+ import { RegisterGraphicalItemId } from '../context/RegisterGraphicalItemId';
60
+ import { SetPolarGraphicalItem } from '../state/SetGraphicalItem';
61
+ import type { PiePresentationProps, PieSettings } from '../state/types/PieSettings';
62
+ import {
63
+ svgPropertiesNoEvents,
64
+ svgPropertiesNoEventsFromUnknown,
65
+ SVGPropsNoEvents,
66
+ } from '../util/svgPropertiesNoEvents';
67
+ import {
68
+ LabelListFromLabelProp,
69
+ PolarLabelListContextProvider,
70
+ PolarLabelListEntry,
71
+ Props as LabelListProps,
72
+ } from '../component/LabelList';
73
+ import { GraphicalItemId } from '../state/graphicalItemsSlice';
74
+ import { ZIndexable, ZIndexLayer } from '../zIndex/ZIndexLayer';
75
+ import { DefaultZIndexes } from '../zIndex/DefaultZIndexes';
76
+ import { ChartData } from '../state/chartDataSlice';
77
+ import { getClassNameFromUnknown } from '../util/getClassNameFromUnknown';
78
+ import { WithIdRequired } from '../util/useUniqueId';
79
+ import { usePolarChartLayout } from '../context/chartLayoutContext';
80
+ import { CellsContext, useCellRegistry } from '../context/CellsContext';
81
+
82
+ interface PieDef {
83
+ /**
84
+ * The x-coordinate of center. If set a percentage, the final value is obtained by multiplying the percentage of container width.
85
+ */
86
+ cx?: number | string;
87
+ /**
88
+ * The y-coordinate of center. If set a percentage, the final value is obtained by multiplying the percentage of container height.
89
+ */
90
+ cy?: number | string;
91
+ /**
92
+ * Angle in degrees from which the chart should start.
93
+ */
94
+ startAngle?: number;
95
+ /**
96
+ * Angle, in degrees, at which the chart should end.
97
+ */
98
+ endAngle?: number;
99
+ /**
100
+ * The angle between two sectors.
101
+ *
102
+ * @example <Pie paddingAngle={5} />
103
+ * @example https://recharts.github.io/examples/PieChartWithPaddingAngle
104
+ */
105
+ paddingAngle?: number;
106
+ /**
107
+ * The inner radius of the sectors.
108
+ * If set a percentage, the final value is obtained by multiplying the percentage of maxRadius which is calculated by the width, height, cx, cy.
109
+ */
110
+ innerRadius?: number | string;
111
+ /**
112
+ * The outer radius of the sectors.
113
+ * If set a percentage, the final value is obtained by multiplying the percentage of maxRadius which is calculated by the width, height, cx, cy.
114
+ * Function should return a string percentage or number.
115
+ */
116
+ outerRadius?: number | string | ((dataPoint: any) => number | string);
117
+ cornerRadius?: number | string;
118
+ }
119
+
120
+ type PieLabelLine =
121
+ | ReactElement<SVGElement>
122
+ | ((props: any) => ReactElement<SVGElement>)
123
+ | SVGProps<SVGPathElement>
124
+ | boolean;
125
+
126
+ interface PieLabelExtraProps {
127
+ stroke: string;
128
+ index: number;
129
+ textAnchor: string;
130
+ }
131
+
132
+ export type PieLabelRenderProps =
133
+ Omit<SVGPropsNoEvents<PieSvgAttributes>, 'offset'> & Omit<
134
+ PieSectorDataItem,
135
+ 'offset'
136
+ > & PieLabelExtraProps & Coordinate;
137
+
138
+ export type LabelListPropsWithPosition = LabelListProps & { position: LabelListProps['position'] };
139
+
140
+ /**
141
+ * The `label` prop in Pie accepts a variety of alternatives.
142
+ */
143
+ export type PieLabel =
144
+ /*
145
+ * If label prop is false, labels don't show.
146
+ * If label prop is true, labels will be rendered with default props.
147
+ */
148
+ | boolean
149
+ | /*
150
+ * If label prop is an object, and it contains a `position` prop,
151
+ * the object will become props to a <LabelList /> component.
152
+ */
153
+ LabelListPropsWithPosition
154
+ | /*
155
+ * If label prop is an object, and it does not have a `position` prop,
156
+ * this renders a special Pie label that is similar to <Label position="outside"> and supports an optional labelLine.
157
+ * labelLine is a small line connecting the sector to the label.
158
+ */
159
+ Partial<PieLabelRenderProps>
160
+ | /*
161
+ * If label prop is a function, or a React component, it will be called once for each label.
162
+ * The result of each call will be rendered.
163
+ */
164
+ ((props: PieLabelRenderProps) => ReactNode | ReactElement<SVGElement>)
165
+ | /*
166
+ * If label prop is a ReactElement, it will get cloned and receive brand-new surprise props.
167
+ * I do not recommend this variant
168
+ */
169
+ ReactElement<SVGElement>;
170
+
171
+ export type PieSectorData =
172
+ GeometrySector & TooltipTriggerInfo & {
173
+ dataKey?: DataKey<any>;
174
+ midAngle?: number;
175
+ middleRadius?: number;
176
+ name?: string | number;
177
+ paddingAngle?: number;
178
+ payload?: any;
179
+ percent?: number;
180
+ value: number;
181
+ };
182
+
183
+ /**
184
+ * We spread the data object into the sector data item,
185
+ * so we can't really know what is going to be inside.
186
+ *
187
+ * This type represents our best effort, but it all depends on the input data
188
+ * and what is inside of it.
189
+ *
190
+ * https://github.com/recharts/recharts/issues/6380
191
+ * https://github.com/recharts/recharts/discussions/6375
192
+ */
193
+ export type PieSectorDataItem =
194
+ PiePresentationProps & PieCoordinate & PieSectorData & {
195
+ cornerRadius: number | undefined;
196
+ };
197
+
198
+ export type PieSectorShapeProps =
199
+ PieSectorDataItem & ShapeAnimationProps & {
200
+ [DATA_ITEM_INDEX_ATTRIBUTE_NAME]: number;
201
+ [DATA_ITEM_GRAPHICAL_ITEM_ID_ATTRIBUTE_NAME]: GraphicalItemId;
202
+ isActive: boolean;
203
+ index: number;
204
+ } & SVGProps<SVGPathElement>;
205
+ export type PieShape = ActiveShape<PieSectorShapeProps, SVGPathElement>;
206
+
207
+ interface PieEvents {
208
+ /**
209
+ * The customized event handler of click on the sectors in this group.
210
+ */
211
+ onClick?: (data: PieSectorDataItem, index: number, e: React.MouseEvent<
212
+ SVGGraphicsElement
213
+ >) => void;
214
+ /**
215
+ * The customized event handler of mousedown on the sectors in this group.
216
+ */
217
+ onMouseDown?: (data: PieSectorDataItem, index: number, e: React.MouseEvent<
218
+ SVGGraphicsElement
219
+ >) => void;
220
+ /**
221
+ * The customized event handler of mouseup on the sectors in this group.
222
+ */
223
+ onMouseUp?: (data: PieSectorDataItem, index: number, e: React.MouseEvent<
224
+ SVGGraphicsElement
225
+ >) => void;
226
+ /**
227
+ * The customized event handler of mousemove on the sectors in this group.
228
+ */
229
+ onMouseMove?: (data: PieSectorDataItem, index: number, e: React.MouseEvent<
230
+ SVGGraphicsElement
231
+ >) => void;
232
+ /**
233
+ * The customized event handler of mouseover on the sectors in this group.
234
+ */
235
+ onMouseOver?: (data: PieSectorDataItem, index: number, e: React.MouseEvent<
236
+ SVGGraphicsElement
237
+ >) => void;
238
+ /**
239
+ * The customized event handler of mouseout on the sectors in this group.
240
+ */
241
+ onMouseOut?: (data: PieSectorDataItem, index: number, e: React.MouseEvent<
242
+ SVGGraphicsElement
243
+ >) => void;
244
+ /**
245
+ * The customized event handler of mouseenter on the sectors in this group.
246
+ */
247
+ onMouseEnter?: (data: PieSectorDataItem, index: number, e: React.MouseEvent<
248
+ SVGGraphicsElement
249
+ >) => void;
250
+ /**
251
+ * The customized event handler of mouseleave on the sectors in this group.
252
+ */
253
+ onMouseLeave?: (data: PieSectorDataItem, index: number, e: React.MouseEvent<
254
+ SVGGraphicsElement
255
+ >) => void;
256
+ onTouchStart?: (data: PieSectorDataItem, index: number, e: React.TouchEvent<
257
+ SVGGraphicsElement
258
+ >) => void;
259
+ onTouchMove?: (data: PieSectorDataItem, index: number, e: React.TouchEvent<
260
+ SVGGraphicsElement
261
+ >) => void;
262
+ onTouchEnd?: (data: PieSectorDataItem, index: number, e: React.TouchEvent<
263
+ SVGGraphicsElement
264
+ >) => void;
265
+ }
266
+
267
+ /**
268
+ * Internal props, combination of external props + defaultProps + private Recharts state
269
+ */
270
+ interface InternalPieProps extends PropsWithResolvedDefaults {
271
+ id: GraphicalItemId;
272
+ sectors: ReadonlyArray<PieSectorDataItem>;
273
+ }
274
+
275
+ interface PieProps<DataPointType = any, DataValueType = any> extends DataProvider<
276
+ DataPointType
277
+ >, DataConsumer<DataPointType, DataValueType>, PieDef, PieEvents, ZIndexable {
278
+ /**
279
+ * This component is rendered when this graphical item is activated
280
+ * (could be by mouse hover, touch, keyboard, programmatically).
281
+ *
282
+ * @deprecated Use the `shape` prop to create each sector. `isActive` designates the "active" shape.
283
+ * @example <Pie activeShape={<CustomActiveShape />} />
284
+ * @example https://recharts.github.io/examples/CustomActiveShapePieChart
285
+ */
286
+ activeShape?: ActiveShape<PieSectorDataItem>;
287
+ /**
288
+ * Specifies when the animation should begin, the unit of this option is ms.
289
+ * @defaultValue 400
290
+ */
291
+ animationBegin?: number;
292
+ /**
293
+ * Specifies the duration of animation, the unit of this option is ms.
294
+ * @defaultValue 1500
295
+ */
296
+ animationDuration?: AnimationDuration;
297
+ /**
298
+ * The type of easing function.
299
+ * @defaultValue ease
300
+ */
301
+ animationEasing?: EasingInput;
302
+ /**
303
+ * Custom animation function for interpolating data items.
304
+ * When provided, this replaces the default animation interpolation.
305
+ *
306
+ * @param prevItems The items from the previous animation frame, or null on first render
307
+ * @param nextItems The target items to animate towards
308
+ * @param animationElapsedTime A normalized time value (0 = start, 1 = end)
309
+ * @returns The interpolated items at time animationElapsedTime
310
+ *
311
+ * @since 3.9
312
+ * @see {@link https://recharts.github.io/en-US/guide/animations/ Animations guide}
313
+ */
314
+ animationInterpolateFn?: AnimationInterpolateFn<PieSectorDataItem, PolarLayout>;
315
+ /**
316
+ * Strategy for matching previous items to next items during animation.
317
+ * Determines how Recharts pairs old data points with new data points
318
+ * to create smooth transitions.
319
+ *
320
+ * - `matchAppend` (default): match sequentially by index and treat newly appended items as new
321
+ * - `matchByIndex`: match by array position with proportional stretching
322
+ * - `matchByDataKey('someKey')`: match by a data key from the payload
323
+ * - Custom function `(item, index) => key`: match by the returned key
324
+ *
325
+ * @defaultValue append
326
+ * @see matchByIndex
327
+ * @see matchByDataKey
328
+ * @see matchAppend
329
+ *
330
+ * @since 3.9
331
+ * @see {@link https://recharts.github.io/en-US/guide/animations/ Animations guide}
332
+ */
333
+ animationMatchBy?: AnimationMatchByProp<PieSectorDataItem>;
334
+ className?: string;
335
+ /**
336
+ * Hides the whole graphical element when true.
337
+ *
338
+ * Hiding an element is different from removing it from the chart:
339
+ * Hidden graphical elements are still visible in Legend,
340
+ * and can be included in axis domain calculations,
341
+ * depending on `includeHidden` props of your XAxis/YAxis.
342
+ *
343
+ * @defaultValue false
344
+ */
345
+ hide?: boolean;
346
+ id?: string;
347
+ /**
348
+ * The shape of inactive sector.
349
+ * @deprecated Use the `shape` prop to modify each sector.
350
+ */
351
+ inactiveShape?: ActiveShape<PieSectorDataItem>;
352
+ /**
353
+ * If set false, animation will be disabled.
354
+ * If set "auto", the animation will be disabled in SSR and will respect the user's prefers-reduced-motion system preference for accessibility.
355
+ * @defaultValue auto
356
+ */
357
+ isAnimationActive?: boolean | 'auto';
358
+ /**
359
+ * Renders one label for each pie sector. Options:
360
+ * - `true`: renders default labels;
361
+ * - `false`: no labels are rendered;
362
+ * - `object` that has `position` prop: the props of LabelList component;
363
+ * - `object` that does not have `position` prop: the props of a custom Pie label (similar to Label with position "outside"); this variant supports `labelLine`
364
+ * - `ReactElement`: a custom SVG label element, such as `<text>` or `<g>`.
365
+ * HTML elements such as `<div>` are not valid inside the chart SVG and may trigger React DOM warnings.
366
+ * - `function`: a render function of custom label.
367
+ *
368
+ * Also see the `labelLine` prop that draws a line connecting each label to the corresponding sector.
369
+ *
370
+ * @defaultValue false
371
+ * @example <Pie label={<CustomizedLabel />} />
372
+ * @example https://recharts.github.io/examples/PieChartWithCustomizedLabel
373
+ */
374
+ label?: PieLabel;
375
+ /**
376
+ * If false set, label lines will not be drawn. If true set, label lines will be drawn which have the props calculated internally.
377
+ * If object set, label lines will be drawn which have the props merged by the internal calculated props and the option.
378
+ * If ReactElement set, the option can be the custom label line element.
379
+ * If set a function, the function will be called to render customized label line.
380
+ * @defaultValue true
381
+ * @example <Pie labelLine={<CustomizedLabelLine />} />
382
+ * @example https://recharts.github.io/examples/PieChartWithCustomizedLabel
383
+ */
384
+ labelLine?: PieLabelLine;
385
+ /**
386
+ * The type of icon in legend. If set to 'none', no legend item will be rendered.
387
+ * @defaultValue rect
388
+ */
389
+ legendType?: LegendType;
390
+ /** the max radius of pie */
391
+ maxRadius?: number;
392
+ /**
393
+ * The minimum angle of each unzero data.
394
+ * @defaultValue 0
395
+ */
396
+ minAngle?: number;
397
+ /**
398
+ * Name represents each sector in the tooltip, and legend.
399
+ * This allows you to extract the name from the data:
400
+ *
401
+ * - `string`: the name of the field in the data object;
402
+ * - `number`: the index of the field in the data;
403
+ * - `function`: a function that receives the data object and returns the name.
404
+ *
405
+ * @defaultValue name
406
+ */
407
+ nameKey?: DataKey<DataPointType, DataValueType>;
408
+ /**
409
+ * The customized event handler of animation end.
410
+ */
411
+ onAnimationEnd?: () => void;
412
+ /**
413
+ * The customized event handler of animation start.
414
+ */
415
+ onAnimationStart?: () => void;
416
+ /**
417
+ * The tabindex of wrapper surrounding the cells.
418
+ * @defaultValue 0
419
+ */
420
+ rootTabIndex?: number;
421
+ /**
422
+ * The custom shape of a Pie Sector.
423
+ * Can also be used to render active sector by checking isActive.
424
+ * During animations, the function shape also receives `animationElapsedTime`, `isAnimating`, and `isEntrance`.
425
+ * If undefined, renders {@link Sector} shape.
426
+ */
427
+ shape?: PieShape;
428
+ tooltipType?: TooltipType;
429
+ /**
430
+ * Formats the value displayed in the tooltip for this Pie.
431
+ * When set, takes precedence over the `formatter` prop on the Tooltip component.
432
+ */
433
+ formatter?: Formatter;
434
+ /**
435
+ * @defaultValue 100
436
+ */
437
+ zIndex?: number;
438
+ }
439
+
440
+ type PieSvgAttributes =
441
+ Omit<PresentationAttributesAdaptChildEvent<any, SVGElement>, 'ref' | keyof PieEvents>;
442
+
443
+ type InternalProps = PieSvgAttributes & InternalPieProps;
444
+
445
+ export type Props<DataPointType = any, DataValueType = any> =
446
+ PieSvgAttributes & PieProps<DataPointType, DataValueType>;
447
+
448
+ type RealPieData = Record<string, unknown>;
449
+
450
+ export type PieCoordinate = {
451
+ cx: number;
452
+ cy: number;
453
+ innerRadius: number;
454
+ outerRadius: number;
455
+ maxRadius: number;
456
+ };
457
+
458
+ const defaultPieSectorShape = Sector;
459
+
460
+ function SetPiePayloadLegend(props: {
461
+ cells: ReadonlyArray<ReactElement> | undefined;
462
+ id: GraphicalItemId;
463
+ }) {
464
+ const legendPayload = useAppSelector((state) => selectPieLegend(state, props.id, props.cells));
465
+ if (legendPayload == null) {
466
+ return null;
467
+ }
468
+ return <SetPolarLegendPayload legendPayload={legendPayload} />;
469
+ }
470
+
471
+ type PieSectorsProps =
472
+ ShapeAnimationProps & {
473
+ sectors: Readonly<PieSectorDataItem[]>;
474
+ /**
475
+ * @deprecated
476
+ */
477
+ activeShape: ActiveShape<Readonly<PieSectorDataItem>> | undefined;
478
+ /**
479
+ * @deprecated
480
+ */
481
+ inactiveShape: ActiveShape<Readonly<PieSectorDataItem>> | undefined;
482
+ shape: PieShape;
483
+ allOtherPieProps: WithoutId<InternalProps>;
484
+ id: GraphicalItemId;
485
+ };
486
+
487
+ function getActiveShapeFill(
488
+ activeShape: ActiveShape<Readonly<PieSectorDataItem>> | undefined,
489
+ ): string | undefined {
490
+ // activeShape can be boolean/function/element/object; only element/object can carry a static fill value.
491
+ if (
492
+ activeShape == null || typeof activeShape === 'boolean' || typeof activeShape === 'function'
493
+ ) {
494
+ return undefined;
495
+ }
496
+
497
+ if (React.isValidElement(activeShape)) {
498
+ // React element form: <Sector fill="..."/> or custom element with fill prop.
499
+ const fill = activeShape.props?.fill;
500
+ return typeof fill === 'string' ? fill : undefined;
501
+ }
502
+
503
+ const { fill } = activeShape;
504
+ return typeof fill === 'string' ? fill : undefined;
505
+ }
506
+
507
+ const SetPieTooltipEntrySettings = React.memo(({
508
+ dataKey,
509
+ nameKey,
510
+ sectors,
511
+ stroke,
512
+ strokeWidth,
513
+ fill,
514
+ name,
515
+ hide,
516
+ tooltipType,
517
+ formatter,
518
+ id,
519
+ activeShape,
520
+ }: Pick<
521
+ InternalProps,
522
+ | 'dataKey'
523
+ | 'nameKey'
524
+ | 'sectors'
525
+ | 'stroke'
526
+ | 'strokeWidth'
527
+ | 'fill'
528
+ | 'name'
529
+ | 'hide'
530
+ | 'tooltipType'
531
+ | 'formatter'
532
+ | 'id'
533
+ | 'activeShape'
534
+ >) => {
535
+ const activeShapeFill = getActiveShapeFill(activeShape);
536
+
537
+ const tooltipDataDefinedOnItem = sectors.map((sector) => {
538
+ const sectorTooltipPayload = sector.tooltipPayload;
539
+ if (activeShapeFill == null || sectorTooltipPayload == null) {
540
+ return sectorTooltipPayload;
541
+ }
542
+
543
+ return sectorTooltipPayload.map(
544
+ (item) => ({
545
+ ...item,
546
+ color: activeShapeFill,
547
+ fill: activeShapeFill,
548
+ }),
549
+ );
550
+ });
551
+
552
+ const tooltipEntrySettings: TooltipPayloadConfiguration = {
553
+ dataDefinedOnItem: tooltipDataDefinedOnItem,
554
+ getPosition: (index) => sectors[Number(index)]?.tooltipPosition,
555
+ settings: {
556
+ stroke,
557
+ strokeWidth,
558
+ fill,
559
+ dataKey,
560
+ nameKey,
561
+ name: getTooltipNameProp(name, dataKey),
562
+ hide,
563
+ type: tooltipType,
564
+ color: fill,
565
+ unit: '', // why doesn't Pie support unit?
566
+ formatter,
567
+ graphicalItemId: id,
568
+ },
569
+ };
570
+ return <SetTooltipEntrySettings tooltipEntrySettings={tooltipEntrySettings} />;
571
+ });
572
+
573
+ const getTextAnchor = (x: number, cx: number) => {
574
+ if (x > cx) {
575
+ return 'start';
576
+ }
577
+ if (x < cx) {
578
+ return 'end';
579
+ }
580
+
581
+ return 'middle';
582
+ };
583
+
584
+ const getOuterRadius = (
585
+ dataPoint: any,
586
+ outerRadius: number | string | ((element: any) => number | string),
587
+ maxPieRadius: number,
588
+ ) => {
589
+ if (typeof outerRadius === 'function') {
590
+ return getPercentValue(outerRadius(dataPoint), maxPieRadius, maxPieRadius * 0.8);
591
+ }
592
+ return getPercentValue(outerRadius, maxPieRadius, maxPieRadius * 0.8);
593
+ };
594
+
595
+ const parseCoordinateOfPie = (
596
+ pieSettings: PieSettings,
597
+ offset: ChartOffsetInternal,
598
+ dataPoint: any,
599
+ ): PieCoordinate => {
600
+ const { top, left, width, height } = offset;
601
+ const maxPieRadius = getMaxRadius(width, height);
602
+ const cx = left + getPercentValue(pieSettings.cx, width, width / 2);
603
+ const cy = top + getPercentValue(pieSettings.cy, height, height / 2);
604
+ const innerRadius = getPercentValue(pieSettings.innerRadius, maxPieRadius, 0);
605
+
606
+ const outerRadius = getOuterRadius(dataPoint, pieSettings.outerRadius, maxPieRadius);
607
+
608
+ const maxRadius = pieSettings.maxRadius || Math.sqrt(width * width + height * height) / 2;
609
+
610
+ return { cx, cy, innerRadius, outerRadius, maxRadius };
611
+ };
612
+
613
+ const parseDeltaAngle = (startAngle: number, endAngle: number) => {
614
+ const sign = mathSign(endAngle - startAngle);
615
+ const deltaAngle = Math.min(Math.abs(endAngle - startAngle), 360);
616
+
617
+ return sign * deltaAngle;
618
+ };
619
+
620
+ const renderLabelLineItem = (option: PieLabelLine, props: CurveProps) => {
621
+ if (React.isValidElement(option)) {
622
+ // @ts-expect-error we can't know if the type of props matches the element
623
+ return React.cloneElement(option, props);
624
+ }
625
+ if (typeof option === 'function') {
626
+ return option(props);
627
+ }
628
+
629
+ const className = clsx(
630
+ 'recharts-pie-label-line',
631
+ typeof option !== 'boolean' ? option.className : '',
632
+ );
633
+ // React doesn't like it when we spread a key property onto an element
634
+ const { key, ...otherProps } = props;
635
+ return <Curve {...otherProps} type="linear" className={className} />;
636
+ };
637
+
638
+ const renderLabelItem = (option: PieLabel, props: PieLabelRenderProps, value: unknown) => {
639
+ if (React.isValidElement(option)) {
640
+ // @ts-expect-error element cloning is not typed
641
+ return React.cloneElement(option, props);
642
+ }
643
+ let label = value;
644
+ if (typeof option === 'function') {
645
+ label = option(props);
646
+ if (React.isValidElement(label)) {
647
+ return label;
648
+ }
649
+ }
650
+
651
+ const className = clsx('recharts-pie-label-text', getClassNameFromUnknown(option));
652
+ return <Text {...props} alignmentBaseline="middle" className={className}>
653
+ {/* @ts-expect-error we pass unknown but Text expects string | number */}
654
+ {label}
655
+ </Text>;
656
+ };
657
+
658
+ function PieLabels({
659
+ sectors,
660
+ props,
661
+ showLabels,
662
+ }: {
663
+ sectors: ReadonlyArray<PieSectorDataItem>;
664
+ props: WithoutId<InternalProps>;
665
+ showLabels: boolean;
666
+ }) {
667
+ const { label, labelLine, dataKey } = props;
668
+ if (!showLabels || !label || !sectors) {
669
+ return null;
670
+ }
671
+ const pieProps = svgPropertiesNoEvents(props);
672
+ const customLabelProps = svgPropertiesNoEventsFromUnknown(label);
673
+ const customLabelLineProps = svgPropertiesNoEventsFromUnknown(labelLine);
674
+ const offsetRadius =
675
+ typeof label === 'object' && 'offsetRadius' in label &&
676
+ typeof label.offsetRadius === 'number' &&
677
+ label.offsetRadius ||
678
+ 20;
679
+
680
+ const labels = sectors.map((entry, i) => {
681
+ const midAngle = (entry.startAngle + entry.endAngle) / 2;
682
+ const endPoint = polarToCartesian(
683
+ entry.cx,
684
+ entry.cy,
685
+ entry.outerRadius + offsetRadius,
686
+ midAngle,
687
+ );
688
+ const labelProps: PieLabelRenderProps = {
689
+ ...pieProps,
690
+ ...entry,
691
+ // @ts-expect-error customLabelProps is contributing unknown props
692
+ stroke: 'none',
693
+ ...customLabelProps,
694
+ index: i,
695
+ textAnchor: getTextAnchor(endPoint.x, entry.cx),
696
+ ...endPoint,
697
+ };
698
+ const lineProps: CurveProps = {
699
+ ...pieProps,
700
+ ...entry,
701
+ // @ts-expect-error customLabelLineProps is contributing unknown props
702
+ fill: 'none',
703
+ // @ts-expect-error customLabelLineProps is contributing unknown props
704
+ stroke: entry.fill,
705
+ ...customLabelLineProps,
706
+ index: i,
707
+ points: [polarToCartesian(entry.cx, entry.cy, entry.outerRadius, midAngle), endPoint],
708
+ key: 'line',
709
+ };
710
+
711
+ return <ZIndexLayer
712
+ zIndex={DefaultZIndexes.label}
713
+ key={`label-${entry.startAngle}-${entry.endAngle}-${entry.midAngle}-${i}`}
714
+ >
715
+ <Layer>
716
+ {labelLine && renderLabelLineItem(labelLine, lineProps)}
717
+ {/* @ts-expect-error we send the full PieSectorDataItem */}
718
+ {renderLabelItem(label, labelProps, getValueByDataKey(entry, dataKey))}
719
+ </Layer>
720
+ </ZIndexLayer>;
721
+ });
722
+
723
+ return <Layer className="recharts-pie-labels">{labels}</Layer>;
724
+ }
725
+
726
+ function PieLabelList({
727
+ sectors,
728
+ props,
729
+ showLabels,
730
+ }: {
731
+ sectors: ReadonlyArray<PieSectorDataItem>;
732
+ props: WithoutId<InternalProps>;
733
+ showLabels: boolean;
734
+ }) {
735
+ const { label } = props;
736
+ if (typeof label === 'object' && label != null && 'position' in label) {
737
+ return <LabelListFromLabelProp label={label} />;
738
+ }
739
+ return <PieLabels sectors={sectors} props={props} showLabels={showLabels} />;
740
+ }
741
+
742
+ function PieSectors(props: PieSectorsProps) {
743
+ const {
744
+ sectors,
745
+ activeShape,
746
+ inactiveShape: inactiveShapeProp,
747
+ allOtherPieProps,
748
+ shape,
749
+ id,
750
+ animationElapsedTime,
751
+ isAnimating,
752
+ isEntrance,
753
+ } = props;
754
+
755
+ const activeIndex = useAppSelector(selectActiveTooltipIndex);
756
+ const activeDataKey = useAppSelector(selectActiveTooltipDataKey);
757
+ const activeGraphicalItemId = useAppSelector(selectActiveTooltipGraphicalItemId);
758
+ const {
759
+ onMouseEnter: onMouseEnterFromProps,
760
+ onClick: onItemClickFromProps,
761
+ onMouseLeave: onMouseLeaveFromProps,
762
+ ...restOfAllOtherProps
763
+ } = allOtherPieProps;
764
+
765
+ const onMouseEnterFromContext = useMouseEnterItemDispatch(
766
+ onMouseEnterFromProps,
767
+ allOtherPieProps.dataKey,
768
+ id,
769
+ );
770
+ const onMouseLeaveFromContext = useMouseLeaveItemDispatch(onMouseLeaveFromProps);
771
+ const onClickFromContext = useMouseClickItemDispatch(
772
+ onItemClickFromProps,
773
+ allOtherPieProps.dataKey,
774
+ id,
775
+ );
776
+
777
+ if (sectors == null || sectors.length === 0) {
778
+ return null;
779
+ }
780
+
781
+ return <>
782
+ {sectors.map((entry: PieSectorDataItem, i: number) => {
783
+ if (entry?.startAngle === 0 && entry?.endAngle === 0 && sectors.length !== 1) return null;
784
+
785
+ // For Pie charts, when multiple Pies share the same dataKey, we need to ensure only the hovered Pie's sector is active.
786
+ // We do this by checking if the active graphical item ID matches this Pie's ID.
787
+ const graphicalItemMatches = activeGraphicalItemId == null || activeGraphicalItemId === id;
788
+
789
+ const isActive =
790
+ String(i) === activeIndex &&
791
+ (activeDataKey == null || allOtherPieProps.dataKey === activeDataKey) &&
792
+ graphicalItemMatches;
793
+ const inactiveShape = activeIndex ? inactiveShapeProp : null;
794
+ const sectorOptions =
795
+ activeShape && isActive ? activeShape : inactiveShape;
796
+ const sectorProps: PieSectorShapeProps = {
797
+ ...entry,
798
+ stroke: entry.stroke,
799
+ tabIndex: -1,
800
+ index: i,
801
+ isActive,
802
+ animationElapsedTime,
803
+ isAnimating,
804
+ isEntrance,
805
+ [DATA_ITEM_INDEX_ATTRIBUTE_NAME]: i,
806
+ [DATA_ITEM_GRAPHICAL_ITEM_ID_ATTRIBUTE_NAME]: id,
807
+ };
808
+
809
+ return <Layer
810
+ key={`sector-${entry?.startAngle}-${entry?.endAngle}-${entry.midAngle}-${i}`}
811
+ tabIndex={-1}
812
+ className="recharts-pie-sector"
813
+ {...adaptEventsOfChild(restOfAllOtherProps, entry, i)}
814
+ onMouseEnter={onMouseEnterFromContext(entry, i)}
815
+ onMouseLeave={onMouseLeaveFromContext(entry, i)}
816
+ onClick={onClickFromContext(entry, i)}
817
+ >
818
+ <Shape
819
+ option={sectorOptions ?? shape}
820
+ DefaultShape={defaultPieSectorShape}
821
+ shapeProps={sectorProps}
822
+ />
823
+ </Layer>;
824
+ })}
825
+ </>;
826
+ }
827
+
828
+ export function computePieSectors({
829
+ pieSettings,
830
+ displayedData,
831
+ cells,
832
+ offset,
833
+ }: {
834
+ pieSettings: PieSettings;
835
+ displayedData: ChartData;
836
+ cells: ReadonlyArray<ReactElement> | undefined;
837
+ offset: ChartOffsetInternal;
838
+ }): ReadonlyArray<PieSectorDataItem> | undefined {
839
+ const { cornerRadius, startAngle, endAngle, dataKey, nameKey, tooltipType } = pieSettings;
840
+ const minAngle = Math.abs(pieSettings.minAngle);
841
+ const deltaAngle = parseDeltaAngle(startAngle, endAngle);
842
+ const absDeltaAngle = Math.abs(deltaAngle);
843
+ const paddingAngle =
844
+ displayedData.length <= 1 ? 0 : pieSettings.paddingAngle ?? 0;
845
+
846
+ const notZeroItemCount = displayedData.filter(
847
+ (entry) => getValueByDataKey(entry, dataKey, 0) !== 0,
848
+ ).length;
849
+ const totalPaddingAngle =
850
+ (absDeltaAngle >= 360 ? notZeroItemCount : notZeroItemCount - 1) * paddingAngle;
851
+
852
+ const sum = displayedData.reduce((result: number, entry: any) => {
853
+ const val = getValueByDataKey(entry, dataKey, 0);
854
+ return result + (isNumber(val) ? val : 0);
855
+ }, 0);
856
+
857
+ // Only apply minAngle redistribution when at least one non-zero segment's
858
+ // natural angle falls below the minAngle threshold. Otherwise, minAngle
859
+ // unnecessarily shifts all segments even when none need the boost.
860
+ // See: https://github.com/recharts/recharts/issues/6814
861
+ const needsMinAngleAdjustment = minAngle > 0 && sum > 0 && displayedData.some((entry) => {
862
+ const val = getValueByDataKey(entry, dataKey, 0);
863
+ const percent = (isNumber(val) ? val : 0) / sum;
864
+ return val !== 0 && percent * absDeltaAngle < minAngle;
865
+ });
866
+ const effectiveMinAngle = needsMinAngleAdjustment ? minAngle : 0;
867
+
868
+ const realTotalAngle = absDeltaAngle - notZeroItemCount * effectiveMinAngle - totalPaddingAngle;
869
+ let sectors;
870
+
871
+ if (sum > 0) {
872
+ let prev: PieSectorDataItem;
873
+ sectors = displayedData.map((entry: unknown, i: number) => {
874
+ const val: number = getValueByDataKey(entry, dataKey, 0);
875
+ const name: string = getValueByDataKey(entry, nameKey, i);
876
+ const coordinate: PieCoordinate = parseCoordinateOfPie(pieSettings, offset, entry);
877
+ const percent = (isNumber(val) ? val : 0) / sum;
878
+ let tempStartAngle;
879
+
880
+ // @ts-expect-error can't spread unknown
881
+ const entryWithCellInfo: RealPieData = { ...entry, ...cells && cells[i] && cells[i].props };
882
+ const sectorColor =
883
+ entryWithCellInfo != null && 'fill' in entryWithCellInfo &&
884
+ typeof entryWithCellInfo.fill === 'string'
885
+ ? entryWithCellInfo.fill
886
+ : pieSettings.fill;
887
+
888
+ if (i) {
889
+ tempStartAngle = prev.endAngle + mathSign(deltaAngle) * paddingAngle * (val !== 0 ? 1 : 0);
890
+ } else {
891
+ tempStartAngle = startAngle;
892
+ }
893
+
894
+ const tempEndAngle =
895
+ tempStartAngle +
896
+ mathSign(deltaAngle) * ((val !== 0 ? effectiveMinAngle : 0) + percent * realTotalAngle);
897
+ const midAngle = (tempStartAngle + tempEndAngle) / 2;
898
+ const middleRadius = (coordinate.innerRadius + coordinate.outerRadius) / 2;
899
+
900
+ const tooltipPayload: TooltipPayload = [
901
+ {
902
+ name,
903
+ value: val,
904
+ payload: entryWithCellInfo,
905
+ dataKey,
906
+ type: tooltipType,
907
+ color: sectorColor,
908
+ fill: sectorColor,
909
+ graphicalItemId: pieSettings.id,
910
+ },
911
+ ];
912
+ const tooltipPosition = polarToCartesian(
913
+ coordinate.cx,
914
+ coordinate.cy,
915
+ middleRadius,
916
+ midAngle,
917
+ );
918
+
919
+ prev = {
920
+ ...pieSettings.presentationProps,
921
+ percent,
922
+ cornerRadius: typeof cornerRadius === 'string' ? parseFloat(cornerRadius) : cornerRadius,
923
+ name,
924
+ tooltipPayload,
925
+ midAngle,
926
+ middleRadius,
927
+ tooltipPosition,
928
+ ...entryWithCellInfo,
929
+ ...coordinate,
930
+ value: val,
931
+ dataKey,
932
+ startAngle: tempStartAngle,
933
+ endAngle: tempEndAngle,
934
+ payload: entryWithCellInfo,
935
+ paddingAngle: val !== 0 ? mathSign(deltaAngle) * paddingAngle : 0,
936
+ };
937
+ return prev;
938
+ });
939
+ }
940
+ return sectors;
941
+ }
942
+
943
+ function PieLabelListProvider({
944
+ showLabels,
945
+ sectors,
946
+ children,
947
+ }: {
948
+ showLabels: boolean;
949
+ sectors: ReadonlyArray<PieSectorDataItem>;
950
+ children: ReactNode;
951
+ }) {
952
+ const labelListEntries: ReadonlyArray<PolarLabelListEntry> = useMemo(() => {
953
+ if (!showLabels || !sectors) {
954
+ return [];
955
+ }
956
+ return sectors.map(
957
+ (entry): PolarLabelListEntry => ({
958
+ value: entry.value,
959
+ payload: entry.payload,
960
+ clockWise: false,
961
+ parentViewBox: undefined,
962
+ viewBox: {
963
+ cx: entry.cx,
964
+ cy: entry.cy,
965
+ innerRadius: entry.innerRadius,
966
+ outerRadius: entry.outerRadius,
967
+ startAngle: entry.startAngle,
968
+ endAngle: entry.endAngle,
969
+ clockWise: false,
970
+ },
971
+ fill: entry.fill,
972
+ }),
973
+ );
974
+ }, [sectors, showLabels]);
975
+ return <PolarLabelListContextProvider
976
+ value={showLabels ? labelListEntries : undefined}
977
+ >{children}</PolarLabelListContextProvider>;
978
+ }
979
+
980
+ type WithoutId<T> = Omit<T, 'id'>;
981
+
982
+ const defaultPieAnimateItems: AnimationInterpolateFn<PieSectorDataItem, PolarLayout> = (
983
+ items,
984
+ animationElapsedTime,
985
+ ) => {
986
+ if (items == null) return [];
987
+ const stepData: PieSectorDataItem[] = [];
988
+ const firstNonRemoved = items.find((item) => item.status !== 'removed');
989
+ let curAngle: number = firstNonRemoved ? firstNonRemoved.next.startAngle : 0;
990
+
991
+ items.forEach((item, index) => {
992
+ if (item.status === 'removed') return;
993
+ const paddingAngle =
994
+ index > 0 ? get(item.next, 'paddingAngle', 0) : 0;
995
+
996
+ if (item.status === 'matched') {
997
+ const angle = interpolate(
998
+ item.prev.endAngle - item.prev.startAngle,
999
+ item.next.endAngle - item.next.startAngle,
1000
+ animationElapsedTime,
1001
+ );
1002
+ const latest = {
1003
+ ...item.next,
1004
+ startAngle: curAngle + paddingAngle,
1005
+ endAngle: curAngle + angle + paddingAngle,
1006
+ };
1007
+ stepData.push(latest);
1008
+ curAngle = latest.endAngle;
1009
+ } else {
1010
+ // added
1011
+ const deltaAngle = interpolate(
1012
+ 0,
1013
+ item.next.endAngle - item.next.startAngle,
1014
+ animationElapsedTime,
1015
+ );
1016
+ const latest = {
1017
+ ...item.next,
1018
+ startAngle: curAngle + paddingAngle,
1019
+ endAngle: curAngle + deltaAngle + paddingAngle,
1020
+ };
1021
+ stepData.push(latest);
1022
+ curAngle = latest.endAngle;
1023
+ }
1024
+ });
1025
+ return stepData;
1026
+ };
1027
+
1028
+ function SectorsWithAnimation({
1029
+ props,
1030
+ previousSectorsRef,
1031
+ id,
1032
+ }: {
1033
+ props: WithoutId<InternalProps>;
1034
+ previousSectorsRef: MutableRefObject<ReadonlyArray<PieSectorDataItem> | null>;
1035
+ id: GraphicalItemId;
1036
+ }) {
1037
+ const { sectors, activeShape, inactiveShape, animationInterpolateFn } = props;
1038
+ const animationInput = JSON.stringify(
1039
+ sectors.map(
1040
+ ({ cx, cy, innerRadius, outerRadius, startAngle, endAngle, paddingAngle, cornerRadius }) => [
1041
+ cx,
1042
+ cy,
1043
+ innerRadius,
1044
+ outerRadius,
1045
+ startAngle,
1046
+ endAngle,
1047
+ paddingAngle,
1048
+ cornerRadius,
1049
+ ],
1050
+ ),
1051
+ );
1052
+
1053
+ const { isAnimating, handleAnimationStart, handleAnimationEnd } = useAnimationCallbacks(
1054
+ props.onAnimationStart,
1055
+ props.onAnimationEnd,
1056
+ );
1057
+
1058
+ const layout = usePolarChartLayout();
1059
+
1060
+ if (layout == null) return null;
1061
+
1062
+ return <PieLabelListProvider showLabels={!isAnimating} sectors={sectors}>
1063
+ <AnimatedItems
1064
+ animationInput={animationInput}
1065
+ animationIdPrefix="recharts-pie-"
1066
+ items={sectors}
1067
+ previousItemsRef={previousSectorsRef}
1068
+ isAnimationActive={props.isAnimationActive}
1069
+ animationBegin={props.animationBegin}
1070
+ animationDuration={props.animationDuration}
1071
+ animationEasing={props.animationEasing}
1072
+ onAnimationStart={handleAnimationStart}
1073
+ onAnimationEnd={handleAnimationEnd}
1074
+ animationInterpolateFn={animationInterpolateFn}
1075
+ animationMatchBy={props.animationMatchBy}
1076
+ layout={layout}
1077
+ >
1078
+ {(stepData, animationElapsedTime, isEntrance) => <Layer>
1079
+ <PieSectors
1080
+ sectors={stepData}
1081
+ activeShape={activeShape}
1082
+ inactiveShape={inactiveShape}
1083
+ allOtherPieProps={props}
1084
+ shape={props.shape}
1085
+ id={id}
1086
+ animationElapsedTime={animationElapsedTime}
1087
+ isAnimating={isAnimating || animationElapsedTime < 1}
1088
+ isEntrance={isEntrance}
1089
+ />
1090
+ </Layer>}
1091
+ </AnimatedItems>
1092
+ <PieLabelList showLabels={!isAnimating} sectors={sectors} props={props} />
1093
+ {props.children}
1094
+ </PieLabelListProvider>;
1095
+ }
1096
+
1097
+ export const defaultPieProps = {
1098
+ animationBegin: 400,
1099
+ animationDuration: 1500,
1100
+ animationEasing: 'ease',
1101
+ animationInterpolateFn: defaultPieAnimateItems,
1102
+ animationMatchBy: matchAppend,
1103
+ cx: '50%',
1104
+ cy: '50%',
1105
+ dataKey: 'value',
1106
+ endAngle: 360,
1107
+ fill: '#808080',
1108
+ hide: false,
1109
+ innerRadius: 0,
1110
+ isAnimationActive: 'auto',
1111
+ label: false,
1112
+ labelLine: true,
1113
+ legendType: 'rect',
1114
+ minAngle: 0,
1115
+ nameKey: 'name',
1116
+ outerRadius: '80%',
1117
+ paddingAngle: 0,
1118
+ rootTabIndex: 0,
1119
+ shape: defaultPieSectorShape,
1120
+ startAngle: 0,
1121
+ stroke: '#fff',
1122
+ zIndex: DefaultZIndexes.area,
1123
+ } as const satisfies Partial<Props>;
1124
+
1125
+ function PieImpl(props: WithIdRequired<PropsWithResolvedDefaults>) {
1126
+ const { id, ...propsWithoutId } = props;
1127
+ const { hide, className, rootTabIndex } = props;
1128
+
1129
+ const [cells, cellRegistry] = useCellRegistry();
1130
+
1131
+ const sectors = useAppSelector((state) => selectPieSectors(state, id, cells));
1132
+
1133
+ const previousSectorsRef = useRef<ReadonlyArray<PieSectorDataItem> | null>(null);
1134
+
1135
+ const layerClass = clsx('recharts-pie', className);
1136
+
1137
+ if (sectors == null) {
1138
+ previousSectorsRef.current = null;
1139
+ return <CellsContext.Provider value={cellRegistry}>
1140
+ <SetPiePayloadLegend cells={cells} id={id} />
1141
+ <Layer tabIndex={rootTabIndex} className={layerClass}>{props.children}</Layer>
1142
+ </CellsContext.Provider>;
1143
+ }
1144
+
1145
+ if (hide) {
1146
+ previousSectorsRef.current = null;
1147
+ }
1148
+
1149
+ return <CellsContext.Provider value={cellRegistry}>
1150
+ <SetPiePayloadLegend cells={cells} id={id} />
1151
+ <ZIndexLayer zIndex={props.zIndex}>
1152
+ <SetPieTooltipEntrySettings
1153
+ dataKey={props.dataKey}
1154
+ nameKey={props.nameKey}
1155
+ sectors={sectors}
1156
+ stroke={props.stroke}
1157
+ strokeWidth={props.strokeWidth}
1158
+ fill={props.fill}
1159
+ name={props.name}
1160
+ hide={props.hide}
1161
+ tooltipType={props.tooltipType}
1162
+ formatter={props.formatter}
1163
+ id={id}
1164
+ activeShape={props.activeShape}
1165
+ />
1166
+ <Layer tabIndex={rootTabIndex} className={layerClass}>
1167
+ {!hide
1168
+ ? <SectorsWithAnimation
1169
+ props={{ ...propsWithoutId, sectors }}
1170
+ previousSectorsRef={previousSectorsRef}
1171
+ id={id}
1172
+ />
1173
+ : props.children}
1174
+ </Layer>
1175
+ </ZIndexLayer>
1176
+ </CellsContext.Provider>;
1177
+ }
1178
+
1179
+ type PropsWithResolvedDefaults = RequiresDefaultProps<Props, typeof defaultPieProps>;
1180
+
1181
+ /**
1182
+ * @consumes PolarChartContext
1183
+ * @provides LabelListContext
1184
+ * @provides CellReader
1185
+ */
1186
+ function PieFn(outsideProps: Props) {
1187
+ const props: PropsWithResolvedDefaults = resolveDefaultProps(outsideProps, defaultPieProps);
1188
+ const { id: externalId, ...propsWithoutId } = props;
1189
+ const presentationProps: PiePresentationProps | null = svgPropertiesNoEvents(propsWithoutId);
1190
+
1191
+ return <RegisterGraphicalItemId id={externalId} type="pie">
1192
+ {(id) => <>
1193
+ <SetPolarGraphicalItem
1194
+ type="pie"
1195
+ id={id}
1196
+ data={propsWithoutId.data}
1197
+ dataKey={propsWithoutId.dataKey}
1198
+ hide={propsWithoutId.hide}
1199
+ angleAxisId={0}
1200
+ radiusAxisId={0}
1201
+ name={propsWithoutId.name}
1202
+ nameKey={propsWithoutId.nameKey}
1203
+ tooltipType={propsWithoutId.tooltipType}
1204
+ legendType={propsWithoutId.legendType}
1205
+ fill={propsWithoutId.fill}
1206
+ cx={propsWithoutId.cx}
1207
+ cy={propsWithoutId.cy}
1208
+ startAngle={propsWithoutId.startAngle}
1209
+ endAngle={propsWithoutId.endAngle}
1210
+ paddingAngle={propsWithoutId.paddingAngle}
1211
+ minAngle={propsWithoutId.minAngle}
1212
+ innerRadius={propsWithoutId.innerRadius}
1213
+ outerRadius={propsWithoutId.outerRadius}
1214
+ cornerRadius={propsWithoutId.cornerRadius}
1215
+ presentationProps={presentationProps}
1216
+ maxRadius={props.maxRadius}
1217
+ />
1218
+ <PieImpl {...propsWithoutId} id={id} />
1219
+ </>}
1220
+ </RegisterGraphicalItemId>;
1221
+ }
1222
+ export const Pie = PieFn as (outsideProps: Props<DataPointType, DataValueType>) => ReactElement;
1223
+ // @ts-expect-error we need to set the displayName for debugging purposes
1224
+ Pie.displayName = 'Pie';