@automattic/charts 0.26.0 → 0.28.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/cjs/components/bar-chart/bar-chart.js +16 -6
  3. package/dist/cjs/components/leaderboard-chart/leaderboard-chart.js +23 -14
  4. package/dist/cjs/components/leaderboard-chart/leaderboard-chart.module.scss.js +1 -1
  5. package/dist/cjs/components/legend/base-legend.js +6 -2
  6. package/dist/cjs/components/legend/use-chart-legend-data.js +6 -2
  7. package/dist/cjs/components/line-chart/line-chart-annotation.js +7 -4
  8. package/dist/cjs/components/line-chart/line-chart.js +16 -6
  9. package/dist/cjs/components/pie-chart/index.js +1 -0
  10. package/dist/cjs/components/pie-chart/pie-chart.js +115 -31
  11. package/dist/cjs/components/pie-semi-circle-chart/pie-semi-circle-chart.js +111 -19
  12. package/dist/cjs/hooks/use-chart-mouse-handler.js +1 -3
  13. package/dist/cjs/hooks/use-deep-memo.js +21 -0
  14. package/dist/cjs/hooks/use-global-chart-theme.js +28 -0
  15. package/dist/cjs/hooks/use-xychart-theme.js +20 -0
  16. package/dist/cjs/index.js +9 -1
  17. package/dist/cjs/providers/chart-context/global-charts-provider.js +5 -2
  18. package/dist/cjs/providers/chart-context/utils.js +11 -5
  19. package/dist/cjs/providers/theme/index.js +0 -1
  20. package/dist/cjs/providers/theme/theme-provider.js +2 -18
  21. package/dist/cjs/style.css +1 -1
  22. package/dist/cjs/utils/merge-themes.js +21 -0
  23. package/dist/mjs/components/bar-chart/bar-chart.js +15 -5
  24. package/dist/mjs/components/leaderboard-chart/leaderboard-chart.js +24 -15
  25. package/dist/mjs/components/leaderboard-chart/leaderboard-chart.module.scss.js +1 -1
  26. package/dist/mjs/components/legend/base-legend.js +6 -2
  27. package/dist/mjs/components/legend/use-chart-legend-data.js +6 -2
  28. package/dist/mjs/components/line-chart/line-chart-annotation.js +7 -4
  29. package/dist/mjs/components/line-chart/line-chart.js +15 -5
  30. package/dist/mjs/components/pie-chart/index.js +1 -1
  31. package/dist/mjs/components/pie-chart/pie-chart.js +116 -33
  32. package/dist/mjs/components/pie-semi-circle-chart/pie-semi-circle-chart.js +114 -22
  33. package/dist/mjs/hooks/use-chart-mouse-handler.js +1 -1
  34. package/dist/mjs/hooks/use-deep-memo.js +19 -0
  35. package/dist/mjs/hooks/use-global-chart-theme.js +26 -0
  36. package/dist/mjs/hooks/use-xychart-theme.js +18 -0
  37. package/dist/mjs/index.js +5 -1
  38. package/dist/mjs/providers/chart-context/global-charts-provider.js +6 -3
  39. package/dist/mjs/providers/chart-context/utils.js +11 -5
  40. package/dist/mjs/providers/theme/index.js +1 -1
  41. package/dist/mjs/providers/theme/theme-provider.js +4 -19
  42. package/dist/mjs/style.css +1 -1
  43. package/dist/mjs/utils/merge-themes.js +19 -0
  44. package/dist/types/components/bar-list-chart/bar-list-chart.d.ts +1 -1
  45. package/dist/types/components/leaderboard-chart/leaderboard-chart.d.ts +22 -13
  46. package/dist/types/components/legend/base-legend.d.ts +9 -9
  47. package/dist/types/components/legend/use-chart-legend-data.d.ts +2 -3
  48. package/dist/types/components/pie-chart/index.d.ts +1 -1
  49. package/dist/types/components/pie-chart/pie-chart.d.ts +14 -5
  50. package/dist/types/components/pie-semi-circle-chart/pie-semi-circle-chart.d.ts +16 -4
  51. package/dist/types/hooks/use-chart-mouse-handler.d.ts +1 -1
  52. package/dist/types/hooks/use-deep-memo.d.ts +10 -0
  53. package/dist/types/hooks/use-global-chart-theme.d.ts +14 -0
  54. package/dist/types/hooks/use-xychart-theme.d.ts +6 -0
  55. package/dist/types/index.d.ts +5 -1
  56. package/dist/types/providers/chart-context/global-charts-provider.d.ts +3 -0
  57. package/dist/types/providers/chart-context/types.d.ts +2 -1
  58. package/dist/types/providers/chart-context/utils.d.ts +7 -2
  59. package/dist/types/providers/theme/index.d.ts +1 -1
  60. package/dist/types/providers/theme/theme-provider.d.ts +3 -5
  61. package/dist/types/utils/merge-themes.d.ts +13 -0
  62. package/package.json +2 -1
@@ -10,12 +10,16 @@ var text = require('@visx/text');
10
10
  var tooltip = require('@visx/tooltip');
11
11
  var clsx = require('clsx');
12
12
  var react = require('react');
13
+ require('fast-deep-equal');
14
+ var useGlobalChartTheme = require('../../hooks/use-global-chart-theme.js');
15
+ require('@visx/xychart');
13
16
  var globalChartsProvider = require('../../providers/chart-context/global-charts-provider.js');
14
17
  var utils = require('../../providers/chart-context/utils.js');
15
- var themeProvider = require('../../providers/theme/theme-provider.js');
18
+ var createComposition = require('../../utils/create-composition.js');
16
19
  var legend = require('../legend/legend.js');
17
20
  require('../legend/base-legend.js');
18
21
  var useChartLegendData = require('../legend/use-chart-legend-data.js');
22
+ var singleChartContext = require('../shared/single-chart-context.js');
19
23
  var useElementHeight = require('../shared/use-element-height.js');
20
24
  var withResponsive = require('../shared/with-responsive.js');
21
25
  var baseTooltip = require('../tooltip/base-tooltip.js');
@@ -43,8 +47,34 @@ const validateData = (data) => {
43
47
  }
44
48
  return { isValid: true, message: '' };
45
49
  };
