@automattic/charts 2.0.1 → 3.1.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 (102) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/TOKENS.md +109 -50
  3. package/dist/index.cjs +529 -233
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.css +118 -55
  6. package/dist/index.d.cts +90 -48
  7. package/dist/index.d.ts +90 -48
  8. package/dist/index.js +527 -235
  9. package/dist/index.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/charts/area-chart/area-chart.module.scss +8 -3
  12. package/src/charts/area-chart/area-chart.tsx +25 -25
  13. package/src/charts/area-chart/private/overlays.tsx +3 -3
  14. package/src/charts/area-chart/test/area-chart.test.tsx +301 -11
  15. package/src/charts/area-chart/types.ts +4 -3
  16. package/src/charts/bar-chart/bar-chart.module.scss +14 -6
  17. package/src/charts/bar-chart/bar-chart.tsx +21 -15
  18. package/src/charts/bar-chart/test/bar-chart.test.tsx +255 -2
  19. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +23 -18
  20. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +107 -86
  21. package/src/charts/conversion-funnel-chart/test/chart-scope.test.tsx +36 -0
  22. package/src/charts/geo-chart/geo-chart.module.scss +8 -0
  23. package/src/charts/geo-chart/geo-chart.tsx +12 -4
  24. package/src/charts/heatmap-chart/heatmap-chart.module.scss +22 -9
  25. package/src/charts/heatmap-chart/heatmap-chart.tsx +43 -13
  26. package/src/charts/heatmap-chart/private/build-calendar-data.ts +65 -5
  27. package/src/charts/heatmap-chart/test/build-calendar-data.test.ts +125 -0
  28. package/src/charts/heatmap-chart/types.ts +8 -0
  29. package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +13 -7
  30. package/src/charts/leaderboard-chart/leaderboard-chart.tsx +17 -22
  31. package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +107 -1
  32. package/src/charts/leaderboard-chart/types.ts +0 -2
  33. package/src/charts/line-chart/line-chart.module.scss +12 -7
  34. package/src/charts/line-chart/line-chart.tsx +32 -28
  35. package/src/charts/line-chart/private/line-chart-annotation.tsx +4 -4
  36. package/src/charts/line-chart/private/line-chart-annotations-overlay.tsx +2 -2
  37. package/src/charts/line-chart/test/line-chart.test.tsx +262 -5
  38. package/src/charts/line-chart/types.ts +4 -3
  39. package/src/charts/pie-chart/pie-chart.tsx +12 -14
  40. package/src/charts/pie-chart/test/pie-chart.test.tsx +73 -1
  41. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +2 -2
  42. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +12 -14
  43. package/src/charts/private/{single-chart-context/single-chart-context.tsx → chart-instance-context/chart-instance-context.ts} +1 -5
  44. package/src/charts/private/chart-instance-context/index.ts +3 -0
  45. package/src/charts/private/{single-chart-context/use-single-chart-context.ts → chart-instance-context/use-chart-instance-context.ts} +1 -3
  46. package/src/charts/private/svg-empty-state/get-all-hidden-message.ts +41 -0
  47. package/src/charts/private/svg-empty-state/index.ts +1 -0
  48. package/src/charts/private/svg-empty-state/svg-empty-state.module.scss +1 -1
  49. package/src/charts/private/svg-empty-state/test/get-all-hidden-message.test.ts +15 -0
  50. package/src/charts/private/with-responsive/test/with-responsive.test.tsx +16 -0
  51. package/src/charts/private/with-responsive/with-responsive.tsx +18 -11
  52. package/src/charts/private/x-zoom/test/x-zoom.test.tsx +2 -2
  53. package/src/charts/private/x-zoom/x-zoom.module.scss +3 -3
  54. package/src/charts/private/x-zoom/x-zoom.tsx +2 -2
  55. package/src/components/legend/legend.tsx +4 -4
  56. package/src/components/legend/private/base-legend.module.scss +1 -1
  57. package/src/components/legend/private/base-legend.tsx +70 -19
  58. package/src/components/legend/test/legend.test.tsx +100 -4
  59. package/src/components/tooltip/accessible-tooltip.tsx +17 -4
  60. package/src/components/tooltip/base-tooltip.tsx +8 -1
  61. package/src/components/trend-indicator/test/trend-indicator.test.tsx +20 -0
  62. package/src/components/trend-indicator/trend-indicator.module.scss +4 -7
  63. package/src/components/trend-indicator/trend-indicator.tsx +3 -0
  64. package/src/hooks/index.ts +1 -1
  65. package/src/hooks/test/use-xychart-theme-provider-override.test.tsx +28 -0
  66. package/src/hooks/test/use-xychart-theme-style-reads.test.tsx +19 -13
  67. package/src/hooks/test/use-xychart-theme.test.tsx +25 -0
  68. package/src/hooks/{use-interactive-legend-data.ts → use-legend-visibility-data.ts} +29 -30
  69. package/src/hooks/use-xychart-theme.ts +6 -4
  70. package/src/index.ts +12 -3
  71. package/src/providers/chart-context/global-charts-provider.tsx +127 -30
  72. package/src/providers/chart-context/hooks/use-default-hidden-series.ts +47 -0
  73. package/src/providers/chart-context/private/theme-override-vars.ts +87 -0
  74. package/src/providers/chart-context/private/with-catalog-pointers.ts +50 -0
  75. package/src/providers/chart-context/test/chart-context.test.tsx +142 -40
  76. package/src/providers/chart-context/test/chart-scope-class.test.tsx +19 -0
  77. package/src/providers/chart-context/test/sparse-theme.test.ts +35 -0
  78. package/src/providers/chart-context/test/theme-override-vars-guard.test.tsx +29 -0
  79. package/src/providers/chart-context/test/theme-override-vars.test.ts +118 -0
  80. package/src/providers/chart-context/test/use-default-hidden-series-paint.test.tsx +56 -0
  81. package/src/providers/chart-context/test/use-default-hidden-series.ssr.test.tsx +39 -0
  82. package/src/providers/chart-context/test/use-default-hidden-series.test.tsx +222 -0
  83. package/src/providers/chart-context/test/with-catalog-pointers.test.ts +47 -0
  84. package/src/providers/chart-context/themes.ts +18 -27
  85. package/src/providers/chart-context/types.ts +7 -4
  86. package/src/providers/chart-scope/chart-scope-context.ts +8 -0
  87. package/src/providers/chart-scope/index.ts +3 -0
  88. package/src/providers/chart-scope/test/chart-scope.test.tsx +24 -0
  89. package/src/providers/chart-scope/test/use-standalone-scope-class.test.tsx +20 -0
  90. package/src/providers/chart-scope/use-chart-scope-element.ts +9 -0
  91. package/src/providers/chart-scope/use-standalone-scope-class.ts +13 -0
  92. package/src/providers/index.ts +1 -0
  93. package/src/styles/chart-scope-class.ts +6 -0
  94. package/src/styles/chart-scope.scss +83 -0
  95. package/src/styles/test/chart-scope.test.ts +165 -0
  96. package/src/styles/test/wpds-fallbacks.test.ts +131 -0
  97. package/src/types.ts +12 -34
  98. package/src/charts/private/grid-control/grid-control.module.scss +0 -8
  99. package/src/charts/private/grid-control/grid-control.tsx +0 -26
  100. package/src/charts/private/grid-control/index.ts +0 -1
  101. package/src/charts/private/grid-control/test/grid-control.test.tsx +0 -56
  102. package/src/charts/private/single-chart-context/index.ts +0 -8
package/dist/index.cjs CHANGED
@@ -52,17 +52,23 @@ require("@babel/runtime/helpers/esm/extends");
52
52
  require("@babel/runtime/helpers/extends");
53
53
  let _wordpress_icons = require("@wordpress/icons");
54
54
  let _visx_shape = require("@visx/shape");
55
- //#region src/charts/private/single-chart-context/single-chart-context.tsx
55
+ //#region src/charts/private/chart-instance-context/chart-instance-context.ts
56
56
  const ChartInstanceContext = (0, react.createContext)(null);
57
- const SingleChartContext = ChartInstanceContext;
58
57
  //#endregion
59
- //#region src/charts/private/single-chart-context/use-single-chart-context.ts
58
+ //#region src/charts/private/chart-instance-context/use-chart-instance-context.ts
60
59
  const useChartInstanceContext = () => {
61
60
  const context = (0, react.useContext)(ChartInstanceContext);
62
61
  if (!context) throw new Error("useChartInstanceContext must be used within a Chart component");
63
62
  return context;
64
63
  };
65
- const useSingleChartContext = useChartInstanceContext;
64
+ //#endregion
65
+ //#region src/styles/chart-scope-class.ts
66
+ /**
67
+ * Stable class that carries the `--a8c-charts-*` catalog. Applied to the `GlobalChartsProvider` wrapper, to portal-rendered surfaces (which sit outside the provider tree), and to standalone components when no provider is above them.
68
+ *
69
+ * Deliberately unhashed: consumers are documented as being able to target it to override a token on the provider element.
70
+ */
71
+ const CHART_SCOPE_CLASS = "a8c-charts-scope";
66
72
  //#endregion
67
73
  //#region src/utils/create-composition.ts
68
74
  /**
@@ -620,6 +626,14 @@ const resolveFontSize = (val) => {
620
626
  }
621
627
  };
622
628
  //#endregion
629
+ //#region src/providers/chart-scope/chart-scope-context.ts
630
+ /**
631
+ * The nearest chart element, published so JS token resolution reads the same CSS cascade that paints the chart's own colours.
632
+ *
633
+ * All JS token resolution reads this element rather than `document.documentElement`, so a `getComputedStyle` call sees the same `--a8c-charts-*` values — including any override set inside the provider tree — that a CSS-painted sibling element does.
634
+ */
635
+ const ChartScopeContext = (0, react.createContext)(null);
636
+ //#endregion
623
637
  //#region src/providers/chart-context/private/get-chart-color.ts
