@automattic/charts 4.1.1 → 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 (73) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/TOKENS.md +1 -1
  3. package/dist/index.cjs +875 -237
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.css +30 -1
  6. package/dist/index.d.cts +106 -6
  7. package/dist/index.d.ts +106 -6
  8. package/dist/index.js +875 -239
  9. package/dist/index.js.map +1 -1
  10. package/package.json +8 -8
  11. package/src/charts/area-chart/private/overlays.tsx +1 -2
  12. package/src/charts/bar-chart/bar-chart.tsx +67 -17
  13. package/src/charts/bar-chart/index.ts +1 -1
  14. package/src/charts/bar-chart/private/band-highlight.tsx +85 -0
  15. package/src/charts/bar-chart/private/band-scale.ts +13 -0
  16. package/src/charts/bar-chart/private/band-tooltip.tsx +138 -0
  17. package/src/charts/bar-chart/private/comparison-bars.tsx +5 -10
  18. package/src/charts/bar-chart/private/index.ts +2 -0
  19. package/src/charts/bar-chart/private/test/band-highlight.test.tsx +109 -0
  20. package/src/charts/bar-chart/private/test/band-tooltip.test.ts +26 -0
  21. package/src/charts/bar-chart/private/truncated-tick-component.tsx +1 -1
  22. package/src/charts/bar-chart/private/use-bar-chart-options.ts +1 -1
  23. package/src/charts/bar-chart/test/bar-chart-pointer-events.test.tsx +11 -23
  24. package/src/charts/bar-chart/test/bar-chart.test.tsx +565 -1
  25. package/src/charts/bar-list-chart/bar-list-chart.tsx +5 -3
  26. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +1 -1
  27. package/src/charts/conversion-funnel-chart/types.ts +4 -2
  28. package/src/charts/geo-chart/types.ts +4 -2
  29. package/src/charts/heatmap-chart/build-calendar-data.ts +5 -11
  30. package/src/charts/heatmap-chart/build-month-calendar-data.ts +124 -0
  31. package/src/charts/heatmap-chart/heatmap-chart.module.scss +32 -0
  32. package/src/charts/heatmap-chart/heatmap-chart.tsx +191 -114
  33. package/src/charts/heatmap-chart/index.ts +6 -0
  34. package/src/charts/heatmap-chart/private/civil-day.ts +26 -0
  35. package/src/charts/heatmap-chart/private/column-groups.ts +71 -0
  36. package/src/charts/heatmap-chart/private/heatmap-legend.tsx +19 -3
  37. package/src/charts/heatmap-chart/private/index.ts +2 -0
  38. package/src/charts/heatmap-chart/private/keyboard-navigation.ts +201 -0
  39. package/src/charts/heatmap-chart/test/build-calendar-data.test.ts +36 -0
  40. package/src/charts/heatmap-chart/test/build-month-calendar-data.test.ts +194 -0
  41. package/src/charts/heatmap-chart/test/column-groups.test.ts +83 -0
  42. package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +428 -2
  43. package/src/charts/heatmap-chart/test/keyboard-navigation.test.ts +25 -0
  44. package/src/charts/heatmap-chart/test/use-month-calendar-heatmap-data.test.tsx +90 -0
  45. package/src/charts/heatmap-chart/test/zoned-month-calendar-data.test.ts +23 -0
  46. package/src/charts/heatmap-chart/types.ts +59 -3
  47. package/src/charts/heatmap-chart/use-month-calendar-heatmap-data.ts +34 -0
  48. package/src/charts/leaderboard-chart/types.ts +13 -14
  49. package/src/charts/line-chart/line-chart.tsx +1 -1
  50. package/src/charts/line-chart/private/line-chart-annotation.tsx +1 -1
  51. package/src/charts/line-chart/types.ts +2 -2
  52. package/src/charts/private/time-axis-options.ts +1 -1
  53. package/src/charts/private/with-responsive/with-responsive.tsx +2 -2
  54. package/src/charts/private/x-zoom/test/x-zoom.test.tsx +1 -3
  55. package/src/charts/private/x-zoom/x-zoom.tsx +1 -2
  56. package/src/components/legend/private/base-legend.tsx +1 -1
  57. package/src/components/tooltip/accessible-tooltip.tsx +127 -40
  58. package/src/components/tooltip/base-tooltip.module.scss +6 -1
  59. package/src/components/tooltip/private/bounded-tooltip.tsx +12 -6
  60. package/src/components/tooltip/test/accessible-tooltip.test.tsx +128 -0
  61. package/src/components/tooltip/test/bounded-tooltip.test.tsx +17 -0
  62. package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +7 -0
  63. package/src/components/tooltip/xy-chart-tooltip.tsx +9 -10
  64. package/src/hooks/test/use-element-size.test.tsx +13 -13
  65. package/src/hooks/use-xychart-theme.ts +1 -1
  66. package/src/index.ts +7 -1
  67. package/src/providers/chart-context/themes.ts +1 -0
  68. package/src/styles/test/chart-scope.test.ts +1 -1
  69. package/src/types.ts +6 -13
  70. package/src/utils/format-metric-value.ts +4 -4
  71. package/src/utils/get-edge-tick-widths.ts +1 -1
  72. package/src/utils/get-styles.ts +1 -1
  73. package/src/visx/types.ts +7 -10
package/dist/index.cjs CHANGED
@@ -983,7 +983,8 @@ const defaultTheme = {
983
983
  },
984
984
  heatmapChart: {
985
985
  compactCellGap: 2,
986
- compactCellSize: 11
986
+ compactCellSize: 11,
987
+ groupGap: 24
987
988
  }
988
989
  };
989
990
  //#endregion
@@ -2183,7 +2184,10 @@ function useChartLegendItems(data, options = {}, legendShape) {
2183
2184
  }
2184
2185
  //#endregion
2185
2186
  //#region src/components/tooltip/base-tooltip.module.scss
2186
- var base_tooltip_module_default = { "tooltip": "a8ccharts--zY0xG-tooltip" };
2187
+ var base_tooltip_module_default = {
2188
+ "surface": "a8ccharts--zY0xG-surface",
2189
+ "tooltip": "a8ccharts--zY0xG-tooltip"
2190
+ };
2187
2191
  //#endregion
2188
2192
  //#region src/components/tooltip/base-tooltip.tsx