46
- const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width = 400, thickness = 0.4, clockwise = true, withTooltips = false, showLegend = false, legendOrientation = 'horizontal', legendPosition = 'bottom', legendAlignment = 'center', legendShape = 'circle', label, note, className, }) => {
47
- const providerTheme = themeProvider.useChartTheme();
50
+ /**
51
+ * Compound component for SVG children in the PieSemiCircleChart
52
+ * @param {PropsWithChildren} props - Component props
53
+ * @param {ReactNode} props.children - Child elements to render
54
+ * @return {JSX.Element} The children wrapped in a fragment
55
+ */
56
+ const PieSemiCircleChartSVG = ({ children }) => {
57
+ // This component doesn't render directly - its children are extracted by PieSemiCircleChart
58
+ // We just return the children as-is
59
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children });
60
+ };
61
+ // Set displayName for better debugging and type checking
62
+ PieSemiCircleChartSVG.displayName = 'PieSemiCircleChart.SVG';
63
+ /**
64
+ * Compound component for HTML children in the PieSemiCircleChart
65
+ * @param {PropsWithChildren} props - Component props
66
+ * @param {ReactNode} props.children - Child elements to render
67
+ * @return {JSX.Element} The children wrapped in a fragment
68
+ */
69
+ const PieSemiCircleChartHTML = ({ children }) => {
70
+ // This component doesn't render directly - its children are extracted by PieSemiCircleChart
71
+ // We just return the children as-is
72
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children });
73
+ };
74
+ // Set displayName for better debugging and type checking
75
+ PieSemiCircleChartHTML.displayName = 'PieSemiCircleChart.HTML';
76
+ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width = 400, thickness = 0.4, clockwise = true, withTooltips = false, showLegend = false, legendOrientation = 'horizontal', legendPosition = 'bottom', legendAlignment = 'center', legendShape = 'circle', label, note, className, children, }) => {
77
+ const providerTheme = useGlobalChartTheme.useGlobalChartTheme();
48
78
  const chartId = utils.useChartId(providedChartId);
49
79
  const [legendRef, legendHeight] = useElementHeight.useElementHeight();
50
80
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = tooltip.useTooltip();
@@ -76,14 +106,53 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width = 40
76
106
  // Memoize legend options to prevent unnecessary re-calculations
77
107
  const legendOptions = react.useMemo(() => ({ showValues: true }), []);
78
108
  // Create legend items using the reusable hook
79
- const legendItems = useChartLegendData.useChartLegendData(data, providerTheme, legendOptions);
109
+ const legendItems = useChartLegendData.useChartLegendData(data, legendOptions);
110
+ // Process children to extract compound components
111
+ const { svgChildren, htmlChildren, otherChildren } = react.useMemo(() => {
112
+ const svg = [];
113
+ const html = [];
114
+ const other = [];
115
+ react.Children.forEach(children, child => {
116
+ if (react.isValidElement(child)) {
117
+ // Check displayName for compound components
118
+ const childType = child.type;
119
+ const displayName = childType?.displayName;
120
+ if (displayName === 'PieSemiCircleChart.SVG') {
121
+ // Extract children from PieSemiCircleChart.SVG
122
+ react.Children.forEach(child.props.children, svgChild => {
123
+ svg.push(svgChild);
124
+ });
125
+ }
126
+ else if (displayName === 'PieSemiCircleChart.HTML') {
127
+ // Extract children from PieSemiCircleChart.HTML
128
+ react.Children.forEach(child.props.children, htmlChild => {
129
+ html.push(htmlChild);
130
+ });
131
+ }
132
+ else if (child.type === group.Group) {
133
+ // Legacy support: still check for Group type for backward compatibility
134
+ svg.push(child);
135
+ }
136
+ else {
137
+ other.push(child);
138
+ }
139
+ }
140
+ });
141
+ return { svgChildren: svg, htmlChildren: html, otherChildren: other };
142
+ }, [children]);
80
143
  // Memoize metadata to prevent unnecessary re-registration
81
144
  const chartMetadata = react.useMemo(() => ({
82
145
  thickness,
83
146
  clockwise,
84
147
  }), [thickness, clockwise]);
85
148
  // Register chart with context only if data is valid
86
- utils.useChartRegistration(chartId, legendItems, providerTheme, 'pie-semi-circle', isValid, chartMetadata);
149
+ utils.useChartRegistration({
150
+ chartId,
151
+ legendItems,
152
+ chartType: 'pie-semi-circle',
153
+ isDataValid: isValid,
154
+ metadata: chartMetadata,
155
+ });
87
156
  if (!isValid) {
88
157
  return (jsxRuntime.jsx("div", { className: pieSemiCircleChart_module.default['pie-semi-circle-chart'], children: jsxRuntime.jsx("svg", { width: width, height: width / 2, "data-testid": "pie-chart-svg", children: jsxRuntime.jsx("text", { x: "50%", y: "50%", textAnchor: "middle", className: pieSemiCircleChart_module.default.error, children: message }) }) }));
89
158
  }
@@ -102,19 +171,42 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width = 40
102
171
  // Configure pie angles based on clockwise direction
103
172
  const startAngle = clockwise ? -Math.PI / 2 : Math.PI / 2;
104
173
  const endAngle = clockwise ? Math.PI / 2 : -Math.PI / 2;
105
- return (jsxRuntime.jsxs("div", { className: clsx('pie-semi-circle-chart', pieSemiCircleChart_module.default['pie-semi-circle-chart'], className), "data-testid": "pie-chart-container", style: {
106
- display: 'flex',
107
- flexDirection: showLegend && legendPosition === 'top' ? 'column-reverse' : 'column',
108
- }, children: [jsxRuntime.jsx("svg", { width: width, height: radius, viewBox: `0 0 ${width} ${chartHeight}`, "data-testid": "pie-chart-svg", children: jsxRuntime.jsxs(group.Group, { top: chartHeight, left: width / 2, children: [jsxRuntime.jsx(shape.Pie, { data: dataWithIndex, pieValue: accessors.value, outerRadius: radius, innerRadius: innerRadius, cornerRadius: 3, padAngle: PAD_ANGLE, startAngle: startAngle, endAngle: endAngle, pieSort: accessors.sort, children: pie => {
109
- return pie.arcs.map(arc => (jsxRuntime.jsx("g", { onMouseMove: handleArcMouseMove(arc), onMouseLeave: handleMouseLeave, children: jsxRuntime.jsx("path", { d: pie.path(arc) || '', fill: accessors.fill(arc.data), "data-testid": "pie-segment" }) }, arc.data.label)));
110
- } }), jsxRuntime.jsxs(group.Group, { children: [jsxRuntime.jsx(text.Text, { textAnchor: "middle", verticalAnchor: "start", y: -40, className: pieSemiCircleChart_module.default.label, children: label }), jsxRuntime.jsx(text.Text, { textAnchor: "middle", verticalAnchor: "start", y: -20, className: pieSemiCircleChart_module.default.note, children: note })] })] }) }), withTooltips && tooltipOpen && tooltipData && (jsxRuntime.jsx(baseTooltip.BaseTooltip, { data: {
111
- label: tooltipData.label,
112
- value: tooltipData.value,
113
- valueDisplay: tooltipData.valueDisplay,
114
- }, top: tooltipTop || 0, left: tooltipLeft || 0 })), showLegend && (jsxRuntime.jsx(legend.Legend, { items: legendItems, orientation: legendOrientation, position: legendPosition, alignment: legendAlignment, shape: legendShape, ref: legendRef, chartId: chartId }))] }));
174
+ return (jsxRuntime.jsx(singleChartContext.SingleChartContext.Provider, { value: {
175
+ chartId,
176
+ chartWidth: width,
177
+ chartHeight: radius,
178
+ }, children: jsxRuntime.jsxs("div", { className: clsx('pie-semi-circle-chart', pieSemiCircleChart_module.default['pie-semi-circle-chart'], className), "data-testid": "pie-chart-container", style: {
179
+ display: 'flex',
180
+ flexDirection: showLegend && legendPosition === 'top' ? 'column-reverse' : 'column',
181
+ }, children: [jsxRuntime.jsx("svg", { width: width, height: radius, viewBox: `0 0 ${width} ${chartHeight}`, "data-testid": "pie-chart-svg", children: jsxRuntime.jsxs(group.Group, { top: chartHeight, left: width / 2, children: [jsxRuntime.jsx(shape.Pie, { data: dataWithIndex, pieValue: accessors.value, outerRadius: radius, innerRadius: innerRadius, cornerRadius: 3, padAngle: PAD_ANGLE, startAngle: startAngle, endAngle: endAngle, pieSort: accessors.sort, children: pie => {
182
+ return pie.arcs.map(arc => (jsxRuntime.jsx("g", { onMouseMove: handleArcMouseMove(arc), onMouseLeave: handleMouseLeave, children: jsxRuntime.jsx("path", { d: pie.path(arc) || '', fill: accessors.fill(arc.data), "data-testid": "pie-segment" }) }, arc.data.label)));
183
+ } }), jsxRuntime.jsxs(group.Group, { children: [jsxRuntime.jsx(text.Text, { textAnchor: "middle", verticalAnchor: "start", y: -40, className: pieSemiCircleChart_module.default.label, children: label }), jsxRuntime.jsx(text.Text, { textAnchor: "middle", verticalAnchor: "start", y: -20, className: pieSemiCircleChart_module.default.note, children: note })] }), svgChildren] }) }), withTooltips && tooltipOpen && tooltipData && (jsxRuntime.jsx(baseTooltip.BaseTooltip, { data: {
184
+ label: tooltipData.label,
185
+ value: tooltipData.value,
186
+ valueDisplay: tooltipData.valueDisplay,
187
+ }, top: tooltipTop || 0, left: tooltipLeft || 0 })), showLegend && (jsxRuntime.jsx(legend.Legend, { items: legendItems, orientation: legendOrientation, position: legendPosition, alignment: legendAlignment, shape: legendShape, ref: legendRef, chartId: chartId })), htmlChildren, otherChildren] }) }));
188
+ };
189
+ const PieSemiCircleChartWithProvider = props => {
190
+ const existingContext = react.useContext(globalChartsProvider.GlobalChartsContext);
191
+ // If we're already in a GlobalChartsProvider context, don't create a new one
192
+ if (existingContext) {
193
+ return jsxRuntime.jsx(PieSemiCircleChartInternal, { ...props });
194
+ }
195
+ // Otherwise, create our own GlobalChartsProvider
196
+ return (jsxRuntime.jsx(globalChartsProvider.GlobalChartsProvider, { children: jsxRuntime.jsx(PieSemiCircleChartInternal, { ...props }) }));
115
197
  };