624
638
  /**
625
639
  * Golden ratio for mathematically pleasing color distribution
@@ -749,12 +763,75 @@ const getChartColor = (index, colorCache) => {
749
763
  return (0, _visx_vendor_d3_color.hsl)(Math.round(fallbackHue), fallbackSaturation / 100, fallbackLightness / 100).formatHex();
750
764
  };
751
765
  //#endregion
766
+ //#region src/providers/chart-context/private/theme-override-vars.ts
767
+ const ROLE_FOR_FIELD = [
768
+ ["--a8c-charts-color-background", (theme) => theme.backgroundColor],
769
+ ["--a8c-charts-color-grid", (theme) => theme.gridStyles?.stroke],
770
+ ["--a8c-charts-color-axis", (theme) => theme.xAxisLineStyles?.stroke],
771
+ ["--a8c-charts-color-tick", (theme) => theme.xTickLineStyles?.stroke],
772
+ ["--a8c-charts-color-label-axis", (theme) => theme.svgLabelSmall?.fill]
773
+ ];
774
+ /**
775
+ * The variable a `theme` prop override is published as, one layer outside the role itself.
776
+ *
777
+ * `chart-scope.scss` declares each of these roles as `var(<role>-theme, <catalog default>)`, so an override that is invalid at computed-value time — a fallback-less `var()` naming a token the host never set — only invalidates this variable, and the role still resolves its mapped `--wpds-*` token. Published as the role directly, such a value took the role and every bare `var(<role>)` read site down with it.
778
+ *
779
+ * @param role - The catalog role being overridden.
780
+ * @return The custom property carrying the consumer's value.
781
+ */
782
+ const themeLayerVar = (role) => `${role}-theme`;
783
+ ROLE_FOR_FIELD.map(([role]) => role);
784
+ /**
785
+ * Whether a value reads the role it is being published for, which would make the catalog entry depend on itself.
786
+ *
787
+ * The dependency runs `<role>: var(<role>-theme, …)`, so a value naming `<role>` closes a cycle through the catalog entry. CSS marks every custom property in a cycle invalid at computed-value time — the role's own fallback is *not* used, so the token resolves to nothing and every chart loses that colour. Verified in Chrome: `--role-theme: var(--role, blue); --role: var(--role-theme, green)` leaves `--role` empty, not `green`.
788
+ *
789
+ * Three things this has to get right, none of which a plain substring test gets:
790
+ *
791
+ * `var()` permits whitespace after the opening paren, so testing for `var(<role>` misses `var( --a8c-charts-color-grid, … )`.
792
+ *
793
+ * Several roles are prefixes of others (`--a8c-charts-color-label` of `--a8c-charts-color-label-secondary`), and every role is a prefix of its own theme layer. A prefix match would read a legitimate cross-role pointer as a self-reference and silently drop the override, so the role must be followed by something that cannot continue an identifier — `-` included. A value naming `<role>-theme` is therefore not treated as a self-reference: that cycle is confined to the theme layer, which leaves the role free to fall back to the catalog default.
794
+ *
795
+ * `resolveCssVariable` also accepts a bare custom-property name with no `var()` wrapper (`--a8c-charts-color-grid`), so that is legal `theme` input too. It forms no cycle — custom properties accept arbitrary token streams — so it survives as a literal string and drops silently at the use site instead of falling back to the catalog default.
796
+ *
797
+ * @param value - The consumer's value for the field.
798
+ * @param role - The catalog role this value would override.
799
+ * @return True when the value reads `role`, so it must not be published.
800
+ */
801
+ const readsOwnRole = (value, role) => value.trim() === role || new RegExp(`var\\(\\s*${role.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`).test(value);
802
+ /**
803
+ * Maps a sparse consumer theme onto the theme-layer variables its overridden catalog roles read, so CSS-painted and JS-resolved colours read one source.
804
+ *
805
+ * A value that reads the role it would override is left unpublished — that is the default theme's own pointer surviving `mergeThemes`, and publishing it would invalidate the role. The role is still reported in `roles`: `withCatalogPointers` restores its theme field to the catalog pointer either way, so CSS and visx agree on the catalog default rather than visx painting a literal CSS never sees.
806
+ *
807
+ * @param theme - The consumer's `theme` prop, before merging with the default theme.
808
+ * @return The wrapper's custom properties, and the roles the consumer overrode.
809
+ */
810
+ const themeOverrideVars = (theme) => {
811
+ if (!theme) return {
812
+ vars: {},
813
+ roles: []
814
+ };
815
+ const vars = {};
816
+ const roles = [];
817
+ for (const [role, read] of ROLE_FOR_FIELD) {
818
+ const value = read(theme);
819
+ if (typeof value !== "string" || value === "") continue;
820
+ roles.push(role);
821
+ if (!readsOwnRole(value, role)) vars[themeLayerVar(role)] = value;
822
+ }
823
+ return {
824
+ vars,
825
+ roles
826
+ };
827
+ };
828
+ //#endregion
752
829
  //#region src/providers/chart-context/themes.ts
753
830
  /**
754
831
  * Default theme configuration
755
832
  */