2189
2193
  const DefaultTooltipContent = ({ data }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
@@ -2233,7 +2237,8 @@ const findClippingAncestor = (wrapper) => {
2233
2237
  /**
2234
2238
  * Where the box goes, in wrapper coordinates. Automatic placement flips to
2235
2239
  * the side that clips less, then clamps inside `bounds`; below-axis placement
2236
- * is centered on the anchor and clamped horizontally.
2240
+ * is centered on the anchor and clamped horizontally; beside placement preserves
2241
+ * the anchor's y and only flips horizontally before bounds clamping.
2237
2242
  *
2238
2243
  * The flip measures against the visible part of the wrapper, its intersection
2239
2244
  * with `bounds`. Only the clamp is bound by the page: a box that fits inside the
@@ -2247,7 +2252,7 @@ const findClippingAncestor = (wrapper) => {
2247
2252
  * @param params.offsetTop - Gap between the anchor and the box, vertically.
2248
2253
  * @param params.box - Rendered size of the box.
2249
2254
  * @param params.wrapper - The wrapper's own edges, in wrapper coordinates.
2250
- * @param params.placement - Fixed below-axis placement or automatic flipping.
2255
+ * @param params.placement - Below-axis, beside without vertical flipping, or automatic flipping.
2251
2256
  * @param params.bounds - Edges the box must keep inside, in wrapper coordinates.
2252
2257
  * @return The box's top-left corner; fixed placement preserves the axis's subpixel y.
2253
2258
  */
@@ -2271,7 +2276,8 @@ const getBoundedPosition = ({ left, top, offsetLeft, offsetTop, box, wrapper, bo
2271
2276
  const upY = top - offsetTop - box.height;
2272
2277
  const downOverflow = downY + box.height - fit.bottom;
2273
2278
  const upOverflow = fit.top - upY;
2274
- let y = downOverflow > 0 && downOverflow > upOverflow ? upY : downY;
2279
+ let y = top;
2280
+ if (placement !== "beside") y = downOverflow > 0 && downOverflow > upOverflow ? upY : downY;
2275
2281
  x = clamp(x, bounds.left, bounds.right, box.width);
2276
2282
  y = clamp(y, bounds.top, bounds.bottom, box.height);
2277
2283
  return {
@@ -2287,8 +2293,7 @@ const getBoundedPosition = ({ left, top, offsetLeft, offsetTop, box, wrapper, bo
2287
2293
  * alone would let the box spill into an `overflow: hidden` card and be cut off.
2288
2294
  *
2289
2295
  * Re-measures on every render, so a box whose content changes width between
2290
- * two hovers is placed for its current size. See `getBoundedPosition` for the
2291
- * below-axis exception.
2296
+ * two hovers is placed for its current size.
2292
2297
  *
2293
2298
  * @param props - visx `Tooltip` props.
2294
2299
  * @param props.left - Anchor x, in wrapper coordinates.
@@ -2298,7 +2303,7 @@ const getBoundedPosition = ({ left, top, offsetLeft, offsetTop, box, wrapper, bo
2298
2303
  * @param props.style - Box styles; visx's defaults unless `unstyled`.
2299
2304
  * @param props.unstyled - Skip `style` and leave the box bare.
2300
2305
  * @param props.children - Box content.
2301
- * @param props.placement - Fixed below-axis placement or automatic flipping.
2306
+ * @param props.placement - Below-axis, beside without vertical flipping, or automatic flipping.
2302
2307
  * @return The tooltip box.
2303
2308
  */
2304
2309
  const BoundedTooltip = ({ left = 0, top = 0, offsetLeft = DEFAULT_OFFSET, offsetTop = DEFAULT_OFFSET, style = _visx_tooltip.defaultStyles, unstyled = false, children, placement = "auto", ...rest }) => {
@@ -2401,7 +2406,6 @@ const crosshairPaintProps = (props = {}) => {
2401
2406
  };
2402
2407
  const DEFAULT_GLYPH_RADIUS = 4;
2403
2408
  const FALLBACK_COLOR = "#222";
2404
- const DEFAULT_TOOLTIP_Z_INDEX = 3;
2405
2409
  const PORTAL_OPTIONS = /* @__PURE__ */ new Set([
2406
2410
  "scroll",
2407
2411
  "debounce",
@@ -2429,7 +2433,7 @@ const defaultRenderGlyph$1 = ({ key, ...props }) => /* @__PURE__ */ (0, react_js
2429
2433
  seriesKey: key,
2430
2434
  ...props
2431
2435
  }, key);
2432
- 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 }) => {
2436
+ 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 }) => {
2433
2437
  const tooltipProps = Object.fromEntries(Object.entries(rest).filter(([key]) => !PORTAL_OPTIONS.has(key)));
2434
2438
  const { colorScale, theme, innerHeight = 0, innerWidth = 0, margin, xScale, yScale, dataRegistry } = (0, react.useContext)(_visx_xychart.DataContext) || {};
2435
2439
  const tooltipContent = renderTooltip ? renderTooltip({
@@ -2484,7 +2488,7 @@ const XyChartTooltipContent = ({ tooltipContext, container, renderTooltip, rende
2484
2488
  const crosshairStroke = theme?.gridStyles?.stroke ?? theme?.htmlLabel?.color ?? FALLBACK_COLOR;
2485
2489
  const marginTop = margin?.top ?? 0;
2486
2490
  const marginLeft = margin?.left ?? 0;
2487
- const TooltipComponent = detectBounds || tooltipPlacement === "below-axis" ? BoundedTooltip : _visx_tooltip.Tooltip;
2491
+ const TooltipComponent = detectBounds || tooltipPlacement !== "auto" ? BoundedTooltip : _visx_tooltip.Tooltip;
2488
2492
  const boxStyle = {
2489
2493
  ..._visx_tooltip.defaultStyles,
2490
2494
  zIndex,
@@ -2521,11 +2525,11 @@ const XyChartTooltipContent = ({ tooltipContext, container, renderTooltip, rende
2521
2525
  ]
2522
2526
  }), container && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipComponent, {
2523
2527
  left: tooltipLeft,
2524
- top: tooltipPlacement === "below-axis" ? marginTop + innerHeight + (margin?.bottom ?? 0) : tooltipTop,
2528
+ top: tooltipPlacement === "below-axis" ? marginTop + innerHeight + (margin?.bottom ?? 0) : tooltipAnchorTop ?? tooltipTop,
2525
2529
  style: boxStyle,
2526
2530
  applyPositionStyle: true,
2527
2531
  ...tooltipProps,
2528
- ...tooltipPlacement === "below-axis" && { placement: tooltipPlacement },
2532
+ ...tooltipPlacement !== "auto" && { placement: tooltipPlacement },
2529
2533
  children: tooltipContent
2530
2534
  }), container)] });
2531
2535
  };
@@ -2591,11 +2595,32 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
2591
2595
  return flattened;
2592
2596
  }, [series, mode]);
2593
2597
  const hasKeyboardSelection = (0, react.useRef)(false);
2598
+ const closeTooltipNow = (0, react.useCallback)(() => {
2599
+ tooltipContext?.updateTooltip({
2600
+ tooltipOpen: false,
2601
+ tooltipLeft: void 0,
2602
+ tooltipTop: void 0,
2603
+ tooltipData: void 0
2604
+ });
2605
+ }, []);
2606
+ const chartId = (0, react.useContext)(ChartInstanceContext)?.chartId;
2607
+ const { getHiddenSeries } = useGlobalChartsContext();
2608
+ const hiddenSeriesKey = chartId ? JSON.stringify([...getHiddenSeries(chartId)].sort()) : void 0;
2609
+ const lastHiddenSeriesKey = (0, react.useRef)(hiddenSeriesKey);
2610
+ (0, react.useEffect)(() => {
2611
+ const changed = lastHiddenSeriesKey.current !== hiddenSeriesKey;
2612
+ lastHiddenSeriesKey.current = hiddenSeriesKey;
2613
+ if (changed && selectedIndex === void 0) closeTooltipNow();
2614
+ }, [
2615
+ hiddenSeriesKey,
2616
+ selectedIndex,
2617
+ closeTooltipNow
2618
+ ]);
2594
2619
  (0, react.useEffect)(() => {
2595
2620
  if (selectedIndex === void 0) {
2596
2621
  if (hasKeyboardSelection.current) {
2597
2622
  hasKeyboardSelection.current = false;
2598
- tooltipContext?.hideTooltip();
2623
+ closeTooltipNow();
2599
2624
  }
2600
2625
  return;
2601
2626
  }
@@ -2664,14 +2689,43 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
2664
2689
  renderTooltip: focusableRenderTooltip
2665
2690
  });
2666
2691
  };
2667
- const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints, onActivate, preventTooltipScroll = false }) => {
2692
+ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints, onActivate, preventTooltipScroll = false, visibleSeriesKey }) => {
2693
+ const getChartRoot = (0, react.useCallback)(() => chartRef.current?.closest("[role=\"grid\"]") ?? chartRef.current, [chartRef]);
2694
+ const focusWithoutScrollIfNeeded = (0, react.useCallback)((element) => {
2695
+ if (preventTooltipScroll) element?.focus({ preventScroll: true });
2696
+ else element?.focus();
2697
+ }, [preventTooltipScroll]);
2698
+ const tooltipRef = (0, react.useCallback)((element) => {
2699
+ if (element && selectedIndex !== void 0) focusWithoutScrollIfNeeded(element);
2700
+ }, [focusWithoutScrollIfNeeded, selectedIndex]);
2701
+ const previousTotalPoints = (0, react.useRef)(totalPoints);
2702
+ const previousVisibleSeriesKey = (0, react.useRef)(visibleSeriesKey);
2703
+ (0, react.useEffect)(() => {
2704
+ const countChanged = previousTotalPoints.current !== totalPoints;
2705
+ const seriesChanged = previousVisibleSeriesKey.current !== visibleSeriesKey;
2706
+ previousTotalPoints.current = totalPoints;
2707
+ previousVisibleSeriesKey.current = visibleSeriesKey;
2708
+ if (!countChanged && !seriesChanged || selectedIndex === void 0) return;
2709
+ const { activeElement } = document;
2710
+ const focusIsInChart = activeElement !== null && !!getChartRoot()?.contains(activeElement);
2711
+ if (totalPoints === 0 || !focusIsInChart || !countChanged) {
2712
+ setSelectedIndex(void 0);
2713
+ setIsNavigating(false);
2714
+ if (focusIsInChart) focusWithoutScrollIfNeeded(getChartRoot());
2715
+ return;
2716
+ }
2717
+ setSelectedIndex(Math.min(selectedIndex, totalPoints - 1));
2718
+ }, [
2719
+ selectedIndex,
2720
+ totalPoints,
2721
+ visibleSeriesKey,
2722
+ setSelectedIndex,
2723
+ setIsNavigating,
2724
+ getChartRoot,
2725
+ focusWithoutScrollIfNeeded
2726
+ ]);
2668
2727
  return {
2669
- tooltipRef: (0, react.useCallback)((element) => {
2670
- if (element && selectedIndex !== void 0) {
2671
- if (preventTooltipScroll) element.focus({ preventScroll: true });
2672
- else element.focus();
2673
- }
2674
- }, [preventTooltipScroll, selectedIndex]),
2728
+ tooltipRef,
2675
2729
  onChartFocus: (0, react.useCallback)((event) => {
2676
2730
  if (event.currentTarget.contains(event.relatedTarget)) return;
2677
2731
  if (!isNavigating && selectedIndex !== void 0) setSelectedIndex(0);
@@ -2684,44 +2738,35 @@ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating,
2684
2738
  setIsNavigating(false);
2685
2739
  }, [setIsNavigating]),
2686
2740
  onChartKeyDown: (0, react.useCallback)((event) => {
2687
- if (totalPoints === 0) return;
2688
- const focusChart = () => {
2689
- if (preventTooltipScroll) chartRef.current?.focus({ preventScroll: true });
2690
- else chartRef.current?.focus();
2691
- };
2692
- if (event.key === "Tab") {
2693
- focusChart();
2741
+ if (event.key === "Tab" || event.key === "Escape") {
2742
+ if (event.key === "Escape" && selectedIndex !== void 0) event.preventDefault();
2743
+ focusWithoutScrollIfNeeded(getChartRoot());
2694
2744
  setSelectedIndex(void 0);
2695
2745
  setIsNavigating(false);
2696
2746
  return;
2697
2747
  }
2748
+ if (totalPoints === 0) return;
2698
2749
  const currentSelectedIndex = selectedIndex === void 0 ? -1 : selectedIndex;
2699
- if (currentSelectedIndex + 1 >= totalPoints && ["ArrowRight"].includes(event.key)) {
2700
- focusChart();
2701
- setSelectedIndex(void 0);
2702
- setIsNavigating(false);
2703
- return;
2704
- }
2705
- event.preventDefault();
2706
- if (["ArrowRight"].includes(event.key)) {
2750
+ if (event.key === "ArrowRight") {
2751
+ event.preventDefault();
2707
2752
  setIsNavigating(true);
2708
- setSelectedIndex((currentSelectedIndex + 1) % totalPoints);
2709
- } else if (["ArrowLeft"].includes(event.key)) {
2753
+ setSelectedIndex(Math.min(currentSelectedIndex + 1, totalPoints - 1));
2754
+ } else if (event.key === "ArrowLeft") {
2755
+ event.preventDefault();
2710
2756
  setIsNavigating(true);
2711
- setSelectedIndex((currentSelectedIndex - 1 + totalPoints) % totalPoints);
2712
- } else if (event.key === "Escape") {
2713
- setSelectedIndex(void 0);
2714
- setIsNavigating(false);
2715
- focusChart();
2716
- } else if ((event.key === "Enter" || event.key === " ") && selectedIndex !== void 0) onActivate?.(selectedIndex);
2757
+ setSelectedIndex(Math.max(currentSelectedIndex - 1, 0));
2758
+ } else if ((event.key === "Enter" || event.key === " ") && selectedIndex !== void 0) {
2759
+ event.preventDefault();
2760
+ onActivate?.(selectedIndex);
2761
+ }
2717
2762
  }, [
2718
2763
  totalPoints,
2719
2764
  selectedIndex,
2720
2765
  setSelectedIndex,
2721
2766
  setIsNavigating,
2722
- chartRef,
2723
- onActivate,
2724
- preventTooltipScroll
2767
+ getChartRoot,
2768
+ focusWithoutScrollIfNeeded,
2769
+ onActivate
2725
2770
  ])
2726
2771
  };
2727
2772
  };
@@ -5278,6 +5323,20 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
5278
5323
  ]);
5279
5324
  }
5280
5325
  //#endregion
5326
+ //#region src/charts/bar-chart/private/band-scale.ts
5327
+ /**
5328
+ * Match the inner slot scale used by visx BarGroup.
5329
+ * @param keys - Visible primary series keys.
5330
+ * @param bandwidth - Category band width.
5331
+ * @param padding - Padding between slots.
5332
+ * @return The grouped bar scale.
5333
+ */
5334
+ const createGroupScale = (keys, bandwidth, padding) => (0, _visx_scale.scaleBand)({
5335
+ domain: keys,
5336
+ range: [0, bandwidth],
5337
+ padding
5338
+ });
5339
+ //#endregion
5281
5340
  //#region src/charts/bar-chart/private/comparison-bars-geometry.ts
