@automattic/charts 4.1.0 → 4.2.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 (80) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/TOKENS.md +7 -5
  3. package/dist/index.cjs +1115 -471
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.css +39 -1
  6. package/dist/index.d.cts +116 -16
  7. package/dist/index.d.ts +116 -16
  8. package/dist/index.js +1115 -473
  9. package/dist/index.js.map +1 -1
  10. package/package.json +11 -11
  11. package/src/charts/area-chart/area-chart.tsx +2 -1
  12. package/src/charts/area-chart/private/overlays.tsx +1 -2
  13. package/src/charts/area-chart/test/area-chart.test.tsx +31 -0
  14. package/src/charts/bar-chart/bar-chart.tsx +67 -17
  15. package/src/charts/bar-chart/index.ts +1 -1
  16. package/src/charts/bar-chart/private/band-highlight.tsx +85 -0
  17. package/src/charts/bar-chart/private/band-scale.ts +13 -0
  18. package/src/charts/bar-chart/private/band-tooltip.tsx +138 -0
  19. package/src/charts/bar-chart/private/comparison-bars.tsx +5 -10
  20. package/src/charts/bar-chart/private/index.ts +2 -0
  21. package/src/charts/bar-chart/private/test/band-highlight.test.tsx +109 -0
  22. package/src/charts/bar-chart/private/test/band-tooltip.test.ts +26 -0
  23. package/src/charts/bar-chart/private/truncated-tick-component.tsx +1 -1
  24. package/src/charts/bar-chart/private/use-bar-chart-options.ts +1 -1
  25. package/src/charts/bar-chart/test/bar-chart-pointer-events.test.tsx +11 -23
  26. package/src/charts/bar-chart/test/bar-chart.test.tsx +565 -1
  27. package/src/charts/bar-list-chart/bar-list-chart.tsx +5 -3
  28. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +1 -1
  29. package/src/charts/conversion-funnel-chart/types.ts +4 -2
  30. package/src/charts/geo-chart/types.ts +4 -2
  31. package/src/charts/heatmap-chart/{private/build-calendar-data.ts → build-calendar-data.ts} +15 -45
  32. package/src/charts/heatmap-chart/build-month-calendar-data.ts +124 -0
  33. package/src/charts/heatmap-chart/heatmap-chart.module.scss +32 -0
  34. package/src/charts/heatmap-chart/heatmap-chart.tsx +191 -114
  35. package/src/charts/heatmap-chart/index.ts +15 -3
  36. package/src/charts/heatmap-chart/private/civil-day.ts +26 -0
  37. package/src/charts/heatmap-chart/private/column-groups.ts +71 -0
  38. package/src/charts/heatmap-chart/private/heatmap-legend.tsx +19 -3
  39. package/src/charts/heatmap-chart/private/index.ts +2 -2
  40. package/src/charts/heatmap-chart/private/keyboard-navigation.ts +201 -0
  41. package/src/charts/heatmap-chart/test/build-calendar-data.test.ts +37 -1
  42. package/src/charts/heatmap-chart/test/build-month-calendar-data.test.ts +194 -0
  43. package/src/charts/heatmap-chart/test/column-groups.test.ts +83 -0
  44. package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +428 -2
  45. package/src/charts/heatmap-chart/test/keyboard-navigation.test.ts +25 -0
  46. package/src/charts/heatmap-chart/test/use-calendar-heatmap-data.test.tsx +1 -1
  47. package/src/charts/heatmap-chart/test/use-month-calendar-heatmap-data.test.tsx +90 -0
  48. package/src/charts/heatmap-chart/test/zoned-calendar-data.test.ts +1 -1
  49. package/src/charts/heatmap-chart/test/zoned-month-calendar-data.test.ts +23 -0
  50. package/src/charts/heatmap-chart/types.ts +88 -3
  51. package/src/charts/heatmap-chart/use-calendar-heatmap-data.ts +2 -2
  52. package/src/charts/heatmap-chart/use-month-calendar-heatmap-data.ts +34 -0
  53. package/src/charts/leaderboard-chart/types.ts +13 -14
  54. package/src/charts/line-chart/line-chart.tsx +3 -2
  55. package/src/charts/line-chart/private/line-chart-annotation.tsx +1 -1
  56. package/src/charts/line-chart/test/line-chart.test.tsx +31 -0
  57. package/src/charts/line-chart/types.ts +2 -2
  58. package/src/charts/private/time-axis-options.ts +1 -1
  59. package/src/charts/private/with-responsive/with-responsive.tsx +2 -2
  60. package/src/charts/private/x-zoom/test/x-zoom.test.tsx +1 -3
  61. package/src/charts/private/x-zoom/x-zoom.tsx +1 -2
  62. package/src/components/legend/private/base-legend.tsx +1 -1
  63. package/src/components/tooltip/accessible-tooltip.tsx +146 -37
  64. package/src/components/tooltip/base-tooltip.module.scss +6 -1
  65. package/src/components/tooltip/private/bounded-tooltip.tsx +12 -6
  66. package/src/components/tooltip/test/accessible-tooltip.test.tsx +160 -0
  67. package/src/components/tooltip/test/bounded-tooltip.test.tsx +17 -0
  68. package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +29 -3
  69. package/src/components/tooltip/xy-chart-tooltip.tsx +11 -11
  70. package/src/hooks/test/use-element-size.test.tsx +13 -13
  71. package/src/hooks/use-xychart-theme.ts +1 -1
  72. package/src/index.ts +7 -1
  73. package/src/providers/chart-context/themes.ts +1 -0
  74. package/src/styles/chart-scope.scss +11 -3
  75. package/src/styles/test/chart-scope.test.ts +36 -2
  76. package/src/types.ts +6 -13
  77. package/src/utils/format-metric-value.ts +4 -4
  78. package/src/utils/get-edge-tick-widths.ts +1 -1
  79. package/src/utils/get-styles.ts +1 -1
  80. package/src/visx/types.ts +7 -10
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { getStringWidth as getStringWidth$1 } from "./visx/text/index.js";
2
2
  import { formatNumber, formatNumberCompact } from "@automattic/number-formatters";
3
- import { AnimatedAreaSeries, AnimatedAreaStack, AreaSeries, Axis, BarGroup, BarSeries, DataContext, Grid, TooltipContext, XYChart, buildChartTheme } from "@visx/xychart";
3
+ import { AnimatedAreaSeries, AnimatedAreaStack, AreaSeries, Axis, BarGroup, BarSeries, DataContext, Grid, TooltipContext, XYChart, buildChartTheme, useEventEmitter } from "@visx/xychart";
4
4
  import { __, _x, sprintf } from "@wordpress/i18n";
5
5
  import clsx from "clsx";
6
6
  import * as React from "react";
@@ -981,7 +981,8 @@ const defaultTheme = {
981
981
  },
982
982
  heatmapChart: {
983
983
  compactCellGap: 2,
984
- compactCellSize: 11
984
+ compactCellSize: 11,
985
+ groupGap: 24
985
986
  }
986
987
  };
987
988
  //#endregion
@@ -2181,7 +2182,10 @@ function useChartLegendItems(data, options = {}, legendShape) {
2181
2182
  }
2182
2183
  //#endregion
2183
2184
  //#region src/components/tooltip/base-tooltip.module.scss
2184
- var base_tooltip_module_default = { "tooltip": "a8ccharts--zY0xG-tooltip" };
2185
+ var base_tooltip_module_default = {
2186
+ "surface": "a8ccharts--zY0xG-surface",
2187
+ "tooltip": "a8ccharts--zY0xG-tooltip"
2188
+ };
2185
2189
  //#endregion
2186
2190
  //#region src/components/tooltip/base-tooltip.tsx