756
833
  const defaultTheme = {
757
- backgroundColor: "var(--a8c-charts-color-background, var(--wpds-color-background-surface-neutral-strong, #fff))",
834
+ backgroundColor: "var(--a8c-charts-color-background, #fff)",
758
835
  labelBackgroundColor: "transparent",
759
836
  labelTextColor: "var(--a8c-charts-color-label-on-fill, #FFFFFF)",
760
837
  colors: [
@@ -765,59 +842,59 @@ const defaultTheme = {
765
842
  "#FF8C8F"
766
843
  ],
767
844
  gridStyles: {
768
- stroke: "var(--a8c-charts-color-grid, var(--wpds-color-stroke-surface-neutral, #dbdbdb))",
845
+ stroke: "var(--a8c-charts-color-grid, #dbdbdb)",
769
846
  strokeWidth: 1
770
847
  },
771
848
  tickLength: 4,
772
849
  gridColor: "",
773
850
  gridColorDark: "",
774
851
  xTickLineStyles: {
775
- stroke: "var(--a8c-charts-color-tick, var(--wpds-color-stroke-surface-neutral, #dbdbdb))",
852
+ stroke: "var(--a8c-charts-color-tick, #dbdbdb)",
776
853
  strokeWidth: 1
777
854
  },
778
855
  xAxisLineStyles: {
779
- stroke: "var(--a8c-charts-color-axis, var(--wpds-color-stroke-surface-neutral, #dbdbdb))",
856
+ stroke: "var(--a8c-charts-color-axis, #dbdbdb)",
780
857
  strokeWidth: 1
781
858
  },
782
859
  legend: {
783
- labelStyles: { color: "var(--a8c-charts-color-label, var(--wpds-color-foreground-content-neutral, #1e1e1e))" },
860
+ labelStyles: { color: "var(--a8c-charts-color-label, #1e1e1e)" },
784
861
  containerStyles: {},
785
862
  shapeStyles: []
786
863
  },
787
864
  seriesLineStyles: [],
788
865
  glyphs: [],
789
866
  svgLabelSmall: {
790
- fill: "var(--a8c-charts-color-label, var(--wpds-color-foreground-content-neutral, #1e1e1e))",
867
+ fill: "var(--a8c-charts-color-label-axis, #1e1e1e)",
791
868
  fontFamily: "inherit"
792
869
  },
793
870
  svgLabelBig: { fontFamily: "inherit" },
794
871
  annotationStyles: {
795
872
  label: {
796
- anchorLineStroke: "var(--a8c-charts-color-annotation, var(--wpds-color-foreground-content-neutral, #1e1e1e))",
797
- backgroundFill: "var(--a8c-charts-color-background, var(--wpds-color-background-surface-neutral-strong, #fff))"
873
+ anchorLineStroke: "var(--a8c-charts-color-annotation, #1e1e1e)",
874
+ backgroundFill: "var(--a8c-charts-color-surface, #fff)"
798
875
  },
799
- connector: { stroke: "var(--a8c-charts-color-annotation, var(--wpds-color-foreground-content-neutral, #1e1e1e))" },
876
+ connector: { stroke: "var(--a8c-charts-color-annotation, #1e1e1e)" },
800
877
  circleSubject: {
801
878
  stroke: "transparent",
802
- fill: "var(--a8c-charts-color-annotation, var(--wpds-color-foreground-content-neutral, #1e1e1e))",
879
+ fill: "var(--a8c-charts-color-annotation, #1e1e1e)",
803
880
  radius: 5
804
881
  }
805
882
  },
806
- geoChart: { featureFillColor: "var(--a8c-charts-color-surface-secondary, var(--wpds-color-background-surface-neutral-weak, #f4f4f4))" },
883
+ geoChart: { featureFillColor: "var(--a8c-charts-color-surface-secondary, #f4f4f4)" },
807
884
  leaderboardChart: {
808
885
  rowGap: 12,
809
886
  columnGap: 4,
810
887
  labelSpacing: "xs",
811
888
  deltaColors: [
812
- "var(--a8c-charts-color-trend-down, var(--wpds-color-foreground-content-error-weak, #cc1818))",
813
- "var(--a8c-charts-color-trend-neutral, var(--wpds-color-foreground-content-neutral-weak, #707070))",
814
- "var(--a8c-charts-color-trend-up, var(--wpds-color-foreground-content-success-weak, #008030))"
889
+ "var(--a8c-charts-color-trend-down, #cc1818)",
890
+ "var(--a8c-charts-color-trend-neutral, #707070)",
891
+ "var(--a8c-charts-color-trend-up, #008030)"
815
892
  ]
816
893
  },
817
894
  conversionFunnelChart: {
818
- backgroundColor: "var(--a8c-charts-color-surface-secondary, var(--wpds-color-background-surface-neutral-weak, #f4f4f4))",
819
- positiveChangeColor: "var(--a8c-charts-color-trend-up, var(--wpds-color-foreground-content-success-weak, #008030))",
820
- negativeChangeColor: "var(--a8c-charts-color-trend-down, var(--wpds-color-foreground-content-error-weak, #cc1818))"
895
+ backgroundColor: "var(--a8c-charts-color-surface-secondary, #f4f4f4)",
896
+ positiveChangeColor: "var(--a8c-charts-color-trend-up, #008030)",
897
+ negativeChangeColor: "var(--a8c-charts-color-trend-down, #cc1818)"
821
898
  },
822
899
  lineChart: { lineStyles: { comparison: {
823
900
  strokeDasharray: "4 4",
@@ -842,15 +919,66 @@ const defaultTheme = {
842
919
  }
843
920
  };
844
921
  //#endregion
922
+ //#region src/providers/chart-context/private/with-catalog-pointers.ts
923
+ const CATALOG_RESTORE_FOR_ROLE = {
924
+ "--a8c-charts-color-background": () => ({ backgroundColor: defaultTheme.backgroundColor }),
925
+ "--a8c-charts-color-grid": (theme) => ({ gridStyles: {
926
+ ...theme.gridStyles,
927
+ stroke: defaultTheme.gridStyles.stroke
928
+ } }),
929
+ "--a8c-charts-color-axis": (theme) => ({ xAxisLineStyles: {
930
+ ...theme.xAxisLineStyles,
931
+ stroke: defaultTheme.xAxisLineStyles.stroke
932
+ } }),
933
+ "--a8c-charts-color-tick": (theme) => ({ xTickLineStyles: {
934
+ ...theme.xTickLineStyles,
935
+ stroke: defaultTheme.xTickLineStyles.stroke
936
+ } }),
937
+ "--a8c-charts-color-label-axis": (theme) => ({ svgLabelSmall: {
938
+ ...theme.svgLabelSmall,
939
+ fill: defaultTheme.svgLabelSmall.fill
940
+ } })
941
+ };
942
+ /**
943
+ * Restores the mapped theme fields of `overriddenRoles` to their catalog pointer, so the theme-layer variable `themeOverrideVars` writes on the provider wrapper is the only carrier for an overridden role — CSS and the JS bridge (`useXYChartTheme`) then resolve it through the same cascade instead of visx reading a baked literal that can disagree with a closer CSS override.
944
+ *
945
+ * This covers roles `themeOverrideVars` deliberately left unpublished as well as the ones it published. A value that reads its own role is not publishable, but leaving visx the consumer's literal is what the whole mechanism exists to prevent: `theme={ { gridStyles: { stroke: 'var(--brand, var(--a8c-charts-color-grid, red))' } } }` would have CSS paint the catalog default while visx painted `--brand`.
946
+ *
947
+ * Every field outside the five mapped roles is left exactly as `merged` provided it: those aren't published as theme-layer vars, so rewriting them would erase a consumer's override with no replacement carrier.
948
+ *
949
+ * @param merged - The consumer theme merged onto `defaultTheme` (`mergeThemes` output).
950
+ * @param overriddenRoles - The catalog roles the consumer overrode, from `themeOverrideVars`.
951
+ * @return A new theme object; `merged` is not mutated.
952
+ */
953
+ const withCatalogPointers = (merged, overriddenRoles) => {
954
+ let result = merged;
955
+ for (const role of overriddenRoles) {
956
+ const restore = CATALOG_RESTORE_FOR_ROLE[role];
957
+ if (restore) result = {
958
+ ...result,
959
+ ...restore(result)
960
+ };
961
+ }
962
+ return result;
963
+ };
964
+ //#endregion
845
965
  //#region src/providers/chart-context/global-charts-provider.tsx
846
966
  const GlobalChartsContext = (0, react.createContext)(null);
847
967
  const GlobalChartsProvider = ({ children, theme }) => {
848
968
  const [charts, setCharts] = (0, react.useState)(() => /* @__PURE__ */ new Map());
849
969
  const [hiddenSeries, setHiddenSeries] = (0, react.useState)(() => /* @__PURE__ */ new Map());
970
+ const seededCharts = (0, react.useRef)(/* @__PURE__ */ new Set());
850
971
  const wrapperRef = (0, react.useRef)(null);
972
+ const [scopeNode, setScopeNode] = (0, react.useState)(null);
973
+ const setWrapperNode = (0, react.useCallback)((node) => {
974
+ wrapperRef.current = node;
975
+ setScopeNode(node);
976
+ }, []);
977
+ const { vars: overrideVars, roles: overriddenRoles } = (0, react.useMemo)(() => themeOverrideVars(theme), [theme]);
851
978
  const providerTheme = (0, react.useMemo)(() => {
852
- return theme ? mergeThemes(defaultTheme, theme) : defaultTheme;
853
- }, [theme]);
979
+ if (!theme) return defaultTheme;
980
+ return withCatalogPointers(mergeThemes(defaultTheme, theme), overriddenRoles);
981
+ }, [theme, overriddenRoles]);
854
982
  const [colorCache, setColorCache] = (0, react.useState)(() => ({
855
983
  colors: [],
856
984
  hues: [],
@@ -927,7 +1055,6 @@ const GlobalChartsProvider = ({ children, theme }) => {
927
1055
  }
928
1056
  return getChartColor(index, colorCache);
929
1057
  }, [colorCache, groupToColorMap]);
930
- const resolveThemeColor = (0, react.useCallback)((value) => value ? normalizeColorToHex(value, wrapperRef.current, resolveCssVariable) : "", []);
931
1058
  const getElementStyles = (0, react.useCallback)(({ data, index, overrideColor, legendShape }) => {
932
1059
  const isSeriesData = data && typeof data === "object" && "data" in data && "options" in data;
933
1060
  const isPointPercentageData = data && typeof data === "object" && "value" in data && typeof data.value === "number" && !("data" in data);
@@ -943,18 +1070,41 @@ const GlobalChartsProvider = ({ children, theme }) => {
943
1070
  shapeStyles: isSeriesData ? getItemShapeStyles(data, index, providerTheme, legendShape) : {}
944
1071
  };
945
1072
  }, [providerTheme, resolveColor]);
946
- const toggleSeriesVisibility = (0, react.useCallback)((chartId, seriesLabel) => {
1073
+ const updateHiddenSeries = (0, react.useCallback)((chartId, update) => {
947
1074
  setHiddenSeries((prev) => {
1075
+ const current = prev.get(chartId);
1076
+ const next = update(new Set(current ?? []));
1077
+ if (!current && next.size === 0) return prev;
1078
+ if (current && current.size === next.size && [...next].every((label) => current.has(label))) return prev;
948
1079
  const newMap = new Map(prev);
949
- const chartHidden = newMap.get(chartId) || /* @__PURE__ */ new Set();
950
- const newSet = new Set(chartHidden);
951
- if (newSet.has(seriesLabel)) newSet.delete(seriesLabel);
952
- else newSet.add(seriesLabel);
953
- if (newSet.size === 0) newMap.delete(chartId);
954
- else newMap.set(chartId, newSet);
1080
+ if (next.size === 0) newMap.delete(chartId);
1081
+ else newMap.set(chartId, next);
955
1082
  return newMap;
956
1083
  });
957
1084
  }, []);
1085
+ const toggleSeriesVisibility = (0, react.useCallback)((chartId, seriesLabel) => {
1086
+ updateHiddenSeries(chartId, (current) => {
1087
+ if (current.has(seriesLabel)) current.delete(seriesLabel);
1088
+ else current.add(seriesLabel);
1089
+ return current;
1090
+ });
1091
+ }, [updateHiddenSeries]);
1092
+ const setSeriesVisibility = (0, react.useCallback)((chartId, seriesLabel, visible) => {
1093
+ updateHiddenSeries(chartId, (current) => {
1094
+ if (visible) current.delete(seriesLabel);
1095
+ else current.add(seriesLabel);
1096
+ return current;
1097
+ });
1098
+ }, [updateHiddenSeries]);
1099
+ const setChartHiddenSeries = (0, react.useCallback)((chartId, seriesLabels) => {
1100
+ updateHiddenSeries(chartId, () => new Set(seriesLabels));
1101
+ }, [updateHiddenSeries]);
1102
+ const hasSeededChart = (0, react.useCallback)((chartId) => seededCharts.current.has(chartId), []);
1103
+ const seedChartHiddenSeries = (0, react.useCallback)((chartId, seriesLabels) => {
1104
+ if (seededCharts.current.has(chartId)) return;
1105
+ seededCharts.current.add(chartId);
1106
+ setChartHiddenSeries(chartId, seriesLabels);
1107
+ }, [setChartHiddenSeries]);
958
1108
  const isSeriesVisible = (0, react.useCallback)((chartId, seriesLabel) => {
959
1109
  const chartHidden = hiddenSeries.get(chartId);
960
1110
  return !chartHidden || !chartHidden.has(seriesLabel);
@@ -970,8 +1120,11 @@ const GlobalChartsProvider = ({ children, theme }) => {
970
1120
  getChartData,
971
1121
  theme: providerTheme,
972
1122
  getElementStyles,
973
- resolveThemeColor,
974
1123
  toggleSeriesVisibility,
1124
+ setSeriesVisibility,
1125
+ setChartHiddenSeries,
1126
+ seedChartHiddenSeries,
1127
+ hasSeededChart,
975
1128
  isSeriesVisible,
976
1129
  getHiddenSeries,
977
1130
  isColorPaletteResolved
@@ -982,8 +1135,11 @@ const GlobalChartsProvider = ({ children, theme }) => {
982
1135
  getChartData,
983
1136
  providerTheme,
984
1137
  getElementStyles,
985
- resolveThemeColor,
986
1138
  toggleSeriesVisibility,
1139
+ setSeriesVisibility,
1140
+ setChartHiddenSeries,
1141
+ seedChartHiddenSeries,
1142
+ hasSeededChart,
987
1143
  isSeriesVisible,
988
1144
  getHiddenSeries,
989
1145
  isColorPaletteResolved
@@ -991,9 +1147,16 @@ const GlobalChartsProvider = ({ children, theme }) => {
991
1147
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GlobalChartsContext.Provider, {
992
1148
  value,
993
1149
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
994
- ref: wrapperRef,
995
- style: { display: "contents" },
996
- children
1150
+ ref: setWrapperNode,
1151
+ className: CHART_SCOPE_CLASS,
1152
+ style: {
1153
+ display: "contents",
1154
+ ...overrideVars
1155
+ },
1156
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartScopeContext.Provider, {
1157
+ value: scopeNode,
1158
+ children
1159
+ })
997
1160
  })
998
1161
  });
999
1162
  };
@@ -1053,12 +1216,31 @@ const useDeepMemo = (value) => {
1053
1216
  return ref.current;
1054
1217
  };
1055
1218
  //#endregion
1219
+ //#region src/providers/chart-scope/use-chart-scope-element.ts
1220
+ /**
1221
+ * Returns the nearest chart scope element, or `null` when rendered outside any scope (and during SSR, before the ref is attached).
1222
+ *
1223
+ * @return {HTMLElement | null} The nearest chart scope element, or `null`.
1224
+ */
1225
+ const useChartScopeElement = () => (0, react.useContext)(ChartScopeContext);
1226
+ //#endregion
1227
+ //#region src/providers/chart-scope/use-standalone-scope-class.ts
1228
+ /**
1229
+ * Returns the catalog class when there is no `GlobalChartsProvider` above this component, and `undefined` when there is.
1230
+ *
1231
+ * Standalone components need the catalog to resolve at all, but inside a provider the tokens already inherit — re-declaring them there would shadow a consumer override set anywhere in the tree.
1232
+ *
1233
+ * @return The scope class name, or undefined when a provider is present.
1234
+ */
1235
+ const useStandaloneScopeClass = () => (0, react.useContext)(GlobalChartsContext) ? void 0 : CHART_SCOPE_CLASS;
1236
+ //#endregion
1056
1237
  //#region src/hooks/use-xychart-theme.ts
1057
1238
  const useXYChartTheme = (data) => {
1058
1239
  const theme = useGlobalChartsTheme();
1240
+ const scopeElement = useChartScopeElement();
1059
1241
  const seriesColorKey = JSON.stringify((data ?? []).map((series) => series.options?.stroke).filter((color) => Boolean(color)));
1060
1242
  return (0, react.useMemo)(() => {
1061
- const resolve = createCssVariableResolver();
1243
+ const resolve = createCssVariableResolver(scopeElement);
1062
1244
  const resolveColor = (value) => value ? resolve(value) ?? value : value;
1063
1245
  const seriesColors = JSON.parse(seriesColorKey);
1064
1246
  return (0, _visx_xychart.buildChartTheme)({
@@ -1082,7 +1264,11 @@ const useXYChartTheme = (data) => {
1082
1264
  fill: resolveColor(theme.svgLabelSmall.fill)
1083
1265
  }
1084
1266
  });
1085
- }, [theme, seriesColorKey]);
1267
+ }, [
1268
+ theme,
1269
+ seriesColorKey,
1270
+ scopeElement
1271
+ ]);
1086
1272
  };
1087
1273
  //#endregion
1088
1274
  //#region src/hooks/use-chart-data-transform.ts
@@ -1352,12 +1538,12 @@ const useDataWithPercentages = (data) => {
1352
1538
  }, [data]);
1353
1539
  };
1354
1540
  //#endregion
1355
- //#region src/hooks/use-interactive-legend-data.ts
1541
+ //#region src/hooks/use-legend-visibility-data.ts
1356
1542
  /**
1357
- * Custom hook to filter and recalculate chart data for interactive legends.
1543
+ * Custom hook to filter and recalculate chart data based on series visibility.
1358
1544
  *
1359
- * When interactive legends are enabled, this hook:
1360
- * 1. Filters data to show only visible series based on legend selection
1545
+ * Regardless of whether the legend is interactive (clickable), this hook:
1546
+ * 1. Filters data to show only visible series (visibility can also be set programmatically)
1361
1547
  * 2. Recalculates percentages so visible segments total 100%
1362
1548
  * 3. Tracks whether all segments are hidden to show empty state
1363
1549
  *
@@ -1366,10 +1552,9 @@ const useDataWithPercentages = (data) => {
1366
1552
  *
1367
1553
  * @example
1368
1554
  * ```tsx
1369
- * const { visibleData, allSegmentsHidden, legendData } = useInteractiveLegendData({
1555
+ * const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
1370
1556
  * data: chartData,
1371
1557
  * chartId: 'my-pie-chart',
1372
- * legendInteractive: true,
1373
1558
  * isSeriesVisible: (id, label) => context.isSeriesVisible(id, label),
1374
1559
  * });
1375
1560
  *
@@ -1384,16 +1569,15 @@ const useDataWithPercentages = (data) => {
1384
1569
  * return <PieChart data={visibleData} />;
1385
1570
  * ```
1386
1571
  *
1387
- * @param params - Configuration object for the hook
1388
- * @param params.data - The chart data to filter
1389
- * @param params.chartId - Unique identifier for the chart (required for interactive mode)
1390
- * @param params.legendInteractive - Whether to enable interactive filtering
1391
- * @param params.isSeriesVisible - Function to check series visibility
1572
+ * @param params - Configuration object for the hook
1573
+ * @param params.data - The chart data to filter
1574
+ * @param params.chartId - Unique identifier for the chart (required for filtering)
1575
+ * @param params.isSeriesVisible - Function to check series visibility
1392
1576
  * @return Object containing visibleData, allSegmentsHidden flag, and legendData with recalculated percentages
1393
1577
  */
1394
- const useInteractiveLegendData = ({ data, chartId, legendInteractive, isSeriesVisible }) => {
1578
+ const useLegendVisibilityData = ({ data, chartId, isSeriesVisible }) => {
1395
1579
  const visibleData = (0, react.useMemo)(() => {
1396
- if (!chartId || !legendInteractive) return data;
1580
+ if (!chartId) return data;
1397
1581
  const filtered = data.filter((segment) => isSeriesVisible(chartId, segment.label));
1398
1582
  if (filtered.length === 0) return [];
1399
1583
  const totalValue = filtered.reduce((sum, segment) => sum + segment.value, 0);
@@ -1404,16 +1588,15 @@ const useInteractiveLegendData = ({ data, chartId, legendInteractive, isSeriesVi
1404
1588
  }, [
1405
1589
  data,
1406
1590
  chartId,
1407
- isSeriesVisible,
1408
- legendInteractive
1591
+ isSeriesVisible
1409
1592
  ]);
1410
1593
  return {
1411
1594
  visibleData,
1412
1595
  allSegmentsHidden: (0, react.useMemo)(() => {
1413
- return legendInteractive && visibleData.length === 0;
1414
- }, [legendInteractive, visibleData]),
1596
+ return visibleData.length === 0 && data.length > 0;
1597
+ }, [visibleData, data]),
1415
1598
  legendData: (0, react.useMemo)(() => {
1416
- if (!legendInteractive || !chartId) return data;
1599
+ if (!chartId) return data;
1417
1600
  const visibleDataMap = new Map(visibleData.map((d) => [d.label, d]));
1418
1601
  return data.map((segment) => {
1419
1602
  if (!isSeriesVisible(chartId, segment.label)) return segment;
@@ -1422,7 +1605,6 @@ const useInteractiveLegendData = ({ data, chartId, legendInteractive, isSeriesVi
1422
1605
  }, [
1423
1606
  data,
1424
1607
  visibleData,
1425
- legendInteractive,
1426
1608
  chartId,
1427
1609
  isSeriesVisible
1428
1610
  ])
@@ -1552,12 +1734,23 @@ const LegendText = ({ text, textOverflow, maxWidth }) => {
1552
1734
  children: text
1553
1735
  });
1554
1736
  };
1737
+ const getLegendItemAriaLabel = (text, value, visible, interactive) => {
1738
+ const accessibleText = value != null && value !== "" ? (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n._x)("%1$s, %2$s", "legend item label and value", "jetpack-charts"), text, String(value)) : text;
1739
+ if (interactive) {
1740
+ if (visible) return (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n._x)("%s: visible. Toggle visibility.", "visible interactive legend item", "jetpack-charts"), accessibleText);
1741
+ return (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n._x)("%s: hidden. Toggle visibility.", "hidden interactive legend item", "jetpack-charts"), accessibleText);
1742
+ }
1743
+ if (visible) return;
1744
+ return (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n._x)("%s: hidden", "hidden non-interactive legend item", "jetpack-charts"), accessibleText);
1745
+ };
1555
1746
  const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "horizontal", alignment = "center", shape = "rect", fill = valueOrIdentityString, size = valueOrIdentityString, labelFormat = valueOrIdentity, labelTransform = labelTransformFactory, itemStyles, itemClassName, labelStyles, labelClassName, shapeStyles, render, interactive = false, chartId }, ref) => {
1556
1747
  const { margin: itemMargin = "0", flexDirection: itemDirection = "row" } = itemStyles ?? {};
1557
1748
  const { justifyContent: labelJustifyContent = "flex-start", flex: labelFlex = "0 0 auto", margin: labelMargin = "0 4px", maxWidth, textOverflow = "wrap" } = labelStyles ?? {};
1558
1749
  const { width: shapeWidth = 16, height: shapeHeight = 16, margin: shapeMargin = "2px 4px 2px 0" } = shapeStyles ?? {};
1559
1750
  const theme = useGlobalChartsTheme();
1560
1751
  const context = (0, react.useContext)(GlobalChartsContext);
1752
+ const chartInstanceContext = (0, react.useContext)(ChartInstanceContext);
1753
+ const standaloneScopeClass = useStandaloneScopeClass();
1561
1754
  const legendScale = (0, _visx_scale.scaleOrdinal)({
1562
1755
  domain: items.map((item) => item.label),
1563
1756
  range: items.map((item) => item.color)
@@ -1567,9 +1760,7 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
1567
1760
  const handleLegendClick = (0, react.useCallback)((seriesLabels) => {
1568
1761
  if (interactive && chartId && context) {
1569
1762
  const representativeVisible = context.isSeriesVisible(chartId, seriesLabels[0]);
1570
- seriesLabels.forEach((label) => {
1571
- if (context.isSeriesVisible(chartId, label) === representativeVisible) context.toggleSeriesVisibility(chartId, label);
1572
- });
1763
+ seriesLabels.forEach((label) => context.setSeriesVisibility(chartId, label, !representativeVisible));
1573
1764
  }
1574
1765
  }, [
1575
1766
  interactive,
@@ -1577,11 +1768,12 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
1577
1768
  context
1578
1769
  ]);
1579
1770
  const isSeriesVisible = (0, react.useCallback)((seriesLabel) => {
1580
- if (!interactive || !chartId || !context) return true;
1771
+ if (chartInstanceContext?.isSeriesVisible) return chartInstanceContext.isSeriesVisible(seriesLabel);
1772
+ if (!chartId || !context) return true;
1581
1773
  return context.isSeriesVisible(chartId, seriesLabel);
1582
1774
  }, [
1583
- interactive,
1584
1775
  chartId,
1776
+ chartInstanceContext,
1585
1777
  context
1586
1778
  ]);
1587
1779
  const createClickHandler = (0, react.useCallback)((seriesLabels) => {
@@ -1609,8 +1801,8 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
1609
1801
  align: orientation === "vertical" ? flexAlignment : void 0,
1610
1802
  justify: orientation === "horizontal" ? flexAlignment : void 0,
1611
1803
  wrap: orientation === "horizontal" ? "wrap" : void 0,
1612
- role: "list",
1613
- className: (0, clsx.default)(base_legend_module_default.legend, className),
1804
+ role: interactive ? void 0 : "list",
1805
+ className: (0, clsx.default)(standaloneScopeClass, base_legend_module_default.legend, className),
1614
1806
  style: theme.legend?.containerStyles,
1615
1807
  children: labels.map((label, i) => {
1616
1808
  const matchedItem = items[i];
@@ -1624,10 +1816,10 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
1624
1816
  flexDirection: orientation === "vertical" && alignment === "end" ? "row-reverse" : itemDirection,
1625
1817
  onClick: handleClick,
1626
1818
  onKeyDown: handleKeyDown,
1627
- role: interactive ? "button" : void 0,
1819
+ role: interactive ? "button" : "listitem",
1628
1820
  tabIndex: interactive ? 0 : void 0,
1629
1821
  "aria-pressed": interactive ? visible : void 0,
1630
- "aria-label": interactive ? `${label.text}: ${visible ? "visible" : "hidden"}. Toggle visibility.` : void 0,
1822
+ "aria-label": getLegendItemAriaLabel(label.text, matchedItem?.value, visible, interactive),
1631
1823
  children: [items[i]?.renderGlyph ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1632
1824
  width: items[i]?.glyphSize * 2,
1633
1825
  height: items[i]?.glyphSize * 2,
@@ -1689,8 +1881,8 @@ const defaultShapeByChartType = {
1689
1881
  };
1690
1882
  const Legend = (0, react.forwardRef)(({ chartId, items, shape, ...props }, ref) => {
1691
1883
  const context = (0, react.useContext)(GlobalChartsContext);
1692
- const singleChartContext = (0, react.useContext)(SingleChartContext);
1693
- const contextChartId = chartId ?? singleChartContext?.chartId;
1884
+ const chartInstanceContext = (0, react.useContext)(ChartInstanceContext);
1885
+ const contextChartId = chartId ?? chartInstanceContext?.chartId;
1694
1886
  const chartData = (0, react.useMemo)(() => contextChartId && context ? context.getChartData(contextChartId) : void 0, [contextChartId, context]);
1695
1887
  const contextItems = chartData?.legendItems;
1696
1888
  const resolvedShape = shape ?? (chartData?.chartType ? defaultShapeByChartType[chartData.chartType] : void 0);
@@ -1897,9 +2089,10 @@ const BaseTooltip = ({ data, top, left, component: Component = DefaultTooltipCon
1897
2089
  data,
1898
2090
  className
1899
2091
  });
2092
+ const standaloneScopeClass = useStandaloneScopeClass();
1900
2093
  if (!renderContainer) return content;
1901
2094
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1902
- className: base_tooltip_module_default.tooltip,
2095
+ className: (0, clsx.default)(standaloneScopeClass, base_tooltip_module_default.tooltip),
1903
2096
  style: {
1904
2097
  top,
1905
2098
  left,
@@ -1929,11 +2122,16 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
1929
2122
  }
1930
2123
  return flattened;
1931
2124
  }, [series, mode]);
2125
+ const hasKeyboardSelection = (0, react.useRef)(false);
1932
2126
  (0, react.useEffect)(() => {
1933
2127
  if (selectedIndex === void 0) {
1934
- tooltipContext?.hideTooltip();
2128
+ if (hasKeyboardSelection.current) {
2129
+ hasKeyboardSelection.current = false;
2130
+ tooltipContext?.hideTooltip();
2131
+ }
1935
2132
  return;
1936
2133
  }
2134
+ hasKeyboardSelection.current = true;
1937
2135
  if (mode === "group") series.forEach((s, index) => {
1938
2136
  if (selectedIndex < s.data.length) {
1939
2137
  const datum = s.data[selectedIndex];
@@ -1968,10 +2166,11 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
1968
2166
  tabIndex: -1,
1969
2167
  role: "tooltip",
1970
2168
  "aria-atomic": "true",
1971
- className: keyboardFocusedClassName,
2169
+ className: (0, clsx.default)(CHART_SCOPE_CLASS, keyboardFocusedClassName),
1972
2170
  children: tooltipContent
1973
2171
  }, `chart-tooltip-${selectedIndex}`);
1974
2172
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2173
+ className: CHART_SCOPE_CLASS,
1975
2174
  role: "tooltip",
1976
2175
  "aria-live": "polite",
1977
2176
  children: tooltipContent
@@ -2040,6 +2239,43 @@ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating,
2040
2239
  };
2041
2240
  };
2042
2241
  //#endregion
2242
+ //#region src/providers/chart-context/hooks/use-default-hidden-series.ts
2243
+ const useIsomorphicLayoutEffect$1 = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
2244
+ /**
2245
+ * Seeds a chart's hidden series once per chart ID, the first time the provider
2246
+ * sees that ID.
2247
+ *
2248
+ * The provider owns the record of what has been seeded, not this hook: a
2249
+ * mount-scoped one would re-apply the defaults on every remount, and a chart
2250
+ * remounts for reasons the reader never thinks of as a new chart — switching
2251
+ * between line and bar, tabbing away and back, a resize flipping tabs into a
2252
+ * dropdown. Each of those would throw away the series they had just revealed.
2253
+ *
2254
+ * @param chartId - The chart ID passed to the provider's visibility methods.
2255
+ * @param seriesLabels - Labels to hide when this chart ID is first seen. Later values are ignored.
2256
+ * @return The resolved hidden-series set.
2257
+ */
2258
+ const useDefaultHiddenSeries = (chartId, seriesLabels) => {
2259
+ const { getHiddenSeries, seedChartHiddenSeries, hasSeededChart } = useGlobalChartsContext();
2260
+ const labelsRef = (0, react.useRef)(seriesLabels);
2261
+ labelsRef.current = seriesLabels;
2262
+ const hasSeriesLabels = seriesLabels !== void 0;
2263
+ const shouldUseDefaults = hasSeriesLabels && !hasSeededChart(chartId);
2264
+ useIsomorphicLayoutEffect$1(() => {
2265
+ if (labelsRef.current === void 0) return;
2266
+ seedChartHiddenSeries(chartId, labelsRef.current);
2267
+ }, [
2268
+ chartId,
2269
+ hasSeriesLabels,
2270
+ seedChartHiddenSeries
2271
+ ]);
2272
+ return (0, react.useMemo)(() => shouldUseDefaults ? new Set(labelsRef.current) : getHiddenSeries(chartId), [
2273
+ shouldUseDefaults,
2274
+ getHiddenSeries,
2275
+ chartId
2276
+ ]);
2277
+ };
2278
+ //#endregion
2043
2279
  //#region src/charts/private/chart-composition/chart-svg.tsx
2044
2280
  /**
2045
2281
  * Compound component for SVG children in charts.
@@ -2253,6 +2489,27 @@ const SvgEmptyState = ({ x, y, width, height, children }) => {
2253
2489
  });
2254
2490
  };
2255
2491
  //#endregion
2492
+ //#region src/charts/private/svg-empty-state/get-all-hidden-message.ts
2493
+ /**
2494
+ * Return the empty-state message for a chart whose items are all hidden.
2495
+ *
2496
+ * Each translation call has a distinct gettext context. This prevents Terser
2497
+ * from folding conditional calls into one call with a conditional msgid, which
2498
+ * would make the strings unavailable to static translation extraction.
2499
+ *
2500
+ * @param interactive - Whether the legend can be used to show hidden items.
2501
+ * @param itemType - The chart-specific name for its data items.
2502
+ * @return The translated empty-state message.
2503
+ */
2504
+ const getAllHiddenMessage = (interactive, itemType) => {
2505
+ if (itemType === "segments") {
2506
+ if (interactive) return (0, _wordpress_i18n._x)("All segments are hidden. Click legend items to show data.", "chart empty state: interactive segments", "jetpack-charts");
2507
+ return (0, _wordpress_i18n._x)("All segments are hidden.", "chart empty state: segments", "jetpack-charts");
2508
+ }
2509
+ if (interactive) return (0, _wordpress_i18n._x)("All series are hidden. Click legend items to show data.", "chart empty state: interactive series", "jetpack-charts");
2510
+ return (0, _wordpress_i18n._x)("All series are hidden.", "chart empty state: series", "jetpack-charts");
2511
+ };
2512
+ //#endregion
2256
2513
  //#region src/charts/private/time-axis.ts
2257
2514
  const X_TICK_WIDTH = 60;
2258
2515
  const getCurveType = (type, smoothing) => {
@@ -2424,9 +2681,11 @@ function withResponsive(WrappedComponent) {
2424
2681
  enableDebounceLeadingCall: true
2425
2682
  });
2426
2683
  const hasAspectRatio = aspectRatio !== void 0 && aspectRatio > 0;
2684
+ const [scopeNode, setScopeNode] = (0, react.useState)(null);
2427
2685
  const wrapperRef = (0, react.useRef)(null);
2428
2686
  const setWrapperRef = (0, react.useCallback)((node) => {
2429
2687
  wrapperRef.current = node;
2688
+ setScopeNode(node);
2430
2689
  if (typeof parentRef === "function") parentRef(node);
2431
2690
  else if (parentRef) parentRef.current = node;
2432
2691
  }, [parentRef]);
@@ -2472,14 +2731,17 @@ function withResponsive(WrappedComponent) {
2472
2731
  ...width !== void 0 ? { width } : null,
2473
2732
  ...height !== void 0 ? { height } : null
2474
2733
  },
2475
- children: hasAspectRatio ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2476
- className: with_responsive_module_default.content,
2477
- style: {
2478
- width: boxWidth,
2479
- height: boxHeight
2480
- },
2481
- children: wrappedComponent
2482
- }) : wrappedComponent
2734
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartScopeContext.Provider, {
2735
+ value: scopeNode,
2736
+ children: hasAspectRatio ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2737
+ className: with_responsive_module_default.content,
2738
+ style: {
2739
+ width: boxWidth,
2740
+ height: boxHeight
2741
+ },
2742
+ children: wrappedComponent
2743
+ }) : wrappedComponent
2744
+ })
2483
2745
  });
2484
2746
  };
2485
2747
  }
@@ -2775,7 +3037,7 @@ const LineChartAnnotationLabelWithPopover = ({ title, subtitle, renderLabel, ren
2775
3037
  //#endregion
2776
3038
  //#region src/charts/line-chart/private/line-chart-annotations-overlay.tsx
2777
3039
  const LineChartAnnotationsOverlay = ({ children }) => {
2778
- const { chartRef, chartWidth, chartHeight } = useSingleChartContext();
3040
+ const { chartRef, chartWidth, chartHeight } = useChartInstanceContext();
2779
3041
  const [scales, setScales] = (0, react.useState)(null);
2780
3042
  const [scalesStable, setScalesStable] = (0, react.useState)(false);
2781
3043
  const createScaleSignature = (0, react.useCallback)((scaleData) => {
@@ -2914,11 +3176,12 @@ const getVerticalAnchor = (subjectType, isFlippedVertically, y, yMax, height) =>
2914
3176
  };
2915
3177
  const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", styles: datumStyles, testId, renderLabel, renderLabelPopover }) => {
2916
3178
  const providerTheme = useGlobalChartsTheme();
3179
+ const scopeElement = useChartScopeElement();
2917
3180
  const { xScale, yScale } = (0, react.useContext)(_visx_xychart.DataContext) || {};
2918
3181
  const labelRef = (0, react.useRef)(null);
2919
3182
  const [height, setHeight] = (0, react.useState)(null);
2920
3183
  const styles = (0, deepmerge.default)(providerTheme.annotationStyles ?? {}, datumStyles ?? {});
2921
- const resolveColor = (value) => value ? resolveCssVariable(value) ?? value : value;
3184
+ const resolveColor = (value) => value ? resolveCssVariable(value, scopeElement) ?? value : value;
2922
3185
  (0, react.useEffect)(() => {
2923
3186
  if (labelRef.current?.getBBox) {
2924
3187
  const bbox = labelRef.current.getBBox();
@@ -3159,7 +3422,7 @@ const LineChartScalesRef = ({ chartRef, width, height, margin }) => {
3159
3422
  ]);
3160
3423
  return null;
3161
3424
  };
3162
- const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, withStartGlyphs = false, withEndGlyphs = false, animation, options = {}, onPointerDown = void 0, onPointerUp = void 0, onPointerMove = void 0, onPointerOut = void 0, zoomable = false, rescaleYOnVisibilityChange = true, children, gridVisibility, gap = "md" }, ref) => {
3425
+ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, withStartGlyphs = false, withEndGlyphs = false, animation, options = {}, onPointerDown = void 0, onPointerUp = void 0, onPointerMove = void 0, onPointerOut = void 0, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
3163
3426
  const legendInteractive = legend.interactive ?? false;
3164
3427
  const legendCollapseGroups = legend.collapseGroups ?? false;
3165
3428
  const legendShape = legend.shape ?? "line";
@@ -3168,6 +3431,8 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3168
3431
  const theme = useXYChartTheme(data);
3169
3432
  const resolvedBackgroundColor = theme.backgroundColor ?? providerTheme.backgroundColor;
3170
3433
  const chartId = useChartId(providedChartId);
3434
+ const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
3435
+ const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
3171
3436
  const chartRef = (0, react.useRef)(null);
3172
3437
  const [selectedIndex, setSelectedIndex] = (0, react.useState)(void 0);
3173
3438
  const [isNavigating, setIsNavigating] = (0, react.useState)(false);
@@ -3195,29 +3460,19 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3195
3460
  }
3196
3461
  }), [internalChartRef]);
3197
3462
  const dataSorted = useChartDataTransform(data);
3198
- const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
3463
+ const { getElementStyles } = useGlobalChartsContext();
3199
3464
  const seriesWithVisibility = (0, react.useMemo)(() => {
3200
- if (!chartId || !legendInteractive) return dataSorted.map((series, index) => ({
3201
- series,
3202
- index,
3203
- isVisible: true
3204
- }));
3205
3465
  return dataSorted.map((series, index) => ({
3206
3466
  series,
3207
3467
  index,
3208
- isVisible: isSeriesVisible(chartId, series.label)
3468
+ isVisible: !hiddenSeries.has(series.label)
3209
3469
  }));
3210
- }, [
3211
- dataSorted,
3212
- chartId,
3213
- isSeriesVisible,
3214
- legendInteractive
3215
- ]);
3470
+ }, [dataSorted, hiddenSeries]);
3216
3471
  const allSeriesHidden = (0, react.useMemo)(() => {
3217
3472
  return seriesWithVisibility.every(({ isVisible }) => !isVisible);
3218
3473
  }, [seriesWithVisibility]);