5282
5341
  /**
5283
5342
  * Output position of a value scale's baseline: zero if in-domain, else the
@@ -5350,11 +5409,7 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
5350
5409
  const valueScale = horizontal ? xScale : yScale;
5351
5410
  const bandwidth = bandScale.bandwidth ? bandScale.bandwidth() : 0;
5352
5411
  if (!bandwidth) return null;
5353
- const groupScale = (0, _visx_scale.scaleBand)({
5354
- domain: primaryKeys,
5355
- range: [0, bandwidth],
5356
- padding: groupPadding
5357
- });
5412
+ const groupScale = createGroupScale(primaryKeys, bandwidth, groupPadding);
5358
5413
  const slotThickness = groupScale.bandwidth();
5359
5414
  const baseline = getValueScaleBaseline(valueScale);
5360
5415
  const bandAccessor = horizontal ? yAccessor : xAccessor;
@@ -5406,6 +5461,163 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
5406
5461
  });
5407
5462
  };
5408
5463
  //#endregion
5464
+ //#region src/charts/bar-chart/private/band-highlight.tsx
5465
+ /**
5466
+ * Render and report the active band using the chart’s registered scales.
5467
+ * @param root0 - Highlight options.
5468
+ * @param root0.visible - Whether to paint the band.
5469
+ * @param root0.horizontal - Whether categories run vertically.
5470
+ * @param root0.onChange - Receives selected band geometry.
5471
+ * @return The optional band highlight.
5472
+ */
5473
+ function BandHighlight({ visible, horizontal, onChange }) {
5474
+ const { xScale, yScale, dataRegistry, margin, innerWidth, innerHeight } = (0, react.useContext)(_visx_xychart.DataContext);
5475
+ const { tooltipOpen, tooltipData } = (0, react.useContext)(_visx_xychart.TooltipContext);
5476
+ const nearest = tooltipData?.nearestDatum;
5477
+ const selection = (0, react.useMemo)(() => {
5478
+ if (!tooltipOpen || !nearest) return null;
5479
+ const scale = horizontal ? yScale : xScale;
5480
+ const entry = dataRegistry?.get(nearest.key);
5481
+ const accessor = horizontal ? entry?.yAccessor : entry?.xAccessor;
5482
+ if (!scale?.bandwidth || !accessor) return null;
5483
+ const position = scale(accessor(nearest.datum));
5484
+ if (position === void 0 || !Number.isFinite(position)) return null;
5485
+ return {
5486
+ datum: nearest.datum,
5487
+ x: horizontal ? margin?.left ?? 0 : position,
5488
+ y: horizontal ? position : margin?.top ?? 0,
5489
+ width: horizontal ? innerWidth : scale.bandwidth(),
5490
+ height: horizontal ? scale.bandwidth() : innerHeight
5491
+ };
5492
+ }, [
5493
+ tooltipOpen,
5494
+ nearest,
5495
+ horizontal,
5496
+ xScale,
5497
+ yScale,
5498
+ dataRegistry,
5499
+ margin,
5500
+ innerWidth,
5501
+ innerHeight
5502
+ ]);
5503
+ const stableSelection = useDeepMemo(selection);
5504
+ const onChangeRef = (0, react.useRef)(onChange);
5505
+ onChangeRef.current = onChange;
5506
+ (0, react.useEffect)(() => {
5507
+ onChangeRef.current?.(stableSelection);
5508
+ }, [stableSelection]);
5509
+ (0, react.useEffect)(() => () => onChangeRef.current?.(null), []);
5510
+ if (!visible || !selection) return null;
5511
+ const { x, y, width, height } = selection;
5512
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
5513
+ x,
5514
+ y,
5515
+ width,
5516
+ height,
5517
+ pointerEvents: "none",
5518
+ fill: CATALOG_POINTERS.surfaceSecondary
5519
+ });
5520
+ }
5521
+ //#endregion
5522
+ //#region src/charts/bar-chart/private/band-tooltip.tsx
5523
+ /**
5524
+ * Find a category by its painted center, including outer padding and reversed ranges.
5525
+ * @param data - Registered series points.
5526
+ * @param accessor - Category accessor.
5527
+ * @param scale - Rendered category band scale.
5528
+ * @param position - Pointer coordinate on the category axis.
5529
+ * @return Index of the nearest category, or -1 when none is registered.
5530
+ */
5531
+ function nearestBandIndex(data, accessor, scale, position) {
5532
+ let nearest = -1;
5533
+ let distance = Infinity;
5534
+ data.forEach((datum, index) => {
5535
+ const start = scale(accessor(datum));
5536
+ if (start === void 0) return;
5537
+ const candidate = Math.abs(start + scale.bandwidth() / 2 - position);
5538
+ if (candidate < distance) {
5539
+ distance = candidate;
5540
+ nearest = index;
5541
+ }
5542
+ });
5543
+ return nearest;
5544
+ }
5545
+ /**
5546
+ * Correct visx's range-based band inversion, which omits outer padding.
5547
+ * @param root0 - Registered series selection.
5548
+ * @param root0.keys - Visible series keys.
5549
+ * @param root0.groupPadding - Padding between grouped bars.
5550
+ * @param root0.withTooltips - Whether pointer events update the tooltip.
5551
+ * @param root0.onPointerDown - Receives the corrected pointer-down datum.
5552
+ * @param root0.onPointerUp - Receives the corrected pointer-up datum.
5553
+ * @return No visual content.
5554
+ */
5555
+ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointerUp }) {
5556
+ const { xScale, yScale, dataRegistry, horizontal } = (0, react.useContext)(_visx_xychart.DataContext);
5557
+ const { showTooltip } = (0, react.useContext)(_visx_xychart.TooltipContext);
5558
+ const scale = horizontal ? yScale : xScale;
5559
+ const bandwidth = scale?.bandwidth?.() ?? 0;
5560
+ const groupScale = (0, react.useMemo)(() => createGroupScale(keys, bandwidth, groupPadding), [
5561
+ keys,
5562
+ bandwidth,
5563
+ groupPadding
5564
+ ]);
5565
+ const getSelections = (0, react.useCallback)((params) => {
5566
+ const point = params?.svgPoint;
5567
+ if (!point || !scale?.bandwidth) return [];
5568
+ const selections = [];
5569
+ for (const key of keys) {
5570
+ const entry = dataRegistry?.get(key);
5571
+ if (!entry) continue;
5572
+ const accessor = horizontal ? entry.yAccessor : entry.xAccessor;
5573
+ const index = nearestBandIndex(entry.data, accessor, scale, horizontal ? point.y : point.x);
5574
+ if (index < 0) continue;
5575
+ const datum = entry.data[index];
5576
+ const start = Number(scale(accessor(datum))) + Number(groupScale(key));
5577
+ const end = start + groupScale.step();
5578
+ const position = horizontal ? point.y : point.x;
5579
+ const distance = position >= start && position <= end ? 0 : Math.abs(position - (start + end) / 2);
5580
+ selections.push({
5581
+ event: params.event,
5582
+ key,
5583
+ datum,
5584
+ index,
5585
+ svgPoint: point,
5586
+ distanceX: horizontal ? 0 : distance,
5587
+ distanceY: horizontal ? distance : 0
5588
+ });
5589
+ }
5590
+ return selections;
5591
+ }, [
5592
+ scale,
5593
+ dataRegistry,
5594
+ horizontal,
5595
+ keys,
5596
+ groupScale
5597
+ ]);
5598
+ const handlePointer = (0, react.useCallback)((params) => {
5599
+ const selections = getSelections(params);
5600
+ if (withTooltips && params?.event.type !== "pointerup") selections.forEach(showTooltip);
5601
+ const callback = params?.event.type === "pointerdown" ? onPointerDown : onPointerUp;
5602
+ if (params?.event.type === "pointermove" || !callback) return;
5603
+ const nearest = selections.reduce((best, selection) => {
5604
+ const distance = (value) => Math.hypot(value.distanceX, value.distanceY);
5605
+ return !best || distance(selection) <= distance(best) ? selection : best;
5606
+ }, void 0);
5607
+ if (nearest) callback(nearest);
5608
+ }, [
5609
+ getSelections,
5610
+ withTooltips,
5611
+ showTooltip,
5612
+ onPointerDown,
5613
+ onPointerUp
5614
+ ]);
5615
+ (0, _visx_xychart.useEventEmitter)("pointermove", withTooltips ? handlePointer : void 0);
5616
+ (0, _visx_xychart.useEventEmitter)("pointerdown", handlePointer);
5617
+ (0, _visx_xychart.useEventEmitter)("pointerup", onPointerUp ? handlePointer : void 0);
5618
+ return null;
5619
+ }
5620
+ //#endregion
5409
5621
  //#region src/charts/bar-chart/bar-chart.tsx
5410
5622
  const validateData$2 = (data) => {
5411
5623
  if (!data?.length) return "No data available";
@@ -5417,7 +5629,7 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ (0, react_jsx_runtime
5417
5629
  className: bar_chart_module_default["bar-chart__tooltip-row"],
5418
5630
  children: (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("%1$s: %2$s", "jetpack-charts"), label, value)
5419
5631
  });
5420
- 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 }) => {
5632
+ 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 }) => {
5421
5633
  const legendInteractive = legend.interactive ?? false;
5422
5634
  const legendCollapseGroups = legend.collapseGroups ?? false;
5423
5635
  const horizontal = orientation === "horizontal";
@@ -5443,8 +5655,6 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5443
5655
  }, [height]);
5444
5656
  const [selectedIndex, setSelectedIndex] = (0, react.useState)(void 0);
5445
5657
  const [isNavigating, setIsNavigating] = (0, react.useState)(false);
5446
- const primarySeriesForNav = dataWithVisibleZeros.filter((s) => s.options?.type !== "comparison");
5447
- const totalPoints = Math.max(0, ...primarySeriesForNav.map((s) => s.data?.length || 0)) * primarySeriesForNav.length;
5448
5658
  const seriesWithVisibility = (0, react.useMemo)(() => dataWithVisibleZeros.map((series, index) => ({
5449
5659
  series,
5450
5660
  index,
@@ -5454,6 +5664,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5454
5664
  return seriesWithVisibility.every(({ isVisible }) => !isVisible);
5455
5665
  }, [seriesWithVisibility]);
5456
5666
  const primaryEntries = (0, react.useMemo)(() => seriesWithVisibility.filter(({ isVisible, series }) => isVisible && series.options?.type !== "comparison"), [seriesWithVisibility]);
5667
+ const totalPoints = Math.max(0, ...primaryEntries.map((e) => e.series.data.length)) * primaryEntries.length;
5457
5668
  const primaryKeys = (0, react.useMemo)(() => primaryEntries.map(({ series }) => series.label), [primaryEntries]);
5458
5669
  const primarySeries = (0, react.useMemo)(() => primaryEntries.map(({ series }) => series), [primaryEntries]);
5459
5670
  const activateSelectedBar = (0, react.useCallback)((index) => {
@@ -5468,6 +5679,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5468
5679
  key: series.label
5469
5680
  });
5470
5681
  }, [primaryEntries, onDatumActivate]);
5682
+ const visibleSeriesKey = (0, react.useMemo)(() => JSON.stringify(primaryKeys), [primaryKeys]);
5471
5683
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
5472
5684
  selectedIndex,
5473
5685
  setSelectedIndex,
@@ -5475,7 +5687,8 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5475
5687
  setIsNavigating,
5476
5688
  chartRef,
5477
5689
  totalPoints,
5478
- onActivate: activateSelectedBar
5690
+ onActivate: activateSelectedBar,
5691
+ visibleSeriesKey
5479
5692
  });
5480
5693
  const comparisonEntries = (0, react.useMemo)(() => {
5481
5694
  const primaryByGroup = new Map(primaryEntries.map(({ series, index }) => [series.group, {
@@ -5538,7 +5751,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5538
5751
  chartOptions.yScale,
5539
5752
  horizontal
5540
5753
  ]);
5541
- const getBarBackground = (0, react.useCallback)((index) => () => withPatterns ? `url(#${getPatternId(chartId, index)})` : getElementStyles({
5754
+ const getBarBackground = (0, react.useCallback)((index) => (datum) => withPatterns ? `url(#${getPatternId(chartId, index)})` : datum.color ?? getElementStyles({
5542
5755
  data: dataSorted[index],
5543
5756
  index
5544
5757
  }).color, [
@@ -5708,13 +5921,13 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5708
5921
  const chartHeight = contentHeight > 0 ? contentHeight : height;
5709
5922
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5710
5923
  role: "grid",
5924
+ ref: chartRef,
5711
5925
  "aria-label": (0, _wordpress_i18n.__)("Bar chart", "jetpack-charts"),
5712
5926
  tabIndex: 0,
5713
5927
  onKeyDown: onChartKeyDown,
5714
5928
  onFocus: onChartFocus,
5715
5929
  onBlur: onChartBlur,
5716
5930
  children: chartHeight > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5717
- ref: chartRef,
5718
5931
  className: xy_plot_module_default["xy-plot"],
5719
5932
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_xychart.XYChart, {
5720
5933
  theme,
@@ -5727,15 +5940,24 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5727
5940
  xScale,
5728
5941
  yScale,
5729
5942
  horizontal,
5730
- onPointerDown,
5731
- onPointerUp,
5732
5943
  pointerEventsDataKey: "nearest",
5733
5944
  children: [
5734
- !allSeriesHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Grid, {
5945
+ withTooltips && (withBandHighlight || onBandHighlightChange) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BandHighlight, {
5946
+ visible: withBandHighlight,
5947
+ horizontal,
5948
+ onChange: onBandHighlightChange
5949
+ }),
5950
+ !allSeriesHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Grid, {
5735
5951
  columns: gridVisibility.includes("y"),
5952
+ rows: false,
5953
+ numTicks: 4,
5954
+ tickValues: chartOptions.axis.x.tickValues
5955
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Grid, {
5956
+ columns: false,
5736
5957
  rows: gridVisibility.includes("x"),
5737
- numTicks: 4
5738
- }),
5958
+ numTicks: 4,
5959
+ tickValues: chartOptions.axis.y.tickValues
5960
+ })] }),
5739
5961
  withPatterns && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: dataSorted.map((seriesData, index) => renderPattern(index, getElementStyles({
5740
5962
  data: seriesData,
5741
5963
  index
@@ -5771,8 +5993,17 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5771
5993
  colorAccessor: getBarBackground(index)
5772
5994
  }, seriesData?.label))
5773
5995
  }),
5996
+ (withTooltips || onPointerDown || onPointerUp) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BandTooltip, {
5997
+ keys: primaryKeys,
5998
+ groupPadding,
5999
+ withTooltips,
6000
+ onPointerDown,
6001
+ onPointerUp
6002
+ }),
5774
6003
  !allSeriesHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, { ...chartOptions.axis.x }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, { ...chartOptions.axis.y })] }),