116
- const PieSemiCircleChart = props => (jsxRuntime.jsx(globalChartsProvider.GlobalChartsProvider, { children: jsxRuntime.jsx(PieSemiCircleChartInternal, { ...props }) }));
117
- PieSemiCircleChart.displayName = 'PieSemiCircleChart';
118
- var pieSemiCircleChart = withResponsive.withResponsive(PieSemiCircleChart);
198
+ PieSemiCircleChartWithProvider.displayName = 'PieSemiCircleChart';
199
+ // Create PieSemiCircleChart with composition API
200
+ createComposition.attachSubComponents(PieSemiCircleChartWithProvider, {
201
+ Legend: legend.Legend,
202
+ SVG: PieSemiCircleChartSVG,
203
+ HTML: PieSemiCircleChartHTML,
204
+ });
205
+ // Create responsive PieSemiCircleChart with composition API
206
+ const PieSemiCircleChartResponsive = createComposition.attachSubComponents(withResponsive.withResponsive(PieSemiCircleChartWithProvider), {
207
+ Legend: legend.Legend,
208
+ SVG: PieSemiCircleChartSVG,
209
+ HTML: PieSemiCircleChartHTML,
210
+ });
119
211
 
120
- exports.default = pieSemiCircleChart;
212
+ exports.default = PieSemiCircleChartResponsive;
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var event = require('@visx/event');
6
4
  var tooltip = require('@visx/tooltip');
7
5
  var react = require('react');
@@ -45,4 +43,4 @@ const useChartMouseHandler = ({ withTooltips, }) => {
45
43
  };
46
44
  };
47
45
 
48
- exports.default = useChartMouseHandler;
46
+ exports.useChartMouseHandler = useChartMouseHandler;
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ var isEqual = require('fast-deep-equal');
4
+ var react = require('react');
5
+
6
+ /**
7
+ * Custom hook to memoize a value using deep equality comparison.
8
+ * Prevents unnecessary re-renders when objects have the same content but different references.
9
+ *
10
+ * @param value - The value to memoize with deep equality comparison
11
+ * @return The memoized value that only changes when deeply different
12
+ */
13
+ const useDeepMemo = (value) => {
14
+ const ref = react.useRef(value);
15
+ if (!isEqual(ref.current, value)) {
16
+ ref.current = value;
17
+ }
18
+ return ref.current;
19
+ };
20
+
21
+ exports.useDeepMemo = useDeepMemo;
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var globalChartsProvider = require('../providers/chart-context/global-charts-provider.js');
5
+ var themeProvider = require('../providers/theme/theme-provider.js');
6
+ var themes = require('../providers/theme/themes.js');
7
+ var mergeThemes = require('../utils/merge-themes.js');
8
+
9
+ /**
10
+ * Hook to get the effective chart theme, merging global and local themes.
11
+ *
12
+ * This hook combines the global theme from GlobalChartsProvider with the local theme
13
+ * from ThemeProvider. The global theme provides the base, while the local theme
14
+ * can override specific properties for fine-grained customization.
15
+ *
16
+ * @return The effective chart theme to use
17
+ */
18
+ const useGlobalChartTheme = () => {
19
+ // Get context but don't throw if it doesn't exist (for testing or standalone usage)
20
+ const context = react.useContext(globalChartsProvider.GlobalChartsContext);
21
+ const globalTheme = context?.theme;
22
+ const localTheme = themeProvider.useChartTheme();
23
+ // Memoize the theme to prevent unnecessary re-renders
24
+ const effectiveTheme = react.useMemo(() => mergeThemes.mergeThemes(globalTheme ?? themes.defaultTheme, localTheme), [globalTheme, localTheme]);
25
+ return effectiveTheme;
26
+ };
27
+
28
+ exports.useGlobalChartTheme = useGlobalChartTheme;
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ var xychart = require('@visx/xychart');
4
+ var react = require('react');
5
+ var useGlobalChartTheme = require('./use-global-chart-theme.js');
6
+
7
+ const useXYChartTheme = (data) => {
8
+ const theme = useGlobalChartTheme.useGlobalChartTheme();
9
+ return react.useMemo(() => {
10
+ const seriesColors = (data ?? [])
11
+ .map(series => series.options?.stroke)
12
+ .filter((color) => Boolean(color));
13
+ return xychart.buildChartTheme({
14
+ ...theme,
15
+ colors: [...seriesColors, ...(theme.colors ?? [])],
16
+ });
17
+ }, [theme, data]);
18
+ };
19
+
20
+ exports.useXYChartTheme = useXYChartTheme;
package/dist/cjs/index.js CHANGED
@@ -16,7 +16,11 @@ var group = require('@visx/group');
16
16
  var legend$1 = require('@visx/legend');