3219
3474
  const stableYDomain = (0, react.useMemo)(() => {
3220
- if (!legendInteractive || rescaleYOnVisibilityChange) return;
3475
+ if (rescaleYOnVisibilityChange) return;
3221
3476
  let min = Infinity;
3222
3477
  let max = -Infinity;
3223
3478
  for (const series of dataSorted) for (const point of series.data ?? []) {
@@ -3228,11 +3483,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3228
3483
  }
3229
3484
  }
3230
3485
  return min < max ? [min, max] : void 0;
3231
- }, [
3232
- legendInteractive,
3233
- rescaleYOnVisibilityChange,
3234
- dataSorted
3235
- ]);
3486
+ }, [rescaleYOnVisibilityChange, dataSorted]);
3236
3487
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
3237
3488
  selectedIndex,
3238
3489
  setSelectedIndex,
@@ -3356,10 +3607,11 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3356
3607
  chartId,
3357
3608
  interactive: legendInteractive
3358
3609
  });
3359
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
3610
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
3360
3611
  value: {
3361
3612
  chartId,
3362
3613
  chartRef: internalChartRef,
3614
+ isSeriesVisible,
3363
3615
  chartWidth: width,
3364
3616
  chartHeight: measuredChartHeight || 0
3365
3617
  },
@@ -3414,7 +3666,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3414
3666
  y: chartHeight / 2,