5775
6004
  withTooltips && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccessibleTooltip, {
6005
+ tooltipPlacement,
6006
+ tooltipAnchorTop,
5776
6007
  detectBounds: true,
5777
6008
  snapTooltipToDatumX: true,
5778
6009
  snapTooltipToDatumY: true,
@@ -6594,8 +6825,11 @@ var heatmap_chart_module_default = {
6594
6825
  "heatmap-chart__gap-start": "a8ccharts-O3YMOW-heatmap-chart__gap-start",
6595
6826
  "heatmap-chart__grid": "a8ccharts-O3YMOW-heatmap-chart__grid",
6596
6827
  "heatmap-chart__grid--compact": "a8ccharts-O3YMOW-heatmap-chart__grid--compact",
6828
+ "heatmap-chart__grid--flex-gaps": "a8ccharts-O3YMOW-heatmap-chart__grid--flex-gaps",
6597
6829
  "heatmap-chart__grid--height-capped": "a8ccharts-O3YMOW-heatmap-chart__grid--height-capped",
6830
+ "heatmap-chart__group-label": "a8ccharts-O3YMOW-heatmap-chart__group-label",
6598
6831
  "heatmap-chart__legend-label": "a8ccharts-O3YMOW-heatmap-chart__legend-label",
6832
+ "heatmap-chart__legend-scale--bar": "a8ccharts-O3YMOW-heatmap-chart__legend-scale--bar",
6599
6833
  "heatmap-chart__legend-swatch": "a8ccharts-O3YMOW-heatmap-chart__legend-swatch",
6600
6834
  "heatmap-chart__row": "a8ccharts-O3YMOW-heatmap-chart__row",
6601
6835
  "heatmap-chart__row-label": "a8ccharts-O3YMOW-heatmap-chart__row-label",
@@ -6645,7 +6879,7 @@ const getNormalizedValue = (value, extent) => {
6645
6879
  const HeatmapContext = (0, react.createContext)(null);
6646
6880
  //#endregion
6647
6881
  //#region src/charts/heatmap-chart/private/heatmap-legend.tsx
6648
- const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6882
+ const HeatmapLegend = ({ steps = 5, variant = "swatches", lessLabel, moreLabel }) => {
6649
6883
  const context = (0, react.useContext)(HeatmapContext);
6650
6884
  const { legend } = useGlobalChartsTheme();
6651
6885
  if (!context) return null;
@@ -6664,11 +6898,12 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6664
6898
  }),
6665
6899
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
6666
6900
  direction: "row",
6667
- gap: "xs",
6901
+ gap: variant === "bar" ? void 0 : "xs",
6902
+ "aria-hidden": "true",
6903
+ className: variant === "bar" ? heatmap_chart_module_default["heatmap-chart__legend-scale--bar"] : void 0,
6668
6904
  children: Array.from({ length: steps }, (_, index) => {
6669
6905
  const intensity = steps <= 1 ? 1 : index / (steps - 1);
6670
6906
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6671
- "aria-hidden": "true",
6672
6907
  className: heatmap_chart_module_default["heatmap-chart__legend-swatch"],
6673
6908
  style: {
6674
6909
  "--a8c-charts-color-heatmap-primary": primaryColorHex,
@@ -6687,16 +6922,219 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6687
6922
  });
6688
6923
  };
6689
6924
  //#endregion
6925
+ //#region src/charts/heatmap-chart/private/column-groups.ts
6926
+ const isSpan = (span) => Number.isInteger(span) && span > 0;
6927
+ /**
6928
+ * Validates column groups and lays them over the columns.
6929
+ *
6930
+ * @param groups - Groups as passed to the chart.
6931
+ * @param columnCount - Number of data columns.
6932
+ * @param firstLine - Grid column line of the first data column.
6933
+ * @return Grid column lines for every column and group; no groups when the input was unusable.
6934
+ */
6935
+ const resolveColumnGroups = (groups, columnCount, firstLine) => {
6936
+ const ungrouped = () => ({
6937
+ columns: Array.from({ length: columnCount }, (_, index) => ({
6938
+ line: firstLine + index,
6939
+ gapBefore: false
6940
+ })),
6941
+ groups: []
6942
+ });
6943
+ if (!groups || groups.length === 0 || columnCount === 0) return ungrouped();
6944
+ const spans = groups.map((group) => group.span);
6945
+ const total = spans.reduce((sum, span) => sum + span, 0);
6946
+ if (!spans.every(isSpan) || total > columnCount) {
6947
+ 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.`);
6948
+ return ungrouped();
6949
+ }
6950
+ const columns = [];
6951
+ const laidOut = [];
6952
+ let line = firstLine;
6953
+ groups.forEach((group, groupIndex) => {
6954
+ if (groupIndex > 0) line += 1;
6955
+ laidOut.push({
6956
+ ...group,
6957
+ line
6958
+ });
6959
+ for (let offset = 0; offset < group.span; offset++) {
6960
+ columns.push({
6961
+ line,
6962
+ gapBefore: groupIndex > 0 && offset === 0,
6963
+ group: groupIndex
6964
+ });
6965
+ line += 1;
6966
+ }
6967
+ });
6968
+ while (columns.length < columnCount) {
6969
+ columns.push({
6970
+ line,
6971
+ gapBefore: false
6972
+ });
6973
+ line += 1;
6974
+ }
6975
+ return {
6976
+ columns,
6977
+ groups: laidOut
6978
+ };
6979
+ };
6980
+ //#endregion
6981
+ //#region src/charts/heatmap-chart/private/keyboard-navigation.ts
6982
+ const ARROW_KEYS = [
6983
+ "ArrowLeft",
6984
+ "ArrowRight",
6985
+ "ArrowUp",
6986
+ "ArrowDown"
6987
+ ];
6988
+ const PAGE_KEYS = ["PageUp", "PageDown"];
6989
+ const isNavigationKey = (key, mode) => ARROW_KEYS.includes(key) || mode === "calendar" && PAGE_KEYS.includes(key);
6990
+ const inGrid = (grid, { column, row }) => column >= 0 && column < grid.columns && row >= 0 && row < grid.rows;
6991
+ const firstSelectable = (grid, cells) => cells.find((cell) => inGrid(grid, cell) && !grid.isInert(cell.column, cell.row));
6992
+ /**
6993
+ * Column-major scan for the first selectable cell.
6994
+ *
6995
+ * @param grid - The grid.
6996
+ * @return The cell, or undefined when every slot is inert.
6997
+ */
6998
+ const firstGridCell = (grid) => {
6999
+ for (let column = 0; column < grid.columns; column++) for (let row = 0; row < grid.rows; row++) if (!grid.isInert(column, row)) return {
7000
+ column,
7001
+ row
7002
+ };
7003
+ };
7004
+ /**
7005
+ * Steps across the grid in the key's direction, past inert slots, staying put at the edge.
7006
+ *
7007
+ * @param grid - The grid.
7008
+ * @param from - The selected cell.
7009
+ * @param key - The arrow key.
7010
+ * @return The next cell, or undefined to keep the selection.
7011
+ */
7012
+ const stepGridCell = (grid, from, key) => {
7013
+ const step = {
7014
+ ArrowRight: [1, 0],
7015
+ ArrowLeft: [-1, 0],
7016
+ ArrowDown: [0, 1],
7017
+ ArrowUp: [0, -1]
7018
+ }[key];
7019
+ if (!step) return;
7020
+ const [stepColumn, stepRow] = step;
7021
+ let cell = from;
7022
+ do
7023
+ cell = {
7024
+ column: cell.column + stepColumn,
7025
+ row: cell.row + stepRow
7026
+ };
7027
+ while (inGrid(grid, cell) && grid.isInert(cell.column, cell.row));
7028
+ return inGrid(grid, cell) ? cell : void 0;
7029
+ };
7030
+ /**
7031
+ * Every slot of the blocks in reading order: block by block, each a row at a time.
7032
+ *
7033
+ * @param grid - The grid.
7034
+ * @param blocks - The blocks, left to right.
7035
+ * @return The slots in order.
7036
+ */
7037
+ const readingOrder = (grid, blocks) => blocks.flatMap((block) => Array.from({ length: grid.rows }, (_row, row) => Array.from({ length: block.end - block.start }, (_offset, offset) => ({
7038
+ column: block.start + offset,
7039
+ row
7040
+ }))).flat());
7041
+ const blockOf = (blocks, column) => blocks.findIndex((block) => column >= block.start && column < block.end);
7042
+ /**
7043
+ * The slots after a cell down its column (or before it, going up), continuing at the
7044
+ * same column offset of each following block.
7045
+ *
7046
+ * @param grid - The grid.
7047
+ * @param blocks - The blocks, left to right.
7048
+ * @param from - The selected cell.
7049
+ * @param direction - 1 downwards, -1 upwards.
7050
+ * @return The slots in order.
7051
+ */
7052
+ const columnOrderFrom = (grid, blocks, from, direction) => {
7053
+ const blockIndex = blockOf(blocks, from.column);
7054
+ if (blockIndex < 0) return [];
7055
+ const offset = from.column - blocks[blockIndex].start;
7056
+ const cells = [];
7057
+ const pushRows = (column, startRow) => {
7058
+ for (let row = startRow; row >= 0 && row < grid.rows; row += direction) cells.push({
7059
+ column,
7060
+ row
7061
+ });
7062
+ };
7063
+ pushRows(from.column, from.row + direction);
7064
+ for (let index = blockIndex + direction; index >= 0 && index < blocks.length; index += direction) {
7065
+ const column = blocks[index].start + offset;
7066
+ if (column < blocks[index].end) pushRows(column, direction === 1 ? 0 : grid.rows - 1);
7067
+ }
7068
+ return cells;
7069
+ };
7070
+ /**
7071
+ * The first selectable cell in calendar reading order.
7072
+ *
7073
+ * @param grid - The grid.
7074
+ * @param blocks - The blocks, left to right.
7075
+ * @return The cell, or undefined when every slot is inert.
7076
+ */
7077
+ const firstCalendarCell = (grid, blocks) => firstSelectable(grid, readingOrder(grid, blocks));
7078
+ /**
7079
+ * Steps through the blocks as pages of a calendar: Left/Right by one slot in reading
7080
+ * order, Up/Down by one row (into the neighboring block past the edge), Page Up/Down
7081
+ * to the same slot of the neighboring block or its nearest selectable cell.
7082
+ *
7083
+ * @param grid - The grid.
7084
+ * @param blocks - The blocks, left to right.
7085
+ * @param from - The selected cell.
7086
+ * @param key - The navigation key.
7087
+ * @return The next cell, or undefined to keep the selection.
7088
+ */
7089
+ const stepCalendarCell = (grid, blocks, from, key) => {
7090
+ if (key === "ArrowRight" || key === "ArrowLeft") {
7091
+ const order = readingOrder(grid, blocks);
7092
+ const index = order.findIndex((cell) => cell.column === from.column && cell.row === from.row);
7093
+ if (index < 0) return;
7094
+ const ahead = key === "ArrowRight" ? order.slice(index + 1) : order.slice(0, index).reverse();
7095
+ return firstSelectable(grid, ahead);
7096
+ }
7097
+ if (key === "ArrowDown" || key === "ArrowUp") return firstSelectable(grid, columnOrderFrom(grid, blocks, from, key === "ArrowDown" ? 1 : -1));
7098
+ const blockIndex = blockOf(blocks, from.column);
7099
+ const target = blocks[blockIndex + (key === "PageDown" ? 1 : -1)];
7100
+ if (blockIndex < 0 || !target) return;
7101
+ const same = {
7102
+ column: target.start + (from.column - blocks[blockIndex].start),
7103
+ row: from.row
7104
+ };
7105
+ const page = readingOrder(grid, [target]);
7106
+ const at = page.findIndex((cell) => cell.row > same.row || cell.row === same.row && cell.column >= same.column);
7107
+ const after = at === -1 ? [] : page.slice(at);
7108
+ const before = page.slice(0, at === -1 ? page.length : at).reverse();
7109
+ return firstSelectable(grid, after) ?? firstSelectable(grid, before);
7110
+ };
7111
+ //#endregion
6690
7112
  //#region src/charts/heatmap-chart/heatmap-chart.tsx
6691
7113
  const CELL_MIX_FLOOR = .15;
6692
7114
  const NO_ROW_LABELS = [];
6693
- 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 }) => {
7115
+ const TOOLTIP_BOX_STYLES = {
7116
+ light: {
7117
+ ..._visx_tooltip.defaultStyles,
7118
+ zIndex: 3
7119
+ },
7120
+ dark: { zIndex: 3 }
7121
+ };
7122
+ const cellName = (info) => info.cellLabel || [
7123
+ info.groupLabel,
7124
+ info.columnLabel,
7125
+ info.rowLabel
7126
+ ].filter(Boolean).join(" ");
7127
+ 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 }) => {
6694
7128
  const chartId = useChartId(providedChartId);
7129
+ const tooltipBoxStyle = tooltipStyle ? {
7130
+ ...TOOLTIP_BOX_STYLES[tooltipVariant],
7131
+ ...tooltipStyle
7132
+ } : TOOLTIP_BOX_STYLES[tooltipVariant];
6695
7133
  const { getElementStyles, theme } = useGlobalChartsContext();
6696
7134
  const scopeElement = useChartScopeElement();
6697
7135
  const { heatmapChart: heatmapChartSettings } = theme;
6698
7136
  const { nonLegendChildren } = useChartChildren(children, "HeatmapChart");
6699
- const [selectedIndex, setSelectedIndex] = (0, react.useState)();
7137
+ const [selected, setSelected] = (0, react.useState)();
6700
7138
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, showTooltip, hideTooltip } = (0, _visx_tooltip.useTooltip)();
6701
7139
  const standaloneScopeClass = useStandaloneScopeClass();
6702
7140
  const containerRef = (0, react.useRef)(null);
@@ -6715,69 +7153,75 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6715
7153
  }), [extent, primaryColorHex]);
6716
7154
  const columns = data.length;
6717
7155
  const rows = Math.max(0, ...data.map((column) => column.data.length));
6718
- const { compactCellGap, compactCellSize } = heatmapChartSettings;
7156
+ const groupLayout = (0, react.useMemo)(() => resolveColumnGroups(columnGroups, columns, 2), [columnGroups, columns]);
7157
+ const { compactCellGap, compactCellSize, groupGap } = heatmapChartSettings;
6719
7158
  const drawValues = showValues ?? !compact;
6720
7159
  const buildTooltipData = (0, react.useCallback)((columnIndex, rowIndex) => {
6721
7160
  const cell = data[columnIndex]?.data[rowIndex];
7161
+ const group = groupLayout.columns[columnIndex]?.group;
6722
7162
  return {
6723
7163
  value: cell?.value ?? null,
6724
7164
  rowLabel: rowLabels[rowIndex],
6725
7165
  columnLabel: data[columnIndex]?.label,
7166
+ groupLabel: group === void 0 ? void 0 : groupLayout.groups[group]?.label,
6726
7167
  cellLabel: cell?.label,
6727
7168
  row: rowIndex,
6728
7169
  column: columnIndex
6729
7170
  };
6730
- }, [data, rowLabels]);
7171
+ }, [
7172
+ data,
7173
+ rowLabels,
7174
+ groupLayout
7175
+ ]);
6731
7176
  const onChartBlur = (0, react.useCallback)(() => {
6732
- setSelectedIndex(void 0);
7177
+ setSelected(void 0);
6733
7178
  hideTooltip();
6734
7179
  }, [hideTooltip]);
6735
7180
  const isCellInert = (0, react.useCallback)((col, row) => {
6736
7181
  const cell = data[col]?.data[row];
6737
7182
  return cell?.hidden === true || cell?.placeholder === true;
6738
7183
  }, [data]);
7184
+ const blocks = (0, react.useMemo)(() => {
7185
+ const grouped = groupLayout.groups.map((group, index) => {
7186
+ const start = groupLayout.columns.findIndex((column) => column.group === index);
7187
+ return {
7188
+ start,
7189
+ end: start + group.span
7190
+ };
7191
+ });
7192
+ const groupedEnd = grouped.length ? grouped[grouped.length - 1].end : 0;
7193
+ return groupedEnd < columns ? [...grouped, {
7194
+ start: groupedEnd,
7195
+ end: columns
7196
+ }] : grouped;
7197
+ }, [groupLayout, columns]);
6739
7198
  const onChartKeyDown = (0, react.useCallback)((event) => {
6740
- if (![
6741
- "ArrowLeft",
6742
- "ArrowRight",
6743
- "ArrowUp",
6744
- "ArrowDown",
6745
- "Escape",
6746
- "Tab"
6747
- ].includes(event.key)) return;
6748
7199
  if (event.key === "Tab" || event.key === "Escape") {
6749
- setSelectedIndex(void 0);
7200
+ setSelected(void 0);
6750
7201
  hideTooltip();
6751
7202
  return;
6752
7203
  }
7204
+ if (!isNavigationKey(event.key, keyboardNavigation)) return;
6753
7205
  event.preventDefault();
6754
- if (selectedIndex === void 0) {
6755
- for (let index = 0; index < columns * rows; index++) if (!isCellInert(Math.floor(index / rows), index % rows)) {
6756
- setSelectedIndex(index);
6757
- return;
6758
- }
7206
+ const grid = {
7207
+ columns,
7208
+ rows,
7209
+ isInert: isCellInert
7210
+ };
7211
+ if (selected === void 0) {
7212
+ setSelected(keyboardNavigation === "calendar" ? firstCalendarCell(grid, blocks) : firstGridCell(grid));
6759
7213
  return;
6760
7214
  }
6761
- let stepCol = 0;
6762
- let stepRow = 0;
6763
- if (event.key === "ArrowRight") stepCol = 1;
6764
- else if (event.key === "ArrowLeft") stepCol = -1;
6765
- else if (event.key === "ArrowDown") stepRow = 1;
6766
- else if (event.key === "ArrowUp") stepRow = -1;
6767
- let col = Math.floor(selectedIndex / rows);
6768
- let row = selectedIndex % rows;
6769
- do {
6770
- col += stepCol;
6771
- row += stepRow;
6772
- } while (col >= 0 && col < columns && row >= 0 && row < rows && isCellInert(col, row));
6773
- if (col < 0 || col >= columns || row < 0 || row >= rows) return;
6774
- setSelectedIndex(col * rows + row);
7215
+ const next = keyboardNavigation === "calendar" ? stepCalendarCell(grid, blocks, selected, event.key) : stepGridCell(grid, selected, event.key);
7216
+ if (next) setSelected(next);
6775
7217
  }, [
6776
7218
  rows,
6777
7219
  columns,
6778
- selectedIndex,
7220
+ selected,
6779
7221
  hideTooltip,
6780
- isCellInert
7222
+ isCellInert,
7223
+ keyboardNavigation,
7224
+ blocks
6781
7225
  ]);
6782
7226
  const handleCellMouseMove = (0, react.useCallback)((event) => {
6783
7227
  if (!withTooltips) return;
@@ -6797,35 +7241,47 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6797
7241
  buildTooltipData,
6798
7242
  getTooltipOrigin
6799
7243
  ]);
7244
+ const getCellElement = (0, react.useCallback)((col, row) => typeof document !== "undefined" ? document.getElementById(`${chartId}-cell-${col}-${row}`) : null, [chartId]);
6800
7245
  const handleCellMouseLeave = (0, react.useCallback)(() => {
6801
- if (withTooltips && selectedIndex === void 0) hideTooltip();
7246
+ if (withTooltips && selected === void 0) hideTooltip();
6802
7247
  }, [
6803
7248
  withTooltips,
6804
- selectedIndex,
7249
+ selected,
6805
7250
  hideTooltip
6806
7251
  ]);
6807
7252
  (0, react.useEffect)(() => {
6808
- if (!withTooltips || selectedIndex === void 0) return;
7253
+ if (selected && (selected.column >= columns || selected.row >= rows)) setSelected(void 0);
7254
+ }, [
7255
+ selected,
7256
+ columns,
7257
+ rows
7258
+ ]);
7259
+ (0, react.useEffect)(() => {
7260
+ if (selected === void 0) return;
7261
+ getCellElement(selected.column, selected.row)?.scrollIntoView?.({
7262
+ block: "nearest",
7263
+ inline: "nearest"
7264
+ });
7265
+ }, [selected, getCellElement]);
7266
+ (0, react.useEffect)(() => {
7267
+ if (!withTooltips || selected === void 0) return;
6809
7268
  const origin = getTooltipOrigin();
6810
7269
  if (!origin) return;
6811
- const col = Math.floor(selectedIndex / rows);
6812
- const row = selectedIndex % rows;
6813
- const rect = (typeof document !== "undefined" ? document.getElementById(`${chartId}-cell-${col}-${row}`) : null)?.getBoundingClientRect();
7270
+ const rect = getCellElement(selected.column, selected.row)?.getBoundingClientRect();
6814
7271
  showTooltip({
6815
7272
  tooltipLeft: rect ? rect.left + rect.width / 2 - origin.left : 0,
6816
7273
  tooltipTop: rect ? rect.top + rect.height / 2 - origin.top : 0,
6817
- tooltipData: buildTooltipData(col, row)
7274
+ tooltipData: buildTooltipData(selected.column, selected.row)
6818
7275
  });
6819
7276
  }, [
6820
- selectedIndex,
7277
+ selected,
6821
7278
  withTooltips,
6822
- rows,
6823
- chartId,
7279
+ getCellElement,
6824
7280
  buildTooltipData,
6825
7281
  showTooltip,
6826
7282
  getTooltipOrigin
6827
7283
  ]);
6828
- const defaultRenderTooltip = (0, react.useCallback)((info) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: info.cellLabel || `${info.columnLabel ?? ""} ${info.rowLabel ?? ""}`.trim() }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: info.value === null ? (0, _wordpress_i18n.__)("No data", "jetpack-charts") : (0, _automattic_number_formatters.formatNumber)(info.value) })] }), []);
7284
+ const defaultRenderTooltip = (0, react.useCallback)((info) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: cellName(info) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: info.value === null ? (0, _wordpress_i18n.__)("No data", "jetpack-charts") : (0, _automattic_number_formatters.formatNumber)(info.value) })] }), []);
6829
7285
  if (!columns || !rows) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Center, {
6830
7286
  className: (0, clsx.default)("heatmap-chart", heatmap_chart_module_default["heatmap-chart"], className),
6831
7287
  style: {
@@ -6839,10 +7295,16 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6839
7295
  });
6840
7296
  const columnTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellWidth ?? 0}px, ${maxCellWidth ? `${maxCellWidth}px` : "1fr"})`;
6841
7297
  const rowTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
7298
+ const hasColumnLabels = data.some((column) => Boolean(column.label));
7299
+ const hasGroups = groupLayout.groups.length > 0;
7300
+ const columnLine = (columnIndex) => groupLayout.columns[columnIndex].line;
7301
+ const firstDataRow = hasColumnLabels ? 2 : 1;
7302
+ const dataTrack = (column) => column.summary ? "minmax(auto, max-content)" : columnTrack;
7303
+ const gapTrack = compact ? `minmax(${groupGap}px, 1fr)` : `${groupGap}px`;
6842
7304
  const gridStyle = {
6843
7305
  "--a8c-charts-color-heatmap-primary": primaryColorHex,
6844
- gridTemplateColumns: `auto ${data.some((column) => column.summary) ? data.map((column) => column.summary ? "minmax(auto, max-content)" : columnTrack).join(" ") : `repeat(${columns}, ${columnTrack})`}`,
6845
- gridTemplateRows: `auto repeat(${rows}, ${rowTrack})`
7306
+ gridTemplateColumns: `auto ${data.map((column, columnIndex) => groupLayout.columns[columnIndex].gapBefore ? `${gapTrack} ${dataTrack(column)}` : dataTrack(column)).join(" ")}`,
7307
+ gridTemplateRows: `${hasColumnLabels ? "auto " : ""}repeat(${rows}, ${rowTrack})${hasGroups ? " auto" : ""}`
6846
7308
  };
6847
7309
  if (compact) {
6848
7310
  gridStyle["--a8c-charts-dimension-heatmap-cell-gap"] = `${compactCellGap}px`;
@@ -6855,7 +7317,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6855
7317
  [heatmap_chart_module_default["heatmap-chart__gap-end"]]: columnIndex < columns - 1 && !data[columnIndex + 1]?.summary
6856
7318
  };
6857
7319
  };
6858
- const activeDescendant = selectedIndex !== void 0 ? `${chartId}-cell-${Math.floor(selectedIndex / rows)}-${selectedIndex % rows}` : void 0;
7320
+ const activeDescendant = selected ? `${chartId}-cell-${selected.column}-${selected.row}` : void 0;
6859
7321
  const heightCapped = !compact && Boolean(maxCellHeight);
6860
7322
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeatmapContext.Provider, {
6861
7323
  value: heatmapContext,
@@ -6875,7 +7337,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6875
7337
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6876
7338
  ref: containerRef,
6877
7339
  role: "grid",
6878
- "aria-label": (0, _wordpress_i18n.__)("Heatmap chart", "jetpack-charts"),
7340
+ "aria-label": ariaLabel ?? (0, _wordpress_i18n.__)("Heatmap chart", "jetpack-charts"),
6879
7341
  "aria-rowcount": rows,
6880
7342
  "aria-colcount": columns,
6881
7343
  "aria-activedescendant": activeDescendant,
@@ -6884,76 +7346,115 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6884
7346
  onKeyDown: onChartKeyDown,
6885
7347
  className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__grid"], {
6886
7348
  [heatmap_chart_module_default["heatmap-chart__grid--compact"]]: compact,
7349
+ [heatmap_chart_module_default["heatmap-chart__grid--flex-gaps"]]: compact && hasGroups,
6887
7350
  [heatmap_chart_module_default["heatmap-chart__grid--height-capped"]]: heightCapped
6888
7351
  }),
6889
7352
  style: gridStyle,
6890
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6891
- role: "row",
6892
- "aria-hidden": "true",
6893
- className: heatmap_chart_module_default["heatmap-chart__row"],
6894
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {}), data.map((column, columnIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6895
- className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__col-label"], {
6896
- [heatmap_chart_module_default["heatmap-chart__col-label--summary"]]: column.summary,
6897
- ...summaryGaps(columnIndex)
6898
- }),
6899
- children: column.label
6900
- }, `col-${columnIndex}`))]
6901
- }), Array.from({ length: rows }).map((_row, rowIndex) => {
6902
- const labelVisible = !compact || rowIndex % 2 === 0;
6903
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7353
+ children: [
7354
+ hasColumnLabels && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6904
7355
  role: "row",
6905
- "aria-rowindex": rowIndex + 1,
7356
+ "aria-hidden": "true",
6906
7357
  className: heatmap_chart_module_default["heatmap-chart__row"],
6907
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6908
- "aria-hidden": "true",
6909
- className: heatmap_chart_module_default["heatmap-chart__row-label"],
6910
- children: labelVisible ? rowLabels[rowIndex] ?? "" : ""
6911
- }), data.map((column, columnIndex) => {
6912
- const cell = column.data[rowIndex];
6913
- if (cell?.hidden) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6914
- "aria-hidden": "true",
6915
- className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--hidden"])
6916
- }, `cell-${columnIndex}-${rowIndex}`);
6917
- if (cell?.placeholder) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7358
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
7359
+ gridColumn: 1,
7360
+ gridRow: 1
7361
+ } }), data.map((column, columnIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7362
+ style: {
7363
+ gridColumn: columnLine(columnIndex),
7364
+ gridRow: 1
7365
+ },
7366
+ className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__col-label"], {
7367
+ [heatmap_chart_module_default["heatmap-chart__col-label--summary"]]: column.summary,
7368
+ ...summaryGaps(columnIndex)
7369
+ }),
7370
+ children: column.label
7371
+ }, `col-${columnIndex}`))]
7372
+ }),
7373
+ Array.from({ length: rows }).map((_row, rowIndex) => {
7374
+ const labelVisible = !compact || rowIndex % 2 === 0;
7375
+ const gridRow = firstDataRow + rowIndex;
7376
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7377
+ role: "row",
7378
+ "aria-rowindex": rowIndex + 1,
7379
+ className: heatmap_chart_module_default["heatmap-chart__row"],
7380
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6918
7381
  "aria-hidden": "true",
6919
- className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--placeholder"])
6920
- }, `cell-${columnIndex}-${rowIndex}`);
6921
- const value = cell?.value ?? null;
6922
- const present = isPresent(value);
6923
- const filled = present && !column.summary;
6924
- const normalized = filled ? getNormalizedValue(value, extent) : 0;
6925
- const flatIndex = columnIndex * rows + rowIndex;
6926
- const info = buildTooltipData(columnIndex, rowIndex);
6927
- const accessibleLabel = `${info.cellLabel || `${info.columnLabel ?? ""} ${info.rowLabel ?? ""}`.trim()}: ${info.value === null ? (0, _wordpress_i18n.__)("No data", "jetpack-charts") : (0, _automattic_number_formatters.formatNumber)(info.value)}`;
6928
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6929
- id: `${chartId}-cell-${columnIndex}-${rowIndex}`,
6930
- role: "gridcell",
6931
- tabIndex: -1,
6932
- "aria-colindex": columnIndex + 1,
6933
- "aria-label": accessibleLabel,
6934
- "data-column": columnIndex,
6935
- "data-row": rowIndex,
6936
- className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], {
6937
- [heatmap_chart_module_default["heatmap-chart__cell--filled"]]: filled,
6938
- [heatmap_chart_module_default["heatmap-chart__cell--strong"]]: filled && cellHasLightText(normalized),
6939
- [heatmap_chart_module_default["heatmap-chart__cell--summary"]]: column.summary,
6940
- ...summaryGaps(columnIndex),
6941
- [heatmap_chart_module_default["heatmap-chart__cell--selected"]]: selectedIndex === flatIndex
6942
- }),
6943
- style: filled ? { "--a8c-charts-heatmap-cell-intensity": normalized } : void 0,
6944
- onMouseMove: handleCellMouseMove,
6945
- onMouseLeave: handleCellMouseLeave,
6946
- children: (drawValues || column.summary) && present && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6947
- className: heatmap_chart_module_default["heatmap-chart__cell-value"],
6948
- children: (0, _automattic_number_formatters.formatNumberCompact)(value)
6949
- })
6950
- }, `cell-${columnIndex}-${rowIndex}`);
6951
- })]
6952
- }, `row-${rowIndex}`);
6953
- })]
7382
+ className: heatmap_chart_module_default["heatmap-chart__row-label"],
7383
+ style: {
7384
+ gridColumn: 1,
7385
+ gridRow
7386
+ },
7387
+ children: labelVisible ? rowLabels[rowIndex] ?? "" : ""
7388
+ }), data.map((column, columnIndex) => {
7389
+ const cell = column.data[rowIndex];
7390
+ const placement = {
7391
+ gridColumn: columnLine(columnIndex),
7392
+ gridRow
7393
+ };
7394
+ if (cell?.hidden) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7395
+ "aria-hidden": "true",
7396
+ style: placement,
7397
+ className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--hidden"])
7398
+ }, `cell-${columnIndex}-${rowIndex}`);
7399
+ if (cell?.placeholder) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7400
+ "aria-hidden": "true",
7401
+ style: placement,
7402
+ className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], heatmap_chart_module_default["heatmap-chart__cell--placeholder"])
7403
+ }, `cell-${columnIndex}-${rowIndex}`);
7404
+ const value = cell?.value ?? null;
7405
+ const present = isPresent(value);
7406
+ const filled = present && !column.summary;
7407
+ const normalized = filled ? getNormalizedValue(value, extent) : 0;
7408
+ const info = buildTooltipData(columnIndex, rowIndex);
7409
+ const accessibleLabel = `${cellName(info)}: ${info.value === null ? (0, _wordpress_i18n.__)("No data", "jetpack-charts") : (0, _automattic_number_formatters.formatNumber)(info.value)}`;
7410
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7411
+ id: `${chartId}-cell-${columnIndex}-${rowIndex}`,
7412
+ role: "gridcell",
7413
+ tabIndex: -1,
7414
+ "aria-colindex": columnIndex + 1,
7415
+ "aria-label": accessibleLabel,
7416
+ "data-column": columnIndex,
7417
+ "data-row": rowIndex,
7418
+ className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], {
7419
+ [heatmap_chart_module_default["heatmap-chart__cell--filled"]]: filled,
7420
+ [heatmap_chart_module_default["heatmap-chart__cell--strong"]]: filled && cellHasLightText(normalized),
7421
+ [heatmap_chart_module_default["heatmap-chart__cell--summary"]]: column.summary,
7422
+ ...summaryGaps(columnIndex),
7423
+ [heatmap_chart_module_default["heatmap-chart__cell--selected"]]: selected?.column === columnIndex && selected?.row === rowIndex
7424
+ }),
7425
+ style: {
7426
+ ...placement,
7427
+ ...filled ? { "--a8c-charts-heatmap-cell-intensity": normalized } : {}
7428
+ },
7429
+ onMouseMove: handleCellMouseMove,
7430
+ onMouseLeave: handleCellMouseLeave,
7431
+ children: (drawValues || column.summary) && present && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7432
+ className: heatmap_chart_module_default["heatmap-chart__cell-value"],
7433
+ children: (0, _automattic_number_formatters.formatNumberCompact)(value)
7434
+ })
7435
+ }, `cell-${columnIndex}-${rowIndex}`);
7436
+ })]
7437
+ }, `row-${rowIndex}`);
7438
+ }),
7439
+ hasGroups && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7440
+ role: "row",
7441
+ "aria-hidden": "true",
7442
+ className: heatmap_chart_module_default["heatmap-chart__row"],
7443
+ children: groupLayout.groups.map((group, groupIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7444
+ className: heatmap_chart_module_default["heatmap-chart__group-label"],
7445
+ style: {
7446
+ gridColumn: `${group.line} / span ${group.span}`,
7447
+ gridRow: firstDataRow + rows
7448
+ },
7449
+ children: group.label
7450
+ }, `group-${groupIndex}`))
7451
+ })
7452
+ ]
6954
7453
  }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundedTooltip, {
6955
7454
  top: tooltipTop,
6956
7455
  left: tooltipLeft,
7456
+ className: tooltipVariant === "dark" ? base_tooltip_module_default.surface : void 0,
7457
+ style: tooltipBoxStyle,
6957
7458
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6958
7459
  className: standaloneScopeClass,
6959
7460
  role: "tooltip",
@@ -6989,6 +7490,43 @@ const KEY_OPTIONS = {
6989
7490
  day: "2-digit"
6990
7491
  };
6991
7492
  const DATE_PREFIX = /^(\d{4})-(\d{2})-(\d{2})/;
7493
+ /**
7494
+ * Label formatters for UTC proxies, so both calendar layouts name a day alike.
7495
+ *
7496
+ * `gregory` keeps a locale whose default calendar is not Gregorian (fa-IR) from
7497
+ * splitting one grid month across two names.
7498
+ *
7499
+ * @param locale - BCP 47 locale.
7500
+ * @return Weekday, month and full-day formatters.
7501
+ */
7502
+ const civilLabelFormatters = (locale) => {
7503
+ const formatting = {
7504
+ locale,
7505
+ timeZone: "UTC"
7506
+ };
7507
+ return {
7508
+ formatWeekday: createDateFormatter({
7509
+ weekday: "short",
7510
+ calendar: "gregory"
7511
+ }, formatting),
7512
+ formatMonth: createDateFormatter({
7513
+ month: "short",
7514
+ calendar: "gregory"
7515
+ }, formatting),
7516
+ formatMonthYear: createDateFormatter({
7517
+ month: "short",
7518
+ year: "numeric",
7519
+ calendar: "gregory"
7520
+ }, formatting),
7521
+ formatDay: createDateFormatter({
7522
+ weekday: "short",
7523
+ month: "short",
7524
+ day: "numeric",
7525
+ year: "numeric",
7526
+ calendar: "gregory"
7527
+ }, formatting)
7528
+ };
7529
+ };
6992
7530
  const pad = (value, length) => String(value).padStart(length, "0");
6993
7531
  /**
6994
7532
  * The day a UTC proxy stands for.
@@ -7154,18 +7692,7 @@ const buildCalendarHeatmapData = (series, options = {}) => {
7154
7692
  const gridMaxDayKey = widenTo(options.gridSpan?.end, maxDayKey, "later");
7155
7693
  const requestedMinDate = civilDate(requestedMinDayKey);
7156
7694
  const gridMaxDate = civilDate(gridMaxDayKey);
7157
- const labelFormatting = {
7158
- locale,
7159
- timeZone: "UTC"
7160
- };
7161
- const formatWeekday = createDateFormatter({ weekday: "short" }, labelFormatting);
7162
- const formatMonth = createDateFormatter({ month: "short" }, labelFormatting);
7163
- const formatDay = createDateFormatter({
7164
- weekday: "short",
7165
- month: "short",
7166
- day: "numeric",
7167
- year: "numeric"
7168
- }, labelFormatting);
7695
+ const { formatWeekday, formatMonth, formatDay } = civilLabelFormatters(locale);
7169
7696
  const gridStart = startOfCivilWeek(requestedMinDate, weekStartsOn);
7170
7697
  const gridMinDayKey = requestedMinDayKey < minDayKey ? civilKey(gridStart) : requestedMinDayKey;
7171
7698
  const weekCount = civilWeekSpan(gridStart, gridMaxDate, weekStartsOn);
@@ -7206,6 +7733,89 @@ const buildCalendarHeatmapData = (series, options = {}) => {
7206
7733
  };
7207
7734
  };
7208
7735
  //#endregion
7736
+ //#region src/charts/heatmap-chart/build-month-calendar-data.ts
7737
+ /** Week rows per month, fixed so every month block is the same height. */
7738
+ const MONTH_ROWS = 6;
7739
+ const DAYS_PER_WEEK = 7;
7740
+ const startOfCivilMonth = (date) => {
7741
+ const first = new Date(date.getTime());
7742
+ first.setUTCDate(1);
7743
+ return first;
7744
+ };
7745
+ const startOfNextCivilMonth = (date) => {
7746
+ const next = startOfCivilMonth(date);
7747
+ next.setUTCMonth(next.getUTCMonth() + 1);
7748
+ return next;
7749
+ };
7750
+ /**
7751
+ * Lay a day-keyed map out as one month calendar per month, weekdays across and
7752
+ * weeks down, for `HeatmapChart` with `columnGroups`.
7753
+ *
7754
+ * @param valueByDay - Value per `yyyy-MM-dd`. A missing day is `null`.
7755
+ * @param range - The measured days; every month from `start`'s through `end`'s is drawn.
7756
+ * @param options - Week start and label locale.
7757
+ * @return Columns and one group per month; empty for an invalid or reversed range.
7758
+ */
7759
+ const buildMonthCalendarHeatmapData = (valueByDay, range, options = {}) => {
7760
+ const start = civilDate(range.start);
7761
+ const end = civilDate(range.end);
7762
+ if (!start || !end || start.getTime() > end.getTime()) {
7763
+ 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.`);
7764
+ return {
7765
+ data: [],
7766
+ columnGroups: []
7767
+ };
7768
+ }
7769
+ const startKey = civilKey(start);
7770
+ const endKey = civilKey(end);
7771
+ const weekStartsOn = options.weekStartsOn ?? 1;
7772
+ const { locale } = sanitizeFormatting({ locale: options.locale });
7773
+ 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.`);
7774
+ const readValue = (key) => {
7775
+ const value = valueByDay[key];
7776
+ return isPresent(value) ? value : null;
7777
+ };
7778
+ const { formatMonth, formatMonthYear, formatDay } = civilLabelFormatters(locale);
7779
+ const data = [];
7780
+ const columnGroups = [];
7781
+ const lastMonthStart = startOfCivilMonth(end);
7782
+ const formatGroup = (lastMonthStart.getUTCFullYear() - start.getUTCFullYear()) * 12 + lastMonthStart.getUTCMonth() - start.getUTCMonth() + 1 > 12 ? formatMonthYear : formatMonth;
7783
+ for (let monthStart = startOfCivilMonth(start); monthStart.getTime() <= lastMonthStart.getTime(); monthStart = startOfNextCivilMonth(monthStart)) {
7784
+ const monthStartKey = civilKey(monthStart);
7785
+ const monthEndKey = civilKey(addCivilDays(startOfNextCivilMonth(monthStart), -1));
7786
+ const gridStart = startOfCivilWeek(monthStart, weekStartsOn);
7787
+ for (let weekday = 0; weekday < DAYS_PER_WEEK; weekday++) {
7788
+ const cells = [];
7789
+ for (let week = 0; week < MONTH_ROWS; week++) {
7790
+ const day = addCivilDays(gridStart, week * DAYS_PER_WEEK + weekday);
7791
+ const key = civilKey(day);
7792
+ if (key < monthStartKey || key > monthEndKey) cells.push({
7793
+ value: null,
7794
+ hidden: true
7795
+ });
7796
+ else if (key < startKey || key > endKey) cells.push({
7797
+ label: formatDay(day),
7798
+ value: null,
7799
+ placeholder: true
7800
+ });
7801
+ else cells.push({
7802
+ label: formatDay(day),
7803
+ value: readValue(key)
7804
+ });
7805
+ }
7806
+ data.push({ data: cells });
7807
+ }
7808
+ columnGroups.push({
7809
+ label: formatGroup(monthStart),
7810
+ span: DAYS_PER_WEEK
7811
+ });
7812
+ }
7813
+ return {
7814
+ data,
7815
+ columnGroups
7816
+ };
7817
+ };
7818
+ //#endregion
7209
7819
  //#region src/charts/heatmap-chart/use-calendar-heatmap-data.ts
7210
7820
  /**
7211
7821
  * `buildCalendarHeatmapData` with the locale and zone taken from
@@ -7242,6 +7852,36 @@ const useCalendarHeatmapData = (series, options = {}) => {
7242
7852
  ]);
7243
7853
  };
7244
7854
  //#endregion
7855
+ //#region src/charts/heatmap-chart/use-month-calendar-heatmap-data.ts
7856
+ /**
7857
+ * `buildMonthCalendarHeatmapData` with the locale taken from `GlobalChartsProvider`
7858
+ * where the caller names none.
7859
+ *
7860
+ * @param valueByDay - Value per `yyyy-MM-dd`. Held by reference, so a caller that rebuilds it each render defeats the memo.
7861
+ * @param range - As for `buildMonthCalendarHeatmapData`.
7862
+ * @param options - As for `buildMonthCalendarHeatmapData`; `locale` wins over the provider.
7863
+ * @return Columns and groups for `HeatmapChart`.
7864
+ */
7865
+ const useMonthCalendarHeatmapData = (valueByDay, range, options = {}) => {
7866
+ const formatting = useChartFormatting();
7867
+ const locale = options.locale ?? formatting.locale;
7868
+ const { weekStartsOn } = options;
7869
+ const { start, end } = range;
7870
+ return (0, react.useMemo)(() => buildMonthCalendarHeatmapData(valueByDay, {
7871
+ start,
7872
+ end
7873
+ }, {
7874
+ locale,
7875
+ weekStartsOn
7876
+ }), [
7877
+ valueByDay,
7878
+ start,
7879
+ end,
7880
+ locale,
7881
+ weekStartsOn
7882
+ ]);
7883
+ };
7884
+ //#endregion
7245
7885
  //#region ../../../node_modules/.pnpm/is-plain-object@5.1.0/node_modules/is-plain-object/dist/is-plain-object.mjs
7246
7886
  /*!
7247
7887
  * is-plain-object <https://github.com/jonschlinkert/is-plain-object>
@@ -7605,7 +8245,24 @@ var require_dist = /* @__PURE__ */ __commonJSMin(((exports) => {
7605
8245
  tslib_1.__exportStar(require_dist$1(), exports);
7606
8246
  }));
7607
8247
  //#endregion
7608
- //#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
8248
+ //#region ../../../node_modules/.pnpm/@wordpress+warning@3.55.0/node_modules/@wordpress/warning/build-module/utils.mjs
8249
+ var logged = /* @__PURE__ */ new Set();
8250
+ //#endregion
8251
+ //#region ../../../node_modules/.pnpm/@wordpress+warning@3.55.0/node_modules/@wordpress/warning/build-module/index.mjs
8252
+ function isDev() {
8253
+ return globalThis.SCRIPT_DEBUG === true;
8254
+ }
8255
+ function warning(message) {
8256
+ if (!isDev()) return;
8257
+ if (logged.has(message)) return;
8258
+ console.warn(message);
8259
+ try {
8260
+ throw Error(message);
8261
+ } catch {}
8262
+ logged.add(message);
8263
+ }
8264
+ //#endregion
8265
+ //#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
7609
8266
  function useUpdateEffect(effect, deps) {
7610
8267
  const mountedRef = (0, react.useRef)(false);
7611
8268
  (0, react.useEffect)(() => {
@@ -9243,7 +9900,7 @@ _createEmotion.css;
9243
9900
  _createEmotion.sheet;
9244
9901
  _createEmotion.cache;
9245
9902
  //#endregion
9246
- //#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
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/utils/hooks/use-cx.mjs
9247
9904
  var isSerializedStyles = (o) => typeof o !== "undefined" && o !== null && ["name", "styles"].every((p) => typeof o[p] !== "undefined");
9248
9905
  var useCx = () => {
9249
9906
  const cache = __unsafe_useEmotionCache();
@@ -9350,7 +10007,7 @@ function memize(fn, options) {
9350
10007
  return memoized;
9351
10008
  }
9352
10009
  //#endregion
9353
- //#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
10010
+ //#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
9354
10011
  var white = "#fff";
9355
10012
  var GRAY = {
9356
10013
  900: "#1e1e1e",
@@ -9400,8 +10057,8 @@ var UI = {
9400
10057
  border: THEME.gray[600],
9401
10058
  borderHover: THEME.gray[700],
9402
10059
  borderFocus: THEME.accent,
9403
- borderDisabled: THEME.gray[400],
9404
- textDisabled: THEME.gray[600],
10060
+ borderDisabled: `var(--wpds-color-stroke-interactive-neutral-disabled, #dbdbdb)`,
10061
+ textDisabled: `var(--wpds-color-foreground-interactive-neutral-disabled, #8d8d8d)`,
9405
10062
  darkGrayPlaceholder: `color-mix(in srgb, ${THEME.foreground}, transparent 38%)`,
9406
10063
  lightGrayPlaceholder: `color-mix(in srgb, ${THEME.background}, transparent 35%)`
9407
10064
  };
@@ -9429,7 +10086,7 @@ var COLORS = Object.freeze({
9429
10086
  ui: UI
9430
10087
  });
9431
10088
  //#endregion
9432
- //#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
10089
+ //#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
9433
10090
  var CONTROL_HEIGHT = "36px";
9434
10091
  var CONTROL_PROPS = {
9435
10092
  controlPaddingX: 12,
@@ -9483,8 +10140,8 @@ var config_values_default = Object.assign({}, CONTROL_PROPS, {
9483
10140
  transitionTimingFunctionControl: "cubic-bezier(0.12, 0.8, 0.32, 1)"
9484
10141
  });
9485
10142
  //#endregion
9486
- //#region ../../../node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/es6/index.js
9487
- var require_es6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
10143
+ //#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
10144
+ var import_es6 = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
9488
10145
  module.exports = function equal(a, b) {
9489
10146
  if (a === b) return true;
9490
10147
  if (a && b && typeof a == "object" && typeof b == "object") {
@@ -9528,31 +10185,8 @@ var require_es6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9528
10185
  }
9529
10186
  return a !== a && b !== b;
9530
10187
  };
9531
- }));
9532
- //#endregion
9533
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/utils.mjs
9534
- var logged = /* @__PURE__ */ new Set();
9535
- //#endregion
9536
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/index.mjs
9537
- function isDev() {
9538
- return globalThis.SCRIPT_DEBUG === true;
9539
- }
9540
- function warning(message) {
9541
- if (!isDev()) return;
9542
- if (logged.has(message)) return;
9543
- console.warn(message);
9544
- try {
9545
- throw Error(message);
9546
- } catch {}
9547
- logged.add(message);
9548
- }
9549
- //#endregion
9550
- //#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
9551
- var import_es6 = /* @__PURE__ */ __toESM(require_es6(), 1);
9552
- var ComponentsContext = (0, react.createContext)(
9553
- /** @type {Record<string, any>} */
9554
- {}
9555
- );
10188
+ })))(), 1);
10189
+ var ComponentsContext = (0, react.createContext)({});
9556
10190
  ComponentsContext.displayName = "ComponentsContext";