2187
2191
  const DefaultTooltipContent = ({ data }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
@@ -2231,7 +2235,8 @@ const findClippingAncestor = (wrapper) => {
2231
2235
  /**
2232
2236
  * Where the box goes, in wrapper coordinates. Automatic placement flips to
2233
2237
  * the side that clips less, then clamps inside `bounds`; below-axis placement
2234
- * is centered on the anchor and clamped horizontally.
2238
+ * is centered on the anchor and clamped horizontally; beside placement preserves
2239
+ * the anchor's y and only flips horizontally before bounds clamping.
2235
2240
  *
2236
2241
  * The flip measures against the visible part of the wrapper, its intersection
2237
2242
  * with `bounds`. Only the clamp is bound by the page: a box that fits inside the
@@ -2245,7 +2250,7 @@ const findClippingAncestor = (wrapper) => {
2245
2250
  * @param params.offsetTop - Gap between the anchor and the box, vertically.
2246
2251
  * @param params.box - Rendered size of the box.
2247
2252
  * @param params.wrapper - The wrapper's own edges, in wrapper coordinates.
2248
- * @param params.placement - Fixed below-axis placement or automatic flipping.
2253
+ * @param params.placement - Below-axis, beside without vertical flipping, or automatic flipping.
2249
2254
  * @param params.bounds - Edges the box must keep inside, in wrapper coordinates.
2250
2255
  * @return The box's top-left corner; fixed placement preserves the axis's subpixel y.
2251
2256
  */
@@ -2269,7 +2274,8 @@ const getBoundedPosition = ({ left, top, offsetLeft, offsetTop, box, wrapper, bo
2269
2274
  const upY = top - offsetTop - box.height;
2270
2275
  const downOverflow = downY + box.height - fit.bottom;
2271
2276
  const upOverflow = fit.top - upY;
2272
- let y = downOverflow > 0 && downOverflow > upOverflow ? upY : downY;
2277
+ let y = top;
2278
+ if (placement !== "beside") y = downOverflow > 0 && downOverflow > upOverflow ? upY : downY;
2273
2279
  x = clamp(x, bounds.left, bounds.right, box.width);
2274
2280
  y = clamp(y, bounds.top, bounds.bottom, box.height);
2275
2281
  return {
@@ -2285,8 +2291,7 @@ const getBoundedPosition = ({ left, top, offsetLeft, offsetTop, box, wrapper, bo
2285
2291
  * alone would let the box spill into an `overflow: hidden` card and be cut off.
2286
2292
  *
2287
2293
  * Re-measures on every render, so a box whose content changes width between
2288
- * two hovers is placed for its current size. See `getBoundedPosition` for the
2289
- * below-axis exception.
2294
+ * two hovers is placed for its current size.
2290
2295
  *
2291
2296
  * @param props - visx `Tooltip` props.
2292
2297
  * @param props.left - Anchor x, in wrapper coordinates.
@@ -2296,7 +2301,7 @@ const getBoundedPosition = ({ left, top, offsetLeft, offsetTop, box, wrapper, bo
2296
2301
  * @param props.style - Box styles; visx's defaults unless `unstyled`.
2297
2302
  * @param props.unstyled - Skip `style` and leave the box bare.
2298
2303
  * @param props.children - Box content.
2299
- * @param props.placement - Fixed below-axis placement or automatic flipping.
2304
+ * @param props.placement - Below-axis, beside without vertical flipping, or automatic flipping.
2300
2305
  * @return The tooltip box.
2301
2306
  */
2302
2307
  const BoundedTooltip = ({ left = 0, top = 0, offsetLeft = DEFAULT_OFFSET, offsetTop = DEFAULT_OFFSET, style = defaultStyles, unstyled = false, children, placement = "auto", ...rest }) => {
@@ -2399,7 +2404,6 @@ const crosshairPaintProps = (props = {}) => {
2399
2404
  };
2400
2405
  const DEFAULT_GLYPH_RADIUS = 4;
2401
2406
  const FALLBACK_COLOR = "#222";
2402
- const DEFAULT_TOOLTIP_Z_INDEX = 3;
2403
2407
  const PORTAL_OPTIONS = /* @__PURE__ */ new Set([
2404
2408
  "scroll",
2405
2409
  "debounce",
@@ -2427,7 +2431,7 @@ const defaultRenderGlyph$1 = ({ key, ...props }) => /* @__PURE__ */ jsx(DefaultG
2427
2431
  seriesKey: key,
2428
2432
  ...props
2429
2433
  }, key);
2430
- const XyChartTooltipContent = ({ tooltipContext, container, renderTooltip, renderGlyph = defaultRenderGlyph$1, glyphStyle, snapTooltipToDatumX = false, snapTooltipToDatumY = false, showVerticalCrosshair = false, showHorizontalCrosshair = false, showDatumGlyph = false, showSeriesGlyphs = false, verticalCrosshairStyle, horizontalCrosshairStyle, detectBounds = true, tooltipPlacement = "auto", zIndex = DEFAULT_TOOLTIP_Z_INDEX, style, ...rest }) => {
2434
+ const XyChartTooltipContent = ({ tooltipContext, container, renderTooltip, renderGlyph = defaultRenderGlyph$1, glyphStyle, snapTooltipToDatumX = false, snapTooltipToDatumY = false, showVerticalCrosshair = false, showHorizontalCrosshair = false, showDatumGlyph = false, showSeriesGlyphs = false, verticalCrosshairStyle, horizontalCrosshairStyle, detectBounds = true, tooltipPlacement = "auto", tooltipAnchorTop, zIndex = 3, style, ...rest }) => {
2431
2435
  const tooltipProps = Object.fromEntries(Object.entries(rest).filter(([key]) => !PORTAL_OPTIONS.has(key)));
2432
2436
  const { colorScale, theme, innerHeight = 0, innerWidth = 0, margin, xScale, yScale, dataRegistry } = useContext(DataContext) || {};
2433
2437
  const tooltipContent = renderTooltip ? renderTooltip({
@@ -2482,12 +2486,12 @@ const XyChartTooltipContent = ({ tooltipContext, container, renderTooltip, rende
2482
2486
  const crosshairStroke = theme?.gridStyles?.stroke ?? theme?.htmlLabel?.color ?? FALLBACK_COLOR;
2483
2487
  const marginTop = margin?.top ?? 0;
2484
2488
  const marginLeft = margin?.left ?? 0;
2485
- const TooltipComponent = detectBounds || tooltipPlacement === "below-axis" ? BoundedTooltip : Tooltip;
2489
+ const TooltipComponent = detectBounds || tooltipPlacement !== "auto" ? BoundedTooltip : Tooltip;
2486
2490
  const boxStyle = {
2487
2491
  ...defaultStyles,
2488
2492
  zIndex,
2489
2493
  backgroundColor: theme?.backgroundColor ?? "white",
2490
- boxShadow: `0 1px 2px ${theme?.htmlLabel?.color ? `${theme.htmlLabel.color}55` : "#22222255"}`,
2494
+ boxShadow: `0 1px 2px ${isValidHexColor(theme?.htmlLabel?.color) ? `${theme.htmlLabel.color}55` : "#22222255"}`,
2491
2495
  ...theme?.htmlLabel,
2492
2496
  ...style
2493
2497
  };
@@ -2519,11 +2523,11 @@ const XyChartTooltipContent = ({ tooltipContext, container, renderTooltip, rende
2519
2523
  ]
2520
2524
  }), container && createPortal(/* @__PURE__ */ jsx(TooltipComponent, {
2521
2525
  left: tooltipLeft,
2522
- top: tooltipPlacement === "below-axis" ? marginTop + innerHeight + (margin?.bottom ?? 0) : tooltipTop,
2526
+ top: tooltipPlacement === "below-axis" ? marginTop + innerHeight + (margin?.bottom ?? 0) : tooltipAnchorTop ?? tooltipTop,
2523
2527
  style: boxStyle,
2524
2528
  applyPositionStyle: true,
2525
2529
  ...tooltipProps,
2526
- ...tooltipPlacement === "below-axis" && { placement: tooltipPlacement },
2530
+ ...tooltipPlacement !== "auto" && { placement: tooltipPlacement },
2527
2531
  children: tooltipContent
2528
2532
  }), container)] });
2529
2533
  };
@@ -2589,11 +2593,32 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
2589
2593
  return flattened;
2590
2594
  }, [series, mode]);
2591
2595
  const hasKeyboardSelection = useRef(false);
2596
+ const closeTooltipNow = useCallback(() => {
2597
+ tooltipContext?.updateTooltip({
2598
+ tooltipOpen: false,
2599
+ tooltipLeft: void 0,
2600
+ tooltipTop: void 0,
2601
+ tooltipData: void 0
2602
+ });
2603
+ }, []);
2604
+ const chartId = useContext(ChartInstanceContext)?.chartId;
2605
+ const { getHiddenSeries } = useGlobalChartsContext();
2606
+ const hiddenSeriesKey = chartId ? JSON.stringify([...getHiddenSeries(chartId)].sort()) : void 0;
2607
+ const lastHiddenSeriesKey = useRef(hiddenSeriesKey);
2608
+ useEffect(() => {
2609
+ const changed = lastHiddenSeriesKey.current !== hiddenSeriesKey;
2610
+ lastHiddenSeriesKey.current = hiddenSeriesKey;
2611
+ if (changed && selectedIndex === void 0) closeTooltipNow();
2612
+ }, [
2613
+ hiddenSeriesKey,
2614
+ selectedIndex,
2615
+ closeTooltipNow
2616
+ ]);
2592
2617
  useEffect(() => {
2593
2618
  if (selectedIndex === void 0) {
2594
2619
  if (hasKeyboardSelection.current) {
2595
2620
  hasKeyboardSelection.current = false;
2596
- tooltipContext?.hideTooltip();
2621
+ closeTooltipNow();
2597
2622
  }
2598
2623
  return;
2599
2624
  }
@@ -2662,15 +2687,45 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
2662
2687
  renderTooltip: focusableRenderTooltip
2663
2688
  });
2664
2689
  };
2665
- const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints, onActivate, preventTooltipScroll = false }) => {
2690
+ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints, onActivate, preventTooltipScroll = false, visibleSeriesKey }) => {
2691
+ const getChartRoot = useCallback(() => chartRef.current?.closest("[role=\"grid\"]") ?? chartRef.current, [chartRef]);
2692
+ const focusWithoutScrollIfNeeded = useCallback((element) => {
2693
+ if (preventTooltipScroll) element?.focus({ preventScroll: true });
2694
+ else element?.focus();
2695
+ }, [preventTooltipScroll]);
2696
+ const tooltipRef = useCallback((element) => {
2697
+ if (element && selectedIndex !== void 0) focusWithoutScrollIfNeeded(element);
2698
+ }, [focusWithoutScrollIfNeeded, selectedIndex]);
2699
+ const previousTotalPoints = useRef(totalPoints);
2700
+ const previousVisibleSeriesKey = useRef(visibleSeriesKey);
2701
+ useEffect(() => {
2702
+ const countChanged = previousTotalPoints.current !== totalPoints;
2703
+ const seriesChanged = previousVisibleSeriesKey.current !== visibleSeriesKey;
2704
+ previousTotalPoints.current = totalPoints;
2705
+ previousVisibleSeriesKey.current = visibleSeriesKey;
2706
+ if (!countChanged && !seriesChanged || selectedIndex === void 0) return;
2707
+ const { activeElement } = document;
2708
+ const focusIsInChart = activeElement !== null && !!getChartRoot()?.contains(activeElement);
2709
+ if (totalPoints === 0 || !focusIsInChart || !countChanged) {
2710
+ setSelectedIndex(void 0);
2711
+ setIsNavigating(false);
2712
+ if (focusIsInChart) focusWithoutScrollIfNeeded(getChartRoot());
2713
+ return;
2714
+ }
2715
+ setSelectedIndex(Math.min(selectedIndex, totalPoints - 1));
2716
+ }, [
2717
+ selectedIndex,
2718
+ totalPoints,
2719
+ visibleSeriesKey,
2720
+ setSelectedIndex,
2721
+ setIsNavigating,
2722
+ getChartRoot,
2723
+ focusWithoutScrollIfNeeded
2724
+ ]);
2666
2725
  return {
2667
- tooltipRef: useCallback((element) => {
2668
- if (element && selectedIndex !== void 0) {
2669
- if (preventTooltipScroll) element.focus({ preventScroll: true });
2670
- else element.focus();
2671
- }
2672
- }, [preventTooltipScroll, selectedIndex]),
2673
- onChartFocus: useCallback(() => {
2726
+ tooltipRef,
2727
+ onChartFocus: useCallback((event) => {
2728
+ if (event.currentTarget.contains(event.relatedTarget)) return;
2674
2729
  if (!isNavigating && selectedIndex !== void 0) setSelectedIndex(0);
2675
2730
  }, [
2676
2731
  isNavigating,
@@ -2681,38 +2736,34 @@ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating,
2681
2736
  setIsNavigating(false);
2682
2737
  }, [setIsNavigating]),
2683
2738
  onChartKeyDown: useCallback((event) => {
2684
- if (totalPoints === 0) return;
2685
- if (event.key === "Tab") {
2686
- chartRef.current?.focus();
2739
+ if (event.key === "Tab" || event.key === "Escape") {
2740
+ if (event.key === "Escape" && selectedIndex !== void 0) event.preventDefault();
2741
+ focusWithoutScrollIfNeeded(getChartRoot());
2687
2742
  setSelectedIndex(void 0);
2688
2743
  setIsNavigating(false);
2689
2744
  return;
2690
2745
  }
2746
+ if (totalPoints === 0) return;
2691
2747
  const currentSelectedIndex = selectedIndex === void 0 ? -1 : selectedIndex;
2692
- if (currentSelectedIndex + 1 >= totalPoints && ["ArrowRight"].includes(event.key)) {
2693
- chartRef.current?.focus();
2694
- setSelectedIndex(void 0);
2695
- setIsNavigating(false);
2696
- return;
2697
- }
2698
- event.preventDefault();
2699
- if (["ArrowRight"].includes(event.key)) {
2748
+ if (event.key === "ArrowRight") {
2749
+ event.preventDefault();
2700
2750
  setIsNavigating(true);
2701
- setSelectedIndex((currentSelectedIndex + 1) % totalPoints);
2702
- } else if (["ArrowLeft"].includes(event.key)) {
2751
+ setSelectedIndex(Math.min(currentSelectedIndex + 1, totalPoints - 1));
2752
+ } else if (event.key === "ArrowLeft") {
2753
+ event.preventDefault();
2703
2754
  setIsNavigating(true);
2704
- setSelectedIndex((currentSelectedIndex - 1 + totalPoints) % totalPoints);
2705
- } else if (event.key === "Escape") {
2706
- setSelectedIndex(void 0);
2707
- setIsNavigating(false);
2708
- chartRef.current?.focus();
2709
- } else if ((event.key === "Enter" || event.key === " ") && selectedIndex !== void 0) onActivate?.(selectedIndex);
2755
+ setSelectedIndex(Math.max(currentSelectedIndex - 1, 0));
2756
+ } else if ((event.key === "Enter" || event.key === " ") && selectedIndex !== void 0) {
2757
+ event.preventDefault();
2758
+ onActivate?.(selectedIndex);
2759
+ }
2710
2760
  }, [
2711
2761
  totalPoints,
2712
2762
  selectedIndex,
2713
2763
  setSelectedIndex,
2714
2764
  setIsNavigating,
2715
- chartRef,
2765
+ getChartRoot,
2766
+ focusWithoutScrollIfNeeded,
2716
2767
  onActivate
2717
2768
  ])
2718
2769
  };
@@ -4316,6 +4367,7 @@ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4316
4367
  children: ({ contentHeight }) => {
4317
4368
  const chartHeight = contentHeight > 0 ? contentHeight : height;
4318
4369
  return /* @__PURE__ */ jsx("div", {
4370
+ ref: chartRef,
4319
4371
  role: "grid",
4320
4372
  "aria-label": __("Line chart", "jetpack-charts"),
4321
4373
  tabIndex: 0,
@@ -4323,7 +4375,6 @@ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4323
4375
  onFocus: onChartFocus,
4324
4376
  onBlur: onChartBlur,
4325
4377
  children: chartHeight > 0 && /* @__PURE__ */ jsxs("div", {
4326
- ref: chartRef,
4327
4378
  className: xy_plot_module_default["xy-plot"],
4328
4379
  children: [zoomable && zoom.domain && /* @__PURE__ */ jsx(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ jsxs(XYChart, {
4329
4380
  theme,
@@ -4821,6 +4872,7 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4821
4872
  children: ({ contentHeight }) => {
4822
4873
  const chartHeight = contentHeight > 0 ? contentHeight : height;
4823
4874
  return /* @__PURE__ */ jsx("div", {
4875
+ ref: chartRef,
4824
4876
  role: "grid",
4825
4877
  "aria-label": __("Area chart", "jetpack-charts"),
4826
4878
  tabIndex: 0,
@@ -4828,7 +4880,6 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4828
4880
  onFocus: onChartFocus,
4829
4881
  onBlur: onChartBlur,
4830
4882
  children: chartHeight > 0 && /* @__PURE__ */ jsxs("div", {
4831
- ref: chartRef,
4832
4883
  className: xy_plot_module_default["xy-plot"],
4833
4884
  children: [zoomable && zoom.domain && /* @__PURE__ */ jsx(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ jsxs(XYChart, {
4834
4885
  theme,
@@ -5270,6 +5321,20 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
5270
5321
  ]);
5271
5322
  }
5272
5323
  //#endregion
5324
+ //#region src/charts/bar-chart/private/band-scale.ts
5325
+ /**
5326
+ * Match the inner slot scale used by visx BarGroup.
5327
+ * @param keys - Visible primary series keys.
5328
+ * @param bandwidth - Category band width.
5329
+ * @param padding - Padding between slots.
5330
+ * @return The grouped bar scale.
5331
+ */
5332
+ const createGroupScale = (keys, bandwidth, padding) => scaleBand({
5333
+ domain: keys,
5334
+ range: [0, bandwidth],
5335
+ padding
5336
+ });
5337
+ //#endregion
5273
5338
  //#region src/charts/bar-chart/private/comparison-bars-geometry.ts
5274
5339
  /**
5275
5340
  * Output position of a value scale's baseline: zero if in-domain, else the
@@ -5342,11 +5407,7 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
5342
5407
  const valueScale = horizontal ? xScale : yScale;
5343
5408
  const bandwidth = bandScale.bandwidth ? bandScale.bandwidth() : 0;
5344
5409
  if (!bandwidth) return null;
5345
- const groupScale = scaleBand({
5346
- domain: primaryKeys,
5347
- range: [0, bandwidth],
5348
- padding: groupPadding
5349
- });
5410
+ const groupScale = createGroupScale(primaryKeys, bandwidth, groupPadding);
5350
5411
  const slotThickness = groupScale.bandwidth();
5351
5412
  const baseline = getValueScaleBaseline(valueScale);
5352
5413
  const bandAccessor = horizontal ? yAccessor : xAccessor;
@@ -5398,6 +5459,163 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
5398
5459
  });
5399
5460
  };
5400
5461
  //#endregion
5462
+ //#region src/charts/bar-chart/private/band-highlight.tsx
5463
+ /**
5464
+ * Render and report the active band using the chart’s registered scales.
5465
+ * @param root0 - Highlight options.
5466
+ * @param root0.visible - Whether to paint the band.
5467
+ * @param root0.horizontal - Whether categories run vertically.
5468
+ * @param root0.onChange - Receives selected band geometry.
5469
+ * @return The optional band highlight.
5470
+ */
5471
+ function BandHighlight({ visible, horizontal, onChange }) {
5472
+ const { xScale, yScale, dataRegistry, margin, innerWidth, innerHeight } = useContext(DataContext);
5473
+ const { tooltipOpen, tooltipData } = useContext(TooltipContext);
5474
+ const nearest = tooltipData?.nearestDatum;
5475
+ const selection = useMemo(() => {
5476
+ if (!tooltipOpen || !nearest) return null;
5477
+ const scale = horizontal ? yScale : xScale;
5478
+ const entry = dataRegistry?.get(nearest.key);
5479
+ const accessor = horizontal ? entry?.yAccessor : entry?.xAccessor;
5480
+ if (!scale?.bandwidth || !accessor) return null;
5481
+ const position = scale(accessor(nearest.datum));
5482
+ if (position === void 0 || !Number.isFinite(position)) return null;
5483
+ return {
5484
+ datum: nearest.datum,
5485
+ x: horizontal ? margin?.left ?? 0 : position,
5486
+ y: horizontal ? position : margin?.top ?? 0,
5487
+ width: horizontal ? innerWidth : scale.bandwidth(),
5488
+ height: horizontal ? scale.bandwidth() : innerHeight
5489
+ };
5490
+ }, [
5491
+ tooltipOpen,
5492
+ nearest,
5493
+ horizontal,
5494
+ xScale,
5495
+ yScale,
5496
+ dataRegistry,
5497
+ margin,
5498
+ innerWidth,
5499
+ innerHeight
5500
+ ]);
5501
+ const stableSelection = useDeepMemo(selection);
5502
+ const onChangeRef = useRef(onChange);
5503
+ onChangeRef.current = onChange;
5504
+ useEffect(() => {
5505
+ onChangeRef.current?.(stableSelection);
5506
+ }, [stableSelection]);
5507
+ useEffect(() => () => onChangeRef.current?.(null), []);
5508
+ if (!visible || !selection) return null;
5509
+ const { x, y, width, height } = selection;
5510
+ return /* @__PURE__ */ jsx("rect", {
5511
+ x,
5512
+ y,
5513
+ width,
5514
+ height,
5515
+ pointerEvents: "none",
5516
+ fill: CATALOG_POINTERS.surfaceSecondary
5517
+ });
5518
+ }
5519
+ //#endregion
5520
+ //#region src/charts/bar-chart/private/band-tooltip.tsx
5521
+ /**
5522
+ * Find a category by its painted center, including outer padding and reversed ranges.
5523
+ * @param data - Registered series points.
5524
+ * @param accessor - Category accessor.
5525
+ * @param scale - Rendered category band scale.
5526
+ * @param position - Pointer coordinate on the category axis.
5527
+ * @return Index of the nearest category, or -1 when none is registered.
5528
+ */
5529
+ function nearestBandIndex(data, accessor, scale, position) {
5530
+ let nearest = -1;
5531
+ let distance = Infinity;
5532
+ data.forEach((datum, index) => {
5533
+ const start = scale(accessor(datum));
5534
+ if (start === void 0) return;
5535
+ const candidate = Math.abs(start + scale.bandwidth() / 2 - position);
5536
+ if (candidate < distance) {
5537
+ distance = candidate;
5538
+ nearest = index;
5539
+ }
5540
+ });
5541
+ return nearest;
5542
+ }
5543
+ /**
5544
+ * Correct visx's range-based band inversion, which omits outer padding.
5545
+ * @param root0 - Registered series selection.
5546
+ * @param root0.keys - Visible series keys.
5547
+ * @param root0.groupPadding - Padding between grouped bars.
5548
+ * @param root0.withTooltips - Whether pointer events update the tooltip.
5549
+ * @param root0.onPointerDown - Receives the corrected pointer-down datum.
5550
+ * @param root0.onPointerUp - Receives the corrected pointer-up datum.
5551
+ * @return No visual content.
5552
+ */
5553
+ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointerUp }) {
5554
+ const { xScale, yScale, dataRegistry, horizontal } = useContext(DataContext);
5555
+ const { showTooltip } = useContext(TooltipContext);
5556
+ const scale = horizontal ? yScale : xScale;
5557
+ const bandwidth = scale?.bandwidth?.() ?? 0;
5558
+ const groupScale = useMemo(() => createGroupScale(keys, bandwidth, groupPadding), [
5559
+ keys,
5560
+ bandwidth,
5561
+ groupPadding
5562
+ ]);
5563
+ const getSelections = useCallback((params) => {
5564
+ const point = params?.svgPoint;
5565
+ if (!point || !scale?.bandwidth) return [];
5566
+ const selections = [];
5567
+ for (const key of keys) {
5568
+ const entry = dataRegistry?.get(key);
5569
+ if (!entry) continue;
5570
+ const accessor = horizontal ? entry.yAccessor : entry.xAccessor;
5571
+ const index = nearestBandIndex(entry.data, accessor, scale, horizontal ? point.y : point.x);
5572
+ if (index < 0) continue;
5573
+ const datum = entry.data[index];
5574
+ const start = Number(scale(accessor(datum))) + Number(groupScale(key));
5575
+ const end = start + groupScale.step();
5576
+ const position = horizontal ? point.y : point.x;
5577
+ const distance = position >= start && position <= end ? 0 : Math.abs(position - (start + end) / 2);
5578
+ selections.push({
5579
+ event: params.event,
5580
+ key,
5581
+ datum,
5582
+ index,
5583
+ svgPoint: point,
5584
+ distanceX: horizontal ? 0 : distance,
5585
+ distanceY: horizontal ? distance : 0
5586
+ });
5587
+ }
5588
+ return selections;
5589
+ }, [
5590
+ scale,
5591
+ dataRegistry,
5592
+ horizontal,
5593
+ keys,
5594
+ groupScale
5595
+ ]);
5596
+ const handlePointer = useCallback((params) => {
5597
+ const selections = getSelections(params);
5598
+ if (withTooltips && params?.event.type !== "pointerup") selections.forEach(showTooltip);
5599
+ const callback = params?.event.type === "pointerdown" ? onPointerDown : onPointerUp;
5600
+ if (params?.event.type === "pointermove" || !callback) return;
5601
+ const nearest = selections.reduce((best, selection) => {
5602
+ const distance = (value) => Math.hypot(value.distanceX, value.distanceY);
5603
+ return !best || distance(selection) <= distance(best) ? selection : best;
5604
+ }, void 0);
5605
+ if (nearest) callback(nearest);
5606
+ }, [
5607
+ getSelections,
5608
+ withTooltips,
5609
+ showTooltip,
5610
+ onPointerDown,
5611
+ onPointerUp
5612
+ ]);
5613
+ useEventEmitter("pointermove", withTooltips ? handlePointer : void 0);
5614
+ useEventEmitter("pointerdown", handlePointer);
5615
+ useEventEmitter("pointerup", onPointerUp ? handlePointer : void 0);
5616
+ return null;
5617
+ }
5618
+ //#endregion
5401
5619
  //#region src/charts/bar-chart/bar-chart.tsx
5402
5620
  const validateData$2 = (data) => {
5403
5621
  if (!data?.length) return "No data available";
@@ -5409,7 +5627,7 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ jsx("div", {
5409
5627
  className: bar_chart_module_default["bar-chart__tooltip-row"],
5410
5628
  children: sprintf(__("%1$s: %2$s", "jetpack-charts"), label, value)
5411
5629
  });
5412
- 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", onPointerDown, onPointerUp, onDatumActivate }) => {
5630
+ const BarChartInternal = ({ data, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, tooltipPlacement, tooltipAnchorTop, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, withBandHighlight = false, onBandHighlightChange, defaultHiddenSeries, animation, children, gap = "md", onPointerDown, onPointerUp, onDatumActivate }) => {
5413
5631
  const legendInteractive = legend.interactive ?? false;
5414
5632
  const legendCollapseGroups = legend.collapseGroups ?? false;
5415
5633
  const horizontal = orientation === "horizontal";
@@ -5435,8 +5653,6 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5435
5653
  }, [height]);
5436
5654
  const [selectedIndex, setSelectedIndex] = useState(void 0);
5437
5655
  const [isNavigating, setIsNavigating] = useState(false);
5438
- const primarySeriesForNav = dataWithVisibleZeros.filter((s) => s.options?.type !== "comparison");
5439
- const totalPoints = Math.max(0, ...primarySeriesForNav.map((s) => s.data?.length || 0)) * primarySeriesForNav.length;
5440
5656
  const seriesWithVisibility = useMemo(() => dataWithVisibleZeros.map((series, index) => ({
5441
5657
  series,
5442
5658
  index,
@@ -5446,6 +5662,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5446
5662
  return seriesWithVisibility.every(({ isVisible }) => !isVisible);
5447
5663
  }, [seriesWithVisibility]);
5448
5664
  const primaryEntries = useMemo(() => seriesWithVisibility.filter(({ isVisible, series }) => isVisible && series.options?.type !== "comparison"), [seriesWithVisibility]);
5665
+ const totalPoints = Math.max(0, ...primaryEntries.map((e) => e.series.data.length)) * primaryEntries.length;
5449
5666
  const primaryKeys = useMemo(() => primaryEntries.map(({ series }) => series.label), [primaryEntries]);
5450
5667
  const primarySeries = useMemo(() => primaryEntries.map(({ series }) => series), [primaryEntries]);
5451
5668
  const activateSelectedBar = useCallback((index) => {
@@ -5460,6 +5677,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5460
5677
  key: series.label
5461
5678
  });
5462
5679
  }, [primaryEntries, onDatumActivate]);
5680
+ const visibleSeriesKey = useMemo(() => JSON.stringify(primaryKeys), [primaryKeys]);
5463
5681
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
5464
5682
  selectedIndex,
5465
5683
  setSelectedIndex,
@@ -5467,7 +5685,8 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5467
5685
  setIsNavigating,
5468
5686
  chartRef,
5469
5687
  totalPoints,
5470
- onActivate: activateSelectedBar
5688
+ onActivate: activateSelectedBar,
5689
+ visibleSeriesKey
5471
5690
  });
5472
5691
  const comparisonEntries = useMemo(() => {
5473
5692
  const primaryByGroup = new Map(primaryEntries.map(({ series, index }) => [series.group, {
@@ -5530,7 +5749,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5530
5749
  chartOptions.yScale,
5531
5750
  horizontal
5532
5751
  ]);
5533
- const getBarBackground = useCallback((index) => () => withPatterns ? `url(#${getPatternId(chartId, index)})` : getElementStyles({
5752
+ const getBarBackground = useCallback((index) => (datum) => withPatterns ? `url(#${getPatternId(chartId, index)})` : datum.color ?? getElementStyles({
5534
5753
  data: dataSorted[index],
5535
5754
  index
5536
5755
  }).color, [
@@ -5700,13 +5919,13 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5700
5919
  const chartHeight = contentHeight > 0 ? contentHeight : height;
5701
5920
  return /* @__PURE__ */ jsx("div", {
5702
5921
  role: "grid",
5922
+ ref: chartRef,
5703
5923
  "aria-label": __("Bar chart", "jetpack-charts"),
5704
5924
  tabIndex: 0,
5705
5925
  onKeyDown: onChartKeyDown,
5706
5926
  onFocus: onChartFocus,
5707
5927
  onBlur: onChartBlur,
5708
5928
  children: chartHeight > 0 && /* @__PURE__ */ jsx("div", {
5709
- ref: chartRef,
5710
5929
  className: xy_plot_module_default["xy-plot"],
5711
5930
  children: /* @__PURE__ */ jsxs(XYChart, {
5712
5931
  theme,
@@ -5719,15 +5938,24 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5719
5938
  xScale,
5720
5939
  yScale,
5721
5940
  horizontal,
5722
- onPointerDown,
5723
- onPointerUp,
5724
5941
  pointerEventsDataKey: "nearest",
5725
5942
  children: [
5726
- !allSeriesHidden && /* @__PURE__ */ jsx(Grid, {
5943
+ withTooltips && (withBandHighlight || onBandHighlightChange) && /* @__PURE__ */ jsx(BandHighlight, {
5944
+ visible: withBandHighlight,
5945
+ horizontal,
5946
+ onChange: onBandHighlightChange
5947
+ }),
5948
+ !allSeriesHidden && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Grid, {
5727
5949
  columns: gridVisibility.includes("y"),
5950
+ rows: false,
5951
+ numTicks: 4,
5952
+ tickValues: chartOptions.axis.x.tickValues
5953
+ }), /* @__PURE__ */ jsx(Grid, {
5954
+ columns: false,
5728
5955
  rows: gridVisibility.includes("x"),
5729
- numTicks: 4
5730
- }),
5956
+ numTicks: 4,
5957
+ tickValues: chartOptions.axis.y.tickValues
5958
+ })] }),
5731
5959
  withPatterns && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("defs", { children: dataSorted.map((seriesData, index) => renderPattern(index, getElementStyles({
5732
5960
  data: seriesData,
5733
5961
  index
@@ -5763,8 +5991,17 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5763
5991
  colorAccessor: getBarBackground(index)
5764
5992
  }, seriesData?.label))
5765
5993
  }),
5994
+ (withTooltips || onPointerDown || onPointerUp) && /* @__PURE__ */ jsx(BandTooltip, {
5995
+ keys: primaryKeys,
5996
+ groupPadding,
5997
+ withTooltips,
5998
+ onPointerDown,
5999
+ onPointerUp
6000
+ }),
5766
6001
  !allSeriesHidden && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Axis, { ...chartOptions.axis.x }), /* @__PURE__ */ jsx(Axis, { ...chartOptions.axis.y })] }),
5767
6002
  withTooltips && /* @__PURE__ */ jsx(AccessibleTooltip, {
6003
+ tooltipPlacement,
6004
+ tooltipAnchorTop,
5768
6005
  detectBounds: true,
5769
6006
  snapTooltipToDatumX: true,
5770
6007
  snapTooltipToDatumY: true,
@@ -6586,8 +6823,11 @@ var heatmap_chart_module_default = {
6586
6823
  "heatmap-chart__gap-start": "a8ccharts-O3YMOW-heatmap-chart__gap-start",
6587
6824
  "heatmap-chart__grid": "a8ccharts-O3YMOW-heatmap-chart__grid",
6588
6825
  "heatmap-chart__grid--compact": "a8ccharts-O3YMOW-heatmap-chart__grid--compact",
6826
+ "heatmap-chart__grid--flex-gaps": "a8ccharts-O3YMOW-heatmap-chart__grid--flex-gaps",
6589
6827
  "heatmap-chart__grid--height-capped": "a8ccharts-O3YMOW-heatmap-chart__grid--height-capped",
6828
+ "heatmap-chart__group-label": "a8ccharts-O3YMOW-heatmap-chart__group-label",
6590
6829
  "heatmap-chart__legend-label": "a8ccharts-O3YMOW-heatmap-chart__legend-label",
6830
+ "heatmap-chart__legend-scale--bar": "a8ccharts-O3YMOW-heatmap-chart__legend-scale--bar",
6591
6831
  "heatmap-chart__legend-swatch": "a8ccharts-O3YMOW-heatmap-chart__legend-swatch",
6592
6832
  "heatmap-chart__row": "a8ccharts-O3YMOW-heatmap-chart__row",
6593
6833
  "heatmap-chart__row-label": "a8ccharts-O3YMOW-heatmap-chart__row-label",
@@ -6632,290 +6872,267 @@ const getNormalizedValue = (value, extent) => {
6632
6872
  return Math.min(1, Math.max(0, (value - min) / (max - min)));
6633
6873
  };
6634
6874
  //#endregion
6635
- //#region src/charts/heatmap-chart/private/civil-day.ts
6636
- const DAY_MS = 864e5;
6637
- const KEY_OPTIONS = {
6638
- calendar: "gregory",
6639
- numberingSystem: "latn",
6640
- year: "numeric",
6641
- month: "2-digit",
6642
- day: "2-digit"
6643
- };
6644
- const DATE_PREFIX = /^(\d{4})-(\d{2})-(\d{2})/;
6645
- const pad = (value, length) => String(value).padStart(length, "0");
6646
- /**
6647
- * The day a UTC proxy stands for.
6648
- *
6649
- * @param date - UTC proxy.
6650
- * @return Its day key.
6651
- */
6652
- const civilKey = (date) => `${pad(date.getUTCFullYear(), 4)}-${pad(date.getUTCMonth() + 1, 2)}-${pad(date.getUTCDate(), 2)}`;
6653
- /**
6654
- * A day key as a UTC-midnight proxy. UTC has no DST, so every step below is exactly
6655
- * `DAY_MS`.
6656
- *
6657
- * @param key - Day key.
6658
- * @return The proxy, or null when the key names no real day.
6659
- */
6660
- const civilDate = (key) => {
6661
- const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(key);
6662
- if (!match) return null;
6663
- const date = /* @__PURE__ */ new Date(0);
6664
- date.setUTCFullYear(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
6665
- date.setUTCHours(0, 0, 0, 0);
6666
- return civilKey(date) === key ? date : null;
6875
+ //#region src/charts/heatmap-chart/private/heatmap-context.ts
6876
+ /** Shared by the chart and legend without importing back from `heatmap-chart.tsx`. */
6877
+ const HeatmapContext = createContext(null);
6878
+ //#endregion
6879
+ //#region src/charts/heatmap-chart/private/heatmap-legend.tsx
6880
+ const HeatmapLegend = ({ steps = 5, variant = "swatches", lessLabel, moreLabel }) => {
6881
+ const context = useContext(HeatmapContext);
6882
+ const { legend } = useGlobalChartsTheme();
6883
+ if (!context) return null;
6884
+ const { primaryColorHex } = context;
6885
+ const labelStyle = legend.labelStyles;
6886
+ return /* @__PURE__ */ jsxs(Stack, {
6887
+ direction: "row",
6888
+ gap: "xs",
6889
+ align: "center",
6890
+ children: [
6891
+ /* @__PURE__ */ jsx(Text$1, {
6892
+ variant: "body-sm",
6893
+ className: heatmap_chart_module_default["heatmap-chart__legend-label"],
6894
+ style: labelStyle,
6895
+ children: lessLabel ?? __("Less", "jetpack-charts")
6896
+ }),
6897
+ /* @__PURE__ */ jsx(Stack, {
6898
+ direction: "row",
6899
+ gap: variant === "bar" ? void 0 : "xs",
6900
+ "aria-hidden": "true",
6901
+ className: variant === "bar" ? heatmap_chart_module_default["heatmap-chart__legend-scale--bar"] : void 0,
6902
+ children: Array.from({ length: steps }, (_, index) => {
6903
+ const intensity = steps <= 1 ? 1 : index / (steps - 1);
6904
+ return /* @__PURE__ */ jsx("span", {
6905
+ className: heatmap_chart_module_default["heatmap-chart__legend-swatch"],
6906
+ style: {
6907
+ "--a8c-charts-color-heatmap-primary": primaryColorHex,
6908
+ "--a8c-charts-heatmap-cell-intensity": intensity
6909
+ }
6910
+ }, index);
6911
+ })
6912
+ }),
6913
+ /* @__PURE__ */ jsx(Text$1, {
6914
+ variant: "body-sm",
6915
+ className: heatmap_chart_module_default["heatmap-chart__legend-label"],
6916
+ style: labelStyle,
6917
+ children: moreLabel ?? __("More", "jetpack-charts")
6918
+ })
6919
+ ]
6920
+ });
6667
6921
  };
6922
+ //#endregion
6923
+ //#region src/charts/heatmap-chart/private/column-groups.ts
6924
+ const isSpan = (span) => Number.isInteger(span) && span > 0;
6668
6925
  /**
6669
- * The day a written date names, ignoring any time and zone it carries.
6926
+ * Validates column groups and lays them over the columns.
6670
6927
  *
6671
- * @param text - `yyyy-MM-dd`, optionally followed by a time.
6672
- * @return Its day key, or null.
6928
+ * @param groups - Groups as passed to the chart.
6929
+ * @param columnCount - Number of data columns.
6930
+ * @param firstLine - Grid column line of the first data column.
6931
+ * @return Grid column lines for every column and group; no groups when the input was unusable.
6673
6932
  */
6674
- const writtenDayKey = (text) => {
6675
- const match = DATE_PREFIX.exec(text);
6676
- if (!match) return null;
6677
- const key = `${match[1]}-${match[2]}-${match[3]}`;
6678
- return civilDate(key) ? key : null;
6933
+ const resolveColumnGroups = (groups, columnCount, firstLine) => {
6934
+ const ungrouped = () => ({
6935
+ columns: Array.from({ length: columnCount }, (_, index) => ({
6936
+ line: firstLine + index,
6937
+ gapBefore: false
6938
+ })),
6939
+ groups: []
6940
+ });
6941
+ if (!groups || groups.length === 0 || columnCount === 0) return ungrouped();
6942
+ const spans = groups.map((group) => group.span);
6943
+ const total = spans.reduce((sum, span) => sum + span, 0);
6944
+ if (!spans.every(isSpan) || total > columnCount) {
6945
+ warnOnce(`heatmap:columnGroups:${JSON.stringify(spans)}:${columnCount}`, `columnGroups spans ${JSON.stringify(spans)} must be positive integers that fit the ${columnCount} columns, so no groups are drawn.`);
6946
+ return ungrouped();
6947
+ }
6948
+ const columns = [];
6949
+ const laidOut = [];
6950
+ let line = firstLine;
6951
+ groups.forEach((group, groupIndex) => {
6952
+ if (groupIndex > 0) line += 1;
6953
+ laidOut.push({
6954
+ ...group,
6955
+ line
6956
+ });
6957
+ for (let offset = 0; offset < group.span; offset++) {
6958
+ columns.push({
6959
+ line,
6960
+ gapBefore: groupIndex > 0 && offset === 0,
6961
+ group: groupIndex
6962
+ });
6963
+ line += 1;
6964
+ }
6965
+ });
6966
+ while (columns.length < columnCount) {
6967
+ columns.push({
6968
+ line,
6969
+ gapBefore: false
6970
+ });
6971
+ line += 1;
6972
+ }
6973
+ return {
6974
+ columns,
6975
+ groups: laidOut
6976
+ };
6679
6977
  };
6978
+ //#endregion
6979
+ //#region src/charts/heatmap-chart/private/keyboard-navigation.ts
6980
+ const ARROW_KEYS = [
6981
+ "ArrowLeft",
6982
+ "ArrowRight",
6983
+ "ArrowUp",
6984
+ "ArrowDown"
6985
+ ];
6986
+ const PAGE_KEYS = ["PageUp", "PageDown"];
6987
+ const isNavigationKey = (key, mode) => ARROW_KEYS.includes(key) || mode === "calendar" && PAGE_KEYS.includes(key);
6988
+ const inGrid = (grid, { column, row }) => column >= 0 && column < grid.columns && row >= 0 && row < grid.rows;
6989
+ const firstSelectable = (grid, cells) => cells.find((cell) => inGrid(grid, cell) && !grid.isInert(cell.column, cell.row));
6680
6990
  /**
6681
- * Reads the day an instant falls on in a zone.
6991
+ * Column-major scan for the first selectable cell.
6682
6992
  *
6683
- * @param timeZone - IANA zone name; absent leaves the runtime's own.
6684
- * @return Reader taking a `Date`, null for a day no key can hold.
6993
+ * @param grid - The grid.
6994
+ * @return The cell, or undefined when every slot is inert.
6685
6995
  */
6686
- const instantDayReader = (timeZone) => {
6687
- const formatter = new Intl.DateTimeFormat("en-US", {
6688
- ...KEY_OPTIONS,
6689
- timeZone
6690
- });
6691
- return (date) => {
6692
- const parts = formatter.formatToParts(date);
6693
- const read = (type) => parts.find((part) => part.type === type)?.value ?? "";
6694
- const key = `${pad(Number(read("year")), 4)}-${read("month")}-${read("day")}`;
6695
- return civilDate(key) ? key : null;
6996
+ const firstGridCell = (grid) => {
6997
+ for (let column = 0; column < grid.columns; column++) for (let row = 0; row < grid.rows; row++) if (!grid.isInert(column, row)) return {
6998
+ column,
6999
+ row
6696
7000
  };
6697
7001
  };
6698
7002
  /**
6699
- * The day a series point belongs to.
6700
- *
6701
- * A `Date`, or a string carrying `Z` or an offset, is an instant read in the host's
6702
- * zone. Anything else is a wall clock the host wrote, so its date part is the day.
7003
+ * Steps across the grid in the key's direction, past inert slots, staying put at the edge.
6703
7004
  *
6704
- * @param point - Series point.
6705
- * @param readInstant - Reader from `instantDayReader`.
6706
- * @return Its day key, or null when the point carries no usable date.
7005
+ * @param grid - The grid.
7006
+ * @param from - The selected cell.
7007
+ * @param key - The arrow key.
7008
+ * @return The next cell, or undefined to keep the selection.
6707
7009
  */
6708
- const pointDayKey = (point, readInstant) => {
6709
- if (point.date instanceof Date && !isNaN(point.date.getTime())) {
6710
- const key = readInstant(point.date);
6711
- if (key) return key;
7010
+ const stepGridCell = (grid, from, key) => {
7011
+ const step = {
7012
+ ArrowRight: [1, 0],
7013
+ ArrowLeft: [-1, 0],
7014
+ ArrowDown: [0, 1],
7015
+ ArrowUp: [0, -1]
7016
+ }[key];
7017
+ if (!step) return;
7018
+ const [stepColumn, stepRow] = step;
7019
+ let cell = from;
7020
+ do
7021
+ cell = {
7022
+ column: cell.column + stepColumn,
7023
+ row: cell.row + stepRow
7024
+ };
7025
+ while (inGrid(grid, cell) && grid.isInert(cell.column, cell.row));
7026
+ return inGrid(grid, cell) ? cell : void 0;
7027
+ };
7028
+ /**
7029
+ * Every slot of the blocks in reading order: block by block, each a row at a time.
7030
+ *
7031
+ * @param grid - The grid.
7032
+ * @param blocks - The blocks, left to right.
7033
+ * @return The slots in order.
7034
+ */
7035
+ const readingOrder = (grid, blocks) => blocks.flatMap((block) => Array.from({ length: grid.rows }, (_row, row) => Array.from({ length: block.end - block.start }, (_offset, offset) => ({
7036
+ column: block.start + offset,
7037
+ row
7038
+ }))).flat());
7039
+ const blockOf = (blocks, column) => blocks.findIndex((block) => column >= block.start && column < block.end);
7040
+ /**
7041
+ * The slots after a cell down its column (or before it, going up), continuing at the
7042
+ * same column offset of each following block.
7043
+ *
7044
+ * @param grid - The grid.
7045
+ * @param blocks - The blocks, left to right.
7046
+ * @param from - The selected cell.
7047
+ * @param direction - 1 downwards, -1 upwards.
7048
+ * @return The slots in order.
7049
+ */
7050
+ const columnOrderFrom = (grid, blocks, from, direction) => {
7051
+ const blockIndex = blockOf(blocks, from.column);
7052
+ if (blockIndex < 0) return [];
7053
+ const offset = from.column - blocks[blockIndex].start;
7054
+ const cells = [];
7055
+ const pushRows = (column, startRow) => {
7056
+ for (let row = startRow; row >= 0 && row < grid.rows; row += direction) cells.push({
7057
+ column,
7058
+ row
7059
+ });
7060
+ };
7061
+ pushRows(from.column, from.row + direction);
7062
+ for (let index = blockIndex + direction; index >= 0 && index < blocks.length; index += direction) {
7063
+ const column = blocks[index].start + offset;
7064
+ if (column < blocks[index].end) pushRows(column, direction === 1 ? 0 : grid.rows - 1);
6712
7065
  }
6713
- const text = point.dateString;
6714
- if (!text) return null;
6715
- if (!hasTimezone(text)) return writtenDayKey(text);
6716
- const parsed = parseISO(text);
6717
- return isNaN(parsed.getTime()) ? null : readInstant(parsed);
7066
+ return cells;
6718
7067
  };
6719
7068
  /**
6720
- * Steps a proxy by whole days.
7069
+ * The first selectable cell in calendar reading order.
6721
7070
  *
6722
- * @param date - UTC proxy.
6723
- * @param days - Days to add; may be negative.
6724
- * @return The shifted proxy.
7071
+ * @param grid - The grid.
7072
+ * @param blocks - The blocks, left to right.
7073
+ * @return The cell, or undefined when every slot is inert.
6725
7074
  */
6726
- const addCivilDays = (date, days) => new Date(date.getTime() + days * DAY_MS);
7075
+ const firstCalendarCell = (grid, blocks) => firstSelectable(grid, readingOrder(grid, blocks));
6727
7076
  /**
6728
- * Rounds a proxy down to its week.
7077
+ * Steps through the blocks as pages of a calendar: Left/Right by one slot in reading
7078
+ * order, Up/Down by one row (into the neighboring block past the edge), Page Up/Down
7079
+ * to the same slot of the neighboring block or its nearest selectable cell.
6729
7080
  *
6730
- * @param date - UTC proxy.
6731
- * @param weekStartsOn - 0 for Sunday, 1 for Monday.
6732
- * @return The proxy for that week's first day.
7081
+ * @param grid - The grid.
7082
+ * @param blocks - The blocks, left to right.
7083
+ * @param from - The selected cell.
7084
+ * @param key - The navigation key.
7085
+ * @return The next cell, or undefined to keep the selection.
6733
7086
  */
6734
- const startOfCivilWeek = (date, weekStartsOn) => {
6735
- const daysSinceWeekStart = (date.getUTCDay() - weekStartsOn + 7) % 7;
6736
- return addCivilDays(date, -daysSinceWeekStart);
6737
- };
6738
- /**
6739
- * Counts the columns a span needs.
6740
- *
6741
- * @param from - Earlier UTC proxy.
6742
- * @param to - Later UTC proxy.
6743
- * @param weekStartsOn - 0 for Sunday, 1 for Monday.
6744
- * @return Columns needed to cover the span, inclusive.
6745
- */
6746
- const civilWeekSpan = (from, to, weekStartsOn) => Math.floor((startOfCivilWeek(to, weekStartsOn).getTime() - startOfCivilWeek(from, weekStartsOn).getTime()) / (7 * DAY_MS)) + 1;
6747
- //#endregion
6748
- //#region src/charts/heatmap-chart/private/build-calendar-data.ts
6749
- /** Rows that get a weekday label (Mon, Wed, Fri with a Monday week start). */
6750
- const LABELLED_ROWS = [
6751
- 0,
6752
- 2,
6753
- 4
6754
- ];
6755
- /**
6756
- * Resolve one grid bound: an unparseable or narrowing value falls back to the
6757
- * series' own bound, so the grid can only ever be widened.
6758
- *
6759
- * @param bound - Requested bound as `yyyy-MM-dd`, if any.
6760
- * @param fallback - The series' own bound on this side.
6761
- * @param direction - Which way the bound is allowed to move.
6762
- * @return The day key to draw to.
6763
- */
6764
- const widenTo = (bound, fallback, direction) => {
6765
- const key = bound ? writtenDayKey(bound) : null;
6766
- if (!key) {
6767
- if (bound) warnOnce(`heatmap:gridSpan:${bound}`, `gridSpan.${direction === "earlier" ? "start" : "end"} ${JSON.stringify(bound)} is not a \`yyyy-MM-dd\` day, so the grid is drawn over the series' own span.`);
6768
- return fallback;
6769
- }
6770
- if (direction === "earlier") return key < fallback ? key : fallback;
6771
- return key > fallback ? key : fallback;
6772
- };
6773
- /**
6774
- * Lay a day-bucketed series out as calendar columns.
6775
- *
6776
- * @param series - Points to bucket.
6777
- * @param options - Grid shape, plus the locale and zone to read days in.
6778
- * @return Columns and row labels for `HeatmapChart`.
6779
- */
6780
- const buildCalendarHeatmapData = (series, options = {}) => {
6781
- const weekStartsOn = options.weekStartsOn ?? 1;
6782
- const hideOutOfRangeDays = options.hideOutOfRangeDays ?? true;
6783
- const { locale, timeZone } = sanitizeFormatting({
6784
- locale: options.locale,
6785
- timeZone: options.timeZone
6786
- });
6787
- const readInstant = instantDayReader(timeZone);
6788
- const valueByDay = /* @__PURE__ */ new Map();
6789
- let minDayKey;
6790
- let maxDayKey;
6791
- for (const point of series) {
6792
- const key = pointDayKey(point, readInstant);
6793
- if (!key) {
6794
- const offending = point.dateString ?? (point.date && String(point.date));
6795
- warnOnce(`heatmap:unreadableDate:${offending}`, offending ? `${JSON.stringify(offending)} is not a day this can read, so its point is left out of the calendar. A \`dateString\` must start \`yyyy-MM-dd\`.` : "A point carries neither `date` nor `dateString`, so it is left out of the calendar.");
6796
- continue;
6797
- }
6798
- valueByDay.set(key, point.value);
6799
- if (!minDayKey || key < minDayKey) minDayKey = key;
6800
- if (!maxDayKey || key > maxDayKey) maxDayKey = key;
6801
- }
6802
- if (!minDayKey || !maxDayKey) return {
6803
- data: [],
6804
- rowLabels: []
6805
- };
6806
- const requestedMinDayKey = widenTo(options.gridSpan?.start, minDayKey, "earlier");
6807
- const gridMaxDayKey = widenTo(options.gridSpan?.end, maxDayKey, "later");
6808
- const requestedMinDate = civilDate(requestedMinDayKey);
6809
- const gridMaxDate = civilDate(gridMaxDayKey);
6810
- const labelFormatting = {
6811
- locale,
6812
- timeZone: "UTC"
6813
- };
6814
- const formatWeekday = createDateFormatter({ weekday: "short" }, labelFormatting);
6815
- const formatMonth = createDateFormatter({ month: "short" }, labelFormatting);
6816
- const formatDay = createDateFormatter({
6817
- weekday: "short",
6818
- month: "short",
6819
- day: "numeric",
6820
- year: "numeric"
6821
- }, labelFormatting);
6822
- const gridStart = startOfCivilWeek(requestedMinDate, weekStartsOn);
6823
- const gridMinDayKey = requestedMinDayKey < minDayKey ? civilKey(gridStart) : requestedMinDayKey;
6824
- const weekCount = civilWeekSpan(gridStart, gridMaxDate, weekStartsOn);
6825
- const rowLabels = Array.from({ length: 7 }, (_, row) => LABELLED_ROWS.includes(row) ? formatWeekday(addCivilDays(gridStart, row)) : "");
6826
- const MIN_FIRST_MONTH_WEEKS = 2;
6827
- const firstMonth = gridStart.getUTCMonth();
6828
- let firstMonthWeeks = 0;
6829
- while (firstMonthWeeks < weekCount && addCivilDays(gridStart, firstMonthWeeks * 7).getUTCMonth() === firstMonth) firstMonthWeeks++;
6830
- const showFirstMonthLabel = !(firstMonthWeeks < weekCount) || firstMonthWeeks >= MIN_FIRST_MONTH_WEEKS;
6831
- const data = [];
6832
- let previousMonth = -1;
6833
- for (let week = 0; week < weekCount; week++) {
6834
- const columnStart = addCivilDays(gridStart, week * 7);
6835
- const month = columnStart.getUTCMonth();
6836
- const label = month !== previousMonth && (week !== 0 || showFirstMonthLabel) ? formatMonth(columnStart) : "";
6837
- previousMonth = month;
6838
- const cells = [];
6839
- for (let row = 0; row < 7; row++) {
6840
- const day = addCivilDays(gridStart, week * 7 + row);
6841
- const key = civilKey(day);
6842
- const cell = {
6843
- label: formatDay(day),
6844
- value: valueByDay.has(key) ? valueByDay.get(key) : null
6845
- };
6846
- if (key < gridMinDayKey || key > gridMaxDayKey) {
6847
- if (hideOutOfRangeDays) cell.hidden = true;
6848
- } else if (key < minDayKey || key > maxDayKey) cell.placeholder = true;
6849
- cells.push(cell);
6850
- }
6851
- data.push({
6852
- label,
6853
- data: cells
6854
- });
6855
- }
6856
- return {
6857
- data,
6858
- rowLabels
6859
- };
6860
- };
6861
- //#endregion
6862
- //#region src/charts/heatmap-chart/private/heatmap-context.ts
6863
- /** Shared by the chart and legend without importing back from `heatmap-chart.tsx`. */
6864
- const HeatmapContext = createContext(null);
6865
- //#endregion
6866
- //#region src/charts/heatmap-chart/private/heatmap-legend.tsx
6867
- const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6868
- const context = useContext(HeatmapContext);
6869
- const { legend } = useGlobalChartsTheme();
6870
- if (!context) return null;
6871
- const { primaryColorHex } = context;
6872
- const labelStyle = legend.labelStyles;
6873
- return /* @__PURE__ */ jsxs(Stack, {
6874
- direction: "row",
6875
- gap: "xs",
6876
- align: "center",
6877
- children: [
6878
- /* @__PURE__ */ jsx(Text$1, {
6879
- variant: "body-sm",
6880
- className: heatmap_chart_module_default["heatmap-chart__legend-label"],
6881
- style: labelStyle,
6882
- children: lessLabel ?? __("Less", "jetpack-charts")
6883
- }),
6884
- /* @__PURE__ */ jsx(Stack, {
6885
- direction: "row",
6886
- gap: "xs",
6887
- children: Array.from({ length: steps }, (_, index) => {
6888
- const intensity = steps <= 1 ? 1 : index / (steps - 1);
6889
- return /* @__PURE__ */ jsx("span", {
6890
- "aria-hidden": "true",
6891
- className: heatmap_chart_module_default["heatmap-chart__legend-swatch"],
6892
- style: {
6893
- "--a8c-charts-color-heatmap-primary": primaryColorHex,
6894
- "--a8c-charts-heatmap-cell-intensity": intensity
6895
- }
6896
- }, index);
6897
- })
6898
- }),
6899
- /* @__PURE__ */ jsx(Text$1, {
6900
- variant: "body-sm",
6901
- className: heatmap_chart_module_default["heatmap-chart__legend-label"],
6902
- style: labelStyle,
6903
- children: moreLabel ?? __("More", "jetpack-charts")
6904
- })
6905
- ]
6906
- });
7087
+ const stepCalendarCell = (grid, blocks, from, key) => {
7088
+ if (key === "ArrowRight" || key === "ArrowLeft") {
7089
+ const order = readingOrder(grid, blocks);
7090
+ const index = order.findIndex((cell) => cell.column === from.column && cell.row === from.row);
7091
+ if (index < 0) return;
7092
+ const ahead = key === "ArrowRight" ? order.slice(index + 1) : order.slice(0, index).reverse();
7093
+ return firstSelectable(grid, ahead);
7094
+ }
7095
+ if (key === "ArrowDown" || key === "ArrowUp") return firstSelectable(grid, columnOrderFrom(grid, blocks, from, key === "ArrowDown" ? 1 : -1));
7096
+ const blockIndex = blockOf(blocks, from.column);
7097
+ const target = blocks[blockIndex + (key === "PageDown" ? 1 : -1)];
7098
+ if (blockIndex < 0 || !target) return;
7099
+ const same = {
7100
+ column: target.start + (from.column - blocks[blockIndex].start),
7101
+ row: from.row
7102
+ };
7103
+ const page = readingOrder(grid, [target]);
7104
+ const at = page.findIndex((cell) => cell.row > same.row || cell.row === same.row && cell.column >= same.column);
7105
+ const after = at === -1 ? [] : page.slice(at);
7106
+ const before = page.slice(0, at === -1 ? page.length : at).reverse();
7107
+ return firstSelectable(grid, after) ?? firstSelectable(grid, before);
6907
7108
  };
6908
7109
  //#endregion
6909
7110
  //#region src/charts/heatmap-chart/heatmap-chart.tsx
6910
7111
  const CELL_MIX_FLOOR = .15;
6911
7112
  const NO_ROW_LABELS = [];
6912
- const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, height = 0, className, compact = false, showValues, maxCellWidth, maxCellHeight, minCellWidth, minCellHeight, rowLabels = NO_ROW_LABELS, primaryColor, gap = "md", withTooltips = false, renderTooltip, children }) => {
7113
+ const TOOLTIP_BOX_STYLES = {
7114
+ light: {
7115
+ ...defaultStyles,
7116
+ zIndex: 3
7117
+ },
7118
+ dark: { zIndex: 3 }
7119
+ };
7120
+ const cellName = (info) => info.cellLabel || [
7121
+ info.groupLabel,
7122
+ info.columnLabel,
7123
+ info.rowLabel
7124
+ ].filter(Boolean).join(" ");
7125
+ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, height = 0, className, compact = false, showValues, maxCellWidth, maxCellHeight, minCellWidth, minCellHeight, rowLabels = NO_ROW_LABELS, columnGroups, keyboardNavigation = "grid", ariaLabel, primaryColor, gap = "md", withTooltips = false, renderTooltip, tooltipVariant = "light", tooltipStyle, children }) => {
6913
7126
  const chartId = useChartId(providedChartId);
7127
+ const tooltipBoxStyle = tooltipStyle ? {
7128
+ ...TOOLTIP_BOX_STYLES[tooltipVariant],
7129
+ ...tooltipStyle
7130
+ } : TOOLTIP_BOX_STYLES[tooltipVariant];
6914
7131
  const { getElementStyles, theme } = useGlobalChartsContext();
6915
7132
  const scopeElement = useChartScopeElement();
6916
7133
  const { heatmapChart: heatmapChartSettings } = theme;
6917
7134
  const { nonLegendChildren } = useChartChildren(children, "HeatmapChart");
6918
- const [selectedIndex, setSelectedIndex] = useState();
7135
+ const [selected, setSelected] = useState();
6919
7136
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, showTooltip, hideTooltip } = useTooltip();
6920
7137
  const standaloneScopeClass = useStandaloneScopeClass();
6921
7138
  const containerRef = useRef(null);
@@ -6934,69 +7151,75 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6934
7151
  }), [extent, primaryColorHex]);
6935
7152
  const columns = data.length;
6936
7153
  const rows = Math.max(0, ...data.map((column) => column.data.length));
6937
- const { compactCellGap, compactCellSize } = heatmapChartSettings;
7154
+ const groupLayout = useMemo(() => resolveColumnGroups(columnGroups, columns, 2), [columnGroups, columns]);
7155
+ const { compactCellGap, compactCellSize, groupGap } = heatmapChartSettings;
6938
7156
  const drawValues = showValues ?? !compact;
6939
7157
  const buildTooltipData = useCallback((columnIndex, rowIndex) => {
6940
7158
  const cell = data[columnIndex]?.data[rowIndex];
7159
+ const group = groupLayout.columns[columnIndex]?.group;
6941
7160
  return {
6942
7161
  value: cell?.value ?? null,
6943
7162
  rowLabel: rowLabels[rowIndex],
6944
7163
  columnLabel: data[columnIndex]?.label,
7164
+ groupLabel: group === void 0 ? void 0 : groupLayout.groups[group]?.label,
6945
7165
  cellLabel: cell?.label,
6946
7166
  row: rowIndex,
6947
7167
  column: columnIndex
6948
7168
  };
6949
- }, [data, rowLabels]);
7169
+ }, [
7170
+ data,
7171
+ rowLabels,
7172
+ groupLayout
7173
+ ]);
6950
7174
  const onChartBlur = useCallback(() => {
6951
- setSelectedIndex(void 0);
7175
+ setSelected(void 0);
6952
7176
  hideTooltip();
6953
7177
  }, [hideTooltip]);
6954
7178
  const isCellInert = useCallback((col, row) => {
6955
7179
  const cell = data[col]?.data[row];
6956
7180
  return cell?.hidden === true || cell?.placeholder === true;
6957
7181
  }, [data]);
7182
+ const blocks = useMemo(() => {
7183
+ const grouped = groupLayout.groups.map((group, index) => {
7184
+ const start = groupLayout.columns.findIndex((column) => column.group === index);
7185
+ return {
7186
+ start,
7187
+ end: start + group.span
7188
+ };
7189
+ });
7190
+ const groupedEnd = grouped.length ? grouped[grouped.length - 1].end : 0;
7191
+ return groupedEnd < columns ? [...grouped, {
7192
+ start: groupedEnd,
7193
+ end: columns
7194
+ }] : grouped;
7195
+ }, [groupLayout, columns]);
6958
7196
  const onChartKeyDown = useCallback((event) => {
6959
- if (![
6960
- "ArrowLeft",
6961
- "ArrowRight",
6962
- "ArrowUp",
6963
- "ArrowDown",
6964
- "Escape",
6965
- "Tab"
6966
- ].includes(event.key)) return;
6967
7197
  if (event.key === "Tab" || event.key === "Escape") {
6968
- setSelectedIndex(void 0);
7198
+ setSelected(void 0);
6969
7199
  hideTooltip();
6970
7200
  return;
6971
7201
  }
7202
+ if (!isNavigationKey(event.key, keyboardNavigation)) return;
6972
7203
  event.preventDefault();
6973
- if (selectedIndex === void 0) {
6974
- for (let index = 0; index < columns * rows; index++) if (!isCellInert(Math.floor(index / rows), index % rows)) {
6975
- setSelectedIndex(index);
6976
- return;
6977
- }
7204
+ const grid = {
7205
+ columns,
7206
+ rows,
7207
+ isInert: isCellInert
7208
+ };
7209
+ if (selected === void 0) {
7210
+ setSelected(keyboardNavigation === "calendar" ? firstCalendarCell(grid, blocks) : firstGridCell(grid));
6978
7211
  return;
6979
7212
  }
6980
- let stepCol = 0;
6981
- let stepRow = 0;
6982
- if (event.key === "ArrowRight") stepCol = 1;
6983
- else if (event.key === "ArrowLeft") stepCol = -1;
6984
- else if (event.key === "ArrowDown") stepRow = 1;
6985
- else if (event.key === "ArrowUp") stepRow = -1;
6986
- let col = Math.floor(selectedIndex / rows);
6987
- let row = selectedIndex % rows;
6988
- do {
6989
- col += stepCol;
6990
- row += stepRow;
6991
- } while (col >= 0 && col < columns && row >= 0 && row < rows && isCellInert(col, row));
6992
- if (col < 0 || col >= columns || row < 0 || row >= rows) return;
6993
- setSelectedIndex(col * rows + row);
7213
+ const next = keyboardNavigation === "calendar" ? stepCalendarCell(grid, blocks, selected, event.key) : stepGridCell(grid, selected, event.key);
7214
+ if (next) setSelected(next);
6994
7215
  }, [
6995
7216
  rows,
6996
7217
  columns,
6997
- selectedIndex,
7218
+ selected,
6998
7219
  hideTooltip,
6999
- isCellInert
7220
+ isCellInert,
7221
+ keyboardNavigation,
7222
+ blocks
7000
7223
  ]);
7001
7224
  const handleCellMouseMove = useCallback((event) => {
7002
7225
  if (!withTooltips) return;
@@ -7016,35 +7239,47 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
7016
7239
  buildTooltipData,
7017
7240
  getTooltipOrigin
7018
7241
  ]);
7242
+ const getCellElement = useCallback((col, row) => typeof document !== "undefined" ? document.getElementById(`${chartId}-cell-${col}-${row}`) : null, [chartId]);
7019
7243
  const handleCellMouseLeave = useCallback(() => {
7020
- if (withTooltips && selectedIndex === void 0) hideTooltip();
7244
+ if (withTooltips && selected === void 0) hideTooltip();
7021
7245
  }, [
7022
7246
  withTooltips,
7023
- selectedIndex,
7247
+ selected,
7024
7248
  hideTooltip
7025
7249
  ]);
7026
7250
  useEffect(() => {
7027
- if (!withTooltips || selectedIndex === void 0) return;
7251
+ if (selected && (selected.column >= columns || selected.row >= rows)) setSelected(void 0);
7252
+ }, [
7253
+ selected,
7254
+ columns,
7255
+ rows
7256
+ ]);
7257
+ useEffect(() => {
7258
+ if (selected === void 0) return;
7259
+ getCellElement(selected.column, selected.row)?.scrollIntoView?.({
7260
+ block: "nearest",
7261
+ inline: "nearest"
7262
+ });
7263
+ }, [selected, getCellElement]);
7264
+ useEffect(() => {
7265
+ if (!withTooltips || selected === void 0) return;
7028
7266
  const origin = getTooltipOrigin();
7029
7267
  if (!origin) return;
7030
- const col = Math.floor(selectedIndex / rows);
7031
- const row = selectedIndex % rows;
7032
- const rect = (typeof document !== "undefined" ? document.getElementById(`${chartId}-cell-${col}-${row}`) : null)?.getBoundingClientRect();
7268
+ const rect = getCellElement(selected.column, selected.row)?.getBoundingClientRect();
7033
7269
  showTooltip({
7034
7270
  tooltipLeft: rect ? rect.left + rect.width / 2 - origin.left : 0,
7035
7271
  tooltipTop: rect ? rect.top + rect.height / 2 - origin.top : 0,
7036
- tooltipData: buildTooltipData(col, row)
7272
+ tooltipData: buildTooltipData(selected.column, selected.row)
7037
7273
  });
7038
7274
  }, [
7039
- selectedIndex,
7275
+ selected,
7040
7276
  withTooltips,
7041
- rows,
7042
- chartId,
7277
+ getCellElement,
7043
7278
  buildTooltipData,
7044
7279
  showTooltip,
7045
7280
  getTooltipOrigin
7046
7281
  ]);
7047
- const defaultRenderTooltip = useCallback((info) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("strong", { children: info.cellLabel || `${info.columnLabel ?? ""} ${info.rowLabel ?? ""}`.trim() }), /* @__PURE__ */ jsx("div", { children: info.value === null ? __("No data", "jetpack-charts") : formatNumber(info.value) })] }), []);
7282
+ const defaultRenderTooltip = useCallback((info) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("strong", { children: cellName(info) }), /* @__PURE__ */ jsx("div", { children: info.value === null ? __("No data", "jetpack-charts") : formatNumber(info.value) })] }), []);
7048
7283
  if (!columns || !rows) return /* @__PURE__ */ jsx(Center, {
7049
7284
  className: clsx("heatmap-chart", heatmap_chart_module_default["heatmap-chart"], className),
7050
7285
  style: {
@@ -7058,10 +7293,16 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
7058
7293
  });
7059
7294
  const columnTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellWidth ?? 0}px, ${maxCellWidth ? `${maxCellWidth}px` : "1fr"})`;
7060
7295
  const rowTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
7296
+ const hasColumnLabels = data.some((column) => Boolean(column.label));
7297
+ const hasGroups = groupLayout.groups.length > 0;
7298
+ const columnLine = (columnIndex) => groupLayout.columns[columnIndex].line;
7299
+ const firstDataRow = hasColumnLabels ? 2 : 1;
7300
+ const dataTrack = (column) => column.summary ? "minmax(auto, max-content)" : columnTrack;
7301
+ const gapTrack = compact ? `minmax(${groupGap}px, 1fr)` : `${groupGap}px`;
7061
7302
  const gridStyle = {
7062
7303
  "--a8c-charts-color-heatmap-primary": primaryColorHex,
7063
- gridTemplateColumns: `auto ${data.some((column) => column.summary) ? data.map((column) => column.summary ? "minmax(auto, max-content)" : columnTrack).join(" ") : `repeat(${columns}, ${columnTrack})`}`,
7064
- gridTemplateRows: `auto repeat(${rows}, ${rowTrack})`
7304
+ gridTemplateColumns: `auto ${data.map((column, columnIndex) => groupLayout.columns[columnIndex].gapBefore ? `${gapTrack} ${dataTrack(column)}` : dataTrack(column)).join(" ")}`,
7305
+ gridTemplateRows: `${hasColumnLabels ? "auto " : ""}repeat(${rows}, ${rowTrack})${hasGroups ? " auto" : ""}`
7065
7306
  };
7066
7307
  if (compact) {
7067
7308
  gridStyle["--a8c-charts-dimension-heatmap-cell-gap"] = `${compactCellGap}px`;
@@ -7074,7 +7315,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
7074
7315
  [heatmap_chart_module_default["heatmap-chart__gap-end"]]: columnIndex < columns - 1 && !data[columnIndex + 1]?.summary
7075
7316
  };
7076
7317
  };
7077
- const activeDescendant = selectedIndex !== void 0 ? `${chartId}-cell-${Math.floor(selectedIndex / rows)}-${selectedIndex % rows}` : void 0;
7318
+ const activeDescendant = selected ? `${chartId}-cell-${selected.column}-${selected.row}` : void 0;
7078
7319
  const heightCapped = !compact && Boolean(maxCellHeight);
7079
7320
  return /* @__PURE__ */ jsx(HeatmapContext.Provider, {
7080
7321
  value: heatmapContext,
@@ -7094,7 +7335,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
7094
7335
  children: [/* @__PURE__ */ jsxs("div", {
7095
7336
  ref: containerRef,
7096
7337
  role: "grid",
7097
- "aria-label": __("Heatmap chart", "jetpack-charts"),
7338
+ "aria-label": ariaLabel ?? __("Heatmap chart", "jetpack-charts"),
7098
7339
  "aria-rowcount": rows,
7099
7340
  "aria-colcount": columns,
7100
7341
  "aria-activedescendant": activeDescendant,
@@ -7103,76 +7344,115 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
7103
7344
  onKeyDown: onChartKeyDown,
7104
7345
  className: clsx(heatmap_chart_module_default["heatmap-chart__grid"], {
7105
7346
  [heatmap_chart_module_default["heatmap-chart__grid--compact"]]: compact,
7347
+ [heatmap_chart_module_default["heatmap-chart__grid--flex-gaps"]]: compact && hasGroups,
7106
7348
  [heatmap_chart_module_default["heatmap-chart__grid--height-capped"]]: heightCapped
7107
7349
  }),
7108
7350
  style: gridStyle,
7109
- children: [/* @__PURE__ */ jsxs("div", {
7110
- role: "row",
7111
- "aria-hidden": "true",
7112
- className: heatmap_chart_module_default["heatmap-chart__row"],
7113
- children: [/* @__PURE__ */ jsx("span", {}), data.map((column, columnIndex) => /* @__PURE__ */ jsx("span", {
7114
- className: clsx(heatmap_chart_module_default["heatmap-chart__col-label"], {
7115
- [heatmap_chart_module_default["heatmap-chart__col-label--summary"]]: column.summary,
7116
- ...summaryGaps(columnIndex)
7117
- }),
7118
- children: column.label
7119
- }, `col-${columnIndex}`))]
7120
- }), Array.from({ length: rows }).map((_row, rowIndex) => {
7121
- const labelVisible = !compact || rowIndex % 2 === 0;
7122
- return /* @__PURE__ */ jsxs("div", {
7351
+ children: [
7352
+ hasColumnLabels && /* @__PURE__ */ jsxs("div", {
7123
7353
  role: "row",
7124
- "aria-rowindex": rowIndex + 1,
7354
+ "aria-hidden": "true",
7125
7355
  className: heatmap_chart_module_default["heatmap-chart__row"],
7126
- children: [/* @__PURE__ */ jsx("span", {
7127
- "aria-hidden": "true",
7128
- className: heatmap_chart_module_default["heatmap-chart__row-label"],
7129
- children: labelVisible ? rowLabels[rowIndex] ?? "" : ""
7130
- }), data.map((column, columnIndex) => {
7131
- const cell = column.data[rowIndex];
7132
- if (cell?.hidden) return /* @__PURE__ */ jsx("div", {
7133
- "aria-hidden": "true",
7134
- className: clsx(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--hidden"])
7135
- }, `cell-${columnIndex}-${rowIndex}`);
7136
- if (cell?.placeholder) return /* @__PURE__ */ jsx("div", {
7356
+ children: [/* @__PURE__ */ jsx("span", { style: {
7357
+ gridColumn: 1,
7358
+ gridRow: 1
7359
+ } }), data.map((column, columnIndex) => /* @__PURE__ */ jsx("span", {
7360
+ style: {
7361
+ gridColumn: columnLine(columnIndex),
7362
+ gridRow: 1
7363
+ },
7364
+ className: clsx(heatmap_chart_module_default["heatmap-chart__col-label"], {
7365
+ [heatmap_chart_module_default["heatmap-chart__col-label--summary"]]: column.summary,
7366
+ ...summaryGaps(columnIndex)
7367
+ }),
7368
+ children: column.label
7369
+ }, `col-${columnIndex}`))]
7370
+ }),
7371
+ Array.from({ length: rows }).map((_row, rowIndex) => {
7372
+ const labelVisible = !compact || rowIndex % 2 === 0;
7373
+ const gridRow = firstDataRow + rowIndex;
7374
+ return /* @__PURE__ */ jsxs("div", {
7375
+ role: "row",
7376
+ "aria-rowindex": rowIndex + 1,
7377
+ className: heatmap_chart_module_default["heatmap-chart__row"],
7378
+ children: [/* @__PURE__ */ jsx("span", {
7137
7379
  "aria-hidden": "true",
7138
- className: clsx(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--placeholder"])
7139
- }, `cell-${columnIndex}-${rowIndex}`);
7140
- const value = cell?.value ?? null;
7141
- const present = isPresent(value);
7142
- const filled = present && !column.summary;
7143
- const normalized = filled ? getNormalizedValue(value, extent) : 0;
7144
- const flatIndex = columnIndex * rows + rowIndex;
7145
- const info = buildTooltipData(columnIndex, rowIndex);
7146
- const accessibleLabel = `${info.cellLabel || `${info.columnLabel ?? ""} ${info.rowLabel ?? ""}`.trim()}: ${info.value === null ? __("No data", "jetpack-charts") : formatNumber(info.value)}`;
7147
- return /* @__PURE__ */ jsx("div", {
7148
- id: `${chartId}-cell-${columnIndex}-${rowIndex}`,
7149
- role: "gridcell",
7150
- tabIndex: -1,
7151
- "aria-colindex": columnIndex + 1,
7152
- "aria-label": accessibleLabel,
7153
- "data-column": columnIndex,
7154
- "data-row": rowIndex,
7155
- className: clsx(heatmap_chart_module_default["heatmap-chart__cell"], {
7156
- [heatmap_chart_module_default["heatmap-chart__cell--filled"]]: filled,
7157
- [heatmap_chart_module_default["heatmap-chart__cell--strong"]]: filled && cellHasLightText(normalized),
7158
- [heatmap_chart_module_default["heatmap-chart__cell--summary"]]: column.summary,
7159
- ...summaryGaps(columnIndex),
7160
- [heatmap_chart_module_default["heatmap-chart__cell--selected"]]: selectedIndex === flatIndex
7161
- }),
7162
- style: filled ? { "--a8c-charts-heatmap-cell-intensity": normalized } : void 0,
7163
- onMouseMove: handleCellMouseMove,
7164
- onMouseLeave: handleCellMouseLeave,
7165
- children: (drawValues || column.summary) && present && /* @__PURE__ */ jsx("span", {
7166
- className: heatmap_chart_module_default["heatmap-chart__cell-value"],
7167
- children: formatNumberCompact(value)
7168
- })
7169
- }, `cell-${columnIndex}-${rowIndex}`);
7170
- })]
7171
- }, `row-${rowIndex}`);
7172
- })]
7380
+ className: heatmap_chart_module_default["heatmap-chart__row-label"],
7381
+ style: {
7382
+ gridColumn: 1,
7383
+ gridRow
7384
+ },
7385
+ children: labelVisible ? rowLabels[rowIndex] ?? "" : ""
7386
+ }), data.map((column, columnIndex) => {
7387
+ const cell = column.data[rowIndex];
7388
+ const placement = {
7389
+ gridColumn: columnLine(columnIndex),
7390
+ gridRow
7391
+ };
7392
+ if (cell?.hidden) return /* @__PURE__ */ jsx("div", {
7393
+ "aria-hidden": "true",
7394
+ style: placement,
7395
+ className: clsx(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--hidden"])
7396
+ }, `cell-${columnIndex}-${rowIndex}`);
7397
+ if (cell?.placeholder) return /* @__PURE__ */ jsx("div", {
7398
+ "aria-hidden": "true",
7399
+ style: placement,
7400
+ className: clsx(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--placeholder"])
7401
+ }, `cell-${columnIndex}-${rowIndex}`);
7402
+ const value = cell?.value ?? null;
7403
+ const present = isPresent(value);
7404
+ const filled = present && !column.summary;
7405
+ const normalized = filled ? getNormalizedValue(value, extent) : 0;
7406
+ const info = buildTooltipData(columnIndex, rowIndex);
7407
+ const accessibleLabel = `${cellName(info)}: ${info.value === null ? __("No data", "jetpack-charts") : formatNumber(info.value)}`;
7408
+ return /* @__PURE__ */ jsx("div", {
7409
+ id: `${chartId}-cell-${columnIndex}-${rowIndex}`,
7410
+ role: "gridcell",
7411
+ tabIndex: -1,
7412
+ "aria-colindex": columnIndex + 1,
7413
+ "aria-label": accessibleLabel,
7414
+ "data-column": columnIndex,
7415
+ "data-row": rowIndex,
7416
+ className: clsx(heatmap_chart_module_default["heatmap-chart__cell"], {
7417
+ [heatmap_chart_module_default["heatmap-chart__cell--filled"]]: filled,
7418
+ [heatmap_chart_module_default["heatmap-chart__cell--strong"]]: filled && cellHasLightText(normalized),
7419
+ [heatmap_chart_module_default["heatmap-chart__cell--summary"]]: column.summary,
7420
+ ...summaryGaps(columnIndex),
7421
+ [heatmap_chart_module_default["heatmap-chart__cell--selected"]]: selected?.column === columnIndex && selected?.row === rowIndex
7422
+ }),
7423
+ style: {
7424
+ ...placement,
7425
+ ...filled ? { "--a8c-charts-heatmap-cell-intensity": normalized } : {}
7426
+ },
7427
+ onMouseMove: handleCellMouseMove,
7428
+ onMouseLeave: handleCellMouseLeave,
7429
+ children: (drawValues || column.summary) && present && /* @__PURE__ */ jsx("span", {
7430
+ className: heatmap_chart_module_default["heatmap-chart__cell-value"],
7431
+ children: formatNumberCompact(value)
7432
+ })
7433
+ }, `cell-${columnIndex}-${rowIndex}`);
7434
+ })]
7435
+ }, `row-${rowIndex}`);
7436
+ }),
7437
+ hasGroups && /* @__PURE__ */ jsx("div", {
7438
+ role: "row",
7439
+ "aria-hidden": "true",
7440
+ className: heatmap_chart_module_default["heatmap-chart__row"],
7441
+ children: groupLayout.groups.map((group, groupIndex) => /* @__PURE__ */ jsx("span", {
7442
+ className: heatmap_chart_module_default["heatmap-chart__group-label"],
7443
+ style: {
7444
+ gridColumn: `${group.line} / span ${group.span}`,
7445
+ gridRow: firstDataRow + rows
7446
+ },
7447
+ children: group.label
7448
+ }, `group-${groupIndex}`))
7449
+ })
7450
+ ]
7173
7451
  }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ jsx(BoundedTooltip, {
7174
7452
  top: tooltipTop,
7175
7453
  left: tooltipLeft,
7454
+ className: tooltipVariant === "dark" ? base_tooltip_module_default.surface : void 0,
7455
+ style: tooltipBoxStyle,
7176
7456
  children: /* @__PURE__ */ jsx("div", {
7177
7457
  className: standaloneScopeClass,
7178
7458
  role: "tooltip",
@@ -7198,6 +7478,342 @@ const HeatmapChartResponsiveInner = (props) => /* @__PURE__ */ jsx(HeatmapChartW
7198
7478
  HeatmapChartResponsiveInner.displayName = "HeatmapChart";
7199
7479
  const HeatmapChartResponsive = attachSubComponents(withResponsive(HeatmapChartResponsiveInner), { Legend: HeatmapLegend });
7200
7480
  //#endregion
7481
+ //#region src/charts/heatmap-chart/private/civil-day.ts
7482
+ const DAY_MS = 864e5;
7483
+ const KEY_OPTIONS = {
7484
+ calendar: "gregory",
7485
+ numberingSystem: "latn",
7486
+ year: "numeric",
7487
+ month: "2-digit",
7488
+ day: "2-digit"
7489
+ };
7490
+ const DATE_PREFIX = /^(\d{4})-(\d{2})-(\d{2})/;
7491
+ /**
7492
+ * Label formatters for UTC proxies, so both calendar layouts name a day alike.
7493
+ *
7494
+ * `gregory` keeps a locale whose default calendar is not Gregorian (fa-IR) from
7495
+ * splitting one grid month across two names.
7496
+ *
7497
+ * @param locale - BCP 47 locale.
7498
+ * @return Weekday, month and full-day formatters.
7499
+ */
7500
+ const civilLabelFormatters = (locale) => {
7501
+ const formatting = {
7502
+ locale,
7503
+ timeZone: "UTC"
7504
+ };
7505
+ return {
7506
+ formatWeekday: createDateFormatter({
7507
+ weekday: "short",
7508
+ calendar: "gregory"
7509
+ }, formatting),
7510
+ formatMonth: createDateFormatter({
7511
+ month: "short",
7512
+ calendar: "gregory"
7513
+ }, formatting),
7514
+ formatMonthYear: createDateFormatter({
7515
+ month: "short",
7516
+ year: "numeric",
7517
+ calendar: "gregory"
7518
+ }, formatting),
7519
+ formatDay: createDateFormatter({
7520
+ weekday: "short",
7521
+ month: "short",
7522
+ day: "numeric",
7523
+ year: "numeric",
7524
+ calendar: "gregory"
7525
+ }, formatting)
7526
+ };
7527
+ };
7528
+ const pad = (value, length) => String(value).padStart(length, "0");
7529
+ /**
7530
+ * The day a UTC proxy stands for.
7531
+ *
7532
+ * @param date - UTC proxy.
7533
+ * @return Its day key.
7534
+ */
7535
+ const civilKey = (date) => `${pad(date.getUTCFullYear(), 4)}-${pad(date.getUTCMonth() + 1, 2)}-${pad(date.getUTCDate(), 2)}`;
7536
+ /**
7537
+ * A day key as a UTC-midnight proxy. UTC has no DST, so every step below is exactly
7538
+ * `DAY_MS`.
7539
+ *
7540
+ * @param key - Day key.
7541
+ * @return The proxy, or null when the key names no real day.
7542
+ */
7543
+ const civilDate = (key) => {
7544
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(key);
7545
+ if (!match) return null;
7546
+ const date = /* @__PURE__ */ new Date(0);
7547
+ date.setUTCFullYear(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
7548
+ date.setUTCHours(0, 0, 0, 0);
7549
+ return civilKey(date) === key ? date : null;
7550
+ };
7551
+ /**
7552
+ * The day a written date names, ignoring any time and zone it carries.
7553
+ *
7554
+ * @param text - `yyyy-MM-dd`, optionally followed by a time.
7555
+ * @return Its day key, or null.
7556
+ */
7557
+ const writtenDayKey = (text) => {
7558
+ const match = DATE_PREFIX.exec(text);
7559
+ if (!match) return null;
7560
+ const key = `${match[1]}-${match[2]}-${match[3]}`;
7561
+ return civilDate(key) ? key : null;
7562
+ };
7563
+ /**
7564
+ * Reads the day an instant falls on in a zone.
7565
+ *
7566
+ * @param timeZone - IANA zone name; absent leaves the runtime's own.
7567
+ * @return Reader taking a `Date`, null for a day no key can hold.
7568
+ */
7569
+ const instantDayReader = (timeZone) => {
7570
+ const formatter = new Intl.DateTimeFormat("en-US", {
7571
+ ...KEY_OPTIONS,
7572
+ timeZone
7573
+ });
7574
+ return (date) => {
7575
+ const parts = formatter.formatToParts(date);
7576
+ const read = (type) => parts.find((part) => part.type === type)?.value ?? "";
7577
+ const key = `${pad(Number(read("year")), 4)}-${read("month")}-${read("day")}`;
7578
+ return civilDate(key) ? key : null;
7579
+ };
7580
+ };
7581
+ /**
7582
+ * The day a series point belongs to.
7583
+ *
7584
+ * A `Date`, or a string carrying `Z` or an offset, is an instant read in the host's
7585
+ * zone. Anything else is a wall clock the host wrote, so its date part is the day.
7586
+ *
7587
+ * @param point - Series point.
7588
+ * @param readInstant - Reader from `instantDayReader`.
7589
+ * @return Its day key, or null when the point carries no usable date.
7590
+ */
7591
+ const pointDayKey = (point, readInstant) => {
7592
+ if (point.date instanceof Date && !isNaN(point.date.getTime())) {
7593
+ const key = readInstant(point.date);
7594
+ if (key) return key;
7595
+ }
7596
+ const text = point.dateString;
7597
+ if (!text) return null;
7598
+ if (!hasTimezone(text)) return writtenDayKey(text);
7599
+ const parsed = parseISO(text);
7600
+ return isNaN(parsed.getTime()) ? null : readInstant(parsed);
7601
+ };
7602
+ /**
7603
+ * Steps a proxy by whole days.
7604
+ *
7605
+ * @param date - UTC proxy.
7606
+ * @param days - Days to add; may be negative.
7607
+ * @return The shifted proxy.
7608
+ */
7609
+ const addCivilDays = (date, days) => new Date(date.getTime() + days * DAY_MS);
7610
+ /**
7611
+ * Rounds a proxy down to its week.
7612
+ *
7613
+ * @param date - UTC proxy.
7614
+ * @param weekStartsOn - 0 for Sunday, 1 for Monday.
7615
+ * @return The proxy for that week's first day.
7616
+ */
7617
+ const startOfCivilWeek = (date, weekStartsOn) => {
7618
+ const daysSinceWeekStart = (date.getUTCDay() - weekStartsOn + 7) % 7;
7619
+ return addCivilDays(date, -daysSinceWeekStart);
7620
+ };
7621
+ /**
7622
+ * Counts the columns a span needs.
7623
+ *
7624
+ * @param from - Earlier UTC proxy.
7625
+ * @param to - Later UTC proxy.
7626
+ * @param weekStartsOn - 0 for Sunday, 1 for Monday.
7627
+ * @return Columns needed to cover the span, inclusive.
7628
+ */
7629
+ const civilWeekSpan = (from, to, weekStartsOn) => Math.floor((startOfCivilWeek(to, weekStartsOn).getTime() - startOfCivilWeek(from, weekStartsOn).getTime()) / (7 * DAY_MS)) + 1;
7630
+ //#endregion
7631
+ //#region src/charts/heatmap-chart/build-calendar-data.ts
7632
+ /** Rows that get a weekday label (Mon, Wed, Fri with a Monday week start). */
7633
+ const LABELLED_ROWS = [
7634
+ 0,
7635
+ 2,
7636
+ 4
7637
+ ];
7638
+ /**
7639
+ * Resolve one grid bound: an unparseable or narrowing value falls back to the
7640
+ * series' own bound, so the grid can only ever be widened.
7641
+ *
7642
+ * @param bound - Requested bound as `yyyy-MM-dd`, if any.
7643
+ * @param fallback - The series' own bound on this side.
7644
+ * @param direction - Which way the bound is allowed to move.
7645
+ * @return The day key to draw to.
7646
+ */
7647
+ const widenTo = (bound, fallback, direction) => {
7648
+ const key = bound ? writtenDayKey(bound) : null;
7649
+ if (!key) {
7650
+ if (bound) warnOnce(`heatmap:gridSpan:${bound}`, `gridSpan.${direction === "earlier" ? "start" : "end"} ${JSON.stringify(bound)} is not a \`yyyy-MM-dd\` day, so the grid is drawn over the series' own span.`);
7651
+ return fallback;
7652
+ }
7653
+ if (direction === "earlier") return key < fallback ? key : fallback;
7654
+ return key > fallback ? key : fallback;
7655
+ };
7656
+ /**
7657
+ * Lay a day-bucketed series out as calendar columns.
7658
+ *
7659
+ * @param series - Points to bucket.
7660
+ * @param options - Grid shape, plus the locale and zone to read days in.
7661
+ * @return Columns and row labels for `HeatmapChart`.
7662
+ */
7663
+ const buildCalendarHeatmapData = (series, options = {}) => {
7664
+ const weekStartsOn = options.weekStartsOn ?? 1;
7665
+ const hideOutOfRangeDays = options.hideOutOfRangeDays ?? true;
7666
+ const { locale, timeZone } = sanitizeFormatting({
7667
+ locale: options.locale,
7668
+ timeZone: options.timeZone
7669
+ });
7670
+ const readInstant = instantDayReader(timeZone);
7671
+ const valueByDay = /* @__PURE__ */ new Map();
7672
+ let minDayKey;
7673
+ let maxDayKey;
7674
+ for (const point of series) {
7675
+ const key = pointDayKey(point, readInstant);
7676
+ if (!key) {
7677
+ const offending = point.dateString ?? (point.date && String(point.date));
7678
+ warnOnce(`heatmap:unreadableDate:${offending}`, offending ? `${JSON.stringify(offending)} is not a day this can read, so its point is left out of the calendar. A \`dateString\` must start \`yyyy-MM-dd\`.` : "A point carries neither `date` nor `dateString`, so it is left out of the calendar.");
7679
+ continue;
7680
+ }
7681
+ valueByDay.set(key, point.value);
7682
+ if (!minDayKey || key < minDayKey) minDayKey = key;
7683
+ if (!maxDayKey || key > maxDayKey) maxDayKey = key;
7684
+ }
7685
+ if (!minDayKey || !maxDayKey) return {
7686
+ data: [],
7687
+ rowLabels: []
7688
+ };
7689
+ const requestedMinDayKey = widenTo(options.gridSpan?.start, minDayKey, "earlier");
7690
+ const gridMaxDayKey = widenTo(options.gridSpan?.end, maxDayKey, "later");
7691
+ const requestedMinDate = civilDate(requestedMinDayKey);
7692
+ const gridMaxDate = civilDate(gridMaxDayKey);
7693
+ const { formatWeekday, formatMonth, formatDay } = civilLabelFormatters(locale);
7694
+ const gridStart = startOfCivilWeek(requestedMinDate, weekStartsOn);
7695
+ const gridMinDayKey = requestedMinDayKey < minDayKey ? civilKey(gridStart) : requestedMinDayKey;
7696
+ const weekCount = civilWeekSpan(gridStart, gridMaxDate, weekStartsOn);
7697
+ const rowLabels = Array.from({ length: 7 }, (_, row) => LABELLED_ROWS.includes(row) ? formatWeekday(addCivilDays(gridStart, row)) : "");
7698
+ const MIN_FIRST_MONTH_WEEKS = 2;
7699
+ const firstMonth = gridStart.getUTCMonth();
7700
+ let firstMonthWeeks = 0;
7701
+ while (firstMonthWeeks < weekCount && addCivilDays(gridStart, firstMonthWeeks * 7).getUTCMonth() === firstMonth) firstMonthWeeks++;
7702
+ const showFirstMonthLabel = !(firstMonthWeeks < weekCount) || firstMonthWeeks >= MIN_FIRST_MONTH_WEEKS;
7703
+ const data = [];
7704
+ let previousMonth = -1;
7705
+ for (let week = 0; week < weekCount; week++) {
7706
+ const columnStart = addCivilDays(gridStart, week * 7);
7707
+ const month = columnStart.getUTCMonth();
7708
+ const label = month !== previousMonth && (week !== 0 || showFirstMonthLabel) ? formatMonth(columnStart) : "";
7709
+ previousMonth = month;
7710
+ const cells = [];
7711
+ for (let row = 0; row < 7; row++) {
7712
+ const day = addCivilDays(gridStart, week * 7 + row);
7713
+ const key = civilKey(day);
7714
+ const cell = {
7715
+ label: formatDay(day),
7716
+ value: valueByDay.has(key) ? valueByDay.get(key) : null
7717
+ };
7718
+ if (key < gridMinDayKey || key > gridMaxDayKey) {
7719
+ if (hideOutOfRangeDays) cell.hidden = true;
7720
+ } else if (key < minDayKey || key > maxDayKey) cell.placeholder = true;
7721
+ cells.push(cell);
7722
+ }
7723
+ data.push({
7724
+ label,
7725
+ data: cells
7726
+ });
7727
+ }
7728
+ return {
7729
+ data,
7730
+ rowLabels
7731
+ };
7732
+ };
7733
+ //#endregion
7734
+ //#region src/charts/heatmap-chart/build-month-calendar-data.ts
7735
+ /** Week rows per month, fixed so every month block is the same height. */
7736
+ const MONTH_ROWS = 6;
7737
+ const DAYS_PER_WEEK = 7;
7738
+ const startOfCivilMonth = (date) => {
7739
+ const first = new Date(date.getTime());
7740
+ first.setUTCDate(1);
7741
+ return first;
7742
+ };
7743
+ const startOfNextCivilMonth = (date) => {
7744
+ const next = startOfCivilMonth(date);
7745
+ next.setUTCMonth(next.getUTCMonth() + 1);
7746
+ return next;
7747
+ };
7748
+ /**
7749
+ * Lay a day-keyed map out as one month calendar per month, weekdays across and
7750
+ * weeks down, for `HeatmapChart` with `columnGroups`.
7751
+ *
7752
+ * @param valueByDay - Value per `yyyy-MM-dd`. A missing day is `null`.
7753
+ * @param range - The measured days; every month from `start`'s through `end`'s is drawn.
7754
+ * @param options - Week start and label locale.
7755
+ * @return Columns and one group per month; empty for an invalid or reversed range.
7756
+ */
7757
+ const buildMonthCalendarHeatmapData = (valueByDay, range, options = {}) => {
7758
+ const start = civilDate(range.start);
7759
+ const end = civilDate(range.end);
7760
+ if (!start || !end || start.getTime() > end.getTime()) {
7761
+ warnOnce(`heatmap:monthRange:${range.start}:${range.end}`, `range ${JSON.stringify(range)} must be two \`yyyy-MM-dd\` days with start on or before end, so the calendar is empty.`);
7762
+ return {
7763
+ data: [],
7764
+ columnGroups: []
7765
+ };
7766
+ }
7767
+ const startKey = civilKey(start);
7768
+ const endKey = civilKey(end);
7769
+ const weekStartsOn = options.weekStartsOn ?? 1;
7770
+ const { locale } = sanitizeFormatting({ locale: options.locale });
7771
+ for (const key of Object.keys(valueByDay)) if (!civilDate(key)) warnOnce(`heatmap:monthKey:${key}`, `${JSON.stringify(key)} is not a \`yyyy-MM-dd\` day, so its value is left out of the calendar.`);
7772
+ const readValue = (key) => {
7773
+ const value = valueByDay[key];
7774
+ return isPresent(value) ? value : null;
7775
+ };
7776
+ const { formatMonth, formatMonthYear, formatDay } = civilLabelFormatters(locale);
7777
+ const data = [];
7778
+ const columnGroups = [];
7779
+ const lastMonthStart = startOfCivilMonth(end);
7780
+ const formatGroup = (lastMonthStart.getUTCFullYear() - start.getUTCFullYear()) * 12 + lastMonthStart.getUTCMonth() - start.getUTCMonth() + 1 > 12 ? formatMonthYear : formatMonth;
7781
+ for (let monthStart = startOfCivilMonth(start); monthStart.getTime() <= lastMonthStart.getTime(); monthStart = startOfNextCivilMonth(monthStart)) {
7782
+ const monthStartKey = civilKey(monthStart);
7783
+ const monthEndKey = civilKey(addCivilDays(startOfNextCivilMonth(monthStart), -1));
7784
+ const gridStart = startOfCivilWeek(monthStart, weekStartsOn);
7785
+ for (let weekday = 0; weekday < DAYS_PER_WEEK; weekday++) {
7786
+ const cells = [];
7787
+ for (let week = 0; week < MONTH_ROWS; week++) {
7788
+ const day = addCivilDays(gridStart, week * DAYS_PER_WEEK + weekday);
7789
+ const key = civilKey(day);
7790
+ if (key < monthStartKey || key > monthEndKey) cells.push({
7791
+ value: null,
7792
+ hidden: true
7793
+ });
7794
+ else if (key < startKey || key > endKey) cells.push({
7795
+ label: formatDay(day),
7796
+ value: null,
7797
+ placeholder: true
7798
+ });
7799
+ else cells.push({
7800
+ label: formatDay(day),
7801
+ value: readValue(key)
7802
+ });
7803
+ }
7804
+ data.push({ data: cells });
7805
+ }
7806
+ columnGroups.push({
7807
+ label: formatGroup(monthStart),
7808
+ span: DAYS_PER_WEEK
7809
+ });
7810
+ }
7811
+ return {
7812
+ data,
7813
+ columnGroups
7814
+ };
7815
+ };
7816
+ //#endregion
7201
7817
  //#region src/charts/heatmap-chart/use-calendar-heatmap-data.ts
7202
7818
  /**
7203
7819
  * `buildCalendarHeatmapData` with the locale and zone taken from
@@ -7234,6 +7850,36 @@ const useCalendarHeatmapData = (series, options = {}) => {
7234
7850
  ]);
7235
7851
  };
7236
7852
  //#endregion
7853
+ //#region src/charts/heatmap-chart/use-month-calendar-heatmap-data.ts
7854
+ /**
7855
+ * `buildMonthCalendarHeatmapData` with the locale taken from `GlobalChartsProvider`
7856
+ * where the caller names none.
7857
+ *
7858
+ * @param valueByDay - Value per `yyyy-MM-dd`. Held by reference, so a caller that rebuilds it each render defeats the memo.
7859
+ * @param range - As for `buildMonthCalendarHeatmapData`.
7860
+ * @param options - As for `buildMonthCalendarHeatmapData`; `locale` wins over the provider.
7861
+ * @return Columns and groups for `HeatmapChart`.
7862
+ */
7863
+ const useMonthCalendarHeatmapData = (valueByDay, range, options = {}) => {
7864
+ const formatting = useChartFormatting();
7865
+ const locale = options.locale ?? formatting.locale;
7866
+ const { weekStartsOn } = options;
7867
+ const { start, end } = range;
7868
+ return useMemo(() => buildMonthCalendarHeatmapData(valueByDay, {
7869
+ start,
7870
+ end
7871
+ }, {
7872
+ locale,
7873
+ weekStartsOn
7874
+ }), [
7875
+ valueByDay,
7876
+ start,
7877
+ end,
7878
+ locale,
7879
+ weekStartsOn
7880
+ ]);
7881
+ };
7882
+ //#endregion
7237
7883
  //#region ../../../node_modules/.pnpm/is-plain-object@5.1.0/node_modules/is-plain-object/dist/is-plain-object.mjs
7238
7884
  /*!
7239
7885
  * is-plain-object <https://github.com/jonschlinkert/is-plain-object>
@@ -7301,7 +7947,24 @@ function paramCase(input, options) {
7301
7947
  return dotCase(input, __assign({ delimiter: "-" }, options));
7302
7948
  }
7303
7949
  //#endregion
7304
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
7950
+ //#region ../../../node_modules/.pnpm/@wordpress+warning@3.55.0/node_modules/@wordpress/warning/build-module/utils.mjs
7951
+ var logged = /* @__PURE__ */ new Set();
7952
+ //#endregion
7953
+ //#region ../../../node_modules/.pnpm/@wordpress+warning@3.55.0/node_modules/@wordpress/warning/build-module/index.mjs
7954
+ function isDev() {
7955
+ return globalThis.SCRIPT_DEBUG === true;
7956
+ }
7957
+ function warning(message) {
7958
+ if (!isDev()) return;
7959
+ if (logged.has(message)) return;
7960
+ console.warn(message);
7961
+ try {
7962
+ throw Error(message);
7963
+ } catch {}
7964
+ logged.add(message);
7965
+ }
7966
+ //#endregion
7967
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
7305
7968
  function useUpdateEffect(effect, deps) {
7306
7969
  const mountedRef = useRef$1(false);
7307
7970
  useEffect$1(() => {
@@ -8871,7 +9534,7 @@ _createEmotion.css;
8871
9534
  _createEmotion.sheet;
8872
9535
  _createEmotion.cache;
8873
9536
  //#endregion
8874
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
9537
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
8875
9538
  var isSerializedStyles = (o) => typeof o !== "undefined" && o !== null && ["name", "styles"].every((p) => typeof o[p] !== "undefined");
8876
9539
  var useCx = () => {
8877
9540
  const cache = __unsafe_useEmotionCache();
@@ -8978,7 +9641,7 @@ function memize(fn, options) {
8978
9641
  return memoized;
8979
9642
  }
8980
9643
  //#endregion
8981
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/colors-values.mjs
9644
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/utils/colors-values.mjs
8982
9645
  var white = "#fff";
8983
9646
  var GRAY = {
8984
9647
  900: "#1e1e1e",
@@ -9028,8 +9691,8 @@ var UI = {
9028
9691
  border: THEME.gray[600],
9029
9692
  borderHover: THEME.gray[700],
9030
9693
  borderFocus: THEME.accent,
9031
- borderDisabled: THEME.gray[400],
9032
- textDisabled: THEME.gray[600],
9694
+ borderDisabled: `var(--wpds-color-stroke-interactive-neutral-disabled, #dbdbdb)`,
9695
+ textDisabled: `var(--wpds-color-foreground-interactive-neutral-disabled, #8d8d8d)`,
9033
9696
  darkGrayPlaceholder: `color-mix(in srgb, ${THEME.foreground}, transparent 38%)`,
9034
9697
  lightGrayPlaceholder: `color-mix(in srgb, ${THEME.background}, transparent 35%)`
9035
9698
  };
@@ -9057,7 +9720,7 @@ var COLORS = Object.freeze({
9057
9720
  ui: UI
9058
9721
  });
9059
9722
  //#endregion
9060
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/config-values.mjs
9723
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/utils/config-values.mjs
9061
9724
  var CONTROL_HEIGHT = "36px";
9062
9725
  var CONTROL_PROPS = {
9063
9726
  controlPaddingX: 12,
@@ -9111,8 +9774,8 @@ var config_values_default = Object.assign({}, CONTROL_PROPS, {
9111
9774
  transitionTimingFunctionControl: "cubic-bezier(0.12, 0.8, 0.32, 1)"
9112
9775
  });
9113
9776
  //#endregion
9114
- //#region ../../../node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/es6/index.js
9115
- var require_es6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9777
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
9778
+ var import_es6 = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
9116
9779
  module.exports = function equal(a, b) {
9117
9780
  if (a === b) return true;
9118
9781
  if (a && b && typeof a == "object" && typeof b == "object") {
@@ -9156,31 +9819,8 @@ var require_es6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9156
9819
  }
9157
9820
  return a !== a && b !== b;
9158
9821
  };
9159
- }));
9160
- //#endregion
9161
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/utils.mjs
9162
- var logged = /* @__PURE__ */ new Set();
9163
- //#endregion
9164
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/index.mjs
9165
- function isDev() {
9166
- return globalThis.SCRIPT_DEBUG === true;
9167
- }
9168
- function warning(message) {
9169
- if (!isDev()) return;
9170
- if (logged.has(message)) return;
9171
- console.warn(message);
9172
- try {
9173
- throw Error(message);
9174
- } catch {}
9175
- logged.add(message);
9176
- }
9177
- //#endregion
9178
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
9179
- var import_es6 = /* @__PURE__ */ __toESM(require_es6(), 1);
9180
- var ComponentsContext = createContext$1(
9181
- /** @type {Record<string, any>} */
9182
- {}
9183
- );
9822
+ })))(), 1);
9823
+ var ComponentsContext = createContext$1({});
9184
9824
  ComponentsContext.displayName = "ComponentsContext";
9185
9825
  var useComponentsContext = () => useContext$1(ComponentsContext);
9186
9826
  function useContextSystemBridge({ value }) {
@@ -9202,18 +9842,18 @@ var BaseContextSystemProvider = ({ children, value }) => {
9202
9842
  };
9203
9843
  memo(BaseContextSystemProvider);
9204
9844
  //#endregion
9205
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/constants.mjs
9845
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/context/constants.mjs
9206
9846
  var COMPONENT_NAMESPACE = "data-wp-component";
9207
9847
  var CONNECTED_NAMESPACE = "data-wp-c16t";
9208
9848
  var CONNECT_STATIC_NAMESPACE = "__contextSystemKey__";
9209
9849
  //#endregion
9210
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
9850
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
9211
9851
  function getStyledClassName(namespace) {
9212
9852
  return `components-${paramCase(namespace)}`;
9213
9853
  }
9214
9854
  var getStyledClassNameFromKey = memize(getStyledClassName);
9215
9855
  //#endregion
9216
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/context-connect.mjs
9856
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/context/context-connect.mjs
9217
9857
  function contextConnect(Component, namespace) {
9218
9858
  return _contextConnect(Component, namespace, { forwardsRef: true });
9219
9859
  }
@@ -9230,7 +9870,7 @@ function _contextConnect(Component, namespace, options) {
9230
9870
  });
9231
9871
  }
9232
9872
  //#endregion
9233
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/utils.mjs
9873
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/context/utils.mjs
9234
9874
  function getNamespace(componentName) {
9235
9875
  return { [COMPONENT_NAMESPACE]: componentName };
9236
9876
  }
@@ -9238,7 +9878,7 @@ function getConnectedNamespace() {
9238
9878
  return { [CONNECTED_NAMESPACE]: true };
9239
9879
  }
9240
9880
  //#endregion
9241
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
9881
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
9242
9882
  function useContextSystem(props, namespace) {
9243
9883
  const contextSystemProps = useComponentsContext();
9244
9884
  if (typeof namespace === "undefined") globalThis.SCRIPT_DEBUG === true && warning("useContextSystem: Please provide a namespace");
@@ -9247,10 +9887,12 @@ function useContextSystem(props, namespace) {
9247
9887
  ...getConnectedNamespace(),
9248
9888
  ...getNamespace(namespace)
9249
9889
  };
9250
- const { _overrides: overrideProps, ...otherContextProps } = contextProps;
9890
+ const { _overrides, ...otherContextProps } = contextProps;
9891
+ const overrideProps = _overrides;
9251
9892
  const initialMergedProps = Object.entries(otherContextProps).length ? Object.assign({}, otherContextProps, props) : props;
9893
+ const propsWithChildren = initialMergedProps;
9252
9894
  const classes = useCx()(getStyledClassNameFromKey(namespace), props.className);
9253
- const rendered = typeof initialMergedProps.renderChildren === "function" ? initialMergedProps.renderChildren(initialMergedProps) : initialMergedProps.children;
9895
+ const rendered = typeof propsWithChildren.renderChildren === "function" ? propsWithChildren.renderChildren(initialMergedProps) : propsWithChildren.children;
9254
9896
  for (const key in initialMergedProps) finalComponentProps[key] = initialMergedProps[key];
9255
9897
  for (const key in overrideProps) finalComponentProps[key] = overrideProps[key];
9256
9898
  if (rendered !== void 0) finalComponentProps.children = rendered;
@@ -9258,7 +9900,7 @@ function useContextSystem(props, namespace) {
9258
9900
  return finalComponentProps;
9259
9901
  }
9260
9902
  //#endregion
9261
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
9903
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
9262
9904
  var visuallyHidden = {
9263
9905
  border: 0,
9264
9906
  clip: "rect(1px, 1px, 1px, 1px)",
@@ -9274,7 +9916,7 @@ var visuallyHidden = {
9274
9916
  wordBreak: "normal"
9275
9917
  };
9276
9918
  //#endregion
9277
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
9919
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
9278
9920
  var customAttributeRegExp = /^(data|aria|x)-/i;
9279
9921
  var eventHandlerRegExp = /^on[A-Z]/;
9280
9922
  var svgElementNames = new Set(`animate animateMotion animateTransform circle clipPath defs desc ellipse
@@ -9364,7 +10006,7 @@ function UnforwardedPolymorphicElement({ as, ...props }, ref) {
9364
10006
  }
9365
10007
  var PolymorphicElement = forwardRef$1(UnforwardedPolymorphicElement);
9366
10008
  //#endregion
9367
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/view/component.mjs
10009
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/view/component.mjs
9368
10010
  function UnforwardedView({ css, ...restProps }, ref) {
9369
10011
  return /* @__PURE__ */ jsx(PolymorphicElement, {
9370
10012
  ref,
@@ -9373,7 +10015,7 @@ function UnforwardedView({ css, ...restProps }, ref) {
9373
10015
  }
9374
10016
  var component_default$2 = Object.assign(forwardRef$1(UnforwardedView), { selector: ".components-view" });
9375
10017
  //#endregion
9376
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
10018
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
9377
10019
  function UnconnectedVisuallyHidden(props, forwardedRef) {
9378
10020
  const { style: styleProp, ...contextProps } = useContextSystem(props, "VisuallyHidden");
9379
10021
  return /* @__PURE__ */ jsx(component_default$2, {
@@ -9388,7 +10030,7 @@ function UnconnectedVisuallyHidden(props, forwardedRef) {
9388
10030
  }
9389
10031
  var component_default$1 = contextConnect(UnconnectedVisuallyHidden, "VisuallyHidden");
9390
10032
  //#endregion
9391
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
10033
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
9392
10034
  var breakpoints = [
9393
10035
  "40em",
9394
10036
  "52em",
@@ -9422,7 +10064,7 @@ function useResponsiveValue(values, options = {}) {
9422
10064
  return array[index >= array.length ? array.length - 1 : index];
9423
10065
  }
9424
10066
  //#endregion
9425
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/grid/utils.mjs
10067
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/grid/utils.mjs
9426
10068
  var ALIGNMENTS = {
9427
10069
  bottom: {
9428
10070
  alignItems: "flex-end",
@@ -9470,7 +10112,7 @@ function getAlignmentProps(alignment) {
9470
10112
  return alignment ? ALIGNMENTS[alignment] : {};
9471
10113
  }
9472
10114
  //#endregion
9473
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/grid/hook.mjs
10115
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/grid/hook.mjs
9474
10116
  function useGrid(props) {
9475
10117
  const { align, alignment, className, columnGap, columns = 2, gap = 3, isInline = false, justify, rowGap, rows, templateColumns, templateRows, ...otherProps } = useContextSystem(props, "Grid");
9476
10118
  const column = useResponsiveValue(Array.isArray(columns) ? columns : [columns]);
@@ -9512,7 +10154,7 @@ function useGrid(props) {
9512
10154
  };
9513
10155
  }
9514
10156
  //#endregion
9515
- //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/grid/component.mjs
10157
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.1.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_c300d25e93ed6b8d3659e83df7c1508a/node_modules/@wordpress/components/build-module/grid/component.mjs
9516
10158
  function UnconnectedGrid(props, forwardedRef) {
9517
10159
  const gridProps = useGrid(props);
9518
10160
  return /* @__PURE__ */ jsx(component_default$2, {
@@ -10768,6 +11410,6 @@ function TrendIndicator({ direction, value, className, style, showIcon = true })
10768
11410
  });
10769
11411
  }
10770
11412
  //#endregion
10771
- export { AccessibleTooltip, AreaChartResponsive as AreaChart, AreaChart as AreaChartUnresponsive, BarChartResponsive as BarChart, BarChart as BarChartUnresponsive, BarListChartResponsive as BarListChart, BarListChart as BarListChartUnresponsive, BaseTooltip, ConversionFunnelChartWithProvider as ConversionFunnelChart, GeoChartResponsive as GeoChart, GeoChartWithProvider as GeoChartUnresponsive, GlobalChartsContext, GlobalChartsProvider, GlobalChartsProvider as ThemeProvider, GoogleDataTableColumnRoleType, HeatmapChartResponsive as HeatmapChart, HeatmapChart as HeatmapChartUnresponsive, LeaderboardChartResponsive as LeaderboardChart, LeaderboardChart as LeaderboardChartUnresponsive, Legend, LineChartResponsive as LineChart, LineChart as LineChartUnresponsive, PieChartResponsive as PieChart, PieChart as PieChartUnresponsive, PieSemiCircleChartResponsive as PieSemiCircleChart, PieSemiCircleChart as PieSemiCircleChartUnresponsive, Sparkline, SparklineUnresponsive, TrendIndicator, buildCalendarHeatmapData, defaultTheme, formatMetricValue, formatPercentage, getBucketInfo, getColorDistance, hexToRgba, isValidHexColor, lightenHexColor, mergeThemes, mixHexColors, normalizeColorToHex, parseAsLocalDate, parseHslString, prefersLightText, relativeLuminance, resolveCssVariable, useCalendarHeatmapData, useChartFormatting, useChartLegendItems, useChartRegistration, useChartScopeElement, useGlobalChartsContext, useGlobalChartsTheme, useLeaderboardLegendItems, validateHexColor };
11413
+ export { AccessibleTooltip, AreaChartResponsive as AreaChart, AreaChart as AreaChartUnresponsive, BarChartResponsive as BarChart, BarChart as BarChartUnresponsive, BarListChartResponsive as BarListChart, BarListChart as BarListChartUnresponsive, BaseTooltip, ConversionFunnelChartWithProvider as ConversionFunnelChart, GeoChartResponsive as GeoChart, GeoChartWithProvider as GeoChartUnresponsive, GlobalChartsContext, GlobalChartsProvider, GlobalChartsProvider as ThemeProvider, GoogleDataTableColumnRoleType, HeatmapChartResponsive as HeatmapChart, HeatmapChart as HeatmapChartUnresponsive, LeaderboardChartResponsive as LeaderboardChart, LeaderboardChart as LeaderboardChartUnresponsive, Legend, LineChartResponsive as LineChart, LineChart as LineChartUnresponsive, PieChartResponsive as PieChart, PieChart as PieChartUnresponsive, PieSemiCircleChartResponsive as PieSemiCircleChart, PieSemiCircleChart as PieSemiCircleChartUnresponsive, Sparkline, SparklineUnresponsive, TrendIndicator, buildCalendarHeatmapData, buildMonthCalendarHeatmapData, defaultTheme, formatMetricValue, formatPercentage, getBucketInfo, getColorDistance, hexToRgba, isValidHexColor, lightenHexColor, mergeThemes, mixHexColors, normalizeColorToHex, parseAsLocalDate, parseHslString, prefersLightText, relativeLuminance, resolveCssVariable, useCalendarHeatmapData, useChartFormatting, useChartLegendItems, useChartRegistration, useChartScopeElement, useGlobalChartsContext, useGlobalChartsTheme, useLeaderboardLegendItems, useMonthCalendarHeatmapData, validateHexColor };
10772
11414
 
10773
11415
  //# sourceMappingURL=index.js.map