@automattic/charts 3.0.0 → 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 (62) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/TOKENS.md +3 -3
  3. package/dist/index.cjs +235 -123
  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 +236 -125
  9. package/dist/index.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/charts/area-chart/area-chart.tsx +24 -24
  12. package/src/charts/area-chart/private/overlays.tsx +3 -3
  13. package/src/charts/area-chart/test/area-chart.test.tsx +301 -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/test/bar-chart.test.tsx +255 -2
  17. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +6 -6
  18. package/src/charts/geo-chart/geo-chart.module.scss +8 -0
  19. package/src/charts/heatmap-chart/heatmap-chart.module.scss +9 -0
  20. package/src/charts/heatmap-chart/heatmap-chart.tsx +31 -8
  21. package/src/charts/heatmap-chart/private/build-calendar-data.ts +65 -5
  22. package/src/charts/heatmap-chart/test/build-calendar-data.test.ts +125 -0
  23. package/src/charts/heatmap-chart/types.ts +8 -0
  24. package/src/charts/leaderboard-chart/leaderboard-chart.tsx +17 -22
  25. package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +107 -1
  26. package/src/charts/line-chart/line-chart.module.scss +2 -2
  27. package/src/charts/line-chart/line-chart.tsx +31 -27
  28. package/src/charts/line-chart/private/line-chart-annotations-overlay.tsx +2 -2
  29. package/src/charts/line-chart/test/line-chart.test.tsx +262 -5
  30. package/src/charts/line-chart/types.ts +4 -3
  31. package/src/charts/pie-chart/pie-chart.tsx +8 -13
  32. package/src/charts/pie-chart/test/pie-chart.test.tsx +73 -1
  33. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +2 -2
  34. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +8 -13
  35. package/src/charts/private/{single-chart-context/single-chart-context.tsx → chart-instance-context/chart-instance-context.ts} +1 -5
  36. package/src/charts/private/chart-instance-context/index.ts +3 -0
  37. package/src/charts/private/{single-chart-context/use-single-chart-context.ts → chart-instance-context/use-chart-instance-context.ts} +1 -3
  38. package/src/charts/private/svg-empty-state/get-all-hidden-message.ts +41 -0
  39. package/src/charts/private/svg-empty-state/index.ts +1 -0
  40. package/src/charts/private/svg-empty-state/test/get-all-hidden-message.test.ts +15 -0
  41. package/src/charts/private/x-zoom/test/x-zoom.test.tsx +2 -2
  42. package/src/charts/private/x-zoom/x-zoom.tsx +2 -2
  43. package/src/components/legend/legend.tsx +4 -4
  44. package/src/components/legend/private/base-legend.module.scss +1 -1
  45. package/src/components/legend/private/base-legend.tsx +67 -18
  46. package/src/components/legend/test/legend.test.tsx +100 -4
  47. package/src/components/tooltip/accessible-tooltip.tsx +13 -2
  48. package/src/components/trend-indicator/trend-indicator.module.scss +1 -1
  49. package/src/hooks/index.ts +1 -1
  50. package/src/hooks/{use-interactive-legend-data.ts → use-legend-visibility-data.ts} +29 -30
  51. package/src/index.ts +3 -1
  52. package/src/providers/chart-context/global-charts-provider.tsx +92 -18
  53. package/src/providers/chart-context/hooks/use-default-hidden-series.ts +47 -0
  54. package/src/providers/chart-context/test/chart-context.test.tsx +142 -0
  55. package/src/providers/chart-context/test/use-default-hidden-series-paint.test.tsx +56 -0
  56. package/src/providers/chart-context/test/use-default-hidden-series.ssr.test.tsx +39 -0
  57. package/src/providers/chart-context/test/use-default-hidden-series.test.tsx +222 -0
  58. package/src/providers/chart-context/types.ts +7 -1
  59. package/src/styles/chart-scope.scss +2 -2
  60. package/src/styles/test/wpds-fallbacks.test.ts +131 -0
  61. package/src/types.ts +12 -0
  62. 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,