9557
10191
  var useComponentsContext = () => (0, react.useContext)(ComponentsContext);
9558
10192
  function useContextSystemBridge({ value }) {
@@ -9574,19 +10208,19 @@ var BaseContextSystemProvider = ({ children, value }) => {
9574
10208
  };
9575
10209
  (0, react.memo)(BaseContextSystemProvider);
9576
10210
  //#endregion
9577
- //#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
10211
+ //#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
9578
10212
  var COMPONENT_NAMESPACE = "data-wp-component";
9579
10213
  var CONNECTED_NAMESPACE = "data-wp-c16t";
9580
10214
  var CONNECT_STATIC_NAMESPACE = "__contextSystemKey__";
9581
10215
  //#endregion
9582
- //#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
10216
+ //#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
9583
10217
  var import_dist = require_dist();
9584
10218
  function getStyledClassName(namespace) {
9585
10219
  return `components-${(0, import_dist.paramCase)(namespace)}`;
9586
10220
  }
9587
10221
  var getStyledClassNameFromKey = memize(getStyledClassName);
9588
10222
  //#endregion
9589
- //#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
10223
+ //#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
9590
10224
  function contextConnect(Component, namespace) {
9591
10225
  return _contextConnect(Component, namespace, { forwardsRef: true });
9592
10226
  }
@@ -9603,7 +10237,7 @@ function _contextConnect(Component, namespace, options) {
9603
10237
  });