17
17
  var themeProvider = require('./providers/theme/theme-provider.js');
18
18
  var themes = require('./providers/theme/themes.js');
19
+ var mergeThemes = require('./utils/merge-themes.js');
20
+ var useDeepMemo = require('./hooks/use-deep-memo.js');
21
+ var useGlobalChartTheme = require('./hooks/use-global-chart-theme.js');
19
22
  var useChartMouseHandler = require('./hooks/use-chart-mouse-handler.js');
23
+ var useXychartTheme = require('./hooks/use-xychart-theme.js');
20
24
 
21
25
 
22
26
 
@@ -63,4 +67,8 @@ exports.ThemeProvider = themeProvider.ThemeProvider;
63
67
  exports.defaultTheme = themes.defaultTheme;
64
68
  exports.jetpackTheme = themes.jetpackTheme;
65
69
  exports.wooTheme = themes.wooTheme;
66
- exports.useChartMouseHandler = useChartMouseHandler.default;
70
+ exports.mergeThemes = mergeThemes.mergeThemes;
71
+ exports.useDeepMemo = useDeepMemo.useDeepMemo;
72
+ exports.useGlobalChartTheme = useGlobalChartTheme.useGlobalChartTheme;
73
+ exports.useChartMouseHandler = useChartMouseHandler.useChartMouseHandler;
74
+ exports.useXYChartTheme = useXychartTheme.useXYChartTheme;
@@ -2,10 +2,12 @@
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
+ var themes = require('../theme/themes.js');
5
6
 
6
7
  const GlobalChartsContext = react.createContext(null);
7
- const GlobalChartsProvider = ({ children }) => {
8
+ const GlobalChartsProvider = ({ children, theme = {}, }) => {
8
9
  const [charts, setCharts] = react.useState(() => new Map());
10
+ const providerTheme = react.useMemo(() => ({ ...themes.defaultTheme, ...theme }), [theme]);
9
11
  const registerChart = react.useCallback((id, data) => {
10
12
  setCharts(prev => new Map(prev).set(id, data));
11
13
  }, []);
@@ -24,7 +26,8 @@ const GlobalChartsProvider = ({ children }) => {
24
26
  registerChart,
25
27
  unregisterChart,
26
28
  getChartData,
27
- }), [charts, registerChart, unregisterChart, getChartData]);
29
+ theme: providerTheme,
30
+ }), [charts, registerChart, unregisterChart, getChartData, providerTheme]);
28
31
  return jsxRuntime.jsx(GlobalChartsContext.Provider, { value: value, children: children });
29
32
  };