@@ -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,7 +3957,6 @@ 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)(() => {
@@ -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,
@@ -4633,11 +4707,13 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ (0, react_jsx_runtime
4633
4707
  className: bar_chart_module_default["bar-chart__tooltip-row"],
4634
4708
  children: (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("%1$s: %2$s", "jetpack-charts"), label, value)
4635
4709
  });
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" }) => {
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" }) => {
4637
4711
  const legendInteractive = legend.interactive ?? false;
4638
4712
  const legendCollapseGroups = legend.collapseGroups ?? false;
4639
4713
  const horizontal = orientation === "horizontal";
4640
4714
  const chartId = useChartId(providedChartId);
4715
+ const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
4716
+ const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
4641
4717
  const theme = useXYChartTheme(data);
4642
4718
  const dataSorted = useChartDataTransform(data);
4643
4719
  const dataWithVisibleZeros = useZeroValueDisplay(dataSorted, {
@@ -4645,12 +4721,8 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4645
4721
  valueAxisLength: horizontal ? width : height
4646
4722
  });
4647
4723
  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
- ]);
4724
+ const { getElementStyles } = useGlobalChartsContext();
4725
+ const isSeriesRendered = (0, react.useCallback)((series) => isSeriesVisible(series.label), [isSeriesVisible]);
4654
4726
  const chartOptions = useBarChartOptions(dataWithVisibleZeros, horizontal, options, isSeriesRendered);
4655
4727
  const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme, horizontal);
4656
4728
  const chartRef = (0, react.useRef)(null);
@@ -4886,9 +4958,10 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4886
4958
  chartId,
4887
4959
  interactive: legendInteractive
4888
4960
  });
4889
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
4961
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
4890
4962
  value: {
4891
4963
  chartId,
4964
+ isSeriesVisible,
4892
4965
  chartWidth: width,
4893
4966
  chartHeight: measuredChartHeight || 0
4894
4967
  },
@@ -4947,7 +5020,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4947
5020
  y: chartHeight / 2,
4948
5021
  width,
4949
5022
  height: chartHeight,
4950
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
5023
+ children: getAllHiddenMessage(legendInteractive, "series")
4951
5024
  }) : null,
4952
5025
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComparisonBars, {
4953
5026
  comparisonEntries,
@@ -5760,12 +5833,27 @@ const GeoChartWithProvider = (props) => {
5760
5833
  GeoChartWithProvider.displayName = "GeoChart";
5761
5834
  const GeoChartResponsive = withResponsive(GeoChartWithProvider);
5762
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
5763
5850
  //#region src/charts/heatmap-chart/heatmap-chart.module.scss
5764
5851
  var heatmap_chart_module_default = {
5765
5852
  "heatmap-chart": "a8ccharts-O3YMOW-heatmap-chart",
5766
5853
  "heatmap-chart__cell": "a8ccharts-O3YMOW-heatmap-chart__cell",
5767
5854
  "heatmap-chart__cell--filled": "a8ccharts-O3YMOW-heatmap-chart__cell--filled",
5768
5855
  "heatmap-chart__cell--hidden": "a8ccharts-O3YMOW-heatmap-chart__cell--hidden",
5856
+ "heatmap-chart__cell--placeholder": "a8ccharts-O3YMOW-heatmap-chart__cell--placeholder",
5769
5857
  "heatmap-chart__cell--selected": "a8ccharts-O3YMOW-heatmap-chart__cell--selected",
5770
5858
  "heatmap-chart__cell--strong": "a8ccharts-O3YMOW-heatmap-chart__cell--strong",
5771
5859
  "heatmap-chart__cell-value": "a8ccharts-O3YMOW-heatmap-chart__cell-value",
@@ -5821,6 +5909,22 @@ const LABELLED_ROWS = [
5821
5909
  2,
5822
5910
  4
5823
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
+ };
5824
5928
  const toDate = (point) => {
5825
5929
  if (point.date instanceof Date && !isNaN(point.date.getTime())) return point.date;
5826
5930
  if (point.dateString) {
@@ -5848,10 +5952,14 @@ const buildCalendarHeatmapData = (series, options = {}) => {
5848
5952
  if (date < minDate) minDate = date;
5849
5953
  if (date > maxDate) maxDate = date;
5850
5954
  }
5851
- const gridStart = (0, date_fns.startOfWeek)(minDate, { weekStartsOn });
5852
- const weekCount = (0, date_fns.differenceInCalendarWeeks)(maxDate, gridStart, { weekStartsOn }) + 1;
5853
5955
  const minDayKey = (0, date_fns.format)(minDate, "yyyy-MM-dd");
5854
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;
5855
5963
  const rowLabels = Array.from({ length: 7 }, (_, row) => LABELLED_ROWS.includes(row) ? (0, date_fns.format)((0, date_fns.addDays)(gridStart, row), "EEE") : "");
5856
5964
  const MIN_FIRST_MONTH_WEEKS = 2;
5857
5965
  const firstMonth = gridStart.getMonth();
@@ -5873,7 +5981,9 @@ const buildCalendarHeatmapData = (series, options = {}) => {
5873
5981
  label: (0, date_fns.format)(day, "EEE, MMM d, yyyy"),
5874
5982
  value: valueByDay.has(key) ? valueByDay.get(key) : null
5875
5983
  };
5876
- 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;
5877
5987
  cells.push(cell);
5878
5988
  }
5879
5989
  data.push({
@@ -5980,7 +6090,10 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5980
6090
  setSelectedIndex(void 0);
5981
6091
  hideTooltip();
5982
6092
  }, [hideTooltip]);
5983
- 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]);
5984
6097
  const onChartKeyDown = (0, react.useCallback)((event) => {
5985
6098
  if (![
5986
6099
  "ArrowLeft",
@@ -5997,7 +6110,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
5997
6110
  }
5998
6111
  event.preventDefault();
5999
6112
  if (selectedIndex === void 0) {
6000
- 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)) {
6001
6114
  setSelectedIndex(index);
6002
6115
  return;
6003
6116
  }
@@ -6014,7 +6127,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6014
6127
  do {
6015
6128
  col += stepCol;
6016
6129
  row += stepRow;
6017
- } 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));
6018
6131
  if (col < 0 || col >= columns || row < 0 || row >= rows) return;
6019
6132
  setSelectedIndex(col * rows + row);
6020
6133
  }, [
@@ -6022,7 +6135,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6022
6135
  columns,
6023
6136
  selectedIndex,
6024
6137
  hideTooltip,
6025
- isCellHidden
6138
+ isCellInert
6026
6139
  ]);
