@automattic/charts 3.0.0 → 3.1.1

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 (63) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/TOKENS.md +3 -3
  3. package/dist/index.cjs +250 -136
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.css +23 -14
  6. package/dist/index.d.cts +51 -14
  7. package/dist/index.d.ts +51 -14
  8. package/dist/index.js +251 -138
  9. package/dist/index.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/charts/area-chart/area-chart.tsx +26 -26
  12. package/src/charts/area-chart/private/overlays.tsx +3 -3
  13. package/src/charts/area-chart/test/area-chart.test.tsx +346 -11
  14. package/src/charts/area-chart/types.ts +4 -3
  15. package/src/charts/bar-chart/bar-chart.tsx +21 -15
  16. package/src/charts/bar-chart/private/use-bar-chart-options.ts +19 -12
  17. package/src/charts/bar-chart/test/bar-chart.test.tsx +277 -2
  18. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +6 -6
  19. package/src/charts/geo-chart/geo-chart.module.scss +8 -0
  20. package/src/charts/heatmap-chart/heatmap-chart.module.scss +9 -0
  21. package/src/charts/heatmap-chart/heatmap-chart.tsx +31 -8
  22. package/src/charts/heatmap-chart/private/build-calendar-data.ts +65 -5
  23. package/src/charts/heatmap-chart/test/build-calendar-data.test.ts +125 -0
  24. package/src/charts/heatmap-chart/types.ts +8 -0
  25. package/src/charts/leaderboard-chart/leaderboard-chart.tsx +17 -22
  26. package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +107 -1
  27. package/src/charts/line-chart/line-chart.module.scss +2 -2
  28. package/src/charts/line-chart/line-chart.tsx +33 -29
  29. package/src/charts/line-chart/private/line-chart-annotations-overlay.tsx +2 -2
  30. package/src/charts/line-chart/test/line-chart.test.tsx +313 -5
  31. package/src/charts/line-chart/types.ts +4 -3
  32. package/src/charts/pie-chart/pie-chart.tsx +8 -13
  33. package/src/charts/pie-chart/test/pie-chart.test.tsx +73 -1
  34. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +2 -2
  35. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +8 -13
  36. package/src/charts/private/{single-chart-context/single-chart-context.tsx → chart-instance-context/chart-instance-context.ts} +1 -5
  37. package/src/charts/private/chart-instance-context/index.ts +3 -0
  38. package/src/charts/private/{single-chart-context/use-single-chart-context.ts → chart-instance-context/use-chart-instance-context.ts} +1 -3
  39. package/src/charts/private/svg-empty-state/get-all-hidden-message.ts +41 -0
  40. package/src/charts/private/svg-empty-state/index.ts +1 -0
  41. package/src/charts/private/svg-empty-state/test/get-all-hidden-message.test.ts +15 -0
  42. package/src/charts/private/x-zoom/test/x-zoom.test.tsx +2 -2
  43. package/src/charts/private/x-zoom/x-zoom.tsx +2 -2
  44. package/src/components/legend/legend.tsx +4 -4
  45. package/src/components/legend/private/base-legend.module.scss +1 -1
  46. package/src/components/legend/private/base-legend.tsx +67 -18
  47. package/src/components/legend/test/legend.test.tsx +100 -4
  48. package/src/components/tooltip/accessible-tooltip.tsx +13 -2
  49. package/src/components/trend-indicator/trend-indicator.module.scss +1 -1
  50. package/src/hooks/index.ts +1 -1
  51. package/src/hooks/{use-interactive-legend-data.ts → use-legend-visibility-data.ts} +29 -30
  52. package/src/index.ts +3 -1
  53. package/src/providers/chart-context/global-charts-provider.tsx +92 -18
  54. package/src/providers/chart-context/hooks/use-default-hidden-series.ts +47 -0
  55. package/src/providers/chart-context/test/chart-context.test.tsx +142 -0
  56. package/src/providers/chart-context/test/use-default-hidden-series-paint.test.tsx +56 -0
  57. package/src/providers/chart-context/test/use-default-hidden-series.ssr.test.tsx +39 -0
  58. package/src/providers/chart-context/test/use-default-hidden-series.test.tsx +222 -0
  59. package/src/providers/chart-context/types.ts +7 -1
  60. package/src/styles/chart-scope.scss +2 -2
  61. package/src/styles/test/wpds-fallbacks.test.ts +131 -0
  62. package/src/types.ts +12 -0
  63. package/src/charts/private/single-chart-context/index.ts +0 -8
package/dist/index.cjs CHANGED
@@ -52,17 +52,15 @@ 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;
66
64
  //#endregion
67
65
  //#region src/styles/chart-scope-class.ts
68
66
  /**
@@ -969,6 +967,7 @@ const GlobalChartsContext = (0, react.createContext)(null);
969
967
  const GlobalChartsProvider = ({ children, theme }) => {
970
968
  const [charts, setCharts] = (0, react.useState)(() => /* @__PURE__ */ new Map());
971
969
  const [hiddenSeries, setHiddenSeries] = (0, react.useState)(() => /* @__PURE__ */ new Map());
970
+ const seededCharts = (0, react.useRef)(/* @__PURE__ */ new Set());
972
971
  const wrapperRef = (0, react.useRef)(null);
973
972
  const [scopeNode, setScopeNode] = (0, react.useState)(null);
974
973
  const setWrapperNode = (0, react.useCallback)((node) => {
@@ -1071,18 +1070,41 @@ const GlobalChartsProvider = ({ children, theme }) => {
1071
1070
  shapeStyles: isSeriesData ? getItemShapeStyles(data, index, providerTheme, legendShape) : {}
1072
1071
  };
1073
1072
  }, [providerTheme, resolveColor]);