30
33
  const useGlobalChartsContext = () => {
@@ -1,22 +1,29 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('react');
4
+ var useDeepMemo = require('../../hooks/use-deep-memo.js');
4
5
  var globalChartsProvider = require('./global-charts-provider.js');
6
+ require('../theme/theme-provider.js');
7
+ require('deepmerge');
8
+ require('@visx/event');
9
+ require('@visx/tooltip');
10
+ require('@visx/xychart');
5
11
 
6
12
  const useChartId = (providedId) => {
7
13
  const generatedId = react.useId();
8
14
  return providedId || generatedId;
9
15
  };
10
- const useChartRegistration = (chartId, legendItems, theme, chartType, isDataValid, metadata) => {
16
+ const useChartRegistration = ({ chartId, legendItems, chartType, isDataValid, metadata, }) => {
11
17
  const { registerChart, unregisterChart } = globalChartsProvider.useGlobalChartsContext();
18
+ // Memoize legendItems with deep comparison to prevent infinite loops
19
+ const stableLegendItems = useDeepMemo.useDeepMemo(legendItems);
12
20
  // Memoize metadata to prevent unnecessary re-renders
13
21
  const memoizedMetadata = react.useMemo(() => metadata, [metadata]);
14
22
  react.useEffect(() => {
15
23
  // Only register if data is valid
16
24
  if (isDataValid) {
17
25
  registerChart(chartId, {
18
- legendItems,
19
- theme,
26
+ legendItems: stableLegendItems,
20
27
  chartType,
21
28
  metadata: memoizedMetadata,
22
29
  });
@@ -27,8 +34,7 @@ const useChartRegistration = (chartId, legendItems, theme, chartType, isDataVali
27
34
  // eslint-disable-next-line react-hooks/exhaustive-deps
28
35
  }, [
29
36
  chartId,
30
- legendItems,
31
- theme,
37
+ stableLegendItems,
32
38
  chartType,
33
39
  memoizedMetadata,
34
40
  isDataValid,
@@ -7,7 +7,6 @@ var themes = require('./themes.js');
7
7
 
8
8
  exports.ThemeProvider = themeProvider.ThemeProvider;
9
9
  exports.useChartTheme = themeProvider.useChartTheme;
10
- exports.useXYChartTheme = themeProvider.useXYChartTheme;
11
10
  exports.defaultTheme = themes.defaultTheme;
12
11
  exports.jetpackTheme = themes.jetpackTheme;
13
12
  exports.wooTheme = themes.wooTheme;
@@ -1,14 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
- var xychart = require('@visx/xychart');
5
4
  var react = require('react');
6
- var themes = require('./themes.js');
7
5
 
8
6
  /**
9
7
  * Context for sharing theme configuration across components
10
8
  */
11
- const ThemeContext = react.createContext(themes.defaultTheme);
9
+ const ThemeContext = react.createContext({});
12
10
  /**
13
11
  * Hook to access chart theme
14
12
  * @return {object} A built theme configuration compatible with visx charts
@@ -17,25 +15,11 @@ const useChartTheme = () => {
17
15
  const theme = react.useContext(ThemeContext);
18
16
  return theme;
19
17
  };
20
- const useXYChartTheme = (data) => {
21
- const providerTheme = useChartTheme();
22
- return react.useMemo(() => {
23
- const seriesColors = (data ?? [])
24
- .map(series => series.options?.stroke)
25
- .filter((color) => Boolean(color));
26
- return xychart.buildChartTheme({
27
- ...providerTheme,
28
- colors: [...seriesColors, ...(providerTheme.colors ?? [])],
29
- });
30
- }, [providerTheme, data]);
31
- };
32
18
  // Provider component for chart theming
33
19
  // Allows theme customization through props while maintaining default values
34
20
  const ThemeProvider = ({ theme = {}, children }) => {
35
- const mergedTheme = { ...themes.defaultTheme, ...theme };
36
- return jsxRuntime.jsx(ThemeContext.Provider, { value: mergedTheme, children: children });
21
+ return jsxRuntime.jsx(ThemeContext.Provider, { value: theme, children: children });
37
22
  };
38
23
 
39
24
  exports.ThemeProvider = ThemeProvider;
40
25
  exports.useChartTheme = useChartTheme;
41
- exports.useXYChartTheme = useXYChartTheme;
@@ -1 +1 @@
1
- .legend-module_legend--horizontal__IUN13{display:flex;flex-direction:row;flex-wrap:wrap;gap:16px}.legend-module_legend--vertical__Scfzo{display:flex;flex-direction:column;gap:8px}.legend-module_legend--vertical__Scfzo.legend-module_legend--alignment-start__Na2Qp{align-items:flex-start}.legend-module_legend--vertical__Scfzo.legend-module_legend--alignment-center__FVB2r{align-items:center}.legend-module_legend--vertical__Scfzo.legend-module_legend--alignment-end__6Vuay{align-items:flex-end}.legend-module_legend--position-top__uh7-2{position:relative}.legend-module_legend--position-top__uh7-2.legend-module_legend--alignment-start__Na2Qp{justify-content:flex-start}.legend-module_legend--position-top__uh7-2.legend-module_legend--alignment-center__FVB2r{justify-content:center}.legend-module_legend--position-top__uh7-2.legend-module_legend--alignment-end__6Vuay{justify-content:flex-end}.legend-module_legend--position-bottom__6Q3j-{position:relative}.legend-module_legend--position-bottom__6Q3j-.legend-module_legend--alignment-start__Na2Qp{justify-content:flex-start}.legend-module_legend--position-bottom__6Q3j-.legend-module_legend--alignment-center__FVB2r{justify-content:center}.legend-module_legend--position-bottom__6Q3j-.legend-module_legend--alignment-end__6Vuay{justify-content:flex-end}.legend-module_legend-item__feemn{align-items:center;display:flex;font-size:.875rem}.legend-module_legend-item-label__ksx6I{align-items:center;display:flex;gap:.5rem}.legend-module_legend-item-value__d9x1j{font-weight:500}.bar-chart-module_bar-chart__lmYNi{display:flex;flex-direction:column}.bar-chart-module_bar-chart__lmYNi svg{overflow:visible}.bar-chart-module_bar-chart-legend__vgKKq{margin-top:1rem}.line-chart-module_line-chart__ITM3d{display:flex;flex-direction:column}.line-chart-module_line-chart__ITM3d svg{overflow:visible}.line-chart-module_line-chart__annotation-label-popover__TqNZk,.line-chart-module_line-chart__tooltip__aqcme{background:#fff;padding:.5rem}.line-chart-module_line-chart__tooltip-date__4Dzab{font-weight:700;padding-bottom:10px}.line-chart-module_line-chart__tooltip-row__6A37G{align-items:center;display:flex;justify-content:space-between;padding:4px 0}.line-chart-module_line-chart__tooltip-label__IvnFF{font-weight:500;padding-right:1rem}.line-chart-module_line-chart__annotations-overlay__4nR2p{left:0;overflow:visible;pointer-events:none;position:absolute;top:0}.line-chart-module_line-chart__annotation-label__OmgiT{pointer-events:auto}.line-chart-module_line-chart__annotation-label-trigger-button__mcIb3{align-items:center;background:none;border:none;cursor:pointer;display:flex;justify-content:center;padding:0;pointer-events:auto}.line-chart-module_line-chart__annotation-label-popover__TqNZk{background:#fff;border:none;border-radius:4px;box-shadow:0 1px 2px rgba(0,0,0,.1);font-size:14px;margin:.5rem;min-width:125px;position:fixed;visibility:hidden}.line-chart-module_line-chart__annotation-label-popover--visible__dE0cV{visibility:visible}.line-chart-module_line-chart__annotation-label-popover--safari__i3NHT{margin:auto;position:static}.line-chart-module_line-chart__annotation-label-popover-header__Owypo{align-items:start;display:flex;flex-direction:row;justify-content:space-between}.line-chart-module_line-chart__annotation-label-popover-content__vtgQt{padding:.5rem}.line-chart-module_line-chart__annotation-label-popover-close-button__i8KUc{align-items:center;background:none;border:none;cursor:pointer;display:flex;height:44px;justify-content:center;padding:0;width:44px}.visx-tooltip-glyph svg{height:10px;width:10px}.base-tooltip-module_tooltip__OfX6n{background-color:rgba(0,0,0,.85);border-radius:4px;box-shadow:0 1px 2px rgba(0,0,0,.1);color:#fff;font-size:14px;padding:.5rem;pointer-events:none;position:absolute;transform:translate(-50%,-100%)}.pie-chart-module_pie-chart__R12Vh{display:flex;flex-direction:column;overflow:hidden}.pie-semi-circle-chart-module_pie-semi-circle-chart__r5jk9{display:flex;flex-direction:column;text-align:center}.pie-semi-circle-chart-module_pie-semi-circle-chart__r5jk9 .pie-semi-circle-chart-module_label__nPqOg{font-size:16px;font-weight:600;margin-bottom:0}.pie-semi-circle-chart-module_pie-semi-circle-chart__r5jk9 .pie-semi-circle-chart-module_note__LpBZQ{font-size:14px;margin-top:0}.leaderboard-chart-module_leaderboardChart__zxakP{transition:opacity .3s ease-in-out}.leaderboard-chart-module_leaderboardChart__zxakP.leaderboard-chart-module_loading__-AGv-{opacity:.5}.leaderboard-chart-module_progressContainer__BZGbj{display:flex;flex-direction:column;gap:6px}.leaderboard-chart-module_progressContainer__BZGbj .leaderboard-chart-module_progressBar__LAQaj{background-color:transparent;border-radius:2px;height:6px;overflow:hidden;transition:width .3s ease-in-out;width:100%}.leaderboard-chart-module_progressContainer__BZGbj .leaderboard-chart-module_progressBar__LAQaj>div{background-color:var(--progress-color,#3858e9)}.leaderboard-chart-module_progressContainer__BZGbj .leaderboard-chart-module_primaryBar__iybII{--progress-color:var(--primary-color,#3858e9)}.leaderboard-chart-module_progressContainer__BZGbj .leaderboard-chart-module_secondaryBar__A7tLz{--progress-color:var(--secondary-color,#66bdff)}.leaderboard-chart-module_valueContainer__ZlLh4{display:flex;gap:4px;justify-content:flex-end}.leaderboard-chart-module_emptyState__0dkfy{color:#666;font-size:14px;font-style:italic;padding:32px 16px;text-align:center}
1
+ .legend-module_legend--horizontal__IUN13{display:flex;flex-direction:row;flex-wrap:wrap;gap:16px}.legend-module_legend--vertical__Scfzo{display:flex;flex-direction:column;gap:8px}.legend-module_legend--vertical__Scfzo.legend-module_legend--alignment-start__Na2Qp{align-items:flex-start}.legend-module_legend--vertical__Scfzo.legend-module_legend--alignment-center__FVB2r{align-items:center}.legend-module_legend--vertical__Scfzo.legend-module_legend--alignment-end__6Vuay{align-items:flex-end}.legend-module_legend--position-top__uh7-2{position:relative}.legend-module_legend--position-top__uh7-2.legend-module_legend--alignment-start__Na2Qp{justify-content:flex-start}.legend-module_legend--position-top__uh7-2.legend-module_legend--alignment-center__FVB2r{justify-content:center}.legend-module_legend--position-top__uh7-2.legend-module_legend--alignment-end__6Vuay{justify-content:flex-end}.legend-module_legend--position-bottom__6Q3j-{position:relative}.legend-module_legend--position-bottom__6Q3j-.legend-module_legend--alignment-start__Na2Qp{justify-content:flex-start}.legend-module_legend--position-bottom__6Q3j-.legend-module_legend--alignment-center__FVB2r{justify-content:center}.legend-module_legend--position-bottom__6Q3j-.legend-module_legend--alignment-end__6Vuay{justify-content:flex-end}.legend-module_legend-item__feemn{align-items:center;display:flex;font-size:.875rem}.legend-module_legend-item-label__ksx6I{align-items:center;display:flex;gap:.5rem}.legend-module_legend-item-value__d9x1j{font-weight:500}.bar-chart-module_bar-chart__lmYNi{display:flex;flex-direction:column}.bar-chart-module_bar-chart__lmYNi svg{overflow:visible}.bar-chart-module_bar-chart-legend__vgKKq{margin-top:1rem}.line-chart-module_line-chart__ITM3d{display:flex;flex-direction:column}.line-chart-module_line-chart__ITM3d svg{overflow:visible}.line-chart-module_line-chart__annotation-label-popover__TqNZk,.line-chart-module_line-chart__tooltip__aqcme{background:#fff;padding:.5rem}.line-chart-module_line-chart__tooltip-date__4Dzab{font-weight:700;padding-bottom:10px}.line-chart-module_line-chart__tooltip-row__6A37G{align-items:center;display:flex;justify-content:space-between;padding:4px 0}.line-chart-module_line-chart__tooltip-label__IvnFF{font-weight:500;padding-right:1rem}.line-chart-module_line-chart__annotations-overlay__4nR2p{left:0;overflow:visible;pointer-events:none;position:absolute;top:0}.line-chart-module_line-chart__annotation-label__OmgiT{pointer-events:auto}.line-chart-module_line-chart__annotation-label-trigger-button__mcIb3{align-items:center;background:none;border:none;cursor:pointer;display:flex;justify-content:center;padding:0;pointer-events:auto}.line-chart-module_line-chart__annotation-label-popover__TqNZk{background:#fff;border:none;border-radius:4px;box-shadow:0 1px 2px rgba(0,0,0,.1);font-size:14px;margin:.5rem;min-width:125px;position:fixed;visibility:hidden}.line-chart-module_line-chart__annotation-label-popover--visible__dE0cV{visibility:visible}.line-chart-module_line-chart__annotation-label-popover--safari__i3NHT{margin:auto;position:static}.line-chart-module_line-chart__annotation-label-popover-header__Owypo{align-items:start;display:flex;flex-direction:row;justify-content:space-between}.line-chart-module_line-chart__annotation-label-popover-content__vtgQt{padding:.5rem}.line-chart-module_line-chart__annotation-label-popover-close-button__i8KUc{align-items:center;background:none;border:none;cursor:pointer;display:flex;height:44px;justify-content:center;padding:0;width:44px}.visx-tooltip-glyph svg{height:10px;width:10px}.base-tooltip-module_tooltip__OfX6n{background-color:rgba(0,0,0,.85);border-radius:4px;box-shadow:0 1px 2px rgba(0,0,0,.1);color:#fff;font-size:14px;padding:.5rem;pointer-events:none;position:absolute;transform:translate(-50%,-100%)}.pie-chart-module_pie-chart__R12Vh{display:flex;flex-direction:column;overflow:hidden}.pie-semi-circle-chart-module_pie-semi-circle-chart__r5jk9{display:flex;flex-direction:column;text-align:center}.pie-semi-circle-chart-module_pie-semi-circle-chart__r5jk9 .pie-semi-circle-chart-module_label__nPqOg{font-size:16px;font-weight:600;margin-bottom:0}.pie-semi-circle-chart-module_pie-semi-circle-chart__r5jk9 .pie-semi-circle-chart-module_note__LpBZQ{font-size:14px;margin-top:0}.leaderboard-chart-module_leaderboardChart__zxakP{transition:opacity .3s ease-in-out}.leaderboard-chart-module_leaderboardChart__zxakP.leaderboard-chart-module_loading__-AGv-{opacity:.5}.leaderboard-chart-module_progressContainerWithOverlayLabel__YFPY0{align-items:center;display:grid;grid-template:"overlap" 1fr/1fr}.leaderboard-chart-module_progressContainerWithOverlayLabel__YFPY0>*{grid-area:overlap}.leaderboard-chart-module_progressContainerWithOverlayLabel__YFPY0 .leaderboard-chart-module_progressBar__LAQaj{background-color:var(--primary-color,#3858e9);border-radius:var(--bar-border,9999px);height:100%;transition:width .3s ease-in-out;z-index:-1}.leaderboard-chart-module_progressContainerWithOverlayLabel__YFPY0 .leaderboard-chart-module_progressBarLabel__24t-j{padding-left:8px}.leaderboard-chart-module_progressContainer__BZGbj{display:flex;flex-direction:column;gap:6px}.leaderboard-chart-module_progressContainer__BZGbj .leaderboard-chart-module_progressBar__LAQaj{background-color:transparent;border-radius:2px;height:6px;overflow:hidden;transition:width .3s ease-in-out;width:100%}.leaderboard-chart-module_progressContainer__BZGbj .leaderboard-chart-module_progressBar__LAQaj>div{background-color:var(--progress-color,#3858e9);border-radius:var(--bar-border,9999px)}.leaderboard-chart-module_progressContainer__BZGbj .leaderboard-chart-module_primaryBar__iybII{--progress-color:var(--primary-color,#3858e9)}.leaderboard-chart-module_progressContainer__BZGbj .leaderboard-chart-module_secondaryBar__A7tLz{--progress-color:var(--secondary-color,#66bdff)}.leaderboard-chart-module_valueContainer__ZlLh4{display:flex;gap:4px;justify-content:flex-end}.leaderboard-chart-module_overlayLabel__pRqSh{align-items:center}.leaderboard-chart-module_emptyState__0dkfy{color:#666;font-size:14px;font-style:italic;padding:32px 16px;text-align:center}
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ var deepmerge = require('deepmerge');
4
+
5
+ /**
6
+ * Merges chart themes with proper precedence.
7
+ * The second theme (override) takes precedence over the first theme (base).
8
+ *
9
+ * @param baseTheme - Base theme object
10
+ * @param overrideTheme - Theme to override base with (takes precedence)
11
+ * @return Merged theme with overrideTheme values taking precedence
12
+ */
13
+ function mergeThemes(baseTheme, overrideTheme) {
14
+ // Use deepmerge to properly merge nested objects, with overrideTheme taking precedence
15
+ return deepmerge(baseTheme, overrideTheme, {
16
+ // Ensure arrays are replaced rather than concatenated
17
+ arrayMerge: (_destinationArray, sourceArray) => sourceArray,
18
+ });
19
+ }
20
+
21
+ exports.mergeThemes = mergeThemes;
@@ -4,9 +4,13 @@ import { XYChart, Grid, BarGroup, BarSeries, Axis } from '@visx/xychart';
4
4
  import { __ } from '@wordpress/i18n';
5
5
  import clsx from 'clsx';
6
6
  import { useContext, useRef, useState, useCallback, useMemo } from 'react';
7
+ import 'fast-deep-equal';
8
+ import { useGlobalChartTheme } from '../../hooks/use-global-chart-theme.js';
9
+ import '@visx/event';
10
+ import '@visx/tooltip';
11
+ import { useXYChartTheme } from '../../hooks/use-xychart-theme.js';
7
12
  import { GlobalChartsContext, GlobalChartsProvider } from '../../providers/chart-context/global-charts-provider.js';
8
13
  import { useChartId, useChartRegistration } from '../../providers/chart-context/utils.js';
9
- import { useChartTheme, useXYChartTheme } from '../../providers/theme/theme-provider.js';
10
14
  import { attachSubComponents } from '../../utils/create-composition.js';
11
15
  import { Legend } from '../legend/legend.js';
12
16
  import '../legend/base-legend.js';
@@ -38,7 +42,7 @@ const getPatternId = (chartId, index) => `bar-pattern-${chartId}-${index}`;
38
42
  const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400, className, margin, withTooltips = false, showLegend = false, legendOrientation = 'horizontal', legendPosition = 'bottom', legendAlignment = 'center', legendShape = 'rect', gridVisibility: gridVisibilityProp, renderTooltip, options = {}, orientation = 'vertical', withPatterns = false, showZeroValues = false, children, }) => {
39
43
  const horizontal = orientation === 'horizontal';
40
44
  const chartId = useChartId(providedChartId);
41
- const providerTheme = useChartTheme();
45
+ const providerTheme = useGlobalChartTheme();
42
46
  const theme = useXYChartTheme(data);
43
47
  const dataSorted = useChartDataTransform(data);
44
48
  // Transform data to add a small value for zero bars to make them visible
@@ -46,7 +50,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400,
46
50
  enabled: showZeroValues,
47
51
  });
48
52
  // Create legend items using the reusable hook
49
- const legendItems = useChartLegendData(dataSorted, providerTheme);
53
+ const legendItems = useChartLegendData(dataSorted);
50
54
  const chartOptions = useBarChartOptions(dataWithVisibleZeros, horizontal, options);
51
55
  const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme, horizontal);
52
56
  const [legendRef, legendHeight] = useElementHeight();
@@ -63,7 +67,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400,
63
67
  chartRef,
64
68
  totalPoints,
65
69
  });
66
- const getColor = useCallback((seriesData, index) => seriesData?.options?.stroke || theme.colors[index % theme.colors.length], [theme]);
70
+ const getColor = useCallback((seriesData, index) => seriesData?.options?.stroke || providerTheme.colors[index % providerTheme.colors.length], [providerTheme]);
67
71
  const getBarBackground = useCallback((index) => () => withPatterns
68
72
  ? `url(#${getPatternId(chartId, index)})`
69
73
  : getColor(dataSorted[index], index), [withPatterns, getColor, dataSorted, chartId]);
@@ -142,7 +146,13 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400,
142
146
  withPatterns,
143
147
  }), [orientation, withPatterns]);
