@automattic/charts 0.58.0 → 1.0.0
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/CHANGELOG.md +25 -0
- package/README.md +7 -54
- package/dist/index.cjs +9606 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +20 -25
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +1612 -33
- package/dist/index.d.ts +1612 -33
- package/dist/index.js +9640 -56
- package/dist/index.js.map +1 -1
- package/package.json +8 -125
- package/src/charts/bar-chart/bar-chart.module.scss +0 -5
- package/src/charts/bar-chart/bar-chart.tsx +131 -137
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +23 -40
- package/src/charts/line-chart/line-chart.module.scss +0 -5
- package/src/charts/line-chart/line-chart.tsx +190 -183
- package/src/charts/line-chart/private/line-chart-annotations-overlay.tsx +1 -2
- package/src/charts/pie-chart/pie-chart.module.scss +2 -10
- package/src/charts/pie-chart/pie-chart.tsx +198 -199
- package/src/charts/pie-chart/test/composition-api.test.tsx +3 -3
- package/src/charts/pie-chart/test/pie-chart.test.tsx +42 -35
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +2 -8
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +155 -155
- package/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx +25 -25
- package/src/charts/private/chart-layout/chart-layout.module.scss +7 -0
- package/src/charts/private/chart-layout/chart-layout.tsx +106 -0
- package/src/charts/private/chart-layout/index.ts +2 -0
- package/src/charts/private/chart-layout/test/chart-layout.test.tsx +167 -0
- package/src/charts/private/single-chart-context/single-chart-context.tsx +2 -2
- package/src/charts/private/svg-empty-state/index.ts +1 -0
- package/src/charts/private/svg-empty-state/svg-empty-state.module.scss +7 -0
- package/src/charts/private/svg-empty-state/svg-empty-state.tsx +40 -0
- package/src/components/legend/hooks/test/use-chart-legend-items.test.tsx +12 -8
- package/src/components/legend/hooks/use-chart-legend-items.ts +12 -13
- package/src/components/legend/legend.tsx +33 -8
- package/src/components/legend/test/legend.test.tsx +93 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-data-with-percentages.ts +24 -0
- package/src/hooks/use-interactive-legend-data.ts +18 -15
- package/src/index.ts +65 -2
- package/src/providers/chart-context/global-charts-provider.tsx +7 -1
- package/src/providers/chart-context/hooks/use-chart-registration.ts +2 -1
- package/src/providers/chart-context/types.ts +2 -2
- package/src/types.ts +27 -7
- package/src/utils/test/resolve-css-var.test.ts +2 -0
- package/dist/base-tooltip-DOq93wjU.d.cts +0 -38
- package/dist/base-tooltip-DOq93wjU.d.ts +0 -38
- package/dist/charts/bar-chart/index.cjs +0 -17
- package/dist/charts/bar-chart/index.cjs.map +0 -1
- package/dist/charts/bar-chart/index.css +0 -141
- package/dist/charts/bar-chart/index.css.map +0 -1
- package/dist/charts/bar-chart/index.d.cts +0 -36
- package/dist/charts/bar-chart/index.d.ts +0 -36
- package/dist/charts/bar-chart/index.js +0 -17
- package/dist/charts/bar-chart/index.js.map +0 -1
- package/dist/charts/bar-list-chart/index.cjs +0 -18
- package/dist/charts/bar-list-chart/index.cjs.map +0 -1
- package/dist/charts/bar-list-chart/index.css +0 -141
- package/dist/charts/bar-list-chart/index.css.map +0 -1
- package/dist/charts/bar-list-chart/index.d.cts +0 -92
- package/dist/charts/bar-list-chart/index.d.ts +0 -92
- package/dist/charts/bar-list-chart/index.js +0 -18
- package/dist/charts/bar-list-chart/index.js.map +0 -1
- package/dist/charts/conversion-funnel-chart/index.cjs +0 -11
- package/dist/charts/conversion-funnel-chart/index.cjs.map +0 -1
- package/dist/charts/conversion-funnel-chart/index.css +0 -157
- package/dist/charts/conversion-funnel-chart/index.css.map +0 -1
- package/dist/charts/conversion-funnel-chart/index.d.cts +0 -97
- package/dist/charts/conversion-funnel-chart/index.d.ts +0 -97
- package/dist/charts/conversion-funnel-chart/index.js +0 -11
- package/dist/charts/conversion-funnel-chart/index.js.map +0 -1
- package/dist/charts/geo-chart/index.cjs +0 -13
- package/dist/charts/geo-chart/index.cjs.map +0 -1
- package/dist/charts/geo-chart/index.css +0 -23
- package/dist/charts/geo-chart/index.css.map +0 -1
- package/dist/charts/geo-chart/index.d.cts +0 -67
- package/dist/charts/geo-chart/index.d.ts +0 -67
- package/dist/charts/geo-chart/index.js +0 -13
- package/dist/charts/geo-chart/index.js.map +0 -1
- package/dist/charts/leaderboard-chart/index.cjs +0 -21
- package/dist/charts/leaderboard-chart/index.cjs.map +0 -1
- package/dist/charts/leaderboard-chart/index.css +0 -160
- package/dist/charts/leaderboard-chart/index.css.map +0 -1
- package/dist/charts/leaderboard-chart/index.d.cts +0 -46
- package/dist/charts/leaderboard-chart/index.d.ts +0 -46
- package/dist/charts/leaderboard-chart/index.js +0 -21
- package/dist/charts/leaderboard-chart/index.js.map +0 -1
- package/dist/charts/line-chart/index.cjs +0 -17
- package/dist/charts/line-chart/index.cjs.map +0 -1
- package/dist/charts/line-chart/index.css +0 -213
- package/dist/charts/line-chart/index.css.map +0 -1
- package/dist/charts/line-chart/index.d.cts +0 -98
- package/dist/charts/line-chart/index.d.ts +0 -98
- package/dist/charts/line-chart/index.js +0 -17
- package/dist/charts/line-chart/index.js.map +0 -1
- package/dist/charts/pie-chart/index.cjs +0 -19
- package/dist/charts/pie-chart/index.cjs.map +0 -1
- package/dist/charts/pie-chart/index.css +0 -131
- package/dist/charts/pie-chart/index.css.map +0 -1
- package/dist/charts/pie-chart/index.d.cts +0 -91
- package/dist/charts/pie-chart/index.d.ts +0 -91
- package/dist/charts/pie-chart/index.js +0 -19
- package/dist/charts/pie-chart/index.js.map +0 -1
- package/dist/charts/pie-semi-circle-chart/index.cjs +0 -18
- package/dist/charts/pie-semi-circle-chart/index.cjs.map +0 -1
- package/dist/charts/pie-semi-circle-chart/index.css +0 -132
- package/dist/charts/pie-semi-circle-chart/index.css.map +0 -1
- package/dist/charts/pie-semi-circle-chart/index.d.cts +0 -88
- package/dist/charts/pie-semi-circle-chart/index.d.ts +0 -88
- package/dist/charts/pie-semi-circle-chart/index.js +0 -18
- package/dist/charts/pie-semi-circle-chart/index.js.map +0 -1
- package/dist/charts/sparkline/index.cjs +0 -18
- package/dist/charts/sparkline/index.cjs.map +0 -1
- package/dist/charts/sparkline/index.css +0 -230
- package/dist/charts/sparkline/index.css.map +0 -1
- package/dist/charts/sparkline/index.d.cts +0 -113
- package/dist/charts/sparkline/index.d.ts +0 -113
- package/dist/charts/sparkline/index.js +0 -18
- package/dist/charts/sparkline/index.js.map +0 -1
- package/dist/chunk-2A34OA5O.cjs +0 -51
- package/dist/chunk-2A34OA5O.cjs.map +0 -1
- package/dist/chunk-2I67QUIV.js +0 -895
- package/dist/chunk-2I67QUIV.js.map +0 -1
- package/dist/chunk-2ICEEQOC.js +0 -1071
- package/dist/chunk-2ICEEQOC.js.map +0 -1
- package/dist/chunk-4B7BL2DD.js +0 -120
- package/dist/chunk-4B7BL2DD.js.map +0 -1
- package/dist/chunk-4OXMTKAL.js +0 -401
- package/dist/chunk-4OXMTKAL.js.map +0 -1
- package/dist/chunk-ASLARV7L.cjs +0 -81
- package/dist/chunk-ASLARV7L.cjs.map +0 -1
- package/dist/chunk-B6NLZFRW.js +0 -617
- package/dist/chunk-B6NLZFRW.js.map +0 -1
- package/dist/chunk-BBAUQOW6.cjs +0 -120
- package/dist/chunk-BBAUQOW6.cjs.map +0 -1
- package/dist/chunk-BPYKWMI7.js +0 -194
- package/dist/chunk-BPYKWMI7.js.map +0 -1
- package/dist/chunk-CMMHCTBX.cjs +0 -373
- package/dist/chunk-CMMHCTBX.cjs.map +0 -1
- package/dist/chunk-CPPXJATQ.cjs +0 -1071
- package/dist/chunk-CPPXJATQ.cjs.map +0 -1
- package/dist/chunk-DKU775VC.js +0 -219
- package/dist/chunk-DKU775VC.js.map +0 -1
- package/dist/chunk-GRA7Y2ZG.cjs +0 -401
- package/dist/chunk-GRA7Y2ZG.cjs.map +0 -1
- package/dist/chunk-I2276W3I.cjs +0 -66
- package/dist/chunk-I2276W3I.cjs.map +0 -1
- package/dist/chunk-JJIMABHT.js +0 -351
- package/dist/chunk-JJIMABHT.js.map +0 -1
- package/dist/chunk-KJHWXOCZ.js +0 -421
- package/dist/chunk-KJHWXOCZ.js.map +0 -1
- package/dist/chunk-KRWGSOJ2.js +0 -91
- package/dist/chunk-KRWGSOJ2.js.map +0 -1
- package/dist/chunk-KXRWNFQJ.js +0 -51
- package/dist/chunk-KXRWNFQJ.js.map +0 -1
- package/dist/chunk-LTFH7SEG.js +0 -373
- package/dist/chunk-LTFH7SEG.js.map +0 -1
- package/dist/chunk-MUNOKLLE.js +0 -165
- package/dist/chunk-MUNOKLLE.js.map +0 -1
- package/dist/chunk-MXGLYWVP.cjs +0 -351
- package/dist/chunk-MXGLYWVP.cjs.map +0 -1
- package/dist/chunk-OP6PHB2U.js +0 -81
- package/dist/chunk-OP6PHB2U.js.map +0 -1
- package/dist/chunk-OYC34VTO.cjs +0 -3957
- package/dist/chunk-OYC34VTO.cjs.map +0 -1
- package/dist/chunk-PQL5I3F6.cjs +0 -421
- package/dist/chunk-PQL5I3F6.cjs.map +0 -1
- package/dist/chunk-REZTQ4PH.cjs +0 -488
- package/dist/chunk-REZTQ4PH.cjs.map +0 -1
- package/dist/chunk-TZRUHQOH.cjs +0 -91
- package/dist/chunk-TZRUHQOH.cjs.map +0 -1
- package/dist/chunk-UTYVIOWZ.js +0 -3957
- package/dist/chunk-UTYVIOWZ.js.map +0 -1
- package/dist/chunk-W2LDIX26.cjs +0 -165
- package/dist/chunk-W2LDIX26.cjs.map +0 -1
- package/dist/chunk-WSG64BVN.cjs +0 -219
- package/dist/chunk-WSG64BVN.cjs.map +0 -1
- package/dist/chunk-WTQYGUNF.js +0 -400
- package/dist/chunk-WTQYGUNF.js.map +0 -1
- package/dist/chunk-WYK7EL5R.cjs +0 -895
- package/dist/chunk-WYK7EL5R.cjs.map +0 -1
- package/dist/chunk-XC4KHJYX.cjs +0 -617
- package/dist/chunk-XC4KHJYX.cjs.map +0 -1
- package/dist/chunk-XVBH5XHE.cjs +0 -400
- package/dist/chunk-XVBH5XHE.cjs.map +0 -1
- package/dist/chunk-XWYZIFZW.js +0 -66
- package/dist/chunk-XWYZIFZW.js.map +0 -1
- package/dist/chunk-Y3NNQMAX.cjs +0 -194
- package/dist/chunk-Y3NNQMAX.cjs.map +0 -1
- package/dist/chunk-YAFQVVDI.js +0 -488
- package/dist/chunk-YAFQVVDI.js.map +0 -1
- package/dist/components/legend/index.cjs +0 -12
- package/dist/components/legend/index.cjs.map +0 -1
- package/dist/components/legend/index.css +0 -91
- package/dist/components/legend/index.css.map +0 -1
- package/dist/components/legend/index.d.cts +0 -37
- package/dist/components/legend/index.d.ts +0 -37
- package/dist/components/legend/index.js +0 -12
- package/dist/components/legend/index.js.map +0 -1
- package/dist/components/tooltip/index.cjs +0 -12
- package/dist/components/tooltip/index.cjs.map +0 -1
- package/dist/components/tooltip/index.css +0 -13
- package/dist/components/tooltip/index.css.map +0 -1
- package/dist/components/tooltip/index.d.cts +0 -71
- package/dist/components/tooltip/index.d.ts +0 -71
- package/dist/components/tooltip/index.js +0 -12
- package/dist/components/tooltip/index.js.map +0 -1
- package/dist/components/trend-indicator/index.cjs +0 -8
- package/dist/components/trend-indicator/index.cjs.map +0 -1
- package/dist/components/trend-indicator/index.css +0 -27
- package/dist/components/trend-indicator/index.css.map +0 -1
- package/dist/components/trend-indicator/index.d.cts +0 -44
- package/dist/components/trend-indicator/index.d.ts +0 -44
- package/dist/components/trend-indicator/index.js +0 -8
- package/dist/components/trend-indicator/index.js.map +0 -1
- package/dist/format-metric-value-MXm5DtQ_.d.cts +0 -24
- package/dist/format-metric-value-MXm5DtQ_.d.ts +0 -24
- package/dist/hooks/index.cjs +0 -29
- package/dist/hooks/index.cjs.map +0 -1
- package/dist/hooks/index.css +0 -9
- package/dist/hooks/index.css.map +0 -1
- package/dist/hooks/index.d.cts +0 -264
- package/dist/hooks/index.d.ts +0 -264
- package/dist/hooks/index.js +0 -29
- package/dist/hooks/index.js.map +0 -1
- package/dist/leaderboard-chart-BSbg0ufV.d.cts +0 -79
- package/dist/leaderboard-chart-odEYxxEC.d.ts +0 -79
- package/dist/legend-DFkosEvC.d.cts +0 -9
- package/dist/legend-DLswHhOk.d.ts +0 -9
- package/dist/providers/index.cjs +0 -21
- package/dist/providers/index.cjs.map +0 -1
- package/dist/providers/index.css +0 -9
- package/dist/providers/index.css.map +0 -1
- package/dist/providers/index.d.cts +0 -28
- package/dist/providers/index.d.ts +0 -28
- package/dist/providers/index.js +0 -21
- package/dist/providers/index.js.map +0 -1
- package/dist/themes-D0qc5JaW.d.cts +0 -67
- package/dist/themes-itO4Ht5g.d.ts +0 -67
- package/dist/types-B5f6XQ7Q.d.cts +0 -19
- package/dist/types-BsHooDbM.d.ts +0 -19
- package/dist/types-BuSrRM4p.d.ts +0 -49
- package/dist/types-ChOUI9-N.d.cts +0 -545
- package/dist/types-ChOUI9-N.d.ts +0 -545
- package/dist/types-Dfw9VOKI.d.cts +0 -49
- package/dist/utils/index.cjs +0 -44
- package/dist/utils/index.cjs.map +0 -1
- package/dist/utils/index.d.cts +0 -226
- package/dist/utils/index.d.ts +0 -226
- package/dist/utils/index.js +0 -44
- package/dist/utils/index.js.map +0 -1
- package/dist/with-responsive-CNfhzAUu.d.cts +0 -18
- package/dist/with-responsive-CNfhzAUu.d.ts +0 -18
|
@@ -7,7 +7,11 @@ import clsx from 'clsx';
|
|
|
7
7
|
import { useCallback, useContext, useMemo } from 'react';
|
|
8
8
|
import { Legend, useChartLegendItems } from '../../components/legend';
|
|
9
9
|
import { BaseTooltip } from '../../components/tooltip';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
useDataWithPercentages,
|
|
12
|
+
useInteractiveLegendData,
|
|
13
|
+
usePrefersReducedMotion,
|
|
14
|
+
} from '../../hooks';
|
|
11
15
|
import {
|
|
12
16
|
GlobalChartsProvider,
|
|
13
17
|
useChartId,
|
|
@@ -18,18 +22,20 @@ import {
|
|
|
18
22
|
} from '../../providers';
|
|
19
23
|
import { attachSubComponents } from '../../utils';
|
|
20
24
|
import { getStringWidth } from '../../visx/text';
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
ChartHTML,
|
|
24
|
-
useChartChildren,
|
|
25
|
-
renderLegendSlot,
|
|
26
|
-
} from '../private/chart-composition';
|
|
25
|
+
import { ChartSVG, ChartHTML, useChartChildren } from '../private/chart-composition';
|
|
26
|
+
import { ChartLayout } from '../private/chart-layout';
|
|
27
27
|
import { RadialWipeAnimation } from '../private/radial-wipe-animation/';
|
|
28
28
|
import { SingleChartContext } from '../private/single-chart-context';
|
|
29
|
+
import { SvgEmptyState } from '../private/svg-empty-state';
|
|
29
30
|
import { withResponsive, ResponsiveConfig } from '../private/with-responsive';
|
|
30
31
|
import styles from './pie-chart.module.scss';
|
|
31
32
|
import type { LegendValueDisplay } from '../../components/legend';
|
|
32
|
-
import type {
|
|
33
|
+
import type {
|
|
34
|
+
BaseChartProps,
|
|
35
|
+
DataPointPercentage,
|
|
36
|
+
DataPointPercentageCalculated,
|
|
37
|
+
Optional,
|
|
38
|
+
} from '../../types';
|
|
33
39
|
import type { ChartComponentWithComposition } from '../private/chart-composition';
|
|
34
40
|
import type { SVGProps, MouseEvent, ReactNode, FC } from 'react';
|
|
35
41
|
|
|
@@ -38,9 +44,9 @@ import type { SVGProps, MouseEvent, ReactNode, FC } from 'react';
|
|
|
38
44
|
*/
|
|
39
45
|
export type PieChartRenderTooltipParams = {
|
|
40
46
|
/**
|
|
41
|
-
* The data point being hovered, including label, value, and percentage.
|
|
47
|
+
* The data point being hovered, including label, value, and calculated percentage.
|
|
42
48
|
*/
|
|
43
|
-
tooltipData:
|
|
49
|
+
tooltipData: DataPointPercentageCalculated;
|
|
44
50
|
};
|
|
45
51
|
|
|
46
52
|
/**
|
|
@@ -140,16 +146,15 @@ const validateData = ( data: DataPointPercentage[] ) => {
|
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
// Check for negative values
|
|
143
|
-
const hasNegativeValues = data.some( item => item.
|
|
149
|
+
const hasNegativeValues = data.some( item => item.value < 0 );
|
|
144
150
|
if ( hasNegativeValues ) {
|
|
145
151
|
return { isValid: false, message: 'Invalid data: Negative values are not allowed' };
|
|
146
152
|
}
|
|
147
153
|
|
|
148
|
-
// Validate total
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
return { isValid: false, message: 'Invalid percentage total: Must equal 100' };
|
|
154
|
+
// Validate total value is greater than 0
|
|
155
|
+
const totalValue = data.reduce( ( sum, item ) => sum + item.value, 0 );
|
|
156
|
+
if ( totalValue <= 0 ) {
|
|
157
|
+
return { isValid: false, message: 'Invalid data: Total value must be greater than 0' };
|
|
153
158
|
}
|
|
154
159
|
|
|
155
160
|
return { isValid: true, message: '' };
|
|
@@ -189,9 +194,8 @@ const PieChartInternal = ( {
|
|
|
189
194
|
|
|
190
195
|
const providerTheme = useGlobalChartsTheme();
|
|
191
196
|
const chartId = useChartId( providedChartId );
|
|
192
|
-
const [ svgWrapperRef, svgWrapperWidth, svgWrapperHeight ] = useElementSize< HTMLDivElement >();
|
|
193
197
|
const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } =
|
|
194
|
-
useTooltip<
|
|
198
|
+
useTooltip< DataPointPercentageCalculated >();
|
|
195
199
|
|
|
196
200
|
// Set up portal tooltip for better z-index handling
|
|
197
201
|
// We get containerBounds to cancel out stale offsets in the position calculation
|
|
@@ -210,9 +214,12 @@ const PieChartInternal = ( {
|
|
|
210
214
|
|
|
211
215
|
const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
|
|
212
216
|
|
|
217
|
+
// Calculate percentages from values (single source of truth)
|
|
218
|
+
const dataWithPercentages = useDataWithPercentages( data );
|
|
219
|
+
|
|
213
220
|
// Filter and recalculate data for interactive legends
|
|
214
221
|
const { visibleData, allSegmentsHidden, legendData } = useInteractiveLegendData( {
|
|
215
|
-
data,
|
|
222
|
+
data: dataWithPercentages,
|
|
216
223
|
chartId,
|
|
217
224
|
legendInteractive,
|
|
218
225
|
isSeriesVisible,
|
|
@@ -264,34 +271,9 @@ const PieChartInternal = ( {
|
|
|
264
271
|
);
|
|
265
272
|
}
|
|
266
273
|
|
|
267
|
-
// Calculate the actual pie size:
|
|
268
|
-
// - Measure available space from the svg-wrapper
|
|
269
|
-
// - If size prop provided: use it as max, but shrink if container is smaller
|
|
270
|
-
// - If no size prop: fill available space
|
|
271
|
-
const availableWidth = svgWrapperWidth > 0 ? svgWrapperWidth : 300;
|
|
272
|
-
const availableHeight = svgWrapperHeight > 0 ? svgWrapperHeight : 300;
|
|
273
|
-
const availableSize = Math.min( availableWidth, availableHeight );
|
|
274
|
-
const actualSize = size ? Math.min( size, availableSize ) : availableSize;
|
|
275
|
-
|
|
276
|
-
const width = actualSize;
|
|
277
|
-
const height = actualSize;
|
|
278
|
-
|
|
279
|
-
// Calculate radius based on width/height
|
|
280
|
-
const radius = Math.min( width, height ) / 2;
|
|
281
|
-
|
|
282
|
-
// Center the chart in the available space
|
|
283
|
-
const centerX = width / 2;
|
|
284
|
-
const centerY = height / 2;
|
|
285
|
-
|
|
286
274
|
// Calculate the angle between each (use original data length for consistent spacing)
|
|
287
275
|
const padAngle = gapScale * ( ( 2 * Math.PI ) / data.length );
|
|
288
276
|
|
|
289
|
-
const outerRadius = radius - padding;
|
|
290
|
-
const innerRadius = thickness === 0 ? 0 : outerRadius * ( 1 - thickness );
|
|
291
|
-
|
|
292
|
-
const maxCornerRadius = ( outerRadius - innerRadius ) / 2;
|
|
293
|
-
const cornerRadius = cornerScale ? Math.min( cornerScale * outerRadius, maxCornerRadius ) : 0;
|
|
294
|
-
|
|
295
277
|
// Map the data to include index for color assignment
|
|
296
278
|
// When interactive, we need to find the original index to maintain consistent colors
|
|
297
279
|
const dataWithIndex = visibleData.map( d => {
|
|
@@ -303,8 +285,8 @@ const PieChartInternal = ( {
|
|
|
303
285
|
} );
|
|
304
286
|
|
|
305
287
|
const accessors = {
|
|
306
|
-
value: ( d:
|
|
307
|
-
fill: ( d:
|
|
288
|
+
value: ( d: DataPointPercentageCalculated ) => d.value,
|
|
289
|
+
fill: ( d: DataPointPercentageCalculated & { index: number } ) => {
|
|
308
290
|
return getElementStyles( { data: d, index: d.index } ).color;
|
|
309
291
|
},
|
|
310
292
|
};
|
|
@@ -325,16 +307,11 @@ const PieChartInternal = ( {
|
|
|
325
307
|
);
|
|
326
308
|
|
|
327
309
|
return (
|
|
328
|
-
<SingleChartContext.Provider
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
} }
|
|
334
|
-
>
|
|
335
|
-
<Stack
|
|
336
|
-
ref={ containerRef }
|
|
337
|
-
direction="column"
|
|
310
|
+
<SingleChartContext.Provider value={ { chartId } }>
|
|
311
|
+
<ChartLayout
|
|
312
|
+
legendPosition={ legendPosition }
|
|
313
|
+
legendElement={ legendElement }
|
|
314
|
+
legendChildren={ legendChildren }
|
|
338
315
|
gap={ gap }
|
|
339
316
|
className={ clsx(
|
|
340
317
|
'pie-chart',
|
|
@@ -347,155 +324,177 @@ const PieChartInternal = ( {
|
|
|
347
324
|
width: propWidth || undefined,
|
|
348
325
|
height: propHeight || undefined,
|
|
349
326
|
} }
|
|
327
|
+
trailingContent={
|
|
328
|
+
<>
|
|
329
|
+
{ withTooltips && tooltipOpen && tooltipData && (
|
|
330
|
+
<TooltipInPortal top={ tooltipTop || 0 } left={ tooltipLeft || 0 }>
|
|
331
|
+
<div role="tooltip">{ renderTooltip( { tooltipData } ) }</div>
|
|
332
|
+
</TooltipInPortal>
|
|
333
|
+
) }
|
|
334
|
+
{ htmlChildren }
|
|
335
|
+
{ otherChildren }
|
|
336
|
+
</>
|
|
337
|
+
}
|
|
350
338
|
>
|
|
351
|
-
{
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
339
|
+
{ ( { contentWidth, contentHeight } ) => {
|
|
340
|
+
const availableWidth = contentWidth > 0 ? contentWidth : 300;
|
|
341
|
+
const availableHeight = contentHeight > 0 ? contentHeight : 300;
|
|
342
|
+
const availableSize = Math.min( availableWidth, availableHeight );
|
|
343
|
+
const actualSize = size ? Math.min( size, availableSize ) : availableSize;
|
|
344
|
+
|
|
345
|
+
const width = actualSize;
|
|
346
|
+
const height = actualSize;
|
|
347
|
+
|
|
348
|
+
const radius = Math.min( width, height ) / 2;
|
|
349
|
+
const centerX = width / 2;
|
|
350
|
+
const centerY = height / 2;
|
|
351
|
+
|
|
352
|
+
const outerRadius = radius - padding;
|
|
353
|
+
const innerRadius = thickness === 0 ? 0 : outerRadius * ( 1 - thickness );
|
|
354
|
+
|
|
355
|
+
const maxCornerRadius = ( outerRadius - innerRadius ) / 2;
|
|
356
|
+
const cornerRadius = cornerScale
|
|
357
|
+
? Math.min( cornerScale * outerRadius, maxCornerRadius )
|
|
358
|
+
: 0;
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
<Stack
|
|
362
|
+
ref={ containerRef }
|
|
363
|
+
align="center"
|
|
364
|
+
justify="center"
|
|
365
|
+
className={ styles[ 'pie-chart__centering' ] }
|
|
373
366
|
>
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
367
|
+
<svg
|
|
368
|
+
viewBox={ `0 0 ${ width } ${ height }` }
|
|
369
|
+
preserveAspectRatio="xMidYMid meet"
|
|
370
|
+
width={ width }
|
|
371
|
+
height={ height }
|
|
372
|
+
>
|
|
373
|
+
<defs>
|
|
374
|
+
<RadialWipeAnimation
|
|
375
|
+
id={ `radial-wipe-${ chartId }` }
|
|
376
|
+
radius={ outerRadius }
|
|
377
|
+
innerRadius={ innerRadius }
|
|
378
|
+
/>
|
|
379
|
+
</defs>
|
|
380
|
+
|
|
381
|
+
<Group
|
|
382
|
+
top={ centerY }
|
|
383
|
+
left={ centerX }
|
|
384
|
+
mask={
|
|
385
|
+
animation && ! prefersReducedMotion ? `url(#radial-wipe-${ chartId })` : null
|
|
386
|
+
}
|
|
381
387
|
>
|
|
382
|
-
{
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
388
|
+
{ allSegmentsHidden ? (
|
|
389
|
+
<SvgEmptyState x={ 0 } y={ 0 } width={ width } height={ height }>
|
|
390
|
+
{ __(
|
|
391
|
+
'All segments are hidden. Click legend items to show data.',
|
|
392
|
+
'jetpack-charts'
|
|
393
|
+
) }
|
|
394
|
+
</SvgEmptyState>
|
|
395
|
+
) : (
|
|
396
|
+
<Pie< DataPointPercentageCalculated & { index: number } >
|
|
397
|
+
data={ dataWithIndex }
|
|
398
|
+
pieValue={ accessors.value }
|
|
399
|
+
outerRadius={ outerRadius }
|
|
400
|
+
innerRadius={ innerRadius }
|
|
401
|
+
padAngle={ padAngle }
|
|
402
|
+
cornerRadius={ cornerRadius }
|
|
403
|
+
>
|
|
404
|
+
{ pie => {
|
|
405
|
+
return pie.arcs.map( ( arc, index ) => {
|
|
406
|
+
const [ centroidX, centroidY ] = pie.path.centroid( arc );
|
|
407
|
+
const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.25;
|
|
408
|
+
const handleMouseMove = ( event: MouseEvent< SVGElement > ) => {
|
|
409
|
+
if ( ! withTooltips ) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Don't show tooltip until container bounds are measured
|
|
414
|
+
if ( containerBounds.width === 0 || containerBounds.height === 0 ) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Use clientX/Y and subtract containerBounds to cancel out any stale offset.
|
|
419
|
+
// TooltipInPortal calculates: tooltipLeft + containerBounds.left + scrollX
|
|
420
|
+
// By passing (clientX - containerBounds.left), we get:
|
|
421
|
+
// (clientX - containerBounds.left) + containerBounds.left + scrollX = clientX + scrollX
|
|
422
|
+
// This gives correct page coordinates regardless of stale bounds.
|
|
423
|
+
showTooltip( {
|
|
424
|
+
tooltipData: arc.data,
|
|
425
|
+
tooltipLeft: event.clientX - containerBounds.left + tooltipOffsetX,
|
|
426
|
+
tooltipTop: event.clientY - containerBounds.top + tooltipOffsetY,
|
|
427
|
+
} );
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
const pathProps: SVGProps< SVGPathElement > & {
|
|
431
|
+
'data-testid'?: string;
|
|
432
|
+
} = {
|
|
433
|
+
d: pie.path( arc ) || '',
|
|
434
|
+
fill: accessors.fill( arc.data ),
|
|
435
|
+
'data-testid': 'pie-segment',
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
const groupProps: SVGProps< SVGGElement > = {};
|
|
439
|
+
if ( withTooltips ) {
|
|
440
|
+
groupProps.onMouseMove = handleMouseMove;
|
|
441
|
+
groupProps.onMouseLeave = onMouseLeave;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Estimate text width more accurately for background sizing
|
|
445
|
+
const fontSize = 12;
|
|
446
|
+
const estimatedTextWidth = getStringWidth( arc.data.label, {
|
|
447
|
+
fontSize,
|
|
448
|
+
} );
|
|
449
|
+
const labelPadding = 6;
|
|
450
|
+
const backgroundWidth = estimatedTextWidth + labelPadding * 2;
|
|
451
|
+
const backgroundHeight = fontSize + labelPadding * 2;
|
|
452
|
+
|
|
453
|
+
return (
|
|
454
|
+
<g key={ `arc-${ index }` } { ...groupProps }>
|
|
455
|
+
<path { ...pathProps } />
|
|
456
|
+
{ showLabels && hasSpaceForLabel && (
|
|
457
|
+
<g>
|
|
458
|
+
{ providerTheme.labelBackgroundColor && (
|
|
459
|
+
<rect
|
|
460
|
+
x={ centroidX - backgroundWidth / 2 }
|
|
461
|
+
y={ centroidY - backgroundHeight / 2 }
|
|
462
|
+
width={ backgroundWidth }
|
|
463
|
+
height={ backgroundHeight }
|
|
464
|
+
fill={ providerTheme.labelBackgroundColor }
|
|
465
|
+
rx={ 4 }
|
|
466
|
+
ry={ 4 }
|
|
467
|
+
pointerEvents="none"
|
|
468
|
+
/>
|
|
469
|
+
) }
|
|
470
|
+
<text
|
|
471
|
+
x={ centroidX }
|
|
472
|
+
y={ centroidY }
|
|
473
|
+
dy=".33em"
|
|
474
|
+
fill={ providerTheme.labelTextColor || '#333' }
|
|
475
|
+
fontSize={ fontSize }
|
|
476
|
+
textAnchor="middle"
|
|
477
|
+
pointerEvents="none"
|
|
478
|
+
>
|
|
479
|
+
{ arc.data.label }
|
|
480
|
+
</text>
|
|
481
|
+
</g>
|
|
457
482
|
) }
|
|
458
|
-
<text
|
|
459
|
-
x={ centroidX }
|
|
460
|
-
y={ centroidY }
|
|
461
|
-
dy=".33em"
|
|
462
|
-
fill={ providerTheme.labelTextColor || '#333' }
|
|
463
|
-
fontSize={ fontSize }
|
|
464
|
-
textAnchor="middle"
|
|
465
|
-
pointerEvents="none"
|
|
466
|
-
>
|
|
467
|
-
{ arc.data.label }
|
|
468
|
-
</text>
|
|
469
483
|
</g>
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
</Pie>
|
|
476
|
-
) }
|
|
477
|
-
|
|
478
|
-
{ /* Render SVG children (like Group, Text) inside the SVG */ }
|
|
479
|
-
{ ! allSegmentsHidden && svgChildren }
|
|
480
|
-
</Group>
|
|
481
|
-
</svg>
|
|
482
|
-
</div>
|
|
483
|
-
|
|
484
|
-
{ legendPosition === 'bottom' && legendElement }
|
|
485
|
-
{ renderLegendSlot( legendChildren, 'bottom' ) }
|
|
486
|
-
|
|
487
|
-
{ withTooltips && tooltipOpen && tooltipData && (
|
|
488
|
-
<TooltipInPortal top={ tooltipTop || 0 } left={ tooltipLeft || 0 }>
|
|
489
|
-
<div role="tooltip">{ renderTooltip( { tooltipData } ) }</div>
|
|
490
|
-
</TooltipInPortal>
|
|
491
|
-
) }
|
|
492
|
-
|
|
493
|
-
{ /* Render HTML component children from PieChart.HTML */ }
|
|
494
|
-
{ htmlChildren }
|
|
484
|
+
);
|
|
485
|
+
} );
|
|
486
|
+
} }
|
|
487
|
+
</Pie>
|
|
488
|
+
) }
|
|
495
489
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
490
|
+
{ /* Render SVG children (like Group, Text) inside the SVG */ }
|
|
491
|
+
{ ! allSegmentsHidden && svgChildren }
|
|
492
|
+
</Group>
|
|
493
|
+
</svg>
|
|
494
|
+
</Stack>
|
|
495
|
+
);
|
|
496
|
+
} }
|
|
497
|
+
</ChartLayout>
|
|
499
498
|
</SingleChartContext.Provider>
|
|
500
499
|
);
|
|
501
500
|
};
|
|
@@ -6,9 +6,9 @@ import { PieChartUnresponsive as PieChart } from '../index';
|
|
|
6
6
|
|
|
7
7
|
describe( 'PieChart Composition API', () => {
|
|
8
8
|
const mockData = [
|
|
9
|
-
{ label: 'A', value: 30
|
|
10
|
-
{ label: 'B', value: 40
|
|
11
|
-
{ label: 'C', value: 30
|
|
9
|
+
{ label: 'A', value: 30 },
|
|
10
|
+
{ label: 'B', value: 40 },
|
|
11
|
+
{ label: 'C', value: 30 },
|
|
12
12
|
];
|
|
13
13
|
|
|
14
14
|
const renderWithChildren = ( props = {}, children = undefined ) => {
|