@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
@@ -1,20 +1,27 @@
1
1
  import { useId, useMemo, useEffect } from 'react';
2
+ import { useDeepMemo } from '../../hooks/use-deep-memo.js';
2
3
  import { useGlobalChartsContext } from './global-charts-provider.js';
4
+ import '../theme/theme-provider.js';
5
+ import 'deepmerge';
6
+ import '@visx/event';
7
+ import '@visx/tooltip';
8
+ import '@visx/xychart';
3
9
 
4
10
  const useChartId = (providedId) => {
5
11
  const generatedId = useId();
6
12
  return providedId || generatedId;
7
13
  };
8
- const useChartRegistration = (chartId, legendItems, theme, chartType, isDataValid, metadata) => {
14
+ const useChartRegistration = ({ chartId, legendItems, chartType, isDataValid, metadata, }) => {
9
15
  const { registerChart, unregisterChart } = useGlobalChartsContext();
16
+ // Memoize legendItems with deep comparison to prevent infinite loops
17
+ const stableLegendItems = useDeepMemo(legendItems);
10
18
  // Memoize metadata to prevent unnecessary re-renders
11
19
  const memoizedMetadata = useMemo(() => metadata, [metadata]);
12
20
  useEffect(() => {
13
21
  // Only register if data is valid
14
22
  if (isDataValid) {
15
23
  registerChart(chartId, {
16
- legendItems,
17
- theme,
24
+ legendItems: stableLegendItems,
18
25
  chartType,
19
26
  metadata: memoizedMetadata,
20
27
  });
@@ -25,8 +32,7 @@ const useChartRegistration = (chartId, legendItems, theme, chartType, isDataVali
25
32
  // eslint-disable-next-line react-hooks/exhaustive-deps
26
33
  }, [
27
34
  chartId,
28
- legendItems,
29
- theme,
35
+ stableLegendItems,
30
36
  chartType,
31
37
  memoizedMetadata,
32
38
  isDataValid,
@@ -1,2 +1,2 @@
1
- export { ThemeProvider, useChartTheme, useXYChartTheme } from './theme-provider.js';
1
+ export { ThemeProvider, useChartTheme } from './theme-provider.js';
2
2
  export { defaultTheme, jetpackTheme, wooTheme } from './themes.js';
@@ -1,12 +1,10 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { buildChartTheme } from '@visx/xychart';
3
- import { createContext, useContext, useMemo } from 'react';
4
- import { defaultTheme } from './themes.js';
2
+ import { createContext, useContext } from 'react';
5
3
 
6
4
  /**
7
5
  * Context for sharing theme configuration across components
8
6
  */
9
- const ThemeContext = createContext(defaultTheme);
7
+ const ThemeContext = createContext({});
10
8
  /**
11
9
  * Hook to access chart theme
12
10
  * @return {object} A built theme configuration compatible with visx charts
@@ -15,23 +13,10 @@ const useChartTheme = () => {
15
13
  const theme = useContext(ThemeContext);
16
14
  return theme;
17
15
  };
18
- const useXYChartTheme = (data) => {
19
- const providerTheme = useChartTheme();
20
- return useMemo(() => {
21
- const seriesColors = (data ?? [])
22
- .map(series => series.options?.stroke)
23
- .filter((color) => Boolean(color));
24
- return buildChartTheme({
25
- ...providerTheme,
26
- colors: [...seriesColors, ...(providerTheme.colors ?? [])],
27
- });
28
- }, [providerTheme, data]);
29
- };
30
16
  // Provider component for chart theming
31
17
  // Allows theme customization through props while maintaining default values
32
18
  const ThemeProvider = ({ theme = {}, children }) => {
33
- const mergedTheme = { ...defaultTheme, ...theme };
34
- return jsx(ThemeContext.Provider, { value: mergedTheme, children: children });
19
+ return jsx(ThemeContext.Provider, { value: theme, children: children });
35
20
  };
36
21
 
37
- export { ThemeProvider, useChartTheme, useXYChartTheme };
22
+ export { ThemeProvider, useChartTheme };
@@ -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,19 @@
1
+ import deepmerge from 'deepmerge';
2
+
3
+ /**
4
+ * Merges chart themes with proper precedence.
5
+ * The second theme (override) takes precedence over the first theme (base).
6
+ *
7
+ * @param baseTheme - Base theme object
8
+ * @param overrideTheme - Theme to override base with (takes precedence)
9
+ * @return Merged theme with overrideTheme values taking precedence
10
+ */
11
+ function mergeThemes(baseTheme, overrideTheme) {
12
+ // Use deepmerge to properly merge nested objects, with overrideTheme taking precedence
13
+ return deepmerge(baseTheme, overrideTheme, {
14
+ // Ensure arrays are replaced rather than concatenated
15
+ arrayMerge: (_destinationArray, sourceArray) => sourceArray,
16
+ });
17
+ }
18
+
19
+ export { mergeThemes };
@@ -67,6 +67,6 @@ interface RenderValueProps {
67
67
  index: number;
68
68
  formatter: (value: number) => string;
69
69
  }
70
- declare const _default: ({ resizeDebounceTime, maxWidth, aspectRatio, ...chartProps }: Pick<Partial<BarListChartProps>, "height" | "size" | "width"> & Omit<BarListChartProps, "height" | "size" | "width"> & ResponsiveConfig) => react_jsx_runtime.JSX.Element;
70
+ declare const _default: ({ resizeDebounceTime, maxWidth, aspectRatio, ...chartProps }: Pick<Partial<BarListChartProps>, "width" | "height" | "size"> & Omit<BarListChartProps, "width" | "height" | "size"> & ResponsiveConfig) => react_jsx_runtime.JSX.Element;
71
71
 
72
72
  export { _default as default };
@@ -6,9 +6,9 @@ interface LeaderboardEntry {
6
6
  */
7
7
  id: string;
8
8
  /**
9
- * Human-readable name (e.g., 'Direct')
9
+ * Human-readable name (e.g., 'Direct') or a JSX element (e.g., <h4>Direct</h4>)
10
10
  */
11
- label: string;
11
+ label: string | JSX.Element;
12
12
  /**
13
13
  * Value of the entry
14
14
  */
@@ -39,6 +39,10 @@ interface LeaderboardChartProps {
39
39
  * Whether to show comparison data
40
40
  */
41
41
  withComparison?: boolean;
42
+ /**
43
+ * Whether to overlay the label on top of bar
44
+ */
45
+ withOverlayLabel?: boolean;
42
46
  /**
43
47
  * Primary color for current period bars
44
48
  */
@@ -66,22 +70,27 @@ interface LeaderboardChartProps {
66
70
  /**
67
71
  * Custom styling for the chart container
68
72
  */
69
- style?: React.CSSProperties;
73
+ style?: React.CSSProperties & {
74
+ '--bar-border'?: string;
75
+ '--primary-color'?: string;
76
+ '--secondary-color'?: string;
77
+ };
70
78
  }
71
79
  /**
72
80
  * LeaderboardChart component displays a ranked list of data with progress bars
73
81
  * and optional comparison values.
74
82
  *
75
- * @param props - Component props
76
- * @param props.data - Array of leaderboard entries to display
77
- * @param props.withComparison - Whether to show comparison data
78
- * @param props.primaryColor - Primary color for current period bars
79
- * @param props.secondaryColor - Secondary color for comparison period bars
80
- * @param props.valueFormatter - Custom formatter for values
81
- * @param props.deltaFormatter - Custom formatter for delta values
82
- * @param props.loading - Whether the chart is in loading state
83
- * @param props.className - Additional CSS class name
84
- * @param props.style - Custom styling for the chart container
83
+ * @param props - Component props
84
+ * @param props.data - Array of leaderboard entries to display
85
+ * @param props.withComparison - Whether to show comparison data
86
+ * @param props.withOverlayLabel - Whether to overlay the label on top of the bar
87
+ * @param props.primaryColor - Primary color for current period bars
88
+ * @param props.secondaryColor - Secondary color for comparison period bars
89
+ * @param props.valueFormatter - Custom formatter for values
90
+ * @param props.deltaFormatter - Custom formatter for delta values
91
+ * @param props.loading - Whether the chart is in loading state
92
+ * @param props.className - Additional CSS class name
93
+ * @param props.style - Custom styling for the chart container
85
94
  * @return JSX element representing the leaderboard chart
86
95
  */
87
96
  declare const LeaderboardChart: FC<LeaderboardChartProps>;
@@ -10,15 +10,21 @@ declare const BaseLegend: react.ForwardRefExoticComponent<Omit<{
10
10
  text: string;
11
11
  value?: any;
12
12
  }[]) => React.ReactNode;
13
- shape?: _visx_legend_lib_types.LegendShape<unknown, any>;
13
+ className?: string;
14
+ style?: React.CSSProperties;
15
+ fill?: (label: {
16
+ datum: unknown;
17
+ index: number;
18
+ text: string;
19
+ value?: any;
20
+ }) => string | undefined;
14
21
  size?: (label: {
15
22
  datum: unknown;
16
23
  index: number;
17
24
  text: string;
18
25
  value?: any;
19
26
  }) => string | number | undefined;
20
- className?: string;
21
- style?: React.CSSProperties;
27
+ shape?: _visx_legend_lib_types.LegendShape<unknown, any>;
22
28
  domain?: unknown[];
23
29
  shapeWidth?: string | number;
24
30
  shapeHeight?: string | number;
@@ -28,12 +34,6 @@ declare const BaseLegend: react.ForwardRefExoticComponent<Omit<{
28
34
  labelMargin?: string | number;
29
35
  itemMargin?: string | number;
30
36
  itemDirection?: _visx_legend_lib_types.FlexDirection;
31
- fill?: (label: {
32
- datum: unknown;
33
- index: number;
34
- text: string;
35
- value?: any;
36
- }) => string | undefined;
37
37
  shapeStyle?: (label: {
38
38
  datum: unknown;
39
39
  index: number;
@@ -1,5 +1,5 @@
1
1
  import { LegendItemWithGlyph, LegendItemWithoutGlyph } from './types.js';
2
- import { SeriesData, DataPointDate, DataPointPercentage, ChartTheme } from '../../types.js';
2
+ import { SeriesData, DataPointDate, DataPointPercentage } from '../../types.js';
3
3
  import { LegendShape } from '@visx/legend/lib/types';
4
4
 
5
5
  interface ChartLegendOptions {
@@ -11,11 +11,10 @@ interface ChartLegendOptions {
11
11
  /**
12
12
  * Hook to transform chart data into legend items
13
13
  * @param data - The chart data to transform
14
- * @param theme - The chart theme for colors
15
14
  * @param options - Configuration options for legend generation
16
15
  * @param legendShape - The shape type for legend items (string literal or React component)
17
16
  * @return Array of legend items ready for display
18
17
  */
19
- declare function useChartLegendData<T extends SeriesData[] | DataPointDate[] | DataPointPercentage[]>(data: T, theme: ChartTheme, options?: ChartLegendOptions, legendShape?: LegendShape<SeriesData[], number>): LegendItemWithGlyph[] | LegendItemWithoutGlyph[];
18
+ declare function useChartLegendData<T extends SeriesData[] | DataPointDate[] | DataPointPercentage[]>(data: T, options?: ChartLegendOptions, legendShape?: LegendShape<SeriesData[], number>): LegendItemWithGlyph[] | LegendItemWithoutGlyph[];
20
19
 
21
20
  export { type ChartLegendOptions, useChartLegendData };
@@ -1 +1 @@
1
- export { default as PieChart } from './pie-chart.js';
1
+ export { default as PieChart, PieChartUnresponsive } from './pie-chart.js';
@@ -1,7 +1,7 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import { Legend } from '../legend/legend.js';
2
+ import { FC, ComponentType, PropsWithChildren, ReactNode } from 'react';
3
+ import { Optional, BaseChartProps, DataPointPercentage } from '../../types.js';
2
4
  import { ResponsiveConfig } from '../shared/with-responsive.js';
3
- import { BaseChartProps, DataPointPercentage } from '../../types.js';
4
- import { ReactNode } from 'react';
5
5
 
6
6
  interface PieChartProps extends BaseChartProps<DataPointPercentage[]> {
7
7
  /**
@@ -33,6 +33,15 @@ interface PieChartProps extends BaseChartProps<DataPointPercentage[]> {
33
33
  */
34
34
  children?: ReactNode;
35
35
  }
36
- declare const _default: ({ resizeDebounceTime, maxWidth, aspectRatio, ...chartProps }: Pick<Partial<PieChartProps>, "height" | "size" | "width"> & Omit<PieChartProps, "height" | "size" | "width"> & ResponsiveConfig) => react_jsx_runtime.JSX.Element;
36
+ type PieChartBaseProps = Optional<PieChartProps, 'size'>;
37
+ interface PieChartSubComponents {
38
+ Legend: ComponentType<React.ComponentProps<typeof Legend>>;
39
+ SVG: FC<PropsWithChildren>;
40
+ HTML: FC<PropsWithChildren>;
41
+ }
42
+ type PieChartComponent = FC<PieChartBaseProps> & PieChartSubComponents;
43
+ type PieChartResponsiveComponent = FC<PieChartBaseProps & ResponsiveConfig> & PieChartSubComponents;
44
+ declare const PieChart: PieChartComponent;
45
+ declare const PieChartResponsive: PieChartResponsiveComponent;
37
46
 
38
- export { _default as default };
47
+ export { PieChart as PieChartUnresponsive, PieChartResponsive as default };
@@ -1,6 +1,7 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import { Legend } from '../legend/legend.js';
2
+ import { FC, ComponentType, PropsWithChildren, ReactNode } from 'react';
3
+ import { Optional, BaseChartProps, DataPointPercentage } from '../../types.js';
2
4
  import { ResponsiveConfig } from '../shared/with-responsive.js';
3
- import { BaseChartProps, DataPointPercentage } from '../../types.js';
4
5
 
5
6
  interface PieSemiCircleChartProps extends BaseChartProps<DataPointPercentage[]> {
6
7
  /**
@@ -24,7 +25,18 @@ interface PieSemiCircleChartProps extends BaseChartProps<DataPointPercentage[]>
24
25
  * Note text to display below the label
25
26
  */
26
27
  note?: string;
28
+ /**
29
+ * Use the children prop to render additional elements on the chart.
30
+ */
31
+ children?: ReactNode;
32
+ }
33
+ type PieSemiCircleChartBaseProps = Optional<PieSemiCircleChartProps, 'width'>;
34
+ interface PieSemiCircleChartSubComponents {
35
+ Legend: ComponentType<React.ComponentProps<typeof Legend>>;
36
+ SVG: FC<PropsWithChildren>;
37
+ HTML: FC<PropsWithChildren>;
27
38
  }
28
- declare const _default: ({ resizeDebounceTime, maxWidth, aspectRatio, ...chartProps }: Pick<Partial<PieSemiCircleChartProps>, "height" | "size" | "width"> & Omit<PieSemiCircleChartProps, "height" | "size" | "width"> & ResponsiveConfig) => react_jsx_runtime.JSX.Element;
39
+ type PieSemiCircleChartResponsiveComponent = FC<PieSemiCircleChartBaseProps & ResponsiveConfig> & PieSemiCircleChartSubComponents;
40
+ declare const PieSemiCircleChartResponsive: PieSemiCircleChartResponsiveComponent;
29
41
 
30
- export { _default as default };
42
+ export { PieSemiCircleChartResponsive as default };
@@ -41,4 +41,4 @@ type UseChartMouseHandlerReturn = {
41
41
  */
42
42
  declare const useChartMouseHandler: ({ withTooltips, }: UseChartMouseHandlerProps) => UseChartMouseHandlerReturn;
43
43
 
44
- export { useChartMouseHandler as default };
44
+ export { useChartMouseHandler };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Custom hook to memoize a value using deep equality comparison.
3
+ * Prevents unnecessary re-renders when objects have the same content but different references.
4
+ *
5
+ * @param value - The value to memoize with deep equality comparison
6
+ * @return The memoized value that only changes when deeply different
7
+ */
8
+ declare const useDeepMemo: <T>(value: T) => T;
9
+
10
+ export { useDeepMemo };
@@ -0,0 +1,14 @@
1
+ import { ChartTheme } from '../types.js';
2
+
3
+ /**
4
+ * Hook to get the effective chart theme, merging global and local themes.
5
+ *
6
+ * This hook combines the global theme from GlobalChartsProvider with the local theme
7
+ * from ThemeProvider. The global theme provides the base, while the local theme
8
+ * can override specific properties for fine-grained customization.
9
+ *
10
+ * @return The effective chart theme to use
11
+ */
12
+ declare const useGlobalChartTheme: () => ChartTheme;
13
+
14
+ export { useGlobalChartTheme };
@@ -0,0 +1,6 @@
1
+ import * as _visx_xychart from '@visx/xychart';
2
+ import { SeriesData } from '../types.js';
3
+
4
+ declare const useXYChartTheme: (data: SeriesData[]) => _visx_xychart.XYChartTheme;
5
+
6
+ export { useXYChartTheme };
@@ -15,7 +15,11 @@ export { Group } from '@visx/group';
15
15
  export { CircleShape, LineShape, RectShape } from '@visx/legend';
16
16
  export { ThemeProvider } from './providers/theme/theme-provider.js';
17
17
  export { defaultTheme, jetpackTheme, wooTheme } from './providers/theme/themes.js';
18
- export { default as useChartMouseHandler } from './hooks/use-chart-mouse-handler.js';
18
+ export { mergeThemes } from './utils/merge-themes.js';
19
+ export { useDeepMemo } from './hooks/use-deep-memo.js';
20
+ export { useGlobalChartTheme } from './hooks/use-global-chart-theme.js';
21
+ export { useChartMouseHandler } from './hooks/use-chart-mouse-handler.js';
22
+ export { useXYChartTheme } from './hooks/use-xychart-theme.js';
19
23
  export { BaseChartProps, ButtonWithPopover, ChartTheme, DataPoint, DataPointDate, DataPointPercentage, GridProps, MultipleDataPointsDate, Optional, OrientationType, PopoverButtonAttributes, PopoverElement, PopoverElementAttributes, ScaleOptions, SeriesData, SeriesDataOptions, ToggleEvent } from './types.js';
20
24
  export { RenderTooltipParams } from '@visx/xychart/lib/components/Tooltip';
21
25
  export { EventHandlerParams, GridStyles, LineStyles } from '@visx/xychart';
@@ -1,10 +1,13 @@
1
1
  import * as react from 'react';
2
2
  import { FC, ReactNode } from 'react';
3
3
  import { ChartContextValue } from './types.js';
4
+ import { ChartTheme } from '../../types.js';
4
5
 
5
6
  declare const GlobalChartsContext: react.Context<ChartContextValue>;
6
7
  interface GlobalChartsProviderProps {
7
8
  children: ReactNode;
9
+ /** Optional theme override. Considered static for provider lifecycle. */
10
+ theme?: Partial<ChartTheme>;
8
11
  }
9
12
  declare const GlobalChartsProvider: FC<GlobalChartsProviderProps>;
10
13
  declare const useGlobalChartsContext: () => ChartContextValue;
@@ -3,7 +3,6 @@ import { ChartTheme } from '../../types.js';
3
3
 
4
4
  interface ChartRegistration {
5
5
  legendItems: BaseLegendItem[];
6
- theme: ChartTheme;
7
6
  chartType: string;
8
7
  metadata?: Record<string, unknown>;
9
8
  }
@@ -12,6 +11,8 @@ interface ChartContextValue {
12
11
  registerChart: (id: string, data: ChartRegistration) => void;
13
12
  unregisterChart: (id: string) => void;
14
13
  getChartData: (id: string) => ChartRegistration | undefined;
14
+ /** Theme provided by the GlobalChartsProvider (merged with defaults) */
15
+ theme: ChartTheme;
15
16
  }
16
17
 
17
18
  export type { ChartContextValue, ChartRegistration };
@@ -1,7 +1,12 @@
1
1
  import { BaseLegendItem } from '../../components/legend/types.js';
2
- import { ChartTheme } from '../../types.js';
3
2
 
4
3
  declare const useChartId: (providedId?: string) => string;
5
- declare const useChartRegistration: (chartId: string, legendItems: BaseLegendItem[], theme: ChartTheme, chartType: string, isDataValid: boolean, metadata?: Record<string, unknown>) => void;
4
+ declare const useChartRegistration: ({ chartId, legendItems, chartType, isDataValid, metadata, }: {
5
+ chartId: string;
6
+ legendItems: BaseLegendItem[];
7
+ chartType: string;
8
+ isDataValid: boolean;
9
+ metadata?: Record<string, unknown>;
10
+ }) => void;
6
11
 
7
12
  export { useChartId, useChartRegistration };
@@ -1,2 +1,2 @@
1
- export { ThemeProvider, useChartTheme, useXYChartTheme } from './theme-provider.js';
1
+ export { ThemeProvider, useChartTheme } from './theme-provider.js';
2
2
  export { defaultTheme, jetpackTheme, wooTheme } from './themes.js';
@@ -1,13 +1,11 @@
1
- import * as _visx_xychart from '@visx/xychart';
2
- import { ChartTheme, SeriesData } from '../../types.js';
1
+ import { ChartTheme } from '../../types.js';
3
2
  import { FC, ReactNode } from 'react';
4
3
 
5
4
  /**
6
5
  * Hook to access chart theme
7
6
  * @return {object} A built theme configuration compatible with visx charts
8
7
  */
9
- declare const useChartTheme: () => ChartTheme;
10
- declare const useXYChartTheme: (data: SeriesData[]) => _visx_xychart.XYChartTheme;
8
+ declare const useChartTheme: () => Partial<ChartTheme>;
11
9
  /**
12
10
  * Props for the ThemeProvider component
13
11
  */
@@ -19,4 +17,4 @@ type ThemeProviderProps = {
19
17
  };
20
18
  declare const ThemeProvider: FC<ThemeProviderProps>;
21
19
 
22
- export { ThemeProvider, useChartTheme, useXYChartTheme };
20
+ export { ThemeProvider, useChartTheme };
@@ -0,0 +1,13 @@
1
+ import { ChartTheme } from '../types.js';
2
+
3
+ /**
4
+ * Merges chart themes with proper precedence.
5
+ * The second theme (override) takes precedence over the first theme (base).
6
+ *
7
+ * @param baseTheme - Base theme object
8
+ * @param overrideTheme - Theme to override base with (takes precedence)
9
+ * @return Merged theme with overrideTheme values taking precedence
10
+ */
11
+ declare function mergeThemes(baseTheme: ChartTheme, overrideTheme: Partial<ChartTheme>): ChartTheme;
12
+
13
+ export { mergeThemes };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/charts",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Display charts within Automattic products.",
5
5
  "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/charts/#readme",
6
6
  "bugs": {
@@ -80,6 +80,7 @@
80
80
  "clsx": "2.1.1",
81
81
  "date-fns": "^4.1.0",
82
82
  "deepmerge": "4.3.1",
83
+ "fast-deep-equal": "3.1.3",
83
84
  "gridicons": "3.4.2",
84
85
  "tslib": "2.5.0"
85
86
  },