144
148
  // Register chart with context only if data is valid
145
- useChartRegistration(chartId, legendItems, providerTheme, 'bar', isDataValid, chartMetadata);
149
+ useChartRegistration({
150
+ chartId,
151
+ legendItems,
152
+ chartType: 'bar',
153
+ isDataValid,
154
+ metadata: chartMetadata,
155
+ });
146
156
  if (error) {
147
157
  return jsx("div", { className: clsx('bar-chart', styles['bar-chart']), children: error });
148
158
  }
@@ -1,9 +1,13 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
2
  import { __experimentalGrid, __experimentalVStack, __experimentalText, ProgressBar } from '@wordpress/components';
3
3
  import { Fragment } from '@wordpress/element';
4
4
  import clsx from 'clsx';
5
5
  import 'react';
6
- import { useChartTheme } from '../../providers/theme/theme-provider.js';
6
+ import 'fast-deep-equal';
7
+ import { useGlobalChartTheme } from '../../hooks/use-global-chart-theme.js';
8
+ import '@visx/event';
9
+ import '@visx/tooltip';
10
+ import '@visx/xychart';
7
11
  import { formatMetricValue } from '../shared/format-metric-value.js';
8
12
  import styles from './leaderboard-chart.module.scss.js';
9
13
 
@@ -42,24 +46,27 @@ const defaultDeltaFormatter = (value) => {
42
46
  signDisplay: 'exceptZero',
43
47
  });