3415
3667
  width,
3416
3668
  height: chartHeight,
3417
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
3669
+ children: getAllHiddenMessage(legendInteractive, "series")
3418
3670
  }) : null,
3419
3671
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomClip, {
3420
3672
  active: zoomable && !!zoom.domain,
@@ -3616,7 +3868,7 @@ const HoverGlyphs = ({ visibleSeries, stacked, stackOffset, getElementStyles, st
3616
3868
  };
3617
3869
  //#endregion
3618
3870
  //#region src/charts/area-chart/area-chart.tsx
3619
- const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange, rescaleYOnLegendToggle, children, gridVisibility, gap = "md" }, ref) => {
3871
+ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange, rescaleYOnLegendToggle, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
3620
3872
  const legendInteractive = legend.interactive ?? false;
3621
3873
  const legendShape = legend.shape ?? "rect";
3622
3874
  const legendPosition = legend.position ?? "bottom";
@@ -3624,6 +3876,8 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3624
3876
  const providerTheme = useGlobalChartsTheme();
3625
3877
  const theme = useXYChartTheme(data);
3626
3878
  const chartId = useChartId(providedChartId);
3879
+ const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
3880
+ const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
3627
3881
  const chartRef = (0, react.useRef)(null);
3628
3882
  const [selectedIndex, setSelectedIndex] = (0, react.useState)(void 0);
3629
3883
  const [isNavigating, setIsNavigating] = (0, react.useState)(false);
@@ -3651,24 +3905,14 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3651
3905
  }
3652
3906
  }), [internalChartRef]);
3653
3907
  const dataSorted = useChartDataTransform(data);
3654
- const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
3908
+ const { getElementStyles } = useGlobalChartsContext();
3655
3909
  const seriesWithVisibility = (0, react.useMemo)(() => {
3656
- if (!chartId || !legendInteractive) return dataSorted.map((series, index) => ({
3657
- series,
3658
- index,
3659
- isVisible: true
3660
- }));
3661
3910
  return dataSorted.map((series, index) => ({
3662
3911
  series,
3663
3912
  index,
3664
- isVisible: isSeriesVisible(chartId, series.label)
3913
+ isVisible: !hiddenSeries.has(series.label)
3665
3914
  }));
3666
- }, [
3667
- dataSorted,
3668
- chartId,
3669
- isSeriesVisible,
3670
- legendInteractive
3671
- ]);
3915
+ }, [dataSorted, hiddenSeries]);
3672
3916
  const allSeriesHidden = (0, react.useMemo)(() => seriesWithVisibility.every(({ isVisible }) => !isVisible), [seriesWithVisibility]);