6027
6140
  const handleCellMouseMove = (0, react.useCallback)((event) => {
6028
6141
  if (!withTooltips) return;
@@ -6094,7 +6207,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6094
6207
  const heightCapped = !compact && Boolean(maxCellHeight);
6095
6208
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeatmapContext.Provider, {
6096
6209
  value: heatmapContext,
6097
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
6210
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
6098
6211
  value: { chartId },
6099
6212
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartLayout, {
6100
6213
  legendPosition: "bottom",
@@ -6146,6 +6259,10 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6146
6259
  "aria-hidden": "true",
6147
6260
  className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--hidden"])
6148
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}`);
6149
6266
  const value = cell?.value ?? null;
6150
6267
  const present = isPresent(value);
6151
6268
  const normalized = present ? getNormalizedValue(value, extent) : 0;
@@ -9144,29 +9261,25 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9144
9261
  legendLabels
9145
9262
  });
9146
9263
  const isPrimaryVisible = (0, react.useMemo)(() => {
9147
- if (!chartId || !legendInteractive || legendItems.length === 0) return true;
9264
+ if (legendItems.length === 0) return true;
9148
9265
  return isSeriesVisible(chartId, legendItems[0].label);
9149
9266
  }, [
9150
9267
  chartId,
9151
- legendInteractive,
9152
9268
  legendItems,
9153
9269
  isSeriesVisible
9154
9270
  ]);
9155
9271
  const isComparisonVisible = (0, react.useMemo)(() => {
9156
- if (!chartId || !legendInteractive || legendItems.length < 2) return true;
9272
+ if (legendItems.length < 2) return true;
9157
9273
  return isSeriesVisible(chartId, legendItems[1].label);
9158
9274
  }, [
9159
9275
  chartId,
9160
- legendInteractive,
9161
9276
  legendItems,
9162
9277
  isSeriesVisible
9163
9278
  ]);
9164
9279
  const allSeriesHidden = (0, react.useMemo)(() => {
9165
- if (!legendInteractive) return false;
9166
9280
  if (withComparison && !withOverlayLabel) return !isPrimaryVisible && !isComparisonVisible;
9167
9281
  return !isPrimaryVisible;
9168
9282
  }, [
9169
- legendInteractive,
9170
9283
  isPrimaryVisible,
9171
9284
  isComparisonVisible,
9172
9285
  withComparison,
@@ -9185,7 +9298,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9185
9298
  const prefersReducedMotion = usePrefersReducedMotion();
9186
9299
  const { contentRef, fittedCount, isMeasurable } = useFittedRowCount(fitRows && !allSeriesHidden, data?.length ?? 0, data);
9187
9300
  const shouldFitRows = fitRows && isMeasurable;
9188
- 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, {
9189
9302
  value: { chartId },
9190
9303
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9191
9304
  legendPosition,
@@ -9220,7 +9333,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9220
9333
  chartId,
9221
9334
  interactive: legendInteractive
9222
9335
  });
9223
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9336
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9224
9337
  value: { chartId },
9225
9338
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9226
9339
  legendPosition,
@@ -9245,7 +9358,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9245
9358
  children: (0, _wordpress_i18n.__)("Not enough space to display data", "jetpack-charts")
9246
9359
  }), allSeriesHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9247
9360
  className: leaderboard_chart_module_default.emptyState,
9248
- children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
9361
+ children: getAllHiddenMessage(legendInteractive, "series")
9249
9362
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default, {
9250
9363
  templateColumns: "minmax(0, 1fr) auto",
9251
9364
  rowGap,
@@ -9435,10 +9548,9 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9435
9548
  hideTooltip();
9436
9549
  }, [withTooltips, hideTooltip]);
9437
9550
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
9438
- const { visibleData, allSegmentsHidden, legendData } = useInteractiveLegendData({
9551
+ const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9439
9552
  data: useDataWithPercentages(data),
9440
9553
  chartId,
9441
- legendInteractive,
9442
9554
  isSeriesVisible
9443
9555
  });
9444
9556
  const legendItems = useChartLegendItems(legendData, (0, react.useMemo)(() => ({
@@ -9499,7 +9611,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9499
9611
  chartId,
9500
9612
  interactive: legendInteractive
9501
9613
  });
9502
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9614
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9503
9615
  value: { chartId },
9504
9616
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9505
9617
  legendPosition,
@@ -9556,7 +9668,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9556
9668
  y: 0,
9557
9669
  width,
9558
9670
  height,
9559
- children: (0, _wordpress_i18n.__)("All segments are hidden. Click legend items to show data.", "jetpack-charts")
9671
+ children: getAllHiddenMessage(legendInteractive, "segments")
9560
9672
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_shape.Pie, {
9561
9673
  data: dataWithIndex,
9562
9674
  pieValue: accessors.value,
@@ -9728,10 +9840,9 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9728
9840
  }, [handleMouseMove]);
9729
9841
  const { isValid, message } = validateData(data);
9730
9842
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
9731
- const { visibleData, allSegmentsHidden, legendData } = useInteractiveLegendData({
9843
+ const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9732
9844
  data: useDataWithPercentages(data),
9733
9845
  chartId,
9734
- legendInteractive,
9735
9846
  isSeriesVisible
9736
9847
  });
9737
9848
  const accessors = (0, react.useMemo)(() => ({
@@ -9798,7 +9909,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9798
9909
  chartId,
9799
9910
  interactive: legendInteractive
9800
9911
  });
9801
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
9912
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartInstanceContext.Provider, {
9802
9913
  value: { chartId },
9803
9914
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
9804
9915
  legendPosition,
@@ -9851,7 +9962,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9851
9962
  y: -radius / 2,
9852
9963
  width,
9853
9964
  height,
9854
- children: (0, _wordpress_i18n.__)("All segments are hidden. Click legend items to show data.", "jetpack-charts")
9965
+ children: getAllHiddenMessage(legendInteractive, "segments")
9855
9966
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9856
9967
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_shape.Pie, {
9857
9968
  data: dataWithIndex,
@@ -10126,6 +10237,7 @@ exports.GeoChart = GeoChartResponsive;
10126
10237
  exports.GeoChartUnresponsive = GeoChartWithProvider;
10127
10238
  exports.GlobalChartsContext = GlobalChartsContext;
10128
10239
  exports.GlobalChartsProvider = GlobalChartsProvider;
10240
+ exports.GoogleDataTableColumnRoleType = GoogleDataTableColumnRoleType;
10129
10241
  exports.HeatmapChart = HeatmapChartResponsive;
10130
10242
  exports.HeatmapChartUnresponsive = HeatmapChart;
10131
10243
  exports.LeaderboardChart = LeaderboardChartResponsive;