9604
10238
  }
9605
10239
  //#endregion
9606
- //#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
10240
+ //#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
9607
10241
  function getNamespace(componentName) {
9608
10242
  return { [COMPONENT_NAMESPACE]: componentName };
9609
10243
  }
@@ -9611,7 +10245,7 @@ function getConnectedNamespace() {
9611
10245
  return { [CONNECTED_NAMESPACE]: true };
9612
10246
  }
9613
10247
  //#endregion
9614
- //#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
10248
+ //#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
9615
10249
  function useContextSystem(props, namespace) {
9616
10250
  const contextSystemProps = useComponentsContext();
9617
10251
  if (typeof namespace === "undefined") globalThis.SCRIPT_DEBUG === true && warning("useContextSystem: Please provide a namespace");
@@ -9620,10 +10254,12 @@ function useContextSystem(props, namespace) {
9620
10254
  ...getConnectedNamespace(),
9621
10255
  ...getNamespace(namespace)
9622
10256
  };
9623
- const { _overrides: overrideProps, ...otherContextProps } = contextProps;
10257
+ const { _overrides, ...otherContextProps } = contextProps;
10258
+ const overrideProps = _overrides;
9624
10259
  const initialMergedProps = Object.entries(otherContextProps).length ? Object.assign({}, otherContextProps, props) : props;
10260
+ const propsWithChildren = initialMergedProps;
9625
10261
  const classes = useCx()(getStyledClassNameFromKey(namespace), props.className);
9626
- const rendered = typeof initialMergedProps.renderChildren === "function" ? initialMergedProps.renderChildren(initialMergedProps) : initialMergedProps.children;
10262
+ const rendered = typeof propsWithChildren.renderChildren === "function" ? propsWithChildren.renderChildren(initialMergedProps) : propsWithChildren.children;
9627
10263
  for (const key in initialMergedProps) finalComponentProps[key] = initialMergedProps[key];
9628
10264
  for (const key in overrideProps) finalComponentProps[key] = overrideProps[key];
9629
10265
  if (rendered !== void 0) finalComponentProps.children = rendered;
@@ -9631,7 +10267,7 @@ function useContextSystem(props, namespace) {
9631
10267
  return finalComponentProps;
9632
10268
  }
9633
10269
  //#endregion
9634
- //#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
10270
+ //#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
9635
10271
  var visuallyHidden = {
9636
10272
  border: 0,
9637
10273
  clip: "rect(1px, 1px, 1px, 1px)",
@@ -9647,7 +10283,7 @@ var visuallyHidden = {
9647
10283
  wordBreak: "normal"
9648
10284
  };
9649
10285
  //#endregion
9650
- //#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
10286
+ //#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
9651
10287
  var customAttributeRegExp = /^(data|aria|x)-/i;
9652
10288
  var eventHandlerRegExp = /^on[A-Z]/;
9653
10289
  var svgElementNames = new Set(`animate animateMotion animateTransform circle clipPath defs desc ellipse
@@ -9737,7 +10373,7 @@ function UnforwardedPolymorphicElement({ as, ...props }, ref) {
9737
10373
  }
9738
10374
  var PolymorphicElement = (0, react.forwardRef)(UnforwardedPolymorphicElement);
9739
10375
  //#endregion
9740
- //#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
10376
+ //#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
9741
10377
  function UnforwardedView({ css, ...restProps }, ref) {
9742
10378
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PolymorphicElement, {
9743
10379
  ref,
@@ -9746,7 +10382,7 @@ function UnforwardedView({ css, ...restProps }, ref) {
9746
10382
  }
9747
10383
  var component_default$2 = Object.assign((0, react.forwardRef)(UnforwardedView), { selector: ".components-view" });
9748
10384
  //#endregion
9749
- //#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
10385
+ //#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
9750
10386
  function UnconnectedVisuallyHidden(props, forwardedRef) {
9751
10387
  const { style: styleProp, ...contextProps } = useContextSystem(props, "VisuallyHidden");
9752
10388
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
@@ -9761,7 +10397,7 @@ function UnconnectedVisuallyHidden(props, forwardedRef) {
9761
10397
  }
9762
10398
  var component_default$1 = contextConnect(UnconnectedVisuallyHidden, "VisuallyHidden");
9763
10399
  //#endregion
9764
- //#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
10400
+ //#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
9765
10401
  var breakpoints = [
9766
10402
  "40em",
9767
10403
  "52em",
@@ -9795,7 +10431,7 @@ function useResponsiveValue(values, options = {}) {
9795
10431
  return array[index >= array.length ? array.length - 1 : index];
9796
10432
  }
9797
10433
  //#endregion
9798
- //#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
10434
+ //#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
9799
10435
  var ALIGNMENTS = {
9800
10436
  bottom: {
9801
10437
  alignItems: "flex-end",
@@ -9843,7 +10479,7 @@ function getAlignmentProps(alignment) {
9843
10479
  return alignment ? ALIGNMENTS[alignment] : {};
9844
10480
  }
9845
10481
  //#endregion
9846
- //#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
10482
+ //#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
9847
10483
  function useGrid(props) {
9848
10484
  const { align, alignment, className, columnGap, columns = 2, gap = 3, isInline = false, justify, rowGap, rows, templateColumns, templateRows, ...otherProps } = useContextSystem(props, "Grid");
9849
10485
  const column = useResponsiveValue(Array.isArray(columns) ? columns : [columns]);
@@ -9885,7 +10521,7 @@ function useGrid(props) {
9885
10521
  };
9886
10522
  }
9887
10523
  //#endregion
9888
- //#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
10524
+ //#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
9889
10525
  function UnconnectedGrid(props, forwardedRef) {
9890
10526
  const gridProps = useGrid(props);
9891
10527
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
@@ -11171,6 +11807,7 @@ exports.SparklineUnresponsive = SparklineUnresponsive;
11171
11807
  exports.ThemeProvider = GlobalChartsProvider;
11172
11808
  exports.TrendIndicator = TrendIndicator;
11173
11809
  exports.buildCalendarHeatmapData = buildCalendarHeatmapData;
11810
+ exports.buildMonthCalendarHeatmapData = buildMonthCalendarHeatmapData;
11174
11811
  exports.defaultTheme = defaultTheme;
11175
11812
  exports.formatMetricValue = formatMetricValue;
11176
11813
  exports.formatPercentage = formatPercentage;
@@ -11195,6 +11832,7 @@ exports.useChartScopeElement = useChartScopeElement;
11195
11832
  exports.useGlobalChartsContext = useGlobalChartsContext;
11196
11833
  exports.useGlobalChartsTheme = useGlobalChartsTheme;
11197
11834
  exports.useLeaderboardLegendItems = useLeaderboardLegendItems;
11835
+ exports.useMonthCalendarHeatmapData = useMonthCalendarHeatmapData;
11198
11836
  exports.validateHexColor = validateHexColor;
11199
11837
 
11200
11838
  //# sourceMappingURL=index.cjs.map