3673
3917
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
3674
3918
  selectedIndex,
@@ -3679,7 +3923,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3679
3923
  totalPoints: dataSorted[0]?.data.length || 0
3680
3924
  });
3681
3925
  const fixedYDomain = (0, react.useMemo)(() => {
3682
- if (rescaleYOnVisibility || !legendInteractive || !dataSorted.length || !dataSorted[0].data.length || stacked && stackOffset !== "none") return;
3926
+ if (rescaleYOnVisibility || !dataSorted.length || !dataSorted[0].data.length || stacked && stackOffset !== "none") return;
3683
3927
  if (stacked) {
3684
3928
  const numPoints = Math.max(...dataSorted.map((s) => s.data.length));
3685
3929
  let posMax = 0;
@@ -3713,7 +3957,6 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3713
3957
  dataSorted,
3714
3958
  stacked,
3715
3959
  stackOffset,
3716
- legendInteractive,
3717
3960
  rescaleYOnVisibility
3718
3961
  ]);
3719
3962
  const chartOptions = (0, react.useMemo)(() => {
@@ -3790,7 +4033,6 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3790
4033
  const zeroYAccessor = (0, react.useCallback)(() => 0, []);
3791
4034
  const visibleLabels = (0, react.useMemo)(() => new Set(seriesWithVisibility.filter((s) => s.isVisible).map((s) => s.series.label)), [seriesWithVisibility]);
3792
4035
  const filteredRenderTooltip = (0, react.useCallback)((params) => {
3793
- if (!legendInteractive) return renderTooltip(params);
3794
4036
  const datumByKey = params?.tooltipData?.datumByKey;
3795
4037
  if (!datumByKey) return renderTooltip(params);
3796
4038
  const filtered = Object.fromEntries(Object.entries(datumByKey).filter(([key]) => visibleLabels.has(key)));
@@ -3808,11 +4050,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3808
4050
  nearestDatum: nextNearest
3809
4051
  }
3810
4052
  });
3811
- }, [
3812
- renderTooltip,
3813
- legendInteractive,
3814
- visibleLabels
3815
- ]);
4053
+ }, [renderTooltip, visibleLabels]);
3816
4054
  const resolvedFillOpacity = fillOpacity ?? (stacked ? .85 : .4);
3817
4055
  const resolvedWithStroke = withStroke ?? !stacked;
3818
4056
  if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -3843,7 +4081,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3843
4081
  dataKey: seriesData?.label,
3844
4082
  data: seriesData.data,
3845
4083
  xAccessor: accessors.xAccessor,
3846
- yAccessor: isVisible || !legendInteractive ? accessors.yAccessor : zeroYAccessor,
4084
+ yAccessor: isVisible ? accessors.yAccessor : zeroYAccessor,
3847
4085
  fill: color,
3848
4086
  fillOpacity: resolvedFillOpacity,
3849
4087
  ...stacked ? {} : {
@@ -3856,10 +4094,11 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3856
4094
  }
3857
4095
  }, seriesData?.label || index);
3858
4096
  };
3859
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
4097
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
3860
4098
  value: {
3861
4099
  chartId,
3862
4100
  chartRef: internalChartRef,
4101
+ isSeriesVisible,
3863
4102
  chartWidth: width,
3864
4103
  chartHeight: measuredChartHeight || 0
3865
4104
  },
@@ -3914,7 +4153,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3914
4153
  y: chartHeight / 2,
3915
4154
  width,
3916
4155
  height: chartHeight,
3917
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
4156
+ children: getAllHiddenMessage(legendInteractive, "series")
3918
4157
  }) : null,
3919
4158
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ZoomClip, {
3920
4159
  active: zoomable,
@@ -4468,11 +4707,13 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ (0, react_jsx_runtime
4468
4707
  className: bar_chart_module_default["bar-chart__tooltip-row"],
4469
4708
  children: (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("%1$s: %2$s", "jetpack-charts"), label, value)
4470
4709
  });
4471
- const BarChartInternal = ({ data, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, animation, children, gap = "md" }) => {
4710
+ const BarChartInternal = ({ data, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, defaultHiddenSeries, animation, children, gap = "md" }) => {
4472
4711
  const legendInteractive = legend.interactive ?? false;
4473
4712
  const legendCollapseGroups = legend.collapseGroups ?? false;
4474
4713
  const horizontal = orientation === "horizontal";
4475
4714
  const chartId = useChartId(providedChartId);
4715
+ const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
4716
+ const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
4476
4717
  const theme = useXYChartTheme(data);
4477
4718
  const dataSorted = useChartDataTransform(data);
4478
4719
  const dataWithVisibleZeros = useZeroValueDisplay(dataSorted, {
@@ -4480,12 +4721,8 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4480
4721
  valueAxisLength: horizontal ? width : height
4481
4722
  });
4482
4723
  const legendItems = useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({ collapseGroups: legendCollapseGroups }), [legendCollapseGroups]));
4483
- const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
4484
- const isSeriesRendered = (0, react.useCallback)((series) => !chartId || !legendInteractive || isSeriesVisible(chartId, series.label), [
4485
- chartId,
4486
- legendInteractive,
4487
- isSeriesVisible
4488
- ]);
4724
+ const { getElementStyles } = useGlobalChartsContext();
4725
+ const isSeriesRendered = (0, react.useCallback)((series) => isSeriesVisible(series.label), [isSeriesVisible]);
4489
4726
  const chartOptions = useBarChartOptions(dataWithVisibleZeros, horizontal, options, isSeriesRendered);
4490
4727
  const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme, horizontal);
4491
4728
  const chartRef = (0, react.useRef)(null);
@@ -4721,9 +4958,10 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4721
4958
  chartId,
4722
4959
  interactive: legendInteractive
4723
4960
  });
4724
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
4961
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
4725
4962
  value: {
4726
4963
  chartId,
4964
+ isSeriesVisible,
4727
4965
  chartWidth: width,
4728
4966
  chartHeight: measuredChartHeight || 0
4729
4967
  },
@@ -4782,7 +5020,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4782
5020
  y: chartHeight / 2,
4783
5021
  width,
4784
5022
  height: chartHeight,
4785
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
5023
+ children: getAllHiddenMessage(legendInteractive, "series")
4786
5024
  }) : null,
4787
5025
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComparisonBars, {
4788
5026
  comparisonEntries,
@@ -5069,12 +5307,22 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5069
5307
  const { getElementStyles, isColorPaletteResolved } = useGlobalChartsContext();
5070
5308
  const chartRef = (0, react.useRef)(null);
5071
5309
  const selectedBarRef = (0, react.useRef)(null);
5310
+ const [scopeNode, setScopeNode] = (0, react.useState)(null);
5072
5311
  const { tooltipData, tooltipLeft, tooltipTop, tooltipOpen, showTooltip, hideTooltip } = (0, _visx_tooltip.useTooltip)();
5073
5312
  const { handleBarClick, handleBarKeyDown, clearSelection, getStepState } = useFunnelSelection(hideTooltip);
5074
5313
  const { containerRef: portalContainerRef, TooltipInPortal, containerBounds } = (0, _visx_tooltip.useTooltipInPortal)({
5075
5314
  detectBounds: true,
5076
5315
  scroll: true
5077
5316
  });
5317
+ const portalContainerRefRef = (0, react.useRef)(portalContainerRef);
5318
+ (0, react.useEffect)(() => {
5319
+ portalContainerRefRef.current = portalContainerRef;
5320
+ }, [portalContainerRef]);
5321
+ const setChartRef = (0, react.useCallback)((node) => {
5322
+ portalContainerRefRef.current(node);
5323
+ chartRef.current = node;
5324
+ setScopeNode(node);
5325
+ }, []);
5078
5326
  const clearSelectionAndRef = (0, react.useCallback)(() => {
5079
5327
  clearSelection();
5080
5328
  selectedBarRef.current = null;
@@ -5220,6 +5468,7 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5220
5468
  direction: "column",
5221
5469
  align: "center",
5222
5470
  justify: "center",
5471
+ ref: setScopeNode,
5223
5472
  className: (0, clsx.default)(conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
5224
5473
  style: {
5225
5474
  ...style,
@@ -5231,80 +5480,80 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5231
5480
  })
5232
5481
  });
5233
5482
  const maxRate = Math.max(...steps.map((step) => step.rate));
5234
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
5483
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
5235
5484
  direction: "column",
5236
5485
  gap: "xl",
5237
- ref: (node) => {
5238
- portalContainerRef(node);
5239
- chartRef.current = node;
5240
- },
5486
+ ref: setChartRef,
5241
5487
  className: (0, clsx.default)(conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
5242
5488
  style: {
5243
5489
  ...style,
5244
5490
  height: resolvedHeight
5245
5491
  },
5246
- children: [renderMainMetric ? renderMainMetric({
5247
- mainRate,
5248
- changeIndicator,
5249
- className: conversion_funnel_chart_module_default["main-metric"],
5250
- changeColor
5251
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
5252
- direction: "row",
5253
- align: "baseline",
5254
- gap: "sm",
5255
- className: conversion_funnel_chart_module_default["main-metric"],
5256
- children: renderDefaultMainMetric()
5257
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
5258
- direction: "row",
5259
- align: "flex-end",
5260
- gap: "lg",
5261
- className: conversion_funnel_chart_module_default["funnel-container"],
5262
- children: steps.map((step, index) => {
5263
- const barHeight = step.rate / maxRate * 100;
5264
- const { isBlurred } = getStepState(step.id);
5265
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
5266
- direction: "column",
5267
- className: (0, clsx.default)(conversion_funnel_chart_module_default["funnel-step"], isColorPaletteResolved && conversion_funnel_chart_module_default["funnel-step--animated"], isBlurred && conversion_funnel_chart_module_default["funnel-step--blurred"]),
5268
- gap: "xl",
5269
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
5492
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartScopeContext.Provider, {
5493
+ value: scopeNode,
5494
+ children: [renderMainMetric ? renderMainMetric({
5495
+ mainRate,
5496
+ changeIndicator,
5497
+ className: conversion_funnel_chart_module_default["main-metric"],
5498
+ changeColor
5499
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
5500
+ direction: "row",
5501
+ align: "baseline",
5502
+ gap: "sm",
5503
+ className: conversion_funnel_chart_module_default["main-metric"],
5504
+ children: renderDefaultMainMetric()
5505
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
5506
+ direction: "row",
5507
+ align: "flex-end",
5508
+ gap: "lg",
5509
+ className: conversion_funnel_chart_module_default["funnel-container"],
5510
+ children: steps.map((step, index) => {
5511
+ const barHeight = step.rate / maxRate * 100;
5512
+ const { isBlurred } = getStepState(step.id);
5513
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
5270
5514
  direction: "column",
5271
- gap: "xs",
5272
- children: [renderStepLabel ? renderStepLabel({
5273
- step,
5274
- index,
5275
- className: conversion_funnel_chart_module_default["step-label"]
5276
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5277
- className: conversion_funnel_chart_module_default["step-label"],
5278
- children: step.label
5279
- }), renderStepRate ? renderStepRate({
5280
- step,
5281
- index,
5282
- className: conversion_funnel_chart_module_default["step-rate"]
5283
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5284
- className: conversion_funnel_chart_module_default["step-rate"],
5285
- children: formatPercentage(step.rate)
5515
+ className: (0, clsx.default)(conversion_funnel_chart_module_default["funnel-step"], isColorPaletteResolved && conversion_funnel_chart_module_default["funnel-step--animated"], isBlurred && conversion_funnel_chart_module_default["funnel-step--blurred"]),
5516
+ gap: "xl",
5517
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
5518
+ direction: "column",
5519
+ gap: "xs",
5520
+ children: [renderStepLabel ? renderStepLabel({
5521
+ step,
5522
+ index,
5523
+ className: conversion_funnel_chart_module_default["step-label"]
5524
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5525
+ className: conversion_funnel_chart_module_default["step-label"],
5526
+ children: step.label
5527
+ }), renderStepRate ? renderStepRate({
5528
+ step,
5529
+ index,
5530
+ className: conversion_funnel_chart_module_default["step-rate"]
5531
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5532
+ className: conversion_funnel_chart_module_default["step-rate"],
5533
+ children: formatPercentage(step.rate)
5534
+ })]
5535
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
5536
+ direction: "column",
5537
+ justify: "flex-end",
5538
+ className: conversion_funnel_chart_module_default["bar-container"],
5539
+ onClick: stepHandlers.get(step.id)?.onClick,
5540
+ onKeyDown: stepHandlers.get(step.id)?.onKeyDown,
5541
+ role: "button",
5542
+ tabIndex: isBlurred ? -1 : 0,
5543
+ "aria-label": step.label,
5544
+ style: { backgroundColor: barBackgroundColor },
5545
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5546
+ className: (0, clsx.default)(conversion_funnel_chart_module_default["funnel-bar"], { [conversion_funnel_chart_module_default["funnel-bar--animated"]]: animation && !loading && !prefersReducedMotion }),
5547
+ style: {
5548
+ height: `${barHeight}%`,
5549
+ backgroundColor: barColor
5550
+ }
5551
+ })
5286
5552
  })]
5287
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
5288
- direction: "column",
5289
- justify: "flex-end",
5290
- className: conversion_funnel_chart_module_default["bar-container"],
5291
- onClick: stepHandlers.get(step.id)?.onClick,
5292
- onKeyDown: stepHandlers.get(step.id)?.onKeyDown,
5293
- role: "button",
5294
- tabIndex: isBlurred ? -1 : 0,
5295
- "aria-label": step.label,
5296
- style: { backgroundColor: barBackgroundColor },
5297
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5298
- className: (0, clsx.default)(conversion_funnel_chart_module_default["funnel-bar"], { [conversion_funnel_chart_module_default["funnel-bar--animated"]]: animation && !loading && !prefersReducedMotion }),
5299
- style: {
5300
- height: `${barHeight}%`,
5301
- backgroundColor: barColor
5302
- }
5303
- })
5304
- })]
5305
- }, step.id);
5306
- })
5307
- })]
5553
+ }, step.id);
5554
+ })
5555
+ })]
5556
+ })
5308
5557
  }), tooltipOpen && tooltipData && (() => {
5309
5558
  const tooltipContent = renderTooltip ? renderTooltip({
5310
5559
  step: tooltipData,
@@ -5317,7 +5566,7 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5317
5566
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipInPortal, {
5318
5567
  top: tooltipTop,
5319
5568
  left: tooltipLeft,
5320
- className: conversion_funnel_chart_module_default["tooltip-wrapper"],
5569
+ className: (0, clsx.default)("a8c-charts-scope", conversion_funnel_chart_module_default["tooltip-wrapper"]),
5321
5570
  children: tooltipContent
5322
5571
  }, Math.random());
5323
5572
  })()] });