44
48
  };
49
+ const ProgressBarWithOverlayLabel = ({ entry }) => (jsxs("div", { className: styles.progressContainerWithOverlayLabel, children: [typeof entry.label === 'string' ? (jsx(__experimentalText, { className: styles.progressBarLabel, children: entry.label })) : (entry.label), jsx("div", { className: styles.progressBar, style: { width: entry.currentShare + '%' } })] }));
50
+ const ProgressBarWithLabel = ({ entry, withComparison, }) => (jsxs(Fragment$1, { children: [typeof entry.label === 'string' ? jsx(__experimentalText, { children: entry.label }) : entry.label, jsxs("div", { className: styles.progressContainer, children: [jsx(ProgressBar, { value: entry.currentShare, className: clsx(styles.progressBar, styles.primaryBar) }), withComparison && (jsx(ProgressBar, { value: entry.previousShare, className: clsx(styles.progressBar, styles.secondaryBar) }))] })] }));
45
51
  /**
46
52
  * LeaderboardChart component displays a ranked list of data with progress bars
47
53
  * and optional comparison values.
48
54
  *
49
- * @param props - Component props
50
- * @param props.data - Array of leaderboard entries to display
51
- * @param props.withComparison - Whether to show comparison data
52
- * @param props.primaryColor - Primary color for current period bars
53
- * @param props.secondaryColor - Secondary color for comparison period bars
54
- * @param props.valueFormatter - Custom formatter for values
55
- * @param props.deltaFormatter - Custom formatter for delta values
56
- * @param props.loading - Whether the chart is in loading state
57
- * @param props.className - Additional CSS class name
58
- * @param props.style - Custom styling for the chart container
55
+ * @param props - Component props
56
+ * @param props.data - Array of leaderboard entries to display
57
+ * @param props.withComparison - Whether to show comparison data
58
+ * @param props.withOverlayLabel - Whether to overlay the label on top of the bar
59
+ * @param props.primaryColor - Primary color for current period bars
60
+ * @param props.secondaryColor - Secondary color for comparison period bars
61
+ * @param props.valueFormatter - Custom formatter for values
62
+ * @param props.deltaFormatter - Custom formatter for delta values
63
+ * @param props.loading - Whether the chart is in loading state
64
+ * @param props.className - Additional CSS class name
65
+ * @param props.style - Custom styling for the chart container
59
66
  * @return JSX element representing the leaderboard chart
60
67
  */
