@automattic/charts 0.21.0 → 0.23.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 +28 -6
- package/dist/cjs/components/bar-chart/bar-chart.js +28 -16
- package/dist/cjs/components/bar-chart/use-bar-chart-options.js +5 -1
- package/dist/cjs/components/leaderboard-chart/leaderboard-chart.js +31 -10
- package/dist/cjs/components/leaderboard-chart/leaderboard-chart.module.scss.js +1 -1
- package/dist/cjs/components/legend/index.js +5 -1
- package/dist/cjs/components/legend/legend.js +23 -0
- package/dist/cjs/components/legend/use-chart-legend-data.js +107 -0
- package/dist/cjs/components/line-chart/line-chart.js +24 -20
- package/dist/cjs/components/pie-chart/pie-chart.js +27 -17
- package/dist/cjs/components/pie-semi-circle-chart/pie-semi-circle-chart.js +22 -15
- package/dist/cjs/components/pie-semi-circle-chart/pie-semi-circle-chart.module.scss.js +1 -1
- package/dist/cjs/components/shared/use-zero-value-display.js +43 -0
- package/dist/cjs/components/shared/with-responsive.js +2 -2
- package/dist/cjs/index.js +9 -5
- package/dist/cjs/providers/chart-context/chart-context.js +12 -7
- package/dist/cjs/providers/chart-context/index.js +1 -0
- package/dist/cjs/providers/chart-context/utils.js +3 -2
- package/dist/cjs/providers/theme/themes.js +42 -0
- package/dist/cjs/style.css +1 -1
- package/dist/mjs/components/bar-chart/bar-chart.js +31 -19
- package/dist/mjs/components/bar-chart/use-bar-chart-options.js +5 -1
- package/dist/mjs/components/leaderboard-chart/leaderboard-chart.js +32 -11
- package/dist/mjs/components/leaderboard-chart/leaderboard-chart.module.scss.js +1 -1
- package/dist/mjs/components/legend/index.js +3 -1
- package/dist/mjs/components/legend/legend.js +21 -0
- package/dist/mjs/components/legend/use-chart-legend-data.js +105 -0
- package/dist/mjs/components/line-chart/line-chart.js +26 -22
- package/dist/mjs/components/pie-chart/pie-chart.js +29 -19
- package/dist/mjs/components/pie-semi-circle-chart/pie-semi-circle-chart.js +22 -15
- package/dist/mjs/components/pie-semi-circle-chart/pie-semi-circle-chart.module.scss.js +1 -1
- package/dist/mjs/components/shared/use-zero-value-display.js +41 -0
- package/dist/mjs/components/shared/with-responsive.js +2 -2
- package/dist/mjs/index.js +3 -1
- package/dist/mjs/providers/chart-context/chart-context.js +13 -9
- package/dist/mjs/providers/chart-context/index.js +1 -1
- package/dist/mjs/providers/chart-context/utils.js +3 -2
- package/dist/mjs/providers/theme/themes.js +42 -0
- package/dist/mjs/style.css +1 -1
- package/dist/types/components/bar-chart/bar-chart.d.ts +1 -0
- package/dist/types/components/conversion-funnel-chart/conversion-funnel-chart.d.ts +32 -0
- package/dist/types/components/legend/index.d.ts +4 -2
- package/dist/types/components/legend/legend.d.ts +9 -0
- package/dist/types/components/legend/types.d.ts +6 -2
- package/dist/types/components/legend/use-chart-legend-data.d.ts +19 -0
- package/dist/types/components/line-chart/line-chart.d.ts +5 -2
- package/dist/types/components/pie-chart/pie-chart.d.ts +1 -2
- package/dist/types/index.d.ts +8 -4
- package/dist/types/providers/chart-context/chart-context.d.ts +4 -2
- package/dist/types/providers/chart-context/index.d.ts +1 -1
- package/dist/types/types.d.ts +26 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,25 +5,45 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.23.0] - 2025-07-30
|
|
9
|
+
### Added
|
|
10
|
+
- Add component ConversionFunnelChart. [#44433]
|
|
11
|
+
- Add showZeroValues option to BarChart to render zero-value with a small visible value. [#44443]
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Charts: Fix TS errors related to missing React import in stories. [#44190]
|
|
15
|
+
- Fix top margin for semi cicle chart. [#44539]
|
|
16
|
+
|
|
17
|
+
## [0.22.0] - 2025-07-28
|
|
18
|
+
### Added
|
|
19
|
+
- Add a standalone chart legend component. [#44245]
|
|
20
|
+
- Add docs for various charts. [#44441] [#44465] [#44466] [#44467]
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- Apply theme to the LeaderboardChart component. [#44419]
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- Fix issue where pie chart can be cut off. [#44490]
|
|
27
|
+
|
|
8
28
|
## [0.21.0] - 2025-07-25
|
|
9
29
|
### Added
|
|
10
|
-
- Add Storybook docs introduction [#44427]
|
|
30
|
+
- Add Storybook docs introduction. [#44427]
|
|
11
31
|
|
|
12
32
|
### Changed
|
|
13
|
-
- Fix export structure for charts [#44440]
|
|
14
|
-
-
|
|
33
|
+
- Fix export structure for charts. [#44440]
|
|
34
|
+
- Remove non-production files from built package. [#44438]
|
|
15
35
|
|
|
16
36
|
## [0.20.0] - 2025-07-23
|
|
17
37
|
### Added
|
|
18
|
-
- Line Chart: Add documentation [#44410]
|
|
38
|
+
- Line Chart: Add documentation. [#44410]
|
|
19
39
|
|
|
20
40
|
## [0.19.1] - 2025-07-22
|
|
21
41
|
### Changed
|
|
22
|
-
-
|
|
42
|
+
- Remove dependency on jetpack-components. [#44411]
|
|
23
43
|
|
|
24
44
|
## [0.19.0] - 2025-07-21
|
|
25
45
|
### Added
|
|
26
|
-
- Added more exports for Woo Analytics to get rid of visx dependencies [#44390]
|
|
46
|
+
- Added more exports for Woo Analytics to get rid of visx dependencies. [#44390]
|
|
27
47
|
|
|
28
48
|
## [0.18.0] - 2025-07-21
|
|
29
49
|
### Added
|
|
@@ -316,6 +336,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
316
336
|
- Fixed lints following ESLint rule changes for TS [#40584]
|
|
317
337
|
- Fixing a bug in Chart storybook data. [#40640]
|
|
318
338
|
|
|
339
|
+
[0.23.0]: https://github.com/Automattic/charts/compare/v0.22.0...v0.23.0
|
|
340
|
+
[0.22.0]: https://github.com/Automattic/charts/compare/v0.21.0...v0.22.0
|
|
319
341
|
[0.21.0]: https://github.com/Automattic/charts/compare/v0.20.0...v0.21.0
|
|
320
342
|
[0.20.0]: https://github.com/Automattic/charts/compare/v0.19.1...v0.20.0
|
|
321
343
|
[0.19.1]: https://github.com/Automattic/charts/compare/v0.19.0...v0.19.1
|
|
@@ -10,10 +10,13 @@ var react = require('react');
|
|
|
10
10
|
var chartContext = require('../../providers/chart-context/chart-context.js');
|
|
11
11
|
var utils = require('../../providers/chart-context/utils.js');
|
|
12
12
|
var themeProvider = require('../../providers/theme/theme-provider.js');
|
|
13
|
-
var
|
|
13
|
+
var legend = require('../legend/legend.js');
|
|
14
|
+
require('../legend/base-legend.js');
|
|
15
|
+
var useChartLegendData = require('../legend/use-chart-legend-data.js');
|
|
14
16
|
var useChartDataTransform = require('../shared/use-chart-data-transform.js');
|
|
15
17
|
var useChartMargin = require('../shared/use-chart-margin.js');
|
|
16
18
|
var useElementHeight = require('../shared/use-element-height.js');
|
|
19
|
+
var useZeroValueDisplay = require('../shared/use-zero-value-display.js');
|
|
17
20
|
var withResponsive = require('../shared/with-responsive.js');
|
|
18
21
|
var accessibleTooltip = require('../tooltip/accessible-tooltip.js');
|
|
19
22
|
var barChart_module = require('./bar-chart.module.scss.js');
|
|
@@ -33,14 +36,21 @@ const validateData = (data) => {
|
|
|
33
36
|
return null;
|
|
34
37
|
};
|
|
35
38
|
const getPatternId = (chartId, index) => `bar-pattern-${chartId}-${index}`;
|
|
36
|
-
const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400, className, margin, withTooltips = false, showLegend = false, legendOrientation = 'horizontal', legendAlignmentHorizontal = 'center', legendAlignmentVertical = 'bottom', legendShape = 'rect', gridVisibility: gridVisibilityProp, renderTooltip, options = {}, orientation = 'vertical', withPatterns = false, }) => {
|
|
39
|
+
const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400, className, margin, withTooltips = false, showLegend = false, legendOrientation = 'horizontal', legendAlignmentHorizontal = 'center', legendAlignmentVertical = 'bottom', legendShape = 'rect', gridVisibility: gridVisibilityProp, renderTooltip, options = {}, orientation = 'vertical', withPatterns = false, showZeroValues = false, }) => {
|
|
37
40
|
const horizontal = orientation === 'horizontal';
|
|
38
41
|
// Generate a unique chart ID to avoid pattern conflicts with multiple charts
|
|
39
42
|
const internalChartId = react.useId();
|
|
40
43
|
const chartId = utils.useChartId(providedChartId);
|
|
44
|
+
const providerTheme = themeProvider.useChartTheme();
|
|
41
45
|
const theme = themeProvider.useXYChartTheme(data);
|
|
42
46
|
const dataSorted = useChartDataTransform.useChartDataTransform(data);
|
|
43
|
-
|
|
47
|
+
// Transform data to add a small value for zero bars to make them visible
|
|
48
|
+
const dataWithVisibleZeros = useZeroValueDisplay.useZeroValueDisplay(dataSorted, {
|
|
49
|
+
enabled: showZeroValues,
|
|
50
|
+
});
|
|
51
|
+
// Create legend items using the reusable hook
|
|
52
|
+
const legendItems = useChartLegendData.useChartLegendData(dataSorted, providerTheme);
|
|
53
|
+
const chartOptions = useBarChartOptions.useBarChartOptions(dataWithVisibleZeros, horizontal, options);
|
|
44
54
|
const defaultMargin = useChartMargin.useChartMargin(height, chartOptions, dataSorted, theme, horizontal);
|
|
45
55
|
const [legendRef, legendHeight] = useElementHeight.useElementHeight();
|
|
46
56
|
const chartRef = react.useRef(null);
|
|
@@ -129,19 +139,13 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400,
|
|
|
129
139
|
// Validate data first
|
|
130
140
|
const error = validateData(dataSorted);
|
|
131
141
|
const isDataValid = !error;
|
|
132
|
-
//
|
|
133
|
-
const
|
|
134
|
-
label: group.label, // Label for each unique group
|
|
135
|
-
value: '', // Empty string since we don't want to show a specific value
|
|
136
|
-
color: getColor(group, index),
|
|
137
|
-
shapeStyle: group?.options?.legendShapeStyle,
|
|
138
|
-
})), [dataSorted, getColor]);
|
|
139
|
-
// Register chart with context only if data is valid
|
|
140
|
-
const providerTheme = themeProvider.useChartTheme();
|
|
141
|
-
utils.useChartRegistration(chartId, legendItems, providerTheme, 'bar', isDataValid, {
|
|
142
|
+
// Memoize metadata to prevent unnecessary re-registration
|
|
143
|
+
const chartMetadata = react.useMemo(() => ({
|
|
142
144
|
orientation,
|
|
143
145
|
withPatterns,
|
|
144
|
-
});
|
|
146
|
+
}), [orientation, withPatterns]);
|
|
147
|
+
// Register chart with context only if data is valid
|
|
148
|
+
utils.useChartRegistration(chartId, legendItems, providerTheme, 'bar', isDataValid, chartMetadata);
|
|
145
149
|
if (error) {
|
|
146
150
|
return jsxRuntime.jsx("div", { className: clsx('bar-chart', barChart_module.default['bar-chart']), children: error });
|
|
147
151
|
}
|
|
@@ -158,9 +162,17 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400,
|
|
|
158
162
|
...(showLegend && legendAlignmentVertical === 'top'
|
|
159
163
|
? { top: (defaultMargin.top || 0) + legendHeight }
|
|
160
164
|
: {}),
|
|
161
|
-
}, xScale: chartOptions.xScale, yScale: chartOptions.yScale, horizontal: horizontal, pointerEventsDataKey: "nearest", children: [jsxRuntime.jsx(xychart.Grid, { columns: gridVisibility.includes('y'), rows: gridVisibility.includes('x'), numTicks: 4 }), withPatterns && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("defs", { "data-testid": "bar-chart-patterns", children: dataSorted.map((seriesData, index) => renderPattern(index, getColor(seriesData, index))) }), jsxRuntime.jsx("style", { children: dataSorted.map((seriesData, index) => createPatternBorderStyle(index, getColor(seriesData, index))) })] })), highlightedBarStyle && jsxRuntime.jsx("style", { children: highlightedBarStyle }), jsxRuntime.jsx(xychart.BarGroup, { padding: chartOptions.barGroup.padding, children:
|
|
165
|
+
}, xScale: chartOptions.xScale, yScale: chartOptions.yScale, horizontal: horizontal, pointerEventsDataKey: "nearest", children: [jsxRuntime.jsx(xychart.Grid, { columns: gridVisibility.includes('y'), rows: gridVisibility.includes('x'), numTicks: 4 }), withPatterns && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("defs", { "data-testid": "bar-chart-patterns", children: dataSorted.map((seriesData, index) => renderPattern(index, getColor(seriesData, index))) }), jsxRuntime.jsx("style", { children: dataSorted.map((seriesData, index) => createPatternBorderStyle(index, getColor(seriesData, index))) })] })), highlightedBarStyle && jsxRuntime.jsx("style", { children: highlightedBarStyle }), jsxRuntime.jsx(xychart.BarGroup, { padding: chartOptions.barGroup.padding, children: dataWithVisibleZeros.map((seriesData, index) => (jsxRuntime.jsx(xychart.BarSeries, { dataKey: seriesData?.label, data: seriesData.data, yAccessor: chartOptions.accessors.yAccessor, xAccessor: chartOptions.accessors.xAccessor, colorAccessor: getBarBackground(index) }, seriesData?.label))) }), jsxRuntime.jsx(xychart.Axis, { ...chartOptions.axis.x }), jsxRuntime.jsx(xychart.Axis, { ...chartOptions.axis.y }), withTooltips && (jsxRuntime.jsx(accessibleTooltip.AccessibleTooltip, { detectBounds: true, snapTooltipToDatumX: true, snapTooltipToDatumY: true, renderTooltip: renderTooltip || renderDefaultTooltip, selectedIndex: selectedIndex, tooltipRef: tooltipRef, keyboardFocusedClassName: barChart_module.default['bar-chart__tooltip--keyboard-focused'], series: data, mode: "individual" }))] }), showLegend && (jsxRuntime.jsx(legend.Legend, { items: legendItems, orientation: legendOrientation, alignmentHorizontal: legendAlignmentHorizontal, alignmentVertical: legendAlignmentVertical, className: barChart_module.default['bar-chart__legend'], shape: legendShape, ref: legendRef, chartId: chartId }))] }));
|
|
166
|
+
};
|
|
167
|
+
const BarChart = props => {
|
|
168
|
+
const existingContext = react.useContext(chartContext.ChartContext);
|
|
169
|
+
// If we're already in a ChartProvider context, don't create a new one
|
|
170
|
+
if (existingContext) {
|
|
171
|
+
return jsxRuntime.jsx(BarChartInternal, { ...props });
|
|
172
|
+
}
|
|
173
|
+
// Otherwise, create our own ChartProvider
|
|
174
|
+
return (jsxRuntime.jsx(chartContext.ChartProvider, { children: jsxRuntime.jsx(BarChartInternal, { ...props }) }));
|
|
162
175
|
};
|
|
163
|
-
const BarChart = props => (jsxRuntime.jsx(chartContext.ChartProvider, { children: jsxRuntime.jsx(BarChartInternal, { ...props }) }));
|
|
164
176
|
BarChart.displayName = 'BarChart';
|
|
165
177
|
var BarChart$1 = withResponsive.withResponsive(BarChart);
|
|
166
178
|
|
|
@@ -44,7 +44,11 @@ function useBarChartOptions(data, horizontal, options = {}) {
|
|
|
44
44
|
: formatDateTick;
|
|
45
45
|
const valueFormatter = numberFormatters.formatNumberCompact;
|
|
46
46
|
const labelAccessor = (d) => d?.label || d?.date;
|
|
47
|
-
const valueAccessor = (d) =>
|
|
47
|
+
const valueAccessor = (d) => {
|
|
48
|
+
// Use visualValue for bar rendering if available (for zero values), otherwise use value
|
|
49
|
+
const enhancedPoint = d;
|
|
50
|
+
return enhancedPoint?.visualValue !== undefined ? enhancedPoint.visualValue : d?.value;
|
|
51
|
+
};
|
|
48
52
|
return {
|
|
49
53
|
vertical: {
|
|
50
54
|
xTickFormat: labelFormatter,
|
|
@@ -4,11 +4,24 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
var components = require('@wordpress/components');
|
|
7
|
+
var element = require('@wordpress/element');
|
|
7
8
|
var clsx = require('clsx');
|
|
8
9
|
require('react');
|
|
10
|
+
var themeProvider = require('../../providers/theme/theme-provider.js');
|
|
9
11
|
var formatMetricValue = require('../shared/format-metric-value.js');
|
|
10
12
|
var leaderboardChart_module = require('./leaderboard-chart.module.scss.js');
|
|
11
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Default settings for LeaderboardChart component
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_LEADERBOARD_SETTINGS = {
|
|
18
|
+
labelSpacing: 1.5,
|
|
19
|
+
rowGap: 12,
|
|
20
|
+
columnGap: 4,
|
|
21
|
+
primaryColor: '#3858E9',
|
|
22
|
+
secondaryColor: '#66BDFF',
|
|
23
|
+
deltaColors: ['#D63638', '#757575', '#008A20'],
|
|
24
|
+
};
|
|
12
25
|
/**
|
|
13
26
|
* Default value formatter using formatMetricValue
|
|
14
27
|
*
|
|
@@ -49,25 +62,33 @@ const defaultDeltaFormatter = (value) => {
|
|
|
49
62
|
* @param props.style - Custom styling for the chart container
|
|
50
63
|
* @return JSX element representing the leaderboard chart
|
|
51
64
|
*/
|
|
52
|
-
const LeaderboardChart = ({ data, withComparison = false, primaryColor
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
65
|
+
const LeaderboardChart = ({ data, withComparison = false, primaryColor, secondaryColor, valueFormatter = defaultValueFormatter, deltaFormatter = defaultDeltaFormatter, loading = false, className, style, }) => {
|
|
66
|
+
const theme = themeProvider.useChartTheme();
|
|
67
|
+
// Get component settings from theme with fallbacks
|
|
68
|
+
const leaderboardSettings = theme.leaderboardChart;
|
|
69
|
+
const labelSpacing = leaderboardSettings?.labelSpacing ?? DEFAULT_LEADERBOARD_SETTINGS.labelSpacing;
|
|
70
|
+
const rowGap = leaderboardSettings?.rowGap ?? DEFAULT_LEADERBOARD_SETTINGS.rowGap;
|
|
71
|
+
const columnGap = leaderboardSettings?.columnGap ?? DEFAULT_LEADERBOARD_SETTINGS.columnGap;
|
|
72
|
+
// Use theme colors with prop overrides, fallback to defaults
|
|
73
|
+
const finalPrimaryColor = primaryColor || leaderboardSettings?.primaryColor || DEFAULT_LEADERBOARD_SETTINGS.primaryColor;
|
|
74
|
+
const finalSecondaryColor = secondaryColor ||
|
|
75
|
+
leaderboardSettings?.secondaryColor ||
|
|
76
|
+
DEFAULT_LEADERBOARD_SETTINGS.secondaryColor;
|
|
77
|
+
// Delta sign colors: negative, neutral, positive
|
|
78
|
+
const signColors = leaderboardSettings?.deltaColors ?? DEFAULT_LEADERBOARD_SETTINGS.deltaColors;
|
|
58
79
|
const chartStyle = {
|
|
59
|
-
'--primary-color':
|
|
60
|
-
'--secondary-color':
|
|
80
|
+
'--primary-color': finalPrimaryColor,
|
|
81
|
+
'--secondary-color': finalSecondaryColor,
|
|
61
82
|
...style,
|
|
62
83
|
};
|
|
63
84
|
// Handle empty or undefined data
|
|
64
85
|
if (!data || data.length === 0) {
|
|
65
86
|
return (jsxRuntime.jsx("div", { className: clsx(leaderboardChart_module.default.leaderboardChart, loading && leaderboardChart_module.default.loading, className), style: chartStyle, children: jsxRuntime.jsx("div", { className: leaderboardChart_module.default.emptyState, children: loading ? 'Loading...' : 'No data available' }) }));
|
|
66
87
|
}
|
|
67
|
-
return (jsxRuntime.jsx(
|
|
88
|
+
return (jsxRuntime.jsx(components.__experimentalGrid, { className: clsx(leaderboardChart_module.default.leaderboardChart, loading && leaderboardChart_module.default.loading, className), templateColumns: "minmax(0, 1fr) auto", rowGap: rowGap, columnGap: columnGap, style: chartStyle, children: data.map(entry => {
|
|
68
89
|
const colorIndex = Math.sign(entry.delta) + 1;
|
|
69
90
|
const deltaColor = signColors[colorIndex];
|
|
70
|
-
return (jsxRuntime.jsxs(
|
|
91
|
+
return (jsxRuntime.jsxs(element.Fragment, { children: [jsxRuntime.jsxs(components.__experimentalVStack, { spacing: labelSpacing, children: [jsxRuntime.jsx(components.__experimentalText, { children: entry.label }), jsxRuntime.jsxs("div", { className: leaderboardChart_module.default.progressContainer, children: [jsxRuntime.jsx(components.ProgressBar, { value: entry.currentShare, className: clsx(leaderboardChart_module.default.progressBar, leaderboardChart_module.default.primaryBar) }), withComparison && (jsxRuntime.jsx(components.ProgressBar, { value: entry.previousShare, className: clsx(leaderboardChart_module.default.progressBar, leaderboardChart_module.default.secondaryBar) }))] })] }), jsxRuntime.jsxs("div", { className: leaderboardChart_module.default.valueContainer, children: [jsxRuntime.jsx(components.__experimentalText, { children: valueFormatter(entry.currentValue) }), withComparison && (jsxRuntime.jsx(components.__experimentalText, { style: { color: deltaColor }, children: deltaFormatter(entry.delta) }))] })] }, entry.id));
|
|
71
92
|
}) }));
|
|
72
93
|
};
|
|
73
94
|
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var styles = {"leaderboardChart":"leaderboard-chart-module_leaderboardChart__zxakP","loading":"leaderboard-chart-module_loading__-AGv-","
|
|
5
|
+
var styles = {"leaderboardChart":"leaderboard-chart-module_leaderboardChart__zxakP","loading":"leaderboard-chart-module_loading__-AGv-","progressContainer":"leaderboard-chart-module_progressContainer__BZGbj","progressBar":"leaderboard-chart-module_progressBar__LAQaj","primaryBar":"leaderboard-chart-module_primaryBar__iybII","secondaryBar":"leaderboard-chart-module_secondaryBar__A7tLz","valueContainer":"leaderboard-chart-module_valueContainer__ZlLh4","emptyState":"leaderboard-chart-module_emptyState__0dkfy"};
|
|
6
6
|
|
|
7
7
|
exports.default = styles;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var legend = require('./legend.js');
|
|
3
4
|
var baseLegend = require('./base-legend.js');
|
|
5
|
+
var useChartLegendData = require('./use-chart-legend-data.js');
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
exports.Legend =
|
|
9
|
+
exports.Legend = legend.Legend;
|
|
10
|
+
exports.BaseLegend = baseLegend.BaseLegend;
|
|
11
|
+
exports.useChartLegendData = useChartLegendData.useChartLegendData;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var chartContext = require('../../providers/chart-context/chart-context.js');
|
|
6
|
+
var baseLegend = require('./base-legend.js');
|
|
7
|
+
|
|
8
|
+
const Legend = react.forwardRef(({ chartId, items, ...props }, ref) => {
|
|
9
|
+
// Get context but don't throw if it doesn't exist
|
|
10
|
+
const context = react.useContext(chartContext.ChartContext);
|
|
11
|
+
// Use useMemo to ensure re-rendering when context changes
|
|
12
|
+
const contextItems = react.useMemo(() => {
|
|
13
|
+
return chartId && context ? context.getChartData(chartId)?.legendItems : undefined;
|
|
14
|
+
}, [chartId, context]);
|
|
15
|
+
// Use context items if available, otherwise fall back to provided items
|
|
16
|
+
const legendItems = (contextItems || items);
|
|
17
|
+
if (!legendItems) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return jsxRuntime.jsx(baseLegend.BaseLegend, { ref: ref, items: legendItems, ...props });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
exports.Legend = Legend;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Formats the value for a data point based on its type
|
|
7
|
+
* @param point - The data point to format
|
|
8
|
+
* @param showValues - Whether to show values or return empty string
|
|
9
|
+
* @return Formatted value string
|
|
10
|
+
*/
|
|
11
|
+
function formatPointValue(point, showValues) {
|
|
12
|
+
if (!showValues) {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
if ('percentage' in point) {
|
|
16
|
+
return `${point.percentage}%`;
|
|
17
|
+
}
|
|
18
|
+
else if ('value' in point) {
|
|
19
|
+
return point.value.toString();
|
|
20
|
+
}
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates a base legend item with common properties
|
|
25
|
+
* @param label - The label for the legend item
|
|
26
|
+
* @param value - The value for the legend item
|
|
27
|
+
* @param color - The color for the legend item
|
|
28
|
+
* @return Base legend item object
|
|
29
|
+
*/
|
|
30
|
+
function createBaseLegendItem(label, value, color) {
|
|
31
|
+
return {
|
|
32
|
+
label,
|
|
33
|
+
value,
|
|
34
|
+
color,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Processes SeriesData into legend items
|
|
39
|
+
* @param seriesData - The series data to process
|
|
40
|
+
* @param theme - The chart theme for colors
|
|
41
|
+
* @param showValues - Whether to show values in legend
|
|
42
|
+
* @param withGlyph - Whether to include glyph rendering
|
|
43
|
+
* @param glyphSize - Size of the glyph
|
|
44
|
+
* @param renderGlyph - Component to render the glyph
|
|
45
|
+
* @return Array of processed legend items
|
|
46
|
+
*/
|
|
47
|
+
function processSeriesData(seriesData, theme, showValues, withGlyph, glyphSize, renderGlyph) {
|
|
48
|
+
const mapper = (series, index) => {
|
|
49
|
+
const baseItem = createBaseLegendItem(series.label, showValues ? series.data?.length?.toString() || '0' : '', theme.colors[index % theme.colors.length]);
|
|
50
|
+
if (withGlyph && renderGlyph) {
|
|
51
|
+
return {
|
|
52
|
+
...baseItem,
|
|
53
|
+
glyphSize,
|
|
54
|
+
renderGlyph,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return baseItem;
|
|
58
|
+
};
|
|
59
|
+
return seriesData.map(mapper);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Processes point data into legend items
|
|
63
|
+
* @param pointData - The point data to process
|
|
64
|
+
* @param theme - The chart theme for colors
|
|
65
|
+
* @param showValues - Whether to show values in legend
|
|
66
|
+
* @param withGlyph - Whether to include glyph rendering
|
|
67
|
+
* @param glyphSize - Size of the glyph
|
|
68
|
+
* @param renderGlyph - Component to render the glyph
|
|
69
|
+
* @return Array of processed legend items
|
|
70
|
+
*/
|
|
71
|
+
function processPointData(pointData, theme, showValues, withGlyph, glyphSize, renderGlyph) {
|
|
72
|
+
const mapper = (point, index) => {
|
|
73
|
+
const baseItem = createBaseLegendItem(point.label, formatPointValue(point, showValues), theme.colors[index % theme.colors.length]);
|
|
74
|
+
if (withGlyph && renderGlyph) {
|
|
75
|
+
return {
|
|
76
|
+
...baseItem,
|
|
77
|
+
glyphSize,
|
|
78
|
+
renderGlyph,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return baseItem;
|
|
82
|
+
};
|
|
83
|
+
return pointData.map(mapper);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Hook to transform chart data into legend items
|
|
87
|
+
* @param data - The chart data to transform
|
|
88
|
+
* @param theme - The chart theme for colors
|
|
89
|
+
* @param options - Configuration options for legend generation
|
|
90
|
+
* @return Array of legend items ready for display
|
|
91
|
+
*/
|
|
92
|
+
function useChartLegendData(data, theme, options = {}) {
|
|
93
|
+
const { showValues = false, withGlyph = false, glyphSize = 8, renderGlyph } = options;
|
|
94
|
+
return react.useMemo(() => {
|
|
95
|
+
if (!data || !Array.isArray(data) || data.length === 0) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
// Handle SeriesData (multiple series with data points)
|
|
99
|
+
if ('data' in data[0]) {
|
|
100
|
+
return processSeriesData(data, theme, showValues, withGlyph, glyphSize, renderGlyph);
|
|
101
|
+
}
|
|
102
|
+
// Handle DataPointDate or DataPointPercentage (single data points)
|
|
103
|
+
return processPointData(data, theme, showValues, withGlyph, glyphSize, renderGlyph);
|
|
104
|
+
}, [data, theme, showValues, withGlyph, glyphSize, renderGlyph]);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
exports.useChartLegendData = useChartLegendData;
|
|
@@ -12,7 +12,9 @@ var clsx = require('clsx');
|
|
|
12
12
|
var chartContext = require('../../providers/chart-context/chart-context.js');
|
|
13
13
|
var utils = require('../../providers/chart-context/utils.js');
|
|
14
14
|
var themeProvider = require('../../providers/theme/theme-provider.js');
|
|
15
|
-
var
|
|
15
|
+
var legend = require('../legend/legend.js');
|
|
16
|
+
require('../legend/base-legend.js');
|
|
17
|
+
var useChartLegendData = require('../legend/use-chart-legend-data.js');
|
|
16
18
|
var defaultGlyph = require('../shared/default-glyph.js');
|
|
17
19
|
var useChartDataTransform = require('../shared/use-chart-data-transform.js');
|
|
18
20
|
var useChartMargin = require('../shared/use-chart-margin.js');
|
|
@@ -195,30 +197,24 @@ const LineChartInternal = react.forwardRef(({ data, chartId: providedChartId, wi
|
|
|
195
197
|
const defaultMargin = useChartMargin.useChartMargin(height, chartOptions, dataSorted, theme);
|
|
196
198
|
const error = validateData(dataSorted);
|
|
197
199
|
const isDataValid = !error;
|
|
198
|
-
//
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
value: '', // Empty string since we don't want to show a specific value
|
|
202
|
-
color: group?.options?.stroke ?? providerTheme.colors[index % providerTheme.colors.length],
|
|
203
|
-
shapeStyle: group?.options?.legendShapeStyle,
|
|
204
|
-
renderGlyph: withLegendGlyph ? providerTheme.glyphs?.[index] ?? renderGlyph : undefined,
|
|
200
|
+
// Memoize legend options to prevent unnecessary re-calculations
|
|
201
|
+
const legendOptions = react.useMemo(() => ({
|
|
202
|
+
withGlyph: withLegendGlyph,
|
|
205
203
|
glyphSize: Math.max(0, toNumber(glyphStyle?.radius) ?? 4),
|
|
206
|
-
})), [
|
|
207
|
-
dataSorted,
|
|
208
|
-
providerTheme.colors,
|
|
209
|
-
providerTheme.glyphs,
|
|
210
|
-
withLegendGlyph,
|
|
211
204
|
renderGlyph,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
205
|
+
}), [withLegendGlyph, glyphStyle?.radius, renderGlyph]);
|
|
206
|
+
// Create legend items using the reusable hook
|
|
207
|
+
const legendItems = useChartLegendData.useChartLegendData(dataSorted, providerTheme, legendOptions);
|
|
208
|
+
// Memoize metadata to prevent unnecessary re-registration
|
|
209
|
+
const chartMetadata = react.useMemo(() => ({
|
|
216
210
|
withGradientFill,
|
|
217
211
|
smoothing,
|
|
218
212
|
curveType,
|
|
219
213
|
withStartGlyphs,
|
|
220
214
|
withLegendGlyph,
|
|
221
|
-
});
|
|
215
|
+
}), [withGradientFill, smoothing, curveType, withStartGlyphs, withLegendGlyph]);
|
|
216
|
+
// Register chart with context only if data is valid
|
|
217
|
+
utils.useChartRegistration(chartId, legendItems, providerTheme, 'line', isDataValid, chartMetadata);
|
|
222
218
|
const accessors = {
|
|
223
219
|
xAccessor: (d) => d?.date,
|
|
224
220
|
yAccessor: (d) => d?.value,
|
|
@@ -254,9 +250,17 @@ const LineChartInternal = react.forwardRef(({ data, chartId: providedChartId, wi
|
|
|
254
250
|
return (jsxRuntime.jsxs("g", { children: [withStartGlyphs && (jsxRuntime.jsx(StartGlyph, { index: index, data: seriesData, color: stroke, renderGlyph: providerTheme.glyphs?.[index] ?? renderGlyph, accessors: accessors, glyphStyle: glyphStyle })), withGradientFill && (jsxRuntime.jsx(gradient.LinearGradient, { id: `area-gradient-${internalChartId}-${index + 1}`, from: stroke, fromOpacity: 0.4, toOpacity: 0.1, to: theme.backgroundColor, ...seriesData.options?.gradient, "data-testid": "line-gradient" })), jsxRuntime.jsx(xychart.AreaSeries, { dataKey: seriesData?.label, data: seriesData.data, ...accessors, fill: withGradientFill
|
|
255
251
|
? `url(#area-gradient-${internalChartId}-${index + 1})`
|
|
256
252
|
: 'transparent', renderLine: true, curve: getCurveType(curveType, smoothing), lineProps: lineProps }, seriesData?.label)] }, seriesData?.label || index));
|
|
257
|
-
}), withTooltips && (jsxRuntime.jsx(accessibleTooltip.AccessibleTooltip, { detectBounds: true, snapTooltipToDatumX: true, snapTooltipToDatumY: true, showSeriesGlyphs: true, renderTooltip: renderTooltip, renderGlyph: tooltipRenderGlyph, glyphStyle: glyphStyle, showVerticalCrosshair: withTooltipCrosshairs?.showVertical, showHorizontalCrosshair: withTooltipCrosshairs?.showHorizontal, selectedIndex: selectedIndex, tooltipRef: tooltipRef, keyboardFocusedClassName: lineChart_module.default['line-chart__tooltip--keyboard-focused'], series: dataSorted })), jsxRuntime.jsx(LineChartScalesRef, { chartRef: internalChartRef, width: width, height: height, margin: margin })] }) }), showLegend && (jsxRuntime.jsx(
|
|
253
|
+
}), withTooltips && (jsxRuntime.jsx(accessibleTooltip.AccessibleTooltip, { detectBounds: true, snapTooltipToDatumX: true, snapTooltipToDatumY: true, showSeriesGlyphs: true, renderTooltip: renderTooltip, renderGlyph: tooltipRenderGlyph, glyphStyle: glyphStyle, showVerticalCrosshair: withTooltipCrosshairs?.showVertical, showHorizontalCrosshair: withTooltipCrosshairs?.showHorizontal, selectedIndex: selectedIndex, tooltipRef: tooltipRef, keyboardFocusedClassName: lineChart_module.default['line-chart__tooltip--keyboard-focused'], series: dataSorted })), jsxRuntime.jsx(LineChartScalesRef, { chartRef: internalChartRef, width: width, height: height, margin: margin })] }) }), showLegend && (jsxRuntime.jsx(legend.Legend, { items: legendItems, orientation: legendOrientation, alignmentHorizontal: legendAlignmentHorizontal, alignmentVertical: legendAlignmentVertical, className: lineChart_module.default['line-chart-legend'], shape: legendShape, chartId: chartId, ref: legendRef })), children] }) }));
|
|
254
|
+
});
|
|
255
|
+
const LineChart = react.forwardRef((props, ref) => {
|
|
256
|
+
const existingContext = react.useContext(chartContext.ChartContext);
|
|
257
|
+
// If we're already in a ChartProvider context, don't create a new one
|
|
258
|
+
if (existingContext) {
|
|
259
|
+
return jsxRuntime.jsx(LineChartInternal, { ...props, ref: ref });
|
|
260
|
+
}
|
|
261
|
+
// Otherwise, create our own ChartProvider
|
|
262
|
+
return (jsxRuntime.jsx(chartContext.ChartProvider, { children: jsxRuntime.jsx(LineChartInternal, { ...props, ref: ref }) }));
|
|
258
263
|
});
|
|
259
|
-
const LineChart = react.forwardRef((props, ref) => (jsxRuntime.jsx(chartContext.ChartProvider, { children: jsxRuntime.jsx(LineChartInternal, { ...props, ref: ref }) })));
|
|
260
264
|
LineChart.displayName = 'LineChart';
|
|
261
265
|
LineChart.AnnotationsOverlay = lineChartAnnotationsOverlay.default;
|
|
262
266
|
LineChart.Annotation = lineChartAnnotation.default;
|
|
@@ -12,7 +12,9 @@ var chartContext = require('../../providers/chart-context/chart-context.js');
|
|
|
12
12
|
var utils = require('../../providers/chart-context/utils.js');
|
|
13
13
|
var themeProvider = require('../../providers/theme/theme-provider.js');
|
|
14
14
|
var themes = require('../../providers/theme/themes.js');
|
|
15
|
-
var
|
|
15
|
+
var legend = require('../legend/legend.js');
|
|
16
|
+
require('../legend/base-legend.js');
|
|
17
|
+
var useChartLegendData = require('../legend/use-chart-legend-data.js');
|
|
16
18
|
var useElementHeight = require('../shared/use-element-height.js');
|
|
17
19
|
var withResponsive = require('../shared/with-responsive.js');
|
|
18
20
|
var baseTooltip = require('../tooltip/base-tooltip.js');
|
|
@@ -53,30 +55,30 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
53
55
|
const { onMouseMove, onMouseLeave, tooltipOpen, tooltipData, tooltipLeft, tooltipTop } = useChartMouseHandler.default({
|
|
54
56
|
withTooltips,
|
|
55
57
|
});
|
|
58
|
+
// Memoize legend options to prevent unnecessary re-calculations
|
|
59
|
+
const legendOptions = react.useMemo(() => ({ showValues: true }), []);
|
|
60
|
+
// Create legend items using the reusable hook
|
|
61
|
+
const legendItems = useChartLegendData.useChartLegendData(data, providerTheme, legendOptions);
|
|
56
62
|
const { isValid, message } = validateData(data);
|
|
57
|
-
//
|
|
58
|
-
const
|
|
59
|
-
label: item.label,
|
|
60
|
-
value: item.value.toString(),
|
|
61
|
-
color: providerTheme.colors[index % providerTheme.colors.length],
|
|
62
|
-
})), [data, providerTheme.colors]);
|
|
63
|
-
// Register chart with context only if data is valid
|
|
64
|
-
utils.useChartRegistration(chartId, legendItems, providerTheme, 'pie', isValid, {
|
|
63
|
+
// Memoize metadata to prevent unnecessary re-registration
|
|
64
|
+
const chartMetadata = react.useMemo(() => ({
|
|
65
65
|
thickness,
|
|
66
66
|
gapScale,
|
|
67
67
|
cornerScale,
|
|
68
|
-
});
|
|
68
|
+
}), [thickness, gapScale, cornerScale]);
|
|
69
|
+
// Register chart with context only if data is valid
|
|
70
|
+
utils.useChartRegistration(chartId, legendItems, providerTheme, 'pie', isValid, chartMetadata);
|
|
69
71
|
if (!isValid) {
|
|
70
72
|
return (jsxRuntime.jsx("div", { className: clsx('pie-chart', pieChart_module.default['pie-chart'], className), children: jsxRuntime.jsx("div", { className: pieChart_module.default['error-message'], children: message }) }));
|
|
71
73
|
}
|
|
72
74
|
const width = size;
|
|
73
75
|
const height = size;
|
|
76
|
+
const adjustedHeight = showLegend && legendAlignmentVertical === 'top' ? height - legendHeight : height;
|
|
74
77
|
// Calculate radius based on width/height
|
|
75
|
-
const radius = Math.min(width,
|
|
76
|
-
// Center the chart in the available space
|
|
78
|
+
const radius = Math.min(width, adjustedHeight) / 2;
|
|
79
|
+
// Center the chart in the available space
|
|
77
80
|
const centerX = width / 2;
|
|
78
|
-
const
|
|
79
|
-
const centerY = height / 2 + legendOffset;
|
|
81
|
+
const centerY = adjustedHeight / 2;
|
|
80
82
|
// Calculate the angle between each
|
|
81
83
|
const padAngle = gapScale * ((2 * Math.PI) / data.length);
|
|
82
84
|
const outerRadius = radius - padding;
|
|
@@ -96,7 +98,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
96
98
|
return (jsxRuntime.jsxs("div", { className: clsx('pie-chart', pieChart_module.default['pie-chart'], className), style: {
|
|
97
99
|
display: 'flex',
|
|
98
100
|
flexDirection: showLegend && legendAlignmentVertical === 'top' ? 'column-reverse' : 'column',
|
|
99
|
-
}, children: [jsxRuntime.jsx("svg", { viewBox: `0 0 ${size} ${
|
|
101
|
+
}, children: [jsxRuntime.jsx("svg", { viewBox: `0 0 ${size} ${adjustedHeight}`, preserveAspectRatio: "xMidYMid meet", width: size, height: adjustedHeight, children: jsxRuntime.jsxs(group.Group, { top: centerY, left: centerX, children: [jsxRuntime.jsx(shape.Pie, { data: dataWithIndex, pieValue: accessors.value, outerRadius: outerRadius, innerRadius: innerRadius, padAngle: padAngle, cornerRadius: cornerRadius, children: pie => {
|
|
100
102
|
return pie.arcs.map((arc, index) => {
|
|
101
103
|
const [centroidX, centroidY] = pie.path.centroid(arc);
|
|
102
104
|
const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.25;
|
|
@@ -111,11 +113,19 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
111
113
|
}
|
|
112
114
|
return (jsxRuntime.jsxs("g", { children: [jsxRuntime.jsx("path", { ...pathProps }), hasSpaceForLabel && (jsxRuntime.jsx("text", { x: centroidX, y: centroidY, dy: ".33em", fill: providerTheme.labelBackgroundColor || themes.defaultTheme.labelBackgroundColor, fontSize: 12, textAnchor: "middle", pointerEvents: "none", children: arc.data.label }))] }, `arc-${index}`));
|
|
113
115
|
});
|
|
114
|
-
} }), children] }) }), showLegend && (jsxRuntime.jsx(
|
|
116
|
+
} }), children] }) }), showLegend && (jsxRuntime.jsx(legend.Legend, { items: legendItems, orientation: legendOrientation, alignmentHorizontal: legendAlignmentHorizontal, alignmentVertical: legendAlignmentVertical, className: pieChart_module.default['pie-chart-legend'], shape: legendShape, ref: legendRef, chartId: chartId })), withTooltips && tooltipOpen && tooltipData && (jsxRuntime.jsx(baseTooltip.BaseTooltip, { data: tooltipData, top: tooltipTop || 0, left: tooltipLeft || 0, style: {
|
|
115
117
|
transform: 'translate(-50%, -100%)',
|
|
116
118
|
} }))] }));
|
|
117
119
|
};
|
|
118
|
-
const PieChart = (props) =>
|
|
120
|
+
const PieChart = (props) => {
|
|
121
|
+
const existingContext = react.useContext(chartContext.ChartContext);
|
|
122
|
+
// If we're already in a ChartProvider context, don't create a new one
|
|
123
|
+
if (existingContext) {
|
|
124
|
+
return jsxRuntime.jsx(PieChartInternal, { ...props });
|
|
125
|
+
}
|
|
126
|
+
// Otherwise, create our own ChartProvider
|
|
127
|
+
return (jsxRuntime.jsx(chartContext.ChartProvider, { children: jsxRuntime.jsx(PieChartInternal, { ...props }) }));
|
|
128
|
+
};
|
|
119
129
|
PieChart.displayName = 'PieChart';
|
|
120
130
|
var pieChart = withResponsive.withResponsive(PieChart);
|
|
121
131
|
|