@@ -5467,6 +5716,7 @@ function normalizeGeoChartError(eventArgs) {
5467
5716
  */
5468
5717
  const GeoChartInternal = ({ className, data, width, height, region = "world", resolution = "countries", onError, renderPlaceholder }) => {
5469
5718
  const { getElementStyles, theme: { geoChart: { featureFillColor }, backgroundColor } } = useGlobalChartsContext();
5719
+ const scopeElement = useChartScopeElement();
5470
5720
  const containerRef = (0, react.useRef)(null);
5471
5721
  const reportedErrorIdsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
5472
5722
  (0, react.useEffect)(() => {
@@ -5499,8 +5749,8 @@ const GeoChartInternal = ({ className, data, width, height, region = "world", re
5499
5749
  });
5500
5750
  const fullColorHex = getElementStyles({ index: 0 }).color;
5501
5751
  const lightColorHex = lightenHexColor(fullColorHex, .8);
5502
- const backgroundColorHex = normalizeColorToHex(backgroundColor, null, resolveCssVariable) || DEFAULT_BACKGROUND_COLOR;
5503
- const defaultFillColorHex = normalizeColorToHex(featureFillColor, null, resolveCssVariable) || DEFAULT_FEATURE_FILL_COLOR;
5752
+ const backgroundColorHex = normalizeColorToHex(backgroundColor, scopeElement, resolveCssVariable) || DEFAULT_BACKGROUND_COLOR;
5753
+ const defaultFillColorHex = normalizeColorToHex(featureFillColor, scopeElement, resolveCssVariable) || DEFAULT_FEATURE_FILL_COLOR;
5504
5754
  const sanitizedData = (0, react.useMemo)(() => {
5505
5755
  if (data.length === 0) return {
5506
5756
  data,
@@ -5583,12 +5833,27 @@ const GeoChartWithProvider = (props) => {
5583
5833
  GeoChartWithProvider.displayName = "GeoChart";
5584
5834
  const GeoChartResponsive = withResponsive(GeoChartWithProvider);
5585
5835
  //#endregion
5836
+ //#region src/types.ts
5837
+ let GoogleDataTableColumnRoleType = /* @__PURE__ */ function(GoogleDataTableColumnRoleType) {
5838
+ GoogleDataTableColumnRoleType["annotation"] = "annotation";
5839
+ GoogleDataTableColumnRoleType["annotationText"] = "annotationText";
5840
+ GoogleDataTableColumnRoleType["certainty"] = "certainty";
5841
+ GoogleDataTableColumnRoleType["emphasis"] = "emphasis";
5842
+ GoogleDataTableColumnRoleType["interval"] = "interval";
5843
+ GoogleDataTableColumnRoleType["scope"] = "scope";
5844
+ GoogleDataTableColumnRoleType["style"] = "style";
5845
+ GoogleDataTableColumnRoleType["tooltip"] = "tooltip";
5846
+ GoogleDataTableColumnRoleType["domain"] = "domain";
5847
+ return GoogleDataTableColumnRoleType;
5848
+ }({});
5849
+ //#endregion
5586
5850
  //#region src/charts/heatmap-chart/heatmap-chart.module.scss
5587
5851
  var heatmap_chart_module_default = {
5588
5852
  "heatmap-chart": "a8ccharts-O3YMOW-heatmap-chart",
5589
5853
  "heatmap-chart__cell": "a8ccharts-O3YMOW-heatmap-chart__cell",
5590
5854
  "heatmap-chart__cell--filled": "a8ccharts-O3YMOW-heatmap-chart__cell--filled",
5591
5855
  "heatmap-chart__cell--hidden": "a8ccharts-O3YMOW-heatmap-chart__cell--hidden",
5856
+ "heatmap-chart__cell--placeholder": "a8ccharts-O3YMOW-heatmap-chart__cell--placeholder",
5592
5857
  "heatmap-chart__cell--selected": "a8ccharts-O3YMOW-heatmap-chart__cell--selected",
5593
5858
  "heatmap-chart__cell--strong": "a8ccharts-O3YMOW-heatmap-chart__cell--strong",
5594
5859
  "heatmap-chart__cell-value": "a8ccharts-O3YMOW-heatmap-chart__cell-value",
@@ -5644,6 +5909,22 @@ const LABELLED_ROWS = [
5644
5909
  2,
5645
5910
  4
5646
5911
  ];
5912
+ /**
5913
+ * Resolve one grid bound: an unparseable or narrowing value falls back to the
5914
+ * series' own bound, so the grid can only ever be widened.
5915
+ *
5916
+ * @param bound - Requested bound as `yyyy-MM-dd`, if any.
5917
+ * @param fallback - The series' own bound on this side.
5918
+ * @param direction - Which way the bound is allowed to move.
5919
+ * @return The bound to draw to.
5920
+ */
5921
+ const widenTo = (bound, fallback, direction) => {
5922
+ if (!bound) return fallback;
5923
+ const parsed = (0, date_fns.parseISO)(bound);
5924
+ if (isNaN(parsed.getTime())) return fallback;
5925
+ if (direction === "earlier") return parsed < fallback ? parsed : fallback;
5926
+ return parsed > fallback ? parsed : fallback;
5927
+ };
5647
5928
  const toDate = (point) => {
5648
5929
  if (point.date instanceof Date && !isNaN(point.date.getTime())) return point.date;
5649
5930
  if (point.dateString) {
@@ -5671,10 +5952,14 @@ const buildCalendarHeatmapData = (series, options = {}) => {
5671
5952
  if (date < minDate) minDate = date;
5672
5953
  if (date > maxDate) maxDate = date;
5673
5954
  }
5674
- const gridStart = (0, date_fns.startOfWeek)(minDate, { weekStartsOn });
5675
- const weekCount = (0, date_fns.differenceInCalendarWeeks)(maxDate, gridStart, { weekStartsOn }) + 1;
5676
5955
  const minDayKey = (0, date_fns.format)(minDate, "yyyy-MM-dd");
5677
5956
  const maxDayKey = (0, date_fns.format)(maxDate, "yyyy-MM-dd");
5957
+ const gridMinDate = widenTo(options.gridSpan?.start, minDate, "earlier");
5958
+ const gridMaxDate = widenTo(options.gridSpan?.end, maxDate, "later");
5959
+ const gridMaxDayKey = (0, date_fns.format)(gridMaxDate, "yyyy-MM-dd");
5960
+ const gridStart = (0, date_fns.startOfWeek)(gridMinDate, { weekStartsOn });
5961
+ const gridMinDayKey = (0, date_fns.format)(gridMinDate < minDate ? gridStart : gridMinDate, "yyyy-MM-dd");
5962
+ const weekCount = (0, date_fns.differenceInCalendarWeeks)(gridMaxDate, gridStart, { weekStartsOn }) + 1;
5678
5963
  const rowLabels = Array.from({ length: 7 }, (_, row) => LABELLED_ROWS.includes(row) ? (0, date_fns.format)((0, date_fns.addDays)(gridStart, row), "EEE") : "");
5679
5964
  const MIN_FIRST_MONTH_WEEKS = 2;
5680
5965
  const firstMonth = gridStart.getMonth();
@@ -5696,7 +5981,9 @@ const buildCalendarHeatmapData = (series, options = {}) => {
5696
5981
  label: (0, date_fns.format)(day, "EEE, MMM d, yyyy"),
5697
5982
  value: valueByDay.has(key) ? valueByDay.get(key) : null
5698
5983
  };
5699
- if (hideOutOfRangeDays && (key < minDayKey || key > maxDayKey)) cell.hidden = true;
5984
+ if (key < gridMinDayKey || key > gridMaxDayKey) {
5985
+ if (hideOutOfRangeDays) cell.hidden = true;
5986
+ } else if (key < minDayKey || key > maxDayKey) cell.placeholder = true;
5700
5987
  cells.push(cell);
5701
5988
  }
5702
5989
  data.push({
@@ -5760,7 +6047,8 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
5760
6047
  const CELL_MIX_FLOOR = .15;
5761
6048
  const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, height = 0, className, compact = false, showValues, maxCellWidth, maxCellHeight, minCellWidth, minCellHeight, rowLabels = [], primaryColor, gap = "md", withTooltips = false, renderTooltip, children }) => {
5762
6049
  const chartId = useChartId(providedChartId);
5763
- const { getElementStyles, resolveThemeColor, theme } = useGlobalChartsContext();
6050
+ const { getElementStyles, theme } = useGlobalChartsContext();
6051
+ const scopeElement = useChartScopeElement();
5764
6052
  const { heatmapChart: heatmapChartSettings } = theme;
5765
6053
  const { nonLegendChildren } = useChartChildren(children, "HeatmapChart");
5766
6054
  const [selectedIndex, setSelectedIndex] = (0, react.useState)();
@@ -5775,7 +6063,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5775
6063
  index: 0,
5776
6064
  overrideColor: primaryColor || heatmapChartSettings.primaryColor
5777
6065
  });
5778
- const chartBackgroundHex = resolveThemeColor(theme.backgroundColor);
6066
+ const chartBackgroundHex = normalizeColorToHex(theme.backgroundColor, scopeElement, resolveCssVariable);
5779
6067
  const primaryHex = normalizeColorToHex(primaryColorHex);
5780
6068
  const cellHasLightText = (intensity) => isValidHexColor(primaryHex) && isValidHexColor(chartBackgroundHex) && prefersLightText(mixHexColors(primaryHex, chartBackgroundHex, 1 - (CELL_MIX_FLOOR + (1 - CELL_MIX_FLOOR) * intensity)));
5781
6069
  const extent = (0, react.useMemo)(() => getValueExtent(data), [data]);
@@ -5802,7 +6090,10 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5802
6090
  setSelectedIndex(void 0);
5803
6091
  hideTooltip();
5804
6092
  }, [hideTooltip]);
5805
- const isCellHidden = (0, react.useCallback)((col, row) => data[col]?.data[row]?.hidden === true, [data]);
6093
+ const isCellInert = (0, react.useCallback)((col, row) => {
6094
+ const cell = data[col]?.data[row];
6095
+ return cell?.hidden === true || cell?.placeholder === true;
6096
+ }, [data]);
5806
6097
  const onChartKeyDown = (0, react.useCallback)((event) => {
5807
6098
  if (![
5808
6099
  "ArrowLeft",
@@ -5819,7 +6110,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5819
6110
  }
5820
6111
  event.preventDefault();
5821
6112
  if (selectedIndex === void 0) {
5822
- for (let index = 0; index < columns * rows; index++) if (!isCellHidden(Math.floor(index / rows), index % rows)) {
6113
+ for (let index = 0; index < columns * rows; index++) if (!isCellInert(Math.floor(index / rows), index % rows)) {
5823
6114
  setSelectedIndex(index);
5824
6115
  return;
5825
6116
  }
@@ -5836,7 +6127,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5836
6127
  do {
5837
6128
  col += stepCol;
5838
6129
  row += stepRow;
5839
- } while (col >= 0 && col < columns && row >= 0 && row < rows && isCellHidden(col, row));
6130
+ } while (col >= 0 && col < columns && row >= 0 && row < rows && isCellInert(col, row));
5840
6131
  if (col < 0 || col >= columns || row < 0 || row >= rows) return;
5841
6132
  setSelectedIndex(col * rows + row);
5842
6133
  }, [
@@ -5844,7 +6135,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5844
6135
  columns,
5845
6136
  selectedIndex,
5846
6137
  hideTooltip,
5847
- isCellHidden
6138
+ isCellInert
5848
6139
  ]);
5849
6140
  const handleCellMouseMove = (0, react.useCallback)((event) => {
5850
6141
  if (!withTooltips) return;
@@ -5916,7 +6207,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5916
6207
  const heightCapped = !compact && Boolean(maxCellHeight);
5917
6208
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeatmapContext.Provider, {
5918
6209
  value: heatmapContext,
5919
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
6210
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
5920
6211
  value: { chartId },
5921
6212
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartLayout, {
5922
6213
  legendPosition: "bottom",
@@ -5968,6 +6259,10 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5968
6259
  "aria-hidden": "true",
5969
6260
  className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--hidden"])
5970
6261
  }, `cell-${columnIndex}-${rowIndex}`);
6262
+ if (cell?.placeholder) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6263
+ "aria-hidden": "true",
6264
+ className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--placeholder"])
6265
+ }, `cell-${columnIndex}-${rowIndex}`);
5971
6266
  const value = cell?.value ?? null;
5972
6267
  const present = isPresent(value);
5973
6268
  const normalized = present ? getNormalizedValue(value, extent) : 0;
@@ -6002,6 +6297,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6002
6297
  top: tooltipTop,
6003
6298
  left: tooltipLeft,
6004
6299
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6300
+ className: "a8c-charts-scope",
6005
6301
  role: "tooltip",
6006
6302
  tabIndex: -1,
6007
6303
  children: (renderTooltip ?? defaultRenderTooltip)(tooltipData)
@@ -8965,29 +9261,25 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8965
9261
  legendLabels
8966
9262
  });
8967
9263
  const isPrimaryVisible = (0, react.useMemo)(() => {
8968
- if (!chartId || !legendInteractive || legendItems.length === 0) return true;
9264
+ if (legendItems.length === 0) return true;
8969
9265
  return isSeriesVisible(chartId, legendItems[0].label);
8970
9266
  }, [
8971
9267
  chartId,
8972
- legendInteractive,
8973
9268
  legendItems,
8974
9269
  isSeriesVisible
8975
9270
  ]);
8976
9271
  const isComparisonVisible = (0, react.useMemo)(() => {
8977
- if (!chartId || !legendInteractive || legendItems.length < 2) return true;
9272
+ if (legendItems.length < 2) return true;
8978
9273
  return isSeriesVisible(chartId, legendItems[1].label);
8979
9274
  }, [
8980
9275
  chartId,
8981
- legendInteractive,
8982
9276
  legendItems,
8983
9277
  isSeriesVisible
8984
9278
  ]);
8985
9279
  const allSeriesHidden = (0, react.useMemo)(() => {
8986
- if (!legendInteractive) return false;
8987
9280
  if (withComparison && !withOverlayLabel) return !isPrimaryVisible && !isComparisonVisible;
8988
9281
  return !isPrimaryVisible;
8989
9282
  }, [
8990
- legendInteractive,
8991
9283
  isPrimaryVisible,
8992
9284
  isComparisonVisible,
8993
9285
  withComparison,
@@ -9006,7 +9298,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9006
9298
  const prefersReducedMotion = usePrefersReducedMotion();
9007
9299
  const { contentRef, fittedCount, isMeasurable } = useFittedRowCount(fitRows && !allSeriesHidden, data?.length ?? 0, data);
9008
9300
  const shouldFitRows = fitRows && isMeasurable;
9009
- if (!data || data.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9301
+ if (!data || data.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9010
9302
  value: { chartId },
9011
9303
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9012
9304
  legendPosition,
@@ -9041,7 +9333,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9041
9333
  chartId,
9042
9334
  interactive: legendInteractive
9043
9335
  });
9044
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9336
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9045
9337
  value: { chartId },
9046
9338
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9047
9339
  legendPosition,
@@ -9066,7 +9358,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9066
9358
  children: (0, _wordpress_i18n.__)("Not enough space to display data", "jetpack-charts")
9067
9359
  }), allSeriesHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9068
9360
  className: leaderboard_chart_module_default.emptyState,
9069
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
9361
+ children: getAllHiddenMessage(legendInteractive, "series")
9070
9362
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default, {
9071
9363
  templateColumns: "minmax(0, 1fr) auto",
9072
9364
  rowGap,
@@ -9256,10 +9548,9 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9256
9548
  hideTooltip();
9257
9549
  }, [withTooltips, hideTooltip]);
9258
9550
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
9259
- const { visibleData, allSegmentsHidden, legendData } = useInteractiveLegendData({
9551
+ const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9260
9552
  data: useDataWithPercentages(data),
9261
9553
  chartId,
9262
- legendInteractive,
9263
9554
  isSeriesVisible
9264
9555
  });
9265
9556
  const legendItems = useChartLegendItems(legendData, (0, react.useMemo)(() => ({
@@ -9320,7 +9611,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9320
9611
  chartId,
9321
9612
  interactive: legendInteractive
9322
9613
  });
9323
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9614
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9324
9615
  value: { chartId },
9325
9616
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9326
9617
  legendPosition,
@@ -9337,6 +9628,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9337
9628
  top: tooltipTop || 0,
9338
9629
  left: tooltipLeft || 0,
9339
9630
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9631
+ className: "a8c-charts-scope",
9340
9632
  role: "tooltip",
9341
9633
  children: renderTooltip({ tooltipData })
9342
9634
  })
@@ -9376,7 +9668,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9376
9668
  y: 0,
9377
9669
  width,
9378
9670
  height,
9379
- children: (0, _wordpress_i18n.__)("All segments are hidden. Click legend items to show data.", "jetpack-charts")
9671
+ children: getAllHiddenMessage(legendInteractive, "segments")
9380
9672
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_shape.Pie, {
9381
9673
  data: dataWithIndex,
9382
9674
  pieValue: accessors.value,
@@ -9548,10 +9840,9 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9548
9840
  }, [handleMouseMove]);
9549
9841
  const { isValid, message } = validateData(data);
9550
9842
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
9551
- const { visibleData, allSegmentsHidden, legendData } = useInteractiveLegendData({
9843
+ const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9552
9844
  data: useDataWithPercentages(data),
9553
9845
  chartId,
9554
- legendInteractive,
9555
9846
  isSeriesVisible
9556
9847
  });
9557
9848
  const accessors = (0, react.useMemo)(() => ({
@@ -9618,7 +9909,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9618
9909
  chartId,
9619
9910
  interactive: legendInteractive
9620
9911
  });
9621
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9912
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9622
9913
  value: { chartId },
9623
9914
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9624
9915
  legendPosition,
@@ -9635,6 +9926,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9635
9926
  top: tooltipTop || 0,
9636
9927
  left: tooltipLeft || 0,
9637
9928
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9929
+ className: "a8c-charts-scope",
9638
9930
  role: "tooltip",
9639
9931
  children: renderTooltip({ tooltipData })
9640
9932
  })
@@ -9670,7 +9962,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9670
9962
  y: -radius / 2,
9671
9963
  width,
9672
9964
  height,
9673
- children: (0, _wordpress_i18n.__)("All segments are hidden. Click legend items to show data.", "jetpack-charts")
9965
+ children: getAllHiddenMessage(legendInteractive, "segments")
9674
9966
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9675
9967
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_shape.Pie, {
9676
9968
  data: dataWithIndex,
@@ -9922,7 +10214,7 @@ const Icon = ({ direction }) => {
9922
10214
  function TrendIndicator({ direction, value, className, style, showIcon = true }) {
9923
10215
  const ariaLabel = `${DIRECTION_LABELS[direction]}: ${value}`;
9924
10216
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9925
- className: (0, clsx.default)(trend_indicator_module_default["trend-indicator"], trend_indicator_module_default[`trend-indicator--${direction}`], className),
10217
+ className: (0, clsx.default)(useStandaloneScopeClass(), trend_indicator_module_default["trend-indicator"], trend_indicator_module_default[`trend-indicator--${direction}`], className),
9926
10218
  style,
9927
10219
  "aria-label": ariaLabel,
9928
10220
  children: [showIcon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { direction }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -9945,6 +10237,7 @@ exports.GeoChart = GeoChartResponsive;
9945
10237
  exports.GeoChartUnresponsive = GeoChartWithProvider;
9946
10238
  exports.GlobalChartsContext = GlobalChartsContext;
9947
10239
  exports.GlobalChartsProvider = GlobalChartsProvider;
10240
+ exports.GoogleDataTableColumnRoleType = GoogleDataTableColumnRoleType;
9948
10241
  exports.HeatmapChart = HeatmapChartResponsive;
9949
10242
  exports.HeatmapChartUnresponsive = HeatmapChart;
9950
10243
  exports.LeaderboardChart = LeaderboardChartResponsive;
@@ -9976,7 +10269,10 @@ exports.parseHslString = parseHslString;
9976
10269
  exports.parseRgbString = parseRgbString;
9977
10270
  exports.prefersLightText = prefersLightText;
9978
10271
  exports.relativeLuminance = relativeLuminance;
10272
+ exports.resolveCssVariable = resolveCssVariable;
9979
10273
  exports.useChartLegendItems = useChartLegendItems;
10274
+ exports.useChartRegistration = useChartRegistration;
10275
+ exports.useChartScopeElement = useChartScopeElement;
9980
10276
  exports.useGlobalChartsContext = useGlobalChartsContext;
9981
10277
  exports.useGlobalChartsTheme = useGlobalChartsTheme;
9982
10278
  exports.useLeaderboardLegendItems = useLeaderboardLegendItems;