61
- const LeaderboardChart = ({ data, withComparison = false, primaryColor, secondaryColor, valueFormatter = defaultValueFormatter, deltaFormatter = defaultDeltaFormatter, loading = false, className, style, }) => {
62
- const theme = useChartTheme();
68
+ const LeaderboardChart = ({ data, withComparison = false, withOverlayLabel = false, primaryColor, secondaryColor, valueFormatter = defaultValueFormatter, deltaFormatter = defaultDeltaFormatter, loading = false, className, style, }) => {
69
+ const theme = useGlobalChartTheme();
63
70
  // Get component settings from theme with fallbacks
64
71
  const leaderboardSettings = theme.leaderboardChart;
65
72
  const labelSpacing = leaderboardSettings?.labelSpacing ?? DEFAULT_LEADERBOARD_SETTINGS.labelSpacing;
@@ -84,7 +91,9 @@ const LeaderboardChart = ({ data, withComparison = false, primaryColor, secondar
84
91
  return (jsx(__experimentalGrid, { className: clsx(styles.leaderboardChart, loading && styles.loading, className), templateColumns: "minmax(0, 1fr) auto", rowGap: rowGap, columnGap: columnGap, style: chartStyle, children: data.map(entry => {
85
92
  const colorIndex = Math.sign(entry.delta) + 1;
86
93
  const deltaColor = signColors[colorIndex];
87
- return (jsxs(Fragment, { children: [jsxs(__experimentalVStack, { spacing: labelSpacing, children: [jsx(__experimentalText, { children: entry.label }), jsxs("div", { className: styles.progressContainer, children: [jsx(ProgressBar, { value: entry.currentShare, className: clsx(styles.progressBar, styles.primaryBar) }), withComparison && (jsx(ProgressBar, { value: entry.previousShare, className: clsx(styles.progressBar, styles.secondaryBar) }))] })] }), jsxs("div", { className: styles.valueContainer, children: [jsx(__experimentalText, { children: valueFormatter(entry.currentValue) }), withComparison && (jsx(__experimentalText, { style: { color: deltaColor }, children: deltaFormatter(entry.delta) }))] })] }, entry.id));
94
+ return (jsxs(Fragment, { children: [jsx(__experimentalVStack, { spacing: labelSpacing, children: withOverlayLabel ? (jsx(ProgressBarWithOverlayLabel, { entry: entry })) : (jsx(ProgressBarWithLabel, { entry: entry, withComparison: withComparison })) }), jsxs("div", { className: clsx(styles.valueContainer, {
95
+ [styles.overlayLabel]: withOverlayLabel,
96
+ }), children: [jsx(__experimentalText, { children: valueFormatter(entry.currentValue) }), withComparison && (jsx(__experimentalText, { style: { color: deltaColor }, children: deltaFormatter(entry.delta) }))] })] }, entry.id));
88
97
  }) }));
89
98
  };
90
99
 
@@ -1,3 +1,3 @@
1
- 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"};
1
+ var styles = {"leaderboardChart":"leaderboard-chart-module_leaderboardChart__zxakP","loading":"leaderboard-chart-module_loading__-AGv-","progressContainerWithOverlayLabel":"leaderboard-chart-module_progressContainerWithOverlayLabel__YFPY0","progressBar":"leaderboard-chart-module_progressBar__LAQaj","progressBarLabel":"leaderboard-chart-module_progressBarLabel__24t-j","progressContainer":"leaderboard-chart-module_progressContainer__BZGbj","primaryBar":"leaderboard-chart-module_primaryBar__iybII","secondaryBar":"leaderboard-chart-module_secondaryBar__A7tLz","valueContainer":"leaderboard-chart-module_valueContainer__ZlLh4","overlayLabel":"leaderboard-chart-module_overlayLabel__pRqSh","emptyState":"leaderboard-chart-module_emptyState__0dkfy"};
2
2
 
3
3
  export { styles as default };
@@ -4,7 +4,11 @@ import { LegendOrdinal, LegendItem, LegendShape, LegendLabel } from '@visx/legen
4
4
  import { scaleOrdinal } from '@visx/scale';
5
5
  import clsx from 'clsx';
6
6
  import { forwardRef, useCallback } from 'react';
7
- import { useChartTheme } from '../../providers/theme/theme-provider.js';
7
+ import 'fast-deep-equal';
8
+ import { useGlobalChartTheme } from '../../hooks/use-global-chart-theme.js';
9
+ import '@visx/event';
10
+ import '@visx/tooltip';
11
+ import '@visx/xychart';
8
12
  import styles from './legend.module.scss.js';
9
13
  import { valueOrIdentityString, valueOrIdentity, labelTransformFactory } from './utils.js';
10
14
 
@@ -18,7 +22,7 @@ const orientationToFlexDirection = {
18
22
  */
19
23
  const BaseLegend = forwardRef(({ items, className, orientation = 'horizontal', position = 'bottom', alignment = 'center', shape = 'rect', fill = valueOrIdentityString, size = valueOrIdentityString, labelFormat = valueOrIdentity, labelTransform = labelTransformFactory, shapeWidth = 16, shapeHeight = 16, shapeMargin = '2px 4px 2px 0', labelAlign = 'left', labelFlex = '0 0 auto', // Use natural width instead of expanding to fill space
20
24
  labelMargin = '0 4px', itemMargin = '0', itemDirection = 'row', legendLabelProps, ...legendItemProps }, ref) => {
21
- const theme = useChartTheme();
25
+ const theme = useGlobalChartTheme();
22
26
  const legendScale = scaleOrdinal({
23
27
  domain: items.map(item => item.label),
24
28
  range: items.map(item => item.color),