1074
- const toggleSeriesVisibility = (0, react.useCallback)((chartId, seriesLabel) => {
1073
+ const updateHiddenSeries = (0, react.useCallback)((chartId, update) => {
1075
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;
1076
1079
  const newMap = new Map(prev);
1077
- const chartHidden = newMap.get(chartId) || /* @__PURE__ */ new Set();
1078
- const newSet = new Set(chartHidden);
1079
- if (newSet.has(seriesLabel)) newSet.delete(seriesLabel);
1080
- else newSet.add(seriesLabel);
1081
- if (newSet.size === 0) newMap.delete(chartId);
1082
- else newMap.set(chartId, newSet);
1080
+ if (next.size === 0) newMap.delete(chartId);
1081
+ else newMap.set(chartId, next);
1083
1082
  return newMap;
1084
1083
  });
1085
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]);
1086
1108
  const isSeriesVisible = (0, react.useCallback)((chartId, seriesLabel) => {
1087
1109
  const chartHidden = hiddenSeries.get(chartId);
1088
1110
  return !chartHidden || !chartHidden.has(seriesLabel);
@@ -1099,6 +1121,10 @@ const GlobalChartsProvider = ({ children, theme }) => {
1099
1121
  theme: providerTheme,
1100
1122
  getElementStyles,
1101
1123
  toggleSeriesVisibility,
1124
+ setSeriesVisibility,
1125
+ setChartHiddenSeries,
1126
+ seedChartHiddenSeries,
1127
+ hasSeededChart,
1102
1128
  isSeriesVisible,
1103
1129
  getHiddenSeries,
1104
1130
  isColorPaletteResolved
@@ -1110,6 +1136,10 @@ const GlobalChartsProvider = ({ children, theme }) => {
1110
1136
  providerTheme,
1111
1137
  getElementStyles,
1112
1138
  toggleSeriesVisibility,
1139
+ setSeriesVisibility,
1140
+ setChartHiddenSeries,
1141
+ seedChartHiddenSeries,
1142
+ hasSeededChart,
1113
1143
  isSeriesVisible,
1114
1144
  getHiddenSeries,
1115
1145
  isColorPaletteResolved
@@ -1508,12 +1538,12 @@ const useDataWithPercentages = (data) => {
1508
1538
  }, [data]);
1509
1539
  };
1510
1540
  //#endregion
1511
- //#region src/hooks/use-interactive-legend-data.ts
1541
+ //#region src/hooks/use-legend-visibility-data.ts
1512
1542
  /**
1513
- * Custom hook to filter and recalculate chart data for interactive legends.
1543
+ * Custom hook to filter and recalculate chart data based on series visibility.
1514
1544
  *
1515
- * When interactive legends are enabled, this hook:
1516
- * 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)
1517
1547
  * 2. Recalculates percentages so visible segments total 100%
1518
1548
  * 3. Tracks whether all segments are hidden to show empty state
1519
1549
  *
@@ -1522,10 +1552,9 @@ const useDataWithPercentages = (data) => {
1522
1552
  *
1523
1553
  * @example
1524
1554
  * ```tsx
1525
- * const { visibleData, allSegmentsHidden, legendData } = useInteractiveLegendData({
1555
+ * const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
1526
1556
  * data: chartData,
1527
1557
  * chartId: 'my-pie-chart',
1528
- * legendInteractive: true,
1529
1558
  * isSeriesVisible: (id, label) => context.isSeriesVisible(id, label),
1530
1559
  * });
1531
1560
  *
@@ -1540,16 +1569,15 @@ const useDataWithPercentages = (data) => {
1540
1569
  * return <PieChart data={visibleData} />;
1541
1570
  * ```
1542
1571
  *
1543
- * @param params - Configuration object for the hook
1544
- * @param params.data - The chart data to filter
1545
- * @param params.chartId - Unique identifier for the chart (required for interactive mode)
1546
- * @param params.legendInteractive - Whether to enable interactive filtering
1547
- * @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
1548
1576
  * @return Object containing visibleData, allSegmentsHidden flag, and legendData with recalculated percentages
1549
1577
  */
1550
- const useInteractiveLegendData = ({ data, chartId, legendInteractive, isSeriesVisible }) => {
1578
+ const useLegendVisibilityData = ({ data, chartId, isSeriesVisible }) => {
1551
1579
  const visibleData = (0, react.useMemo)(() => {
1552
- if (!chartId || !legendInteractive) return data;
1580
+ if (!chartId) return data;
1553
1581
  const filtered = data.filter((segment) => isSeriesVisible(chartId, segment.label));
1554
1582
  if (filtered.length === 0) return [];
1555
1583
  const totalValue = filtered.reduce((sum, segment) => sum + segment.value, 0);
@@ -1560,16 +1588,15 @@ const useInteractiveLegendData = ({ data, chartId, legendInteractive, isSeriesVi
1560
1588
  }, [
1561
1589
  data,
1562
1590
  chartId,
1563
- isSeriesVisible,
1564
- legendInteractive
1591
+ isSeriesVisible
1565
1592
  ]);
1566
1593
  return {
1567
1594
  visibleData,
1568
1595
  allSegmentsHidden: (0, react.useMemo)(() => {
1569
- return legendInteractive && visibleData.length === 0;
1570
- }, [legendInteractive, visibleData]),
1596
+ return visibleData.length === 0 && data.length > 0;
1597
+ }, [visibleData, data]),
1571
1598
  legendData: (0, react.useMemo)(() => {
1572
- if (!legendInteractive || !chartId) return data;
1599
+ if (!chartId) return data;
1573
1600
  const visibleDataMap = new Map(visibleData.map((d) => [d.label, d]));
1574
1601
  return data.map((segment) => {
1575
1602
  if (!isSeriesVisible(chartId, segment.label)) return segment;
@@ -1578,7 +1605,6 @@ const useInteractiveLegendData = ({ data, chartId, legendInteractive, isSeriesVi
1578
1605
  }, [
1579
1606
  data,
1580
1607
  visibleData,
1581
- legendInteractive,
1582
1608
  chartId,
1583
1609
  isSeriesVisible
1584
1610
  ])
@@ -1708,12 +1734,22 @@ const LegendText = ({ text, textOverflow, maxWidth }) => {
1708
1734
  children: text
1709
1735
  });
1710
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
+ };
1711
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) => {
1712
1747
  const { margin: itemMargin = "0", flexDirection: itemDirection = "row" } = itemStyles ?? {};
1713
1748
  const { justifyContent: labelJustifyContent = "flex-start", flex: labelFlex = "0 0 auto", margin: labelMargin = "0 4px", maxWidth, textOverflow = "wrap" } = labelStyles ?? {};
1714
1749
  const { width: shapeWidth = 16, height: shapeHeight = 16, margin: shapeMargin = "2px 4px 2px 0" } = shapeStyles ?? {};
1715
1750
  const theme = useGlobalChartsTheme();
1716
1751
  const context = (0, react.useContext)(GlobalChartsContext);
1752
+ const chartInstanceContext = (0, react.useContext)(ChartInstanceContext);
1717
1753
  const standaloneScopeClass = useStandaloneScopeClass();
1718
1754
  const legendScale = (0, _visx_scale.scaleOrdinal)({
1719
1755
  domain: items.map((item) => item.label),
@@ -1724,9 +1760,7 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
1724
1760
  const handleLegendClick = (0, react.useCallback)((seriesLabels) => {
1725
1761
  if (interactive && chartId && context) {
1726
1762
  const representativeVisible = context.isSeriesVisible(chartId, seriesLabels[0]);
1727
- seriesLabels.forEach((label) => {
1728
- if (context.isSeriesVisible(chartId, label) === representativeVisible) context.toggleSeriesVisibility(chartId, label);
1729
- });
1763
+ seriesLabels.forEach((label) => context.setSeriesVisibility(chartId, label, !representativeVisible));
1730
1764
  }
1731
1765
  }, [
1732
1766
  interactive,
@@ -1734,11 +1768,12 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
1734
1768
  context
1735
1769
  ]);
1736
1770
  const isSeriesVisible = (0, react.useCallback)((seriesLabel) => {
1737
- if (!interactive || !chartId || !context) return true;
1771
+ if (chartInstanceContext?.isSeriesVisible) return chartInstanceContext.isSeriesVisible(seriesLabel);
1772
+ if (!chartId || !context) return true;
1738
1773
  return context.isSeriesVisible(chartId, seriesLabel);
1739
1774
  }, [
1740
- interactive,
1741
1775
  chartId,
1776
+ chartInstanceContext,
1742
1777
  context
1743
1778
  ]);
1744
1779
  const createClickHandler = (0, react.useCallback)((seriesLabels) => {
@@ -1766,7 +1801,7 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
1766
1801
  align: orientation === "vertical" ? flexAlignment : void 0,
1767
1802
  justify: orientation === "horizontal" ? flexAlignment : void 0,
1768
1803
  wrap: orientation === "horizontal" ? "wrap" : void 0,
1769
- role: "list",
1804
+ role: interactive ? void 0 : "list",
1770
1805
  className: (0, clsx.default)(standaloneScopeClass, base_legend_module_default.legend, className),
1771
1806
  style: theme.legend?.containerStyles,
1772
1807
  children: labels.map((label, i) => {
@@ -1781,10 +1816,10 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
1781
1816
  flexDirection: orientation === "vertical" && alignment === "end" ? "row-reverse" : itemDirection,
1782
1817
  onClick: handleClick,
1783
1818
  onKeyDown: handleKeyDown,
1784
- role: interactive ? "button" : void 0,
1819
+ role: interactive ? "button" : "listitem",
1785
1820
  tabIndex: interactive ? 0 : void 0,
1786
1821
  "aria-pressed": interactive ? visible : void 0,
1787
- "aria-label": interactive ? `${label.text}: ${visible ? "visible" : "hidden"}. Toggle visibility.` : void 0,
1822
+ "aria-label": getLegendItemAriaLabel(label.text, matchedItem?.value, visible, interactive),
1788
1823
  children: [items[i]?.renderGlyph ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1789
1824
  width: items[i]?.glyphSize * 2,
1790
1825
  height: items[i]?.glyphSize * 2,
@@ -1846,8 +1881,8 @@ const defaultShapeByChartType = {
1846
1881
  };
1847
1882
  const Legend = (0, react.forwardRef)(({ chartId, items, shape, ...props }, ref) => {
1848
1883
  const context = (0, react.useContext)(GlobalChartsContext);
1849
- const singleChartContext = (0, react.useContext)(SingleChartContext);
1850
- const contextChartId = chartId ?? singleChartContext?.chartId;
1884
+ const chartInstanceContext = (0, react.useContext)(ChartInstanceContext);
1885
+ const contextChartId = chartId ?? chartInstanceContext?.chartId;
1851
1886
  const chartData = (0, react.useMemo)(() => contextChartId && context ? context.getChartData(contextChartId) : void 0, [contextChartId, context]);
1852
1887
  const contextItems = chartData?.legendItems;
1853
1888
  const resolvedShape = shape ?? (chartData?.chartType ? defaultShapeByChartType[chartData.chartType] : void 0);
@@ -2087,11 +2122,16 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
2087
2122
  }
2088
2123
  return flattened;
2089
2124
  }, [series, mode]);
2125
+ const hasKeyboardSelection = (0, react.useRef)(false);
2090
2126
  (0, react.useEffect)(() => {
2091
2127
  if (selectedIndex === void 0) {
2092
- tooltipContext?.hideTooltip();
2128
+ if (hasKeyboardSelection.current) {
2129
+ hasKeyboardSelection.current = false;
2130
+ tooltipContext?.hideTooltip();
2131
+ }
2093
2132
  return;
2094
2133
  }
2134
+ hasKeyboardSelection.current = true;
2095
2135
  if (mode === "group") series.forEach((s, index) => {
2096
2136
  if (selectedIndex < s.data.length) {
2097
2137
  const datum = s.data[selectedIndex];
@@ -2199,6 +2239,43 @@ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating,
2199
2239
  };
2200
2240
  };
2201
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
2202
2279
  //#region src/charts/private/chart-composition/chart-svg.tsx
2203
2280
  /**
2204
2281
  * Compound component for SVG children in charts.
@@ -2412,6 +2489,27 @@ const SvgEmptyState = ({ x, y, width, height, children }) => {
2412
2489
  });
2413
2490
  };
2414
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
2415
2513
  //#region src/charts/private/time-axis.ts
2416
2514
  const X_TICK_WIDTH = 60;
2417
2515
  const getCurveType = (type, smoothing) => {
@@ -2939,7 +3037,7 @@ const LineChartAnnotationLabelWithPopover = ({ title, subtitle, renderLabel, ren
2939
3037
  //#endregion
2940
3038
  //#region src/charts/line-chart/private/line-chart-annotations-overlay.tsx
2941
3039
  const LineChartAnnotationsOverlay = ({ children }) => {
2942
- const { chartRef, chartWidth, chartHeight } = useSingleChartContext();
3040
+ const { chartRef, chartWidth, chartHeight } = useChartInstanceContext();
2943
3041
  const [scales, setScales] = (0, react.useState)(null);
2944
3042
  const [scalesStable, setScalesStable] = (0, react.useState)(false);
2945
3043
  const createScaleSignature = (0, react.useCallback)((scaleData) => {
@@ -3324,7 +3422,7 @@ const LineChartScalesRef = ({ chartRef, width, height, margin }) => {
3324
3422
  ]);
3325
3423
  return null;
3326
3424
  };
3327
- 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) => {
3328
3426
  const legendInteractive = legend.interactive ?? false;
3329
3427
  const legendCollapseGroups = legend.collapseGroups ?? false;
3330
3428
  const legendShape = legend.shape ?? "line";
@@ -3333,6 +3431,8 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3333
3431
  const theme = useXYChartTheme(data);
3334
3432
  const resolvedBackgroundColor = theme.backgroundColor ?? providerTheme.backgroundColor;
3335
3433
  const chartId = useChartId(providedChartId);
3434
+ const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
3435
+ const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
3336
3436
  const chartRef = (0, react.useRef)(null);
3337
3437
  const [selectedIndex, setSelectedIndex] = (0, react.useState)(void 0);
3338
3438
  const [isNavigating, setIsNavigating] = (0, react.useState)(false);
@@ -3360,29 +3460,19 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3360
3460
  }
3361
3461
  }), [internalChartRef]);
3362
3462
  const dataSorted = useChartDataTransform(data);
3363
- const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
3463
+ const { getElementStyles } = useGlobalChartsContext();
3364
3464
  const seriesWithVisibility = (0, react.useMemo)(() => {
3365
- if (!chartId || !legendInteractive) return dataSorted.map((series, index) => ({
3366
- series,
3367
- index,
3368
- isVisible: true
3369
- }));
3370
3465
  return dataSorted.map((series, index) => ({
3371
3466
  series,
3372
3467
  index,
3373
- isVisible: isSeriesVisible(chartId, series.label)
3468
+ isVisible: !hiddenSeries.has(series.label)
3374
3469
  }));
3375
- }, [
3376
- dataSorted,
3377
- chartId,
3378
- isSeriesVisible,
3379
- legendInteractive
3380
- ]);
3470
+ }, [dataSorted, hiddenSeries]);
3381
3471
  const allSeriesHidden = (0, react.useMemo)(() => {
3382
3472
  return seriesWithVisibility.every(({ isVisible }) => !isVisible);
3383
3473
  }, [seriesWithVisibility]);
3384
3474
  const stableYDomain = (0, react.useMemo)(() => {
3385
- if (!legendInteractive || rescaleYOnVisibilityChange) return;
3475
+ if (rescaleYOnVisibilityChange) return;
3386
3476
  let min = Infinity;
3387
3477
  let max = -Infinity;
3388
3478
  for (const series of dataSorted) for (const point of series.data ?? []) {
@@ -3393,11 +3483,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3393
3483
  }
3394
3484
  }
3395
3485
  return min < max ? [min, max] : void 0;
3396
- }, [
3397
- legendInteractive,
3398
- rescaleYOnVisibilityChange,
3399
- dataSorted
3400
- ]);
3486
+ }, [rescaleYOnVisibilityChange, dataSorted]);
3401
3487
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
3402
3488
  selectedIndex,
3403
3489
  setSelectedIndex,
@@ -3407,8 +3493,8 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3407
3493
  totalPoints: dataSorted[0]?.data.length || 0
3408
3494
  });
3409
3495
  const chartOptions = (0, react.useMemo)(() => {
3410
- const { tickResolution, ...xAxisOptions } = options?.axis?.x ?? {};
3411
- const formatter = xAxisOptions.tickFormat || getFormatter(dataSorted, tickResolution);
3496
+ const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
3497
+ const formatter = tickFormat || getFormatter(dataSorted, tickResolution);
3412
3498
  return {
3413
3499
  axis: {
3414
3500
  x: {
@@ -3521,10 +3607,11 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3521
3607
  chartId,
3522
3608
  interactive: legendInteractive
3523
3609
  });
3524
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
3610
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
3525
3611
  value: {
3526
3612
  chartId,
3527
3613
  chartRef: internalChartRef,
3614
+ isSeriesVisible,
3528
3615
  chartWidth: width,
3529
3616
  chartHeight: measuredChartHeight || 0
3530
3617
  },
@@ -3579,7 +3666,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3579
3666
  y: chartHeight / 2,
3580
3667
  width,
3581
3668
  height: chartHeight,
3582
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
3669
+ children: getAllHiddenMessage(legendInteractive, "series")
3583
3670
  }) : null,
3584
3671
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomClip, {
3585
3672
  active: zoomable && !!zoom.domain,
@@ -3781,7 +3868,7 @@ const HoverGlyphs = ({ visibleSeries, stacked, stackOffset, getElementStyles, st
3781
3868
  };
3782
3869
  //#endregion
3783
3870
  //#region src/charts/area-chart/area-chart.tsx
3784
- 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) => {
3785
3872
  const legendInteractive = legend.interactive ?? false;
3786
3873
  const legendShape = legend.shape ?? "rect";
3787
3874
  const legendPosition = legend.position ?? "bottom";
@@ -3789,6 +3876,8 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3789
3876
  const providerTheme = useGlobalChartsTheme();
3790
3877
  const theme = useXYChartTheme(data);
3791
3878
  const chartId = useChartId(providedChartId);
3879
+ const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
3880
+ const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
3792
3881
  const chartRef = (0, react.useRef)(null);
3793
3882
  const [selectedIndex, setSelectedIndex] = (0, react.useState)(void 0);
3794
3883
  const [isNavigating, setIsNavigating] = (0, react.useState)(false);
@@ -3816,24 +3905,14 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3816
3905
  }
3817
3906
  }), [internalChartRef]);
3818
3907
  const dataSorted = useChartDataTransform(data);
3819
- const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
3908
+ const { getElementStyles } = useGlobalChartsContext();
3820
3909
  const seriesWithVisibility = (0, react.useMemo)(() => {
3821
- if (!chartId || !legendInteractive) return dataSorted.map((series, index) => ({
3822
- series,
3823
- index,
3824
- isVisible: true
3825
- }));
3826
3910
  return dataSorted.map((series, index) => ({
3827
3911
  series,
3828
3912
  index,
3829
- isVisible: isSeriesVisible(chartId, series.label)
3913
+ isVisible: !hiddenSeries.has(series.label)
3830
3914
  }));
3831
- }, [
3832
- dataSorted,
3833
- chartId,
3834
- isSeriesVisible,
3835
- legendInteractive
3836
- ]);
3915
+ }, [dataSorted, hiddenSeries]);
3837
3916
  const allSeriesHidden = (0, react.useMemo)(() => seriesWithVisibility.every(({ isVisible }) => !isVisible), [seriesWithVisibility]);
3838
3917
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
3839
3918
  selectedIndex,
@@ -3844,7 +3923,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3844
3923
  totalPoints: dataSorted[0]?.data.length || 0
3845
3924
  });
3846
3925
  const fixedYDomain = (0, react.useMemo)(() => {
3847
- 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;
3848
3927
  if (stacked) {
3849
3928
  const numPoints = Math.max(...dataSorted.map((s) => s.data.length));
3850
3929
  let posMax = 0;
@@ -3878,12 +3957,11 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3878
3957
  dataSorted,
3879
3958
  stacked,
3880
3959
  stackOffset,
3881
- legendInteractive,
3882
3960
  rescaleYOnVisibility
3883
3961
  ]);
3884
3962
  const chartOptions = (0, react.useMemo)(() => {
3885
- const { tickResolution, ...xAxisOptions } = options?.axis?.x ?? {};
3886
- const formatter = xAxisOptions.tickFormat || getFormatter(dataSorted, tickResolution);
3963
+ const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
3964
+ const formatter = tickFormat || getFormatter(dataSorted, tickResolution);
3887
3965
  return {
3888
3966
  axis: {
3889
3967
  x: {
@@ -3955,7 +4033,6 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3955
4033
  const zeroYAccessor = (0, react.useCallback)(() => 0, []);
3956
4034
  const visibleLabels = (0, react.useMemo)(() => new Set(seriesWithVisibility.filter((s) => s.isVisible).map((s) => s.series.label)), [seriesWithVisibility]);
3957
4035
  const filteredRenderTooltip = (0, react.useCallback)((params) => {
3958
- if (!legendInteractive) return renderTooltip(params);
3959
4036
  const datumByKey = params?.tooltipData?.datumByKey;
3960
4037
  if (!datumByKey) return renderTooltip(params);
3961
4038
  const filtered = Object.fromEntries(Object.entries(datumByKey).filter(([key]) => visibleLabels.has(key)));
@@ -3973,11 +4050,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3973
4050
  nearestDatum: nextNearest
3974
4051
  }
3975
4052
  });
3976
- }, [
3977
- renderTooltip,
3978
- legendInteractive,
3979
- visibleLabels
3980
- ]);
4053
+ }, [renderTooltip, visibleLabels]);
3981
4054
  const resolvedFillOpacity = fillOpacity ?? (stacked ? .85 : .4);
3982
4055
  const resolvedWithStroke = withStroke ?? !stacked;
3983
4056
  if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -4008,7 +4081,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
4008
4081
  dataKey: seriesData?.label,
4009
4082
  data: seriesData.data,
4010
4083
  xAccessor: accessors.xAccessor,
4011
- yAccessor: isVisible || !legendInteractive ? accessors.yAccessor : zeroYAccessor,
4084
+ yAccessor: isVisible ? accessors.yAccessor : zeroYAccessor,
4012
4085
  fill: color,
4013
4086
  fillOpacity: resolvedFillOpacity,
4014
4087
  ...stacked ? {} : {
@@ -4021,10 +4094,11 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
4021
4094
  }
4022
4095
  }, seriesData?.label || index);
4023
4096
  };
4024
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
4097
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
4025
4098
  value: {
4026
4099
  chartId,
4027
4100
  chartRef: internalChartRef,
4101
+ isSeriesVisible,
4028
4102
  chartWidth: width,
4029
4103
  chartHeight: measuredChartHeight || 0
4030
4104
  },
@@ -4079,7 +4153,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
4079
4153
  y: chartHeight / 2,
4080
4154
  width,
4081
4155
  height: chartHeight,
4082
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
4156
+ children: getAllHiddenMessage(legendInteractive, "series")
4083
4157
  }) : null,
4084
4158
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ZoomClip, {
4085
4159
  active: zoomable,
@@ -4356,11 +4430,13 @@ const getGroupPadding = (scale) => {
4356
4430
  function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = ALL_RENDERED) {
4357
4431
  const stableOptions = useDeepMemo(options);
4358
4432
  const axisConfig = (0, react.useMemo)(() => {
4359
- const { labelOverflow: xLabelOverflow, tickResolution: xTickResolution, ...xAxisOptions } = stableOptions.axis?.x || {};
4360
- const { labelOverflow: yLabelOverflow, tickResolution: yTickResolution, ...yAxisOptions } = stableOptions.axis?.y || {};
4433
+ const { labelOverflow: xLabelOverflow, tickResolution: xTickResolution, tickFormat: xTickFormat, ...xAxisOptions } = stableOptions.axis?.x || {};
4434
+ const { labelOverflow: yLabelOverflow, tickResolution: yTickResolution, tickFormat: yTickFormat, ...yAxisOptions } = stableOptions.axis?.y || {};
4361
4435
  return {
4362
4436
  xLabelOverflow,
4363
4437
  yLabelOverflow,
4438
+ xTickFormat,
4439
+ yTickFormat,
4364
4440
  xAxisOptions,
4365
4441
  yAxisOptions,
4366
4442
  tickResolution: horizontal ? yTickResolution : xTickResolution
@@ -4420,7 +4496,7 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
4420
4496
  isSeriesRendered
4421
4497
  ]);
4422
4498
  return (0, react.useMemo)(() => {
4423
- const { xTickFormat, yTickFormat, tooltipLabelFormatter: defaultTooltipLabelFormatter, xAccessor, yAccessor, gridVisibility, xScale: baseXScale, yScale: baseYScale } = defaultOptions[horizontal ? "horizontal" : "vertical"];
4499
+ const { xTickFormat: defaultXTickFormat, yTickFormat: defaultYTickFormat, tooltipLabelFormatter: defaultTooltipLabelFormatter, xAccessor, yAccessor, gridVisibility, xScale: baseXScale, yScale: baseYScale } = defaultOptions[horizontal ? "horizontal" : "vertical"];
4424
4500
  let valueScaleDomainOverride = {};
4425
4501
  if (data.some((s) => s.options?.type === "comparison")) {
4426
4502
  if (!(!horizontal ? stableOptions.yScale?.domain : stableOptions.xScale?.domain)) {
@@ -4445,11 +4521,11 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
4445
4521
  ...stableOptions.yScale || {},
4446
4522
  ...!horizontal ? valueScaleDomainOverride : {}
4447
4523
  };
4448
- const { xLabelOverflow, yLabelOverflow, xAxisOptions, yAxisOptions } = axisConfig;
4449
- const providedToolTipLabelFormatter = horizontal ? yAxisOptions.tickFormat : xAxisOptions.tickFormat;
4524
+ const { xLabelOverflow, yLabelOverflow, xTickFormat, yTickFormat, xAxisOptions, yAxisOptions } = axisConfig;
4525
+ const dateAxisTickFormat = horizontal ? yTickFormat : xTickFormat;
4450
4526
  const { timeAxis } = defaultOptions;
4451
4527
  const dateAxisOptions = horizontal ? yAxisOptions : xAxisOptions;
4452
- const bandTickValues = timeAxis && !dateAxisOptions.tickFormat ? getBandTickValues(timeAxis.domain, timeAxis.tickFormatter, dateAxisOptions.numTicks ?? DEFAULT_NUM_TICKS) : null;
4528
+ const bandTickValues = timeAxis && !dateAxisTickFormat ? getBandTickValues(timeAxis.domain, timeAxis.tickFormatter, dateAxisOptions.numTicks ?? DEFAULT_NUM_TICKS) : null;
4453
4529
  const dateAxisTickValues = bandTickValues ? { tickValues: bandTickValues } : {};
4454
4530
  return {
4455
4531
  gridVisibility,
@@ -4463,7 +4539,7 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
4463
4539
  x: {
4464
4540
  orientation: "bottom",
4465
4541
  numTicks: DEFAULT_NUM_TICKS,
4466
- tickFormat: xTickFormat,
4542
+ tickFormat: xTickFormat || defaultXTickFormat,
4467
4543
  ...horizontal ? {} : dateAxisTickValues,
4468
4544
  ...xLabelOverflow === "ellipsis" ? { tickComponent: TruncatedXTickComponent } : {},
4469
4545
  ...xAxisOptions
@@ -4471,14 +4547,14 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
4471
4547
  y: {
4472
4548
  orientation: "left",
4473
4549
  numTicks: DEFAULT_NUM_TICKS,
4474
- tickFormat: yTickFormat,
4550
+ tickFormat: yTickFormat || defaultYTickFormat,
4475
4551
  ...horizontal ? dateAxisTickValues : {},
4476
4552
  ...yLabelOverflow === "ellipsis" ? { tickComponent: TruncatedYTickComponent } : {},
4477
4553
  ...yAxisOptions
4478
4554
  }
4479
4555
  },
4480
4556
  barGroup: { padding: getGroupPadding(horizontal ? yScale : xScale) },
4481
- tooltip: { labelFormatter: providedToolTipLabelFormatter || defaultTooltipLabelFormatter }
4557
+ tooltip: { labelFormatter: dateAxisTickFormat || defaultTooltipLabelFormatter }
4482
4558
  };
4483
4559
  }, [
4484
4560
  defaultOptions,
@@ -4633,11 +4709,13 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ (0, react_jsx_runtime
4633
4709
  className: bar_chart_module_default["bar-chart__tooltip-row"],
4634
4710
  children: (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("%1$s: %2$s", "jetpack-charts"), label, value)
4635
4711
  });
4636
- 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" }) => {
4712
+ 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" }) => {
4637
4713
  const legendInteractive = legend.interactive ?? false;
4638
4714
  const legendCollapseGroups = legend.collapseGroups ?? false;
4639
4715
  const horizontal = orientation === "horizontal";
4640
4716
  const chartId = useChartId(providedChartId);
4717
+ const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
4718
+ const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
4641
4719
  const theme = useXYChartTheme(data);
4642
4720
  const dataSorted = useChartDataTransform(data);
4643
4721
  const dataWithVisibleZeros = useZeroValueDisplay(dataSorted, {
@@ -4645,12 +4723,8 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4645
4723
  valueAxisLength: horizontal ? width : height
4646
4724
  });
4647
4725
  const legendItems = useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({ collapseGroups: legendCollapseGroups }), [legendCollapseGroups]));
4648
- const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
4649
- const isSeriesRendered = (0, react.useCallback)((series) => !chartId || !legendInteractive || isSeriesVisible(chartId, series.label), [
4650
- chartId,
4651
- legendInteractive,
4652
- isSeriesVisible
4653
- ]);
4726
+ const { getElementStyles } = useGlobalChartsContext();
4727
+ const isSeriesRendered = (0, react.useCallback)((series) => isSeriesVisible(series.label), [isSeriesVisible]);
4654
4728
  const chartOptions = useBarChartOptions(dataWithVisibleZeros, horizontal, options, isSeriesRendered);
4655
4729
  const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme, horizontal);
4656
4730
  const chartRef = (0, react.useRef)(null);
@@ -4886,9 +4960,10 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4886
4960
  chartId,
4887
4961
  interactive: legendInteractive
4888
4962
  });
4889
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
4963
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
4890
4964
  value: {
4891
4965
  chartId,
4966
+ isSeriesVisible,
4892
4967
  chartWidth: width,
4893
4968
  chartHeight: measuredChartHeight || 0
4894
4969
  },
@@ -4947,7 +5022,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4947
5022
  y: chartHeight / 2,
4948
5023
  width,
4949
5024
  height: chartHeight,
4950
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
5025
+ children: getAllHiddenMessage(legendInteractive, "series")
4951
5026
  }) : null,
4952
5027
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComparisonBars, {
4953
5028
  comparisonEntries,
@@ -5760,12 +5835,27 @@ const GeoChartWithProvider = (props) => {
5760
5835
  GeoChartWithProvider.displayName = "GeoChart";
5761
5836
  const GeoChartResponsive = withResponsive(GeoChartWithProvider);
5762
5837
  //#endregion
5838
+ //#region src/types.ts
5839
+ let GoogleDataTableColumnRoleType = /* @__PURE__ */ function(GoogleDataTableColumnRoleType) {
5840
+ GoogleDataTableColumnRoleType["annotation"] = "annotation";
5841
+ GoogleDataTableColumnRoleType["annotationText"] = "annotationText";
5842
+ GoogleDataTableColumnRoleType["certainty"] = "certainty";
5843
+ GoogleDataTableColumnRoleType["emphasis"] = "emphasis";
5844
+ GoogleDataTableColumnRoleType["interval"] = "interval";
5845
+ GoogleDataTableColumnRoleType["scope"] = "scope";
5846
+ GoogleDataTableColumnRoleType["style"] = "style";
5847
+ GoogleDataTableColumnRoleType["tooltip"] = "tooltip";
5848
+ GoogleDataTableColumnRoleType["domain"] = "domain";
5849
+ return GoogleDataTableColumnRoleType;
5850
+ }({});
5851
+ //#endregion
5763
5852
  //#region src/charts/heatmap-chart/heatmap-chart.module.scss
5764
5853
  var heatmap_chart_module_default = {
5765
5854
  "heatmap-chart": "a8ccharts-O3YMOW-heatmap-chart",
5766
5855
  "heatmap-chart__cell": "a8ccharts-O3YMOW-heatmap-chart__cell",
5767
5856
  "heatmap-chart__cell--filled": "a8ccharts-O3YMOW-heatmap-chart__cell--filled",
5768
5857
  "heatmap-chart__cell--hidden": "a8ccharts-O3YMOW-heatmap-chart__cell--hidden",
5858
+ "heatmap-chart__cell--placeholder": "a8ccharts-O3YMOW-heatmap-chart__cell--placeholder",
5769
5859
  "heatmap-chart__cell--selected": "a8ccharts-O3YMOW-heatmap-chart__cell--selected",
5770
5860
  "heatmap-chart__cell--strong": "a8ccharts-O3YMOW-heatmap-chart__cell--strong",
5771
5861
  "heatmap-chart__cell-value": "a8ccharts-O3YMOW-heatmap-chart__cell-value",
@@ -5821,6 +5911,22 @@ const LABELLED_ROWS = [
5821
5911
  2,
5822
5912
  4
5823
5913
  ];
5914
+ /**
5915
+ * Resolve one grid bound: an unparseable or narrowing value falls back to the
5916
+ * series' own bound, so the grid can only ever be widened.
5917
+ *
5918
+ * @param bound - Requested bound as `yyyy-MM-dd`, if any.
5919
+ * @param fallback - The series' own bound on this side.
5920
+ * @param direction - Which way the bound is allowed to move.
5921
+ * @return The bound to draw to.
5922
+ */
5923
+ const widenTo = (bound, fallback, direction) => {
5924
+ if (!bound) return fallback;
5925
+ const parsed = (0, date_fns.parseISO)(bound);
5926
+ if (isNaN(parsed.getTime())) return fallback;
5927
+ if (direction === "earlier") return parsed < fallback ? parsed : fallback;
5928
+ return parsed > fallback ? parsed : fallback;
5929
+ };
5824
5930
  const toDate = (point) => {
5825
5931
  if (point.date instanceof Date && !isNaN(point.date.getTime())) return point.date;
5826
5932
  if (point.dateString) {
@@ -5848,10 +5954,14 @@ const buildCalendarHeatmapData = (series, options = {}) => {
5848
5954
  if (date < minDate) minDate = date;
5849
5955
  if (date > maxDate) maxDate = date;
5850
5956
  }
5851
- const gridStart = (0, date_fns.startOfWeek)(minDate, { weekStartsOn });
5852
- const weekCount = (0, date_fns.differenceInCalendarWeeks)(maxDate, gridStart, { weekStartsOn }) + 1;
5853
5957
  const minDayKey = (0, date_fns.format)(minDate, "yyyy-MM-dd");
5854
5958
  const maxDayKey = (0, date_fns.format)(maxDate, "yyyy-MM-dd");
5959
+ const gridMinDate = widenTo(options.gridSpan?.start, minDate, "earlier");
5960
+ const gridMaxDate = widenTo(options.gridSpan?.end, maxDate, "later");
5961
+ const gridMaxDayKey = (0, date_fns.format)(gridMaxDate, "yyyy-MM-dd");
5962
+ const gridStart = (0, date_fns.startOfWeek)(gridMinDate, { weekStartsOn });
5963
+ const gridMinDayKey = (0, date_fns.format)(gridMinDate < minDate ? gridStart : gridMinDate, "yyyy-MM-dd");
5964
+ const weekCount = (0, date_fns.differenceInCalendarWeeks)(gridMaxDate, gridStart, { weekStartsOn }) + 1;
5855
5965
  const rowLabels = Array.from({ length: 7 }, (_, row) => LABELLED_ROWS.includes(row) ? (0, date_fns.format)((0, date_fns.addDays)(gridStart, row), "EEE") : "");
5856
5966
  const MIN_FIRST_MONTH_WEEKS = 2;
5857
5967
  const firstMonth = gridStart.getMonth();
@@ -5873,7 +5983,9 @@ const buildCalendarHeatmapData = (series, options = {}) => {
5873
5983
  label: (0, date_fns.format)(day, "EEE, MMM d, yyyy"),
5874
5984
  value: valueByDay.has(key) ? valueByDay.get(key) : null
5875
5985
  };
5876
- if (hideOutOfRangeDays && (key < minDayKey || key > maxDayKey)) cell.hidden = true;
5986
+ if (key < gridMinDayKey || key > gridMaxDayKey) {
5987
+ if (hideOutOfRangeDays) cell.hidden = true;
5988
+ } else if (key < minDayKey || key > maxDayKey) cell.placeholder = true;
5877
5989
  cells.push(cell);
5878
5990
  }
5879
5991
  data.push({
@@ -5980,7 +6092,10 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5980
6092
  setSelectedIndex(void 0);
5981
6093
  hideTooltip();
5982
6094
  }, [hideTooltip]);
5983
- const isCellHidden = (0, react.useCallback)((col, row) => data[col]?.data[row]?.hidden === true, [data]);
6095
+ const isCellInert = (0, react.useCallback)((col, row) => {
6096
+ const cell = data[col]?.data[row];
6097
+ return cell?.hidden === true || cell?.placeholder === true;
6098
+ }, [data]);
5984
6099
  const onChartKeyDown = (0, react.useCallback)((event) => {
5985
6100
  if (![
5986
6101
  "ArrowLeft",
@@ -5997,7 +6112,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5997
6112
  }
5998
6113
  event.preventDefault();
5999
6114
  if (selectedIndex === void 0) {
6000
- for (let index = 0; index < columns * rows; index++) if (!isCellHidden(Math.floor(index / rows), index % rows)) {
6115
+ for (let index = 0; index < columns * rows; index++) if (!isCellInert(Math.floor(index / rows), index % rows)) {
6001
6116
  setSelectedIndex(index);
6002
6117
  return;
6003
6118
  }
@@ -6014,7 +6129,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6014
6129
  do {
6015
6130
  col += stepCol;
6016
6131
  row += stepRow;
6017
- } while (col >= 0 && col < columns && row >= 0 && row < rows && isCellHidden(col, row));
6132
+ } while (col >= 0 && col < columns && row >= 0 && row < rows && isCellInert(col, row));
6018
6133
  if (col < 0 || col >= columns || row < 0 || row >= rows) return;
6019
6134
  setSelectedIndex(col * rows + row);
6020
6135
  }, [
@@ -6022,7 +6137,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6022
6137
  columns,
6023
6138
  selectedIndex,
6024
6139
  hideTooltip,
6025
- isCellHidden
6140
+ isCellInert
6026
6141
  ]);
6027
6142
  const handleCellMouseMove = (0, react.useCallback)((event) => {
6028
6143
  if (!withTooltips) return;
@@ -6094,7 +6209,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6094
6209
  const heightCapped = !compact && Boolean(maxCellHeight);
6095
6210
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeatmapContext.Provider, {
6096
6211
  value: heatmapContext,
6097
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
6212
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
6098
6213
  value: { chartId },
6099
6214
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartLayout, {
6100
6215
  legendPosition: "bottom",
@@ -6146,6 +6261,10 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6146
6261
  "aria-hidden": "true",
6147
6262
  className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--hidden"])
6148
6263
  }, `cell-${columnIndex}-${rowIndex}`);
6264
+ if (cell?.placeholder) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6265
+ "aria-hidden": "true",
6266
+ className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--placeholder"])
6267
+ }, `cell-${columnIndex}-${rowIndex}`);
6149
6268
  const value = cell?.value ?? null;
6150
6269
  const present = isPresent(value);
6151
6270
  const normalized = present ? getNormalizedValue(value, extent) : 0;
@@ -9144,29 +9263,25 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9144
9263
  legendLabels
9145
9264
  });
9146
9265
  const isPrimaryVisible = (0, react.useMemo)(() => {
9147
- if (!chartId || !legendInteractive || legendItems.length === 0) return true;
9266
+ if (legendItems.length === 0) return true;
9148
9267
  return isSeriesVisible(chartId, legendItems[0].label);
9149
9268
  }, [
9150
9269
  chartId,
9151
- legendInteractive,
9152
9270
  legendItems,
9153
9271
  isSeriesVisible
9154
9272
  ]);
9155
9273
  const isComparisonVisible = (0, react.useMemo)(() => {
9156
- if (!chartId || !legendInteractive || legendItems.length < 2) return true;
9274
+ if (legendItems.length < 2) return true;
9157
9275
  return isSeriesVisible(chartId, legendItems[1].label);
9158
9276
  }, [
9159
9277
  chartId,
9160
- legendInteractive,
9161
9278
  legendItems,
9162
9279
  isSeriesVisible
9163
9280
  ]);
9164
9281
  const allSeriesHidden = (0, react.useMemo)(() => {
9165
- if (!legendInteractive) return false;
9166
9282
  if (withComparison && !withOverlayLabel) return !isPrimaryVisible && !isComparisonVisible;
9167
9283
  return !isPrimaryVisible;
9168
9284
  }, [
9169
- legendInteractive,
9170
9285
  isPrimaryVisible,
9171
9286
  isComparisonVisible,
9172
9287
  withComparison,
@@ -9185,7 +9300,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9185
9300
  const prefersReducedMotion = usePrefersReducedMotion();
9186
9301
  const { contentRef, fittedCount, isMeasurable } = useFittedRowCount(fitRows && !allSeriesHidden, data?.length ?? 0, data);
9187
9302
  const shouldFitRows = fitRows && isMeasurable;
9188
- if (!data || data.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9303
+ if (!data || data.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9189
9304
  value: { chartId },
9190
9305
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9191
9306
  legendPosition,
@@ -9220,7 +9335,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9220
9335
  chartId,
9221
9336
  interactive: legendInteractive
9222
9337
  });
9223
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9338
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9224
9339
  value: { chartId },
9225
9340
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9226
9341
  legendPosition,
@@ -9245,7 +9360,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9245
9360
  children: (0, _wordpress_i18n.__)("Not enough space to display data", "jetpack-charts")
9246
9361
  }), allSeriesHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9247
9362
  className: leaderboard_chart_module_default.emptyState,
9248
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
9363
+ children: getAllHiddenMessage(legendInteractive, "series")
9249
9364
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default, {
9250
9365
  templateColumns: "minmax(0, 1fr) auto",
9251
9366
  rowGap,
@@ -9435,10 +9550,9 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9435
9550
  hideTooltip();
9436
9551
  }, [withTooltips, hideTooltip]);
9437
9552
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
9438
- const { visibleData, allSegmentsHidden, legendData } = useInteractiveLegendData({
9553
+ const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9439
9554
  data: useDataWithPercentages(data),
9440
9555
  chartId,
9441
- legendInteractive,
9442
9556
  isSeriesVisible
9443
9557
  });
9444
9558
  const legendItems = useChartLegendItems(legendData, (0, react.useMemo)(() => ({
@@ -9499,7 +9613,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9499
9613
  chartId,
9500
9614
  interactive: legendInteractive
9501
9615
  });
9502
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9616
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9503
9617
  value: { chartId },
9504
9618
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9505
9619
  legendPosition,
@@ -9556,7 +9670,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9556
9670
  y: 0,
9557
9671
  width,
9558
9672
  height,
9559
- children: (0, _wordpress_i18n.__)("All segments are hidden. Click legend items to show data.", "jetpack-charts")
9673
+ children: getAllHiddenMessage(legendInteractive, "segments")
9560
9674
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_shape.Pie, {
9561
9675
  data: dataWithIndex,
9562
9676
  pieValue: accessors.value,
@@ -9728,10 +9842,9 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9728
9842
  }, [handleMouseMove]);
9729
9843
  const { isValid, message } = validateData(data);
9730
9844
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
9731
- const { visibleData, allSegmentsHidden, legendData } = useInteractiveLegendData({
9845
+ const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9732
9846
  data: useDataWithPercentages(data),
9733
9847
  chartId,
9734
- legendInteractive,
9735
9848
  isSeriesVisible
9736
9849
  });
9737
9850
  const accessors = (0, react.useMemo)(() => ({
@@ -9798,7 +9911,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9798
9911
  chartId,
9799
9912
  interactive: legendInteractive
9800
9913
  });
9801
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9914
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9802
9915
  value: { chartId },
9803
9916
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9804
9917
  legendPosition,
@@ -9851,7 +9964,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9851
9964
  y: -radius / 2,
9852
9965
  width,
9853
9966
  height,
9854
- children: (0, _wordpress_i18n.__)("All segments are hidden. Click legend items to show data.", "jetpack-charts")
9967
+ children: getAllHiddenMessage(legendInteractive, "segments")
9855
9968
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9856
9969
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_shape.Pie, {
9857
9970
  data: dataWithIndex,
@@ -10126,6 +10239,7 @@ exports.GeoChart = GeoChartResponsive;
10126
10239
  exports.GeoChartUnresponsive = GeoChartWithProvider;
10127
10240
  exports.GlobalChartsContext = GlobalChartsContext;
10128
10241
  exports.GlobalChartsProvider = GlobalChartsProvider;
10242
+ exports.GoogleDataTableColumnRoleType = GoogleDataTableColumnRoleType;
10129
10243
  exports.HeatmapChart = HeatmapChartResponsive;
10130
10244
  exports.HeatmapChartUnresponsive = HeatmapChart;
10131
10245
  exports.LeaderboardChart = LeaderboardChartResponsive;