@automattic/charts 4.11.1 → 4.13.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 +24 -0
  2. package/dist/index.cjs +1412 -787
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +59 -25
  5. package/dist/index.d.cts +64 -120
  6. package/dist/index.d.ts +64 -120
  7. package/dist/index.js +1400 -765
  8. package/dist/index.js.map +1 -1
  9. package/package.json +9 -9
  10. package/src/charts/area-chart/area-chart.tsx +16 -6
  11. package/src/charts/area-chart/test/area-chart.test.tsx +42 -9
  12. package/src/charts/bar-chart/bar-chart.tsx +143 -142
  13. package/src/charts/bar-chart/index.ts +3 -0
  14. package/src/charts/bar-chart/private/band-tooltip.tsx +41 -15
  15. package/src/charts/bar-chart/private/bar-layer.tsx +132 -0
  16. package/src/charts/bar-chart/private/comparison-bars-geometry.ts +22 -34
  17. package/src/charts/bar-chart/private/comparison-bars.tsx +9 -18
  18. package/src/charts/bar-chart/private/comparison-constants.ts +4 -10
  19. package/src/charts/bar-chart/private/grouped-bars.ts +68 -0
  20. package/src/charts/bar-chart/private/index.ts +7 -1
  21. package/src/charts/bar-chart/private/overlap-bars.ts +183 -0
  22. package/src/charts/bar-chart/private/selected-bar-ring.tsx +84 -0
  23. package/src/charts/bar-chart/private/slot-tooltip-members.tsx +28 -0
  24. package/src/charts/bar-chart/private/slotted-bar-series.tsx +84 -0
  25. package/src/charts/bar-chart/private/test/comparison-bars-geometry.test.ts +4 -11
  26. package/src/charts/bar-chart/private/test/comparison-bars.test.tsx +2 -3
  27. package/src/charts/bar-chart/private/test/grouped-bars.test.ts +87 -0
  28. package/src/charts/bar-chart/private/test/overlap-bars.test.ts +159 -0
  29. package/src/charts/bar-chart/private/use-bar-stops.ts +87 -0
  30. package/src/charts/bar-chart/private/use-overlap-slots.ts +213 -0
  31. package/src/charts/bar-chart/private/value-domain.ts +0 -10
  32. package/src/charts/bar-chart/test/bar-chart-overlap.test.tsx +347 -0
  33. package/src/charts/bar-chart/test/bar-chart.test.tsx +181 -19
  34. package/src/charts/bar-chart/test/use-bar-legend-items.test.tsx +81 -0
  35. package/src/charts/bar-chart/types.ts +3 -0
  36. package/src/charts/bar-chart/use-bar-colors.ts +46 -0
  37. package/src/charts/bar-chart/use-bar-legend-items.ts +87 -0
  38. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +24 -11
  39. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +235 -182
  40. package/src/charts/conversion-funnel-chart/index.ts +2 -0
  41. package/src/charts/conversion-funnel-chart/private/index.ts +1 -0
  42. package/src/charts/conversion-funnel-chart/private/use-funnel-selection.ts +9 -47
  43. package/src/charts/conversion-funnel-chart/private/use-roving-step-focus.ts +83 -0
  44. package/src/charts/conversion-funnel-chart/test/conversion-funnel-chart.test.tsx +312 -92
  45. package/src/charts/conversion-funnel-chart/test/use-funnel-selection.test.ts +23 -188
  46. package/src/charts/conversion-funnel-chart/types.ts +22 -0
  47. package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +41 -1
  48. package/src/charts/leaderboard-chart/leaderboard-chart.tsx +101 -30
  49. package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +47 -0
  50. package/src/charts/line-chart/line-chart.tsx +25 -18
  51. package/src/charts/line-chart/test/line-chart.test.tsx +240 -1
  52. package/src/charts/pie-chart/pie-chart.tsx +7 -2
  53. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +7 -2
  54. package/src/charts/private/pie-keyboard-navigation/index.ts +0 -1
  55. package/src/charts/private/{pie-keyboard-navigation/pie-keyboard-navigation.module.scss → selected-ring/selected-ring.module.scss} +11 -6
  56. package/src/charts/private/selected-ring/selected-ring.tsx +73 -0
  57. package/src/charts/private/{pie-keyboard-navigation/test/selected-segment-ring.test.ts → selected-ring/test/selected-ring.test.ts} +1 -1
  58. package/src/components/legend/hooks/test/use-chart-legend-items.test.tsx +20 -0
  59. package/src/components/legend/hooks/use-chart-legend-items.ts +27 -3
  60. package/src/components/tooltip/private/at-nearest-date.ts +49 -0
  61. package/src/components/tooltip/private/keyboard-stops.ts +67 -0
  62. package/src/components/tooltip/private/xy-chart-tooltip-overlay.tsx +10 -2
  63. package/src/components/tooltip/test/keyboard-stops.test.ts +36 -0
  64. package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +135 -1
  65. package/src/components/tooltip/xy-chart-tooltip.tsx +11 -14
  66. package/src/index.ts +10 -2
  67. package/src/providers/chart-context/global-charts-provider.tsx +2 -0
  68. package/src/providers/chart-context/test/chart-context.test.tsx +0 -13
  69. package/src/providers/chart-context/themes.ts +0 -1
  70. package/src/providers/chart-context/types.ts +2 -0
  71. package/src/test-utils/selected-bar-ring.ts +14 -0
  72. package/src/types.ts +14 -5
  73. package/src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx +0 -47
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getStringWidth as getStringWidth$1 } from "./visx/text/index.js";
2
2
  import { formatNumber, formatNumberCompact } from "@automattic/number-formatters";
3
3
  import { AnimatedAreaSeries, AnimatedAreaStack, AreaSeries, Axis, BarGroup, BarSeries, DataContext, GlyphSeries, Grid, TooltipContext, XYChart, buildChartTheme, useEventEmitter } from "@visx/xychart";
4
- import { __, _x, sprintf } from "@wordpress/i18n";
4
+ import { __, _n, _x, sprintf } from "@wordpress/i18n";
5
5
  import clsx from "clsx";
6
6
  import * as React from "react";
7
7
  import { Children, Fragment, createContext, createContext as createContext$1, createElement, forwardRef, forwardRef as forwardRef$1, isValidElement, memo, useCallback, useCallback as useCallback$1, useContext, useContext as useContext$1, useEffect, useEffect as useEffect$1, useId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useRef, useRef as useRef$1, useState, useState as useState$1, version } from "react";
@@ -30,7 +30,7 @@ import { __assign } from "tslib";
30
30
  import "@babel/runtime/helpers/esm/extends";
31
31
  import { Icon, chevronRight } from "@wordpress/icons";
32
32
  import { arc, pie } from "@visx/shape";
33
- //#region \0rolldown/runtime.js
33
+ // \0rolldown/runtime.js
34
34
  var __create = Object.create;
35
35
  var __defProp = Object.defineProperty;
36
36
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -52,26 +52,22 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
52
52
  value: mod,
53
53
  enumerable: true
54
54
  }) : target, mod));
55
- //#endregion
56
- //#region src/charts/private/chart-instance-context/chart-instance-context.ts
55
+ // src/charts/private/chart-instance-context/chart-instance-context.ts
57
56
  const ChartInstanceContext = createContext(null);
58
- //#endregion
59
- //#region src/charts/private/chart-instance-context/use-chart-instance-context.ts
57
+ // src/charts/private/chart-instance-context/use-chart-instance-context.ts
60
58
  const useChartInstanceContext = () => {
61
59
  const context = useContext(ChartInstanceContext);
62
60
  if (!context) throw new Error("useChartInstanceContext must be used within a Chart component");
63
61
  return context;
64
62
  };
65
- //#endregion
66
- //#region src/styles/chart-scope-class.ts
63
+ // src/styles/chart-scope-class.ts
67
64
  /**
68
65
  * Stable class that carries the `--a8c-charts-*` catalog. Applied to the `GlobalChartsProvider` wrapper, to portal-rendered surfaces (which sit outside the provider tree), and to standalone components when no provider is above them.
69
66
  *
70
67
  * Deliberately unhashed: consumers are documented as being able to target it to override a token on the provider element.
71
68
  */
72
69
  const CHART_SCOPE_CLASS = "a8c-charts-scope";
73
- //#endregion
74
- //#region src/utils/create-composition.ts
70
+ // src/utils/create-composition.ts
75
71
  /**
76
72
  * Utility function to create chart components with composition API.
77
73
  *
@@ -85,8 +81,7 @@ const CHART_SCOPE_CLASS = "a8c-charts-scope";
85
81
  function attachSubComponents(Chart, subComponents) {
86
82
  return Object.assign(Chart, subComponents);
87
83
  }
88
- //#endregion
89
- //#region src/utils/warn-once.ts
84
+ // src/utils/warn-once.ts
90
85
  const warned = /* @__PURE__ */ new Set();
91
86
  /**
92
87
  * Warns about a host-supplied value once per process, and never in production.
@@ -99,8 +94,7 @@ const warnOnce = (key, message) => {
99
94
  warned.add(key);
100
95
  console.warn(`[Charts] ${message}`);
101
96
  };
102
- //#endregion
103
- //#region src/utils/date-parsing.ts
97
+ // src/utils/date-parsing.ts
104
98
  /**
105
99
  * @file Date parsing: a naive string is dated in a supplied IANA zone, or the runtime's own
106
100
  *
@@ -195,8 +189,7 @@ const parseAsLocalDate = (dateString, timeZone) => {
195
189
  }
196
190
  return /* @__PURE__ */ new Date(NaN);
197
191
  };
198
- //#endregion
199
- //#region src/utils/bucket-info.ts
192
+ // src/utils/bucket-info.ts
200
193
  const getInstant = (point) => {
201
194
  if (!point) return;
202
195
  if ("date" in point && point.date) return point.date.getTime();
@@ -228,8 +221,7 @@ const getBucketInfo = (data, tickResolution) => ({
228
221
  bucket: tickResolution ?? getBucketResolution(data),
229
222
  displayResolution: getBucketResolution(data, tickResolution)
230
223
  });
231
- //#endregion
232
- //#region src/utils/format-metric-value.ts
224
+ // src/utils/format-metric-value.ts
233
225
  /**
234
226
  * Format a numeric metric value based on type, precision and scale.
235
227
  * Supports currency, number and percentage, using `@automattic/number-formatters`.
@@ -278,8 +270,7 @@ const formatMetricValue = (value, type = "number", { decimals, useMultipliers =
278
270
  });
279
271
  }
280
272
  };
281
- //#endregion
282
- //#region src/utils/format-percentage.ts
273
+ // src/utils/format-percentage.ts
283
274
  /**
284
275
  * Format a percentage value with smart decimal handling.
285
276
  * Uses `@automattic/number-formatters` for consistent formatting.
@@ -295,8 +286,7 @@ const formatPercentage = (value) => {
295
286
  maximumFractionDigits: 2
296
287
  } });
297
288
  };
298
- //#endregion
299
- //#region src/utils/get-edge-tick-widths.ts
289
+ // src/utils/get-edge-tick-widths.ts
300
290
  /**
301
291
  * Rendered widths of the first and last tick labels on an axis.
302
292
  *
@@ -320,8 +310,7 @@ const getEdgeTickWidths = (ticks, formatTick, labelStyle) => {
320
310
  last: getStringWidth(label(ticks[lastIndex], lastIndex), labelStyle) ?? 0
321
311
  };
322
312
  };
323
- //#endregion
324
- //#region src/utils/get-longest-tick-width.ts
313
+ // src/utils/get-longest-tick-width.ts
325
314
  /**
326
315
  * Returns the width of the longest tick.
327
316
  *
@@ -335,8 +324,7 @@ const getLongestTickWidth = (ticks, formatTick, labelStyle) => {
335
324
  const longestTick = formattedTicks.reduce((longest, current) => longest.length >= current.length ? longest : current, formattedTicks[0]);
336
325
  return getStringWidth(longestTick, labelStyle);
337
326
  };
338
- //#endregion
339
- //#region src/utils/get-styles.ts
327
+ // src/utils/get-styles.ts
340
328
  /**
341
329
  * Utility function to get consolidated line styles for a series
342
330
  * This consolidates the logic used by both LineChart and Legend components
@@ -389,19 +377,16 @@ function getItemShapeStyles(series, index, theme, legendShape) {
389
377
  ...barShapeStyles
390
378
  };
391
379
  }
392
- //#endregion
393
- //#region src/utils/is-safari.ts
380
+ // src/utils/is-safari.ts
394
381
  const isSafari = () => {
395
382
  if (typeof navigator !== "undefined" && navigator.userAgent) return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
396
383
  return false;
397
384
  };
398
- //#endregion
399
- //#region src/utils/merge-themes.ts
385
+ // src/utils/merge-themes.ts
400
386
  function mergeThemes(baseTheme, overrideTheme) {
401
387
  return deepmerge(baseTheme, overrideTheme, { arrayMerge: (_destinationArray, sourceArray) => sourceArray });
402
388
  }
403
- //#endregion
404
- //#region src/utils/color-utils.ts
389
+ // src/utils/color-utils.ts
405
390
  /**
406
391
  * Check if a value is a valid 6-digit hex color
407
392
  * @param hex - The value to check
@@ -568,8 +553,7 @@ const prefersLightText = (backgroundHex) => {
568
553
  if (!isValidHexColor(backgroundHex)) return false;
569
554
  return relativeLuminance(backgroundHex) <= .179;
570
555
  };
571
- //#endregion
572
- //#region src/utils/resolve-css-var.ts
556
+ // src/utils/resolve-css-var.ts
573
557
  /**
574
558
  * Pattern for valid CSS custom property names (e.g., '--my-color', '--jp-gray-10')
575
559
  */
@@ -666,8 +650,7 @@ function readCustomProperty(varName, styles) {
666
650
  return null;
667
651
  }
668
652
  }
669
- //#endregion
670
- //#region src/utils/resolve-font-size.ts
653
+ // src/utils/resolve-font-size.ts
671
654
  const toFontSize = (size) => Number.isFinite(size) && size >= 0 ? size : void 0;
672
655
  /**
673
656
  * Resolve a theme `fontSize` value into a plain number suitable for
@@ -694,8 +677,7 @@ const resolveFontSize = (val) => {
694
677
  return match ? toFontSize(parseFloat(match[1])) : void 0;
695
678
  }
696
679
  };
697
- //#endregion
698
- //#region src/utils/date-formatting.ts
680
+ // src/utils/date-formatting.ts
699
681
  const formatters = /* @__PURE__ */ new Map();
700
682
  const getFormatter$1 = (options, { locale, timeZone }) => {
701
683
  const key = `${locale ?? ""}|${timeZone ?? ""}|${JSON.stringify(options)}`;
@@ -794,8 +776,7 @@ const createZonedClock = (timeZone) => {
794
776
  };
795
777
  };
796
778
  };
797
- //#endregion
798
- //#region src/providers/chart-scope/chart-scope-context.ts
779
+ // src/providers/chart-scope/chart-scope-context.ts
799
780
  /**
800
781
  * The nearest chart element, published so JS token resolution reads the same CSS cascade that paints the chart's own colours.
801
782
  *
@@ -812,8 +793,7 @@ const seriesRole = (slot) => `--a8c-charts-color-series-${slot}`;
812
793
  const SERIES_SLOT_1_FALLBACK = "#3858e9";
813
794
  /** The catalog pointer for every slot, in slot order. */
814
795
  const SERIES_PALETTE_POINTERS = Array.from({ length: 5 }, (_, index) => index === 0 ? `var(${seriesRole(1)}, ${SERIES_SLOT_1_FALLBACK})` : `var(${seriesRole(index + 1)})`);
815
- //#endregion
816
- //#region src/providers/chart-context/private/catalog-pointers.ts
796
+ // src/providers/chart-context/private/catalog-pointers.ts
817
797
  /** Terminal literals the provider also generates from before its first read, so SSR and the first client render agree. */
818
798
  const BACKGROUND_FALLBACK = "#ffffff";
819
799
  const LABEL_FALLBACK = "#1e1e1e";
@@ -853,8 +833,7 @@ const ANNOTATION_POINTERS = {
853
833
  radius: 5
854
834
  }
855
835
  };
856
- //#endregion
857
- //#region src/providers/chart-context/private/perceptual-color.ts
836
+ // src/providers/chart-context/private/perceptual-color.ts
858
837
  const DEUTERANOPIA = [
859
838
  .367322,
860
839
  .860646,
@@ -1260,16 +1239,14 @@ const buildPaletteGenerator = (normalizedSeeds, background, labelColors) => {
1260
1239
  return palette[index].hex;
1261
1240
  };
1262
1241
  };
1263
- //#endregion
1264
- //#region src/providers/chart-context/private/resolve-opaque-hex.ts
1242
+ // src/providers/chart-context/private/resolve-opaque-hex.ts
1265
1243
  const resolveOpaqueHex = (pointer, resolve) => {
1266
1244
  const raw = resolve(pointer);
1267
1245
  if (!raw || color(raw)?.opacity !== 1) return null;
1268
1246
  const hex = normalizeColorToHex(raw);
1269
1247
  return isValidHexColor(hex) ? hex : null;
1270
1248
  };
1271
- //#endregion
1272
- //#region src/providers/chart-context/themes.ts
1249
+ // src/providers/chart-context/themes.ts
1273
1250
  /**
1274
1251
  * Default theme configuration: the shape and spacing a consumer can override.
1275
1252
  *
@@ -1296,10 +1273,7 @@ const defaultTheme = {
1296
1273
  strokeDasharray: "4 4",
1297
1274
  strokeLinecap: "square"
1298
1275
  } } },
1299
- barChart: { barStyles: { comparison: {
1300
- widthFactor: 1.5,
1301
- opacity: .5
1302
- } } },
1276
+ barChart: { barStyles: { comparison: { widthFactor: 1.5 } } },
1303
1277
  sparkline: {
1304
1278
  margin: {
1305
1279
  top: 2,
@@ -1315,8 +1289,7 @@ const defaultTheme = {
1315
1289
  groupGap: 24
1316
1290
  }
1317
1291
  };
1318
- //#endregion
1319
- //#region src/providers/chart-context/global-charts-provider.tsx
1292
+ // src/providers/chart-context/global-charts-provider.tsx
1320
1293
  const PLACEHOLDER_LABEL_COLORS = [LABEL_FALLBACK, LABEL_INVERSE_FALLBACK];
1321
1294
  const GlobalChartsContext = createContext(null);
1322
1295
  const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
@@ -1457,6 +1430,7 @@ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
1457
1430
  theme: providerTheme,
1458
1431
  formatting,
1459
1432
  getElementStyles,
1433
+ backgroundColor: colorCache.background,
1460
1434
  toggleSeriesVisibility,
1461
1435
  setSeriesVisibility,
1462
1436
  setChartHiddenSeries,
@@ -1473,6 +1447,7 @@ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
1473
1447
  providerTheme,
1474
1448
  formatting,
1475
1449
  getElementStyles,
1450
+ colorCache.background,
1476
1451
  toggleSeriesVisibility,
1477
1452
  setSeriesVisibility,
1478
1453
  setChartHiddenSeries,
@@ -1495,21 +1470,18 @@ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
1495
1470
  })
1496
1471
  });
1497
1472
  };
1498
- //#endregion
1499
- //#region src/providers/chart-context/hooks/use-global-charts-context.ts
1473
+ // src/providers/chart-context/hooks/use-global-charts-context.ts
1500
1474
  const useGlobalChartsContext = () => {
1501
1475
  const context = useContext(GlobalChartsContext);
1502
1476
  if (!context) throw new Error("useGlobalChartsContext must be used within a GlobalChartsProvider");
1503
1477
  return context;
1504
1478
  };
1505
- //#endregion
1506
- //#region src/providers/chart-context/hooks/use-chart-id.ts
1479
+ // src/providers/chart-context/hooks/use-chart-id.ts
1507
1480
  const useChartId = (providedId) => {
1508
1481
  const generatedId = useId();
1509
1482
  return providedId || generatedId;
1510
1483
  };
1511
- //#endregion
1512
- //#region src/hooks/use-deep-memo.ts
1484
+ // src/hooks/use-deep-memo.ts
1513
1485
  var import_fast_deep_equal = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
1514
1486
  module.exports = function equal(a, b) {
1515
1487
  if (a === b) return true;
@@ -1550,16 +1522,14 @@ const useDeepMemo = (value) => {
1550
1522
  if (!(0, import_fast_deep_equal.default)(ref.current, value)) ref.current = value;
1551
1523
  return ref.current;
1552
1524
  };
1553
- //#endregion
1554
- //#region src/providers/chart-scope/use-chart-scope-element.ts
1525
+ // src/providers/chart-scope/use-chart-scope-element.ts
1555
1526
  /**
1556
1527
  * Returns the nearest chart scope element, or `null` when rendered outside any scope (and during SSR, before the ref is attached).
1557
1528
  *
1558
1529
  * @return {HTMLElement | null} The nearest chart scope element, or `null`.
1559
1530
  */
1560
1531
  const useChartScopeElement = () => useContext(ChartScopeContext);
1561
- //#endregion
1562
- //#region src/providers/chart-scope/use-standalone-scope-class.ts
1532
+ // src/providers/chart-scope/use-standalone-scope-class.ts
1563
1533
  /**
1564
1534
  * Returns the catalog class when there is no `GlobalChartsProvider` above this component, and `undefined` when there is.
1565
1535
  *
@@ -1568,8 +1538,7 @@ const useChartScopeElement = () => useContext(ChartScopeContext);
1568
1538
  * @return The scope class name, or undefined when a provider is present.
1569
1539
  */
1570
1540
  const useStandaloneScopeClass = () => useContext(GlobalChartsContext) ? void 0 : CHART_SCOPE_CLASS;
1571
- //#endregion
1572
- //#region src/hooks/use-xychart-theme.ts
1541
+ // src/hooks/use-xychart-theme.ts
1573
1542
  const STARTS_LIKE_A_NUMBER = /^[+-]?[\d.]/;
1574
1543
  const NON_NEGATIVE_CSS_NUMBER = /^\+?(?:\d*\.)?\d+(?:e[+-]?\d+)?(?:[a-z]+|%)?$/i;
1575
1544
  const isRenderableFontSize = (fontSize) => {
@@ -1625,8 +1594,7 @@ const useXYChartTheme = (data) => {
1625
1594
  scopeElement
1626
1595
  ]);
1627
1596
  };
1628
- //#endregion
1629
- //#region src/providers/chart-context/hooks/use-chart-formatting.ts
1597
+ // src/providers/chart-context/hooks/use-chart-formatting.ts
1630
1598
  const RUNTIME_FORMATTING = {};
1631
1599
  /**
1632
1600
  * The locale and time zone dates are rendered in, as set on `GlobalChartsProvider`.
@@ -1640,8 +1608,7 @@ const RUNTIME_FORMATTING = {};
1640
1608
  const useChartFormatting = () => {
1641
1609
  return useContext(GlobalChartsContext)?.formatting ?? RUNTIME_FORMATTING;
1642
1610
  };
1643
- //#endregion
1644
- //#region src/hooks/use-chart-data-transform.ts
1611
+ // src/hooks/use-chart-data-transform.ts
1645
1612
  /**
1646
1613
  * Hook that transforms and sorts chart data, handling date parsing and sorting
1647
1614
  *
@@ -1676,8 +1643,7 @@ const useChartDataTransform = (data) => {
1676
1643
  }));
1677
1644
  }, [data, timeZone]);
1678
1645
  };
1679
- //#endregion
1680
- //#region src/hooks/use-chart-margin.tsx
1646
+ // src/hooks/use-chart-margin.tsx
1681
1647
  /**
1682
1648
  * Base top margin used when no dynamic adjustments are necessary.
1683
1649
  */
@@ -1799,8 +1765,7 @@ const useChartMargin = (height, options, data, theme, horizontal = false) => {
1799
1765
  yTicks
1800
1766
  ]);
1801
1767
  };
1802
- //#endregion
1803
- //#region src/hooks/use-element-size.ts
1768
+ // src/hooks/use-element-size.ts
1804
1769
  /**
1805
1770
  * Hook to measure the width and height of a DOM element.
1806
1771
  * Returns a ref callback to attach to the element and the current dimensions in pixels.
@@ -1837,8 +1802,7 @@ function useElementSize({ initialWidth = 0, initialHeight = 0 } = {}) {
1837
1802
  height
1838
1803
  ];
1839
1804
  }
1840
- //#endregion
1841
- //#region src/hooks/use-text-truncation.ts
1805
+ // src/hooks/use-text-truncation.ts
1842
1806
  /**
1843
1807
  * Hook to detect if text content is truncated within its container.
1844
1808
  * Uses ResizeObserver to dynamically track changes in element size.
@@ -1879,8 +1843,7 @@ function useTextTruncation(enabled = true) {
1879
1843
  } else setIsTruncated(false);
1880
1844
  }, [enabled]), isTruncated];
1881
1845
  }
1882
- //#endregion
1883
- //#region src/hooks/use-zero-value-display.ts
1846
+ // src/hooks/use-zero-value-display.ts
1884
1847
  /**
1885
1848
  * Minimum pixel size for near-zero bars (non-zero values that would render too small).
1886
1849
  * Using 3px to be visible but not misleading - larger values might look like actual data.
@@ -1920,8 +1883,7 @@ const useZeroValueDisplay = (data, options = { enabled: false }) => {
1920
1883
  valueAxisLength
1921
1884
  ]);
1922
1885
  };
1923
- //#endregion
1924
- //#region src/hooks/use-data-with-percentages.ts
1886
+ // src/hooks/use-data-with-percentages.ts
1925
1887
  /**
1926
1888
  * Hook to calculate percentages from values for chart data.
1927
1889
  * Ensures percentages are always derived from values (single source of truth).
@@ -1938,8 +1900,7 @@ const useDataWithPercentages = (data) => {
1938
1900
  }));
1939
1901
  }, [data]);
1940
1902
  };
1941
- //#endregion
1942
- //#region src/hooks/use-legend-visibility-data.ts
1903
+ // src/hooks/use-legend-visibility-data.ts
1943
1904
  /**
1944
1905
  * Custom hook to filter and recalculate chart data based on series visibility.
1945
1906
  *
@@ -2011,8 +1972,7 @@ const useLegendVisibilityData = ({ data, chartId, isSeriesVisible }) => {
2011
1972
  ])
2012
1973
  };
2013
1974
  };
2014
- //#endregion
2015
- //#region src/hooks/use-prefers-reduced-motion.ts
1975
+ // src/hooks/use-prefers-reduced-motion.ts
2016
1976
  const QUERY = "(prefers-reduced-motion: no-preference)";
2017
1977
  const getInitialState = () => !window.matchMedia(QUERY).matches;
2018
1978
  /**
@@ -2033,11 +1993,9 @@ function usePrefersReducedMotion() {
2033
1993
  }, []);
2034
1994
  return prefersReducedMotion;
2035
1995
  }
2036
- //#endregion
2037
- //#region src/hooks/use-isomorphic-layout-effect.ts
1996
+ // src/hooks/use-isomorphic-layout-effect.ts
2038
1997
  const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
2039
- //#endregion
2040
- //#region src/providers/chart-context/hooks/use-chart-registration.ts
1998
+ // src/providers/chart-context/hooks/use-chart-registration.ts
2041
1999
  const useChartRegistration = ({ chartId, legendItems, chartType, isDataValid, metadata }) => {
2042
2000
  const { registerChart, unregisterChart } = useGlobalChartsContext();
2043
2001
  const stableLegendItems = useDeepMemo(legendItems);
@@ -2059,8 +2017,7 @@ const useChartRegistration = ({ chartId, legendItems, chartType, isDataValid, me
2059
2017
  isDataValid
2060
2018
  ]);
2061
2019
  };
2062
- //#endregion
2063
- //#region src/providers/chart-context/hooks/use-global-charts-theme.ts
2020
+ // src/providers/chart-context/hooks/use-global-charts-theme.ts
2064
2021
  /**
2065
2022
  * Hook to get the global chart theme from GlobalChartsProvider
2066
2023
  *
@@ -2069,8 +2026,7 @@ const useChartRegistration = ({ chartId, legendItems, chartType, isDataValid, me
2069
2026
  const useGlobalChartsTheme = () => {
2070
2027
  return useContext(GlobalChartsContext)?.theme ?? defaultTheme;
2071
2028
  };
2072
- //#endregion
2073
- //#region src/components/legend/utils/value-or-identity.ts
2029
+ // src/components/legend/utils/value-or-identity.ts
2074
2030
  /**
2075
2031
  * Returns an object's value if defined, or the object.
2076
2032
  * @param _ - The object to return the value of.
@@ -2088,8 +2044,7 @@ function valueOrIdentity(_) {
2088
2044
  function valueOrIdentityString(_) {
2089
2045
  return String(valueOrIdentity(_));
2090
2046
  }
2091
- //#endregion
2092
- //#region src/components/legend/private/base-legend.module.scss
2047
+ // src/components/legend/private/base-legend.module.scss
2093
2048
  var base_legend_module_default = {
2094
2049
  "legend": "a8ccharts-04TogW-legend",
2095
2050
  "legend--horizontal": "a8ccharts-04TogW-legend--horizontal",
@@ -2101,8 +2056,7 @@ var base_legend_module_default = {
2101
2056
  "legend-item-text--wrap": "a8ccharts-04TogW-legend-item-text--wrap",
2102
2057
  "legend-item-value": "a8ccharts-04TogW-legend-item-value"
2103
2058
  };
2104
- //#endregion
2105
- //#region src/components/legend/private/base-legend.tsx
2059
+ // src/components/legend/private/base-legend.tsx
2106
2060
  const ALIGNMENT_TO_FLEX = {
2107
2061
  start: "flex-start",
2108
2062
  center: "center",
@@ -2267,8 +2221,7 @@ const BaseLegend = forwardRef(({ items: allItems, className, orientation = "hori
2267
2221
  })
2268
2222
  });
2269
2223
  });
2270
- //#endregion
2271
- //#region src/components/legend/legend.tsx
2224
+ // src/components/legend/legend.tsx
2272
2225
  const defaultShapeByChartType = {
2273
2226
  area: "rect",
2274
2227
  line: "line",
@@ -2294,8 +2247,22 @@ const Legend = forwardRef(({ chartId, items, shape, ...props }, ref) => {
2294
2247
  chartId: contextChartId
2295
2248
  });
2296
2249
  });
2297
- //#endregion
2298
- //#region src/components/legend/hooks/use-chart-legend-items.ts
2250
+ // src/components/legend/hooks/use-chart-legend-items.ts
2251
+ /**
2252
+ * Wraps `getElementStyles` so a swatch takes the color given for its series.
2253
+ * @param getElementStyles - Function to get element styles
2254
+ * @param seriesColors - Each series' swatch color, by index
2255
+ * @return Element styles with the given color
2256
+ */
2257
+ function withSeriesColors(getElementStyles, seriesColors) {
2258
+ return (params) => {
2259
+ const styles = getElementStyles(params);
2260
+ return {
2261
+ ...styles,
2262
+ color: seriesColors[params.index] ?? styles.color
2263
+ };
2264
+ };
2265
+ }
2299
2266
  /**
2300
2267
  * Formats the value for a data point based on its type and display preference
2301
2268
  * @param point - The data point to format
@@ -2508,21 +2475,22 @@ function processPointData(pointData, getElementStyles, showValues, legendValueDi
2508
2475
  * @return Array of legend items ready for display
2509
2476
  */
2510
2477
  function useChartLegendItems(data, options = {}, legendShape) {
2511
- const { showValues = false, legendValueDisplay = "percentage", withGlyph = false, glyphSize = 8, collapseGroups = false, comparisonItem = false, comparisonSeriesItems, renderGlyph } = options;
2512
- const { getElementStyles } = useGlobalChartsContext();
2478
+ const { showValues = false, legendValueDisplay = "percentage", withGlyph = false, glyphSize = 8, collapseGroups = false, comparisonItem = false, comparisonSeriesItems, seriesColors, renderGlyph } = options;
2479
+ const { getElementStyles: getSeriesStyles } = useGlobalChartsContext();
2513
2480
  return useMemo(() => {
2514
2481
  if (!data || !Array.isArray(data) || data.length === 0) return [];
2515
2482
  if ("data" in data[0]) {
2516
2483
  const seriesData = data;
2484
+ const getElementStyles = seriesColors ? withSeriesColors(getSeriesStyles, seriesColors) : getSeriesStyles;
2517
2485
  const items = processSeriesData(seriesData, getElementStyles, showValues, withGlyph, glyphSize, collapseGroups, renderGlyph, legendShape);
2518
2486
  if (comparisonSeriesItems) return [...items, ...buildPerSeriesComparisonItems(seriesData, items, comparisonSeriesItems, getElementStyles, legendShape)];
2519
2487
  const comparison = comparisonItem ? buildComparisonLegendItem(seriesData, items, typeof comparisonItem === "string" ? comparisonItem : __("Comparison period", "jetpack-charts"), getElementStyles, legendShape) : null;
2520
2488
  return comparison ? [...items, comparison] : items;
2521
2489
  }
2522
- return processPointData(data, getElementStyles, showValues, legendValueDisplay, withGlyph, glyphSize, renderGlyph, legendShape);
2490
+ return processPointData(data, getSeriesStyles, showValues, legendValueDisplay, withGlyph, glyphSize, renderGlyph, legendShape);
2523
2491
  }, [
2524
2492
  data,
2525
- getElementStyles,
2493
+ getSeriesStyles,
2526
2494
  showValues,
2527
2495
  legendValueDisplay,
2528
2496
  withGlyph,
@@ -2530,19 +2498,18 @@ function useChartLegendItems(data, options = {}, legendShape) {
2530
2498
  collapseGroups,
2531
2499
  comparisonItem,
2532
2500
  comparisonSeriesItems,
2501
+ seriesColors,
2533
2502
  renderGlyph,
2534
2503
  legendShape
2535
2504
  ]);
2536
2505
  }
2537
- //#endregion
2538
- //#region src/components/tooltip/private/label-value-content.tsx
2506
+ // src/components/tooltip/private/label-value-content.tsx
2539
2507
  const LabelValueContent = ({ data }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
2540
2508
  data.label,
2541
2509
  ": ",
2542
2510
  data.valueDisplay || formatNumber(data.value)
2543
2511
  ] });
2544
- //#endregion
2545
- //#region src/components/tooltip/private/tooltip-theme.tsx
2512
+ // src/components/tooltip/private/tooltip-theme.tsx
2546
2513
  const DEFAULT_SURFACE = "#1e1e1e";
2547
2514
  const TOOLTIP_SURFACE = `var(--a8c-charts-color-tooltip-surface, ${DEFAULT_SURFACE})`;
2548
2515
  const toSeed = (value) => {
@@ -2565,11 +2532,9 @@ const TooltipTheme = ({ children }) => {
2565
2532
  children
2566
2533
  });
2567
2534
  };
2568
- //#endregion
2569
- //#region src/components/tooltip/tooltip-box.module.scss
2535
+ // src/components/tooltip/tooltip-box.module.scss
2570
2536
  var tooltip_box_module_default = { "surface": "a8ccharts-ddpU1W-surface" };
2571
- //#endregion
2572
- //#region src/components/tooltip/tooltip-box.tsx
2537
+ // src/components/tooltip/tooltip-box.tsx
2573
2538
  /**
2574
2539
  * The chart tooltip box: the dark surface every chart tooltip draws. It does not position itself; place it with `style` or a wrapper.
2575
2540
  *
@@ -2589,8 +2554,7 @@ const TooltipBox = forwardRef(({ unstyled = false, role = "tooltip", className,
2589
2554
  return unstyled ? box : /* @__PURE__ */ jsx(TooltipTheme, { children: box });
2590
2555
  });
2591
2556
  TooltipBox.displayName = "TooltipBox";
2592
- //#endregion
2593
- //#region src/components/tooltip/base-tooltip.tsx
2557
+ // src/components/tooltip/base-tooltip.tsx
2594
2558
  /**
2595
2559
  * A tooltip box with children or `label: value` content. Prefer `TooltipBox`.
2596
2560
  *
@@ -2624,6 +2588,80 @@ const BaseTooltip = ({ data, top, left, component: Component = LabelValueContent
2624
2588
  children: content
2625
2589
  });
2626
2590
  };
2591
+ // src/components/tooltip/private/keyboard-stops.ts
2592
+ const timeOf$1 = (point) => point?.date?.getTime();
2593
+ const getXPositions = (series) => {
2594
+ const positions = /* @__PURE__ */ new Set();
2595
+ for (const { data } of series) for (const point of data) {
2596
+ const time = timeOf$1(point);
2597
+ if (time !== void 0) positions.add(time);
2598
+ }
2599
+ return [...positions].sort((a, b) => a - b);
2600
+ };
2601
+ /**
2602
+ * Keyboard stops over the series: the sorted union of their dates, or array indexes when no point has a date.
2603
+ *
2604
+ * Once any point has a date, a series of undated points has no stop.
2605
+ *
2606
+ * @param series - The series to step through.
2607
+ * @return The stop count, and each series' point at a stop.
2608
+ */
2609
+ const getKeyboardStops = (series) => {
2610
+ const positions = getXPositions(series);
2611
+ if (positions.length === 0) return {
2612
+ count: Math.max(0, ...series.map(({ data }) => data.length)),
2613
+ membersAt: (stop) => series.flatMap((s) => stop < s.data.length ? [{
2614
+ series: s,
2615
+ datum: s.data[stop],
2616
+ index: stop
2617
+ }] : [])
2618
+ };
2619
+ return {
2620
+ count: positions.length,
2621
+ membersAt: (stop) => {
2622
+ const time = positions[stop];
2623
+ if (time === void 0) return [];
2624
+ return series.flatMap((s) => {
2625
+ const index = s.data.findIndex((point) => timeOf$1(point) === time);
2626
+ return index === -1 ? [] : [{
2627
+ series: s,
2628
+ datum: s.data[index],
2629
+ index
2630
+ }];
2631
+ });
2632
+ }
2633
+ };
2634
+ };
2635
+ // src/components/tooltip/private/at-nearest-date.ts
2636
+ const timeOf = (datum) => datum.date?.getTime();
2637
+ /**
2638
+ * Lists every registered series at the nearest datum's date, each with its own point there.
2639
+ *
2640
+ * The pointer hands each series its own nearest point, which for a series that skips that date, or that the pointer sits nearer another date of, is a neighbor's.
2641
+ *
2642
+ * @param tooltipData - The tooltip data visx collected.
2643
+ * @param registry - The chart's data registry.
2644
+ * @return The tooltip data, unchanged when the nearest datum has no date.
2645
+ */
2646
+ const atNearestDate = (tooltipData, registry) => {
2647
+ const nearest = tooltipData?.nearestDatum?.datum;
2648
+ const time = nearest ? timeOf(nearest) : void 0;
2649
+ if (!tooltipData || time === void 0) return tooltipData;
2650
+ const datumByKey = {};
2651
+ for (const { key, data } of registry?.entries() ?? []) {
2652
+ const index = data.findIndex((datum) => timeOf(datum) === time);
2653
+ if (index !== -1) datumByKey[key] = {
2654
+ key,
2655
+ index,
2656
+ datum: data[index]
2657
+ };
2658
+ }
2659
+ for (const [key, entry] of Object.entries(tooltipData.datumByKey)) if (!datumByKey[key] && timeOf(entry.datum) === time) datumByKey[key] = entry;
2660
+ return {
2661
+ ...tooltipData,
2662
+ datumByKey
2663
+ };
2664
+ };
2627
2665
  const POINTER_HEIGHT = 6;
2628
2666
  const clamp = (position, min, max, size) => Math.min(Math.max(position, min), Math.max(min, max - size));
2629
2667
  const isClipping = (element) => {
@@ -2802,8 +2840,7 @@ const BoundedTooltip = ({ left = 0, top = 0, offsetLeft = 10, offsetTop = 10, st
2802
2840
  }), children]
2803
2841
  });
2804
2842
  };
2805
- //#endregion
2806
- //#region src/components/tooltip/private/xy-chart-tooltip-overlay.tsx
2843
+ // src/components/tooltip/private/xy-chart-tooltip-overlay.tsx
2807
2844
  const CROSSHAIR_STROKE_WIDTH = 1.5;
2808
2845
  const CROSSHAIR_PAINT_PROPERTIES = /* @__PURE__ */ new Set([
2809
2846
  "stroke",
@@ -2988,18 +3025,21 @@ const XyChartTooltipContent = ({ tooltipContext, container, renderTooltip, rende
2988
3025
  */
2989
3026
  const XyChartTooltipOverlay = (props) => {
2990
3027
  const tooltipContext = useContext(TooltipContext);
3028
+ const dataRegistry = useContext(DataContext)?.dataRegistry;
2991
3029
  const [container, setContainer] = useState(null);
2992
3030
  const anchorRef = useCallback((node) => {
2993
3031
  setContainer(node?.ownerSVGElement?.parentElement ?? null);
2994
3032
  }, []);
2995
3033
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("g", { ref: anchorRef }), tooltipContext?.tooltipOpen && /* @__PURE__ */ jsx(XyChartTooltipContent, {
2996
3034
  ...props,
2997
- tooltipContext,
3035
+ tooltipContext: {
3036
+ ...tooltipContext,
3037
+ tooltipData: atNearestDate(tooltipContext.tooltipData, dataRegistry)
3038
+ },
2998
3039
  container
2999
3040
  })] });
3000
3041
  };
3001
- //#endregion
3002
- //#region src/components/tooltip/xy-chart-tooltip.tsx
3042
+ // src/components/tooltip/xy-chart-tooltip.tsx
3003
3043
  const XYChartTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocusedClassName, series = [], mode = "group", verticalCrosshairStyle, horizontalCrosshairStyle, ...props }) => {
3004
3044
  const tooltipContext = useContext(TooltipContext);
3005
3045
  const scopeElement = useChartScopeElement();
@@ -3024,6 +3064,7 @@ const XYChartTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocu
3024
3064
  }
3025
3065
  return flattened;
3026
3066
  }, [series, mode]);
3067
+ const stops = useMemo(() => getKeyboardStops(series), [series]);
3027
3068
  const hasKeyboardSelection = useRef(false);
3028
3069
  const closeTooltipNow = useCallback(() => {
3029
3070
  tooltipContext?.updateTooltip({
@@ -3055,19 +3096,22 @@ const XYChartTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocu
3055
3096
  return;
3056
3097
  }
3057
3098
  hasKeyboardSelection.current = true;
3058
- if (mode === "group") series.forEach((s, index) => {
3059
- if (selectedIndex < s.data.length) {
3060
- const datum = s.data[selectedIndex];
3099
+ if (mode === "group") {
3100
+ closeTooltipNow();
3101
+ stops.membersAt(selectedIndex).forEach(({ series: member, datum, index }) => {
3061
3102
  tooltipContext?.showTooltip({
3062
3103
  datum,
3063
- key: s.label,
3104
+ key: member.label,
3064
3105
  index
3065
3106
  });
3066
- }
3067
- });
3068
- else if (mode === "individual") {
3107
+ });
3108
+ } else if (mode === "individual") {
3069
3109
  if (selectedIndex < tooltipData.length) {
3070
3110
  const tooltipItem = tooltipData[selectedIndex];
3111
+ tooltipContext?.updateTooltip((state) => ({
3112
+ ...state,
3113
+ tooltipData: void 0
3114
+ }));
3071
3115
  tooltipContext?.showTooltip({
3072
3116
  datum: tooltipItem.datum,
3073
3117
  key: tooltipItem.seriesLabel,
@@ -3078,7 +3122,7 @@ const XYChartTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocu
3078
3122
  }, [
3079
3123
  selectedIndex,
3080
3124
  tooltipData,
3081
- series
3125
+ stops
3082
3126
  ]);
3083
3127
  const focusableRenderTooltip = useMemo(() => {
3084
3128
  if (!renderTooltip) return void 0;
@@ -3228,8 +3272,7 @@ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating,
3228
3272
  onChartPointerMove
3229
3273
  };
3230
3274
  };
3231
- //#endregion
3232
- //#region src/providers/chart-context/hooks/use-default-hidden-series.ts
3275
+ // src/providers/chart-context/hooks/use-default-hidden-series.ts
3233
3276
  /**
3234
3277
  * Seeds a chart's hidden series once per chart ID, the first time the provider
3235
3278
  * sees that ID.
@@ -3264,8 +3307,7 @@ const useDefaultHiddenSeries = (chartId, seriesLabels) => {
3264
3307
  chartId
3265
3308
  ]);
3266
3309
  };
3267
- //#endregion
3268
- //#region src/charts/private/chart-composition/chart-svg.tsx
3310
+ // src/charts/private/chart-composition/chart-svg.tsx
3269
3311
  /**
3270
3312
  * Compound component for SVG children in charts.
3271
3313
  * This component serves as a marker for SVG content that should be rendered
@@ -3279,8 +3321,7 @@ const ChartSVG = ({ children }) => {
3279
3321
  return /* @__PURE__ */ jsx(Fragment$1, { children });
3280
3322
  };
3281
3323
  ChartSVG.displayName = "Chart.SVG";
3282
- //#endregion
3283
- //#region src/charts/private/chart-composition/chart-html.tsx
3324
+ // src/charts/private/chart-composition/chart-html.tsx
3284
3325
  /**
3285
3326
  * Compound component for HTML children in charts.
3286
3327
  * This component serves as a marker for HTML content that should be rendered
@@ -3294,8 +3335,7 @@ const ChartHTML = ({ children }) => {
3294
3335
  return /* @__PURE__ */ jsx(Fragment$1, { children });
3295
3336
  };
3296
3337
  ChartHTML.displayName = "Chart.HTML";
3297
- //#endregion
3298
- //#region src/charts/private/chart-composition/render-legend-slot.ts
3338
+ // src/charts/private/chart-composition/render-legend-slot.ts
3299
3339
  /**
3300
3340
  * Renders legend children filtered by position slot.
3301
3341
  *
@@ -3306,8 +3346,7 @@ ChartHTML.displayName = "Chart.HTML";
3306
3346
  function renderLegendSlot(legendChildren, position) {
3307
3347
  return legendChildren.filter((l) => l.position === position).map((l, i) => createElement(Fragment, { key: `legend-${position}-${i}` }, l.element));
3308
3348
  }
3309
- //#endregion
3310
- //#region src/charts/private/chart-composition/use-chart-children.ts
3349
+ // src/charts/private/chart-composition/use-chart-children.ts
3311
3350
  /**
3312
3351
  * Custom hook to process and categorize chart children for composition API.
3313
3352
  * Extracts children from compound components (Chart.SVG, Chart.HTML) and
@@ -3360,11 +3399,9 @@ function useChartChildren(children, chartType) {
3360
3399
  };
3361
3400
  }, [children, chartType]);
3362
3401
  }
3363
- //#endregion
3364
- //#region src/charts/private/chart-layout/chart-layout.module.scss
3402
+ // src/charts/private/chart-layout/chart-layout.module.scss
3365
3403
  var chart_layout_module_default = { "chart-layout__content": "a8ccharts-fpNVAq-chart-layout__content" };
3366
- //#endregion
3367
- //#region src/charts/private/chart-layout/chart-layout.tsx
3404
+ // src/charts/private/chart-layout/chart-layout.tsx
3368
3405
  const ChartLayout = ({ legendPosition, legendElement, legendChildren, children, trailingContent, onContentHeightChange, gap, className, rootRef, style, "data-testid": dataTestId, "data-chart-id": dataChartId }) => {
3369
3406
  const [contentRef, contentWidth, contentHeight] = useElementSize();
3370
3407
  const isRenderProp = typeof children === "function";
@@ -3407,8 +3444,7 @@ const ChartLayout = ({ legendPosition, legendElement, legendChildren, children,
3407
3444
  ]
3408
3445
  });
3409
3446
  };
3410
- //#endregion
3411
- //#region src/charts/private/default-glyph/default-glyph.tsx
3447
+ // src/charts/private/default-glyph/default-glyph.tsx
3412
3448
  const DefaultGlyph = (props) => {
3413
3449
  const { theme } = useContext(DataContext) || {};
3414
3450
  props.position;
@@ -3423,8 +3459,7 @@ const DefaultGlyph = (props) => {
3423
3459
  ...props.glyphStyle
3424
3460
  });
3425
3461
  };
3426
- //#endregion
3427
- //#region src/charts/private/readings.ts
3462
+ // src/charts/private/readings.ts
3428
3463
  /**
3429
3464
  * Whether a value is a reading a scale can place.
3430
3465
  *
@@ -3448,11 +3483,9 @@ const isInvalidReading = (value, { allowMissing }) => value === null ? !allowMis
3448
3483
  * @return The formatted value.
3449
3484
  */
3450
3485
  const formatReading = (value) => value == null ? __("No data", "jetpack-charts") : formatNumber(value);
3451
- //#endregion
3452
- //#region src/charts/private/center/center.module.scss
3486
+ // src/charts/private/center/center.module.scss
3453
3487
  var center_module_default = { "center": "a8ccharts-w3qxlG-center" };
3454
- //#endregion
3455
- //#region src/charts/private/center/center.tsx
3488
+ // src/charts/private/center/center.tsx
3456
3489
  /**
3457
3490
  * Centers its children on both axes and fills its parent.
3458
3491
  *
@@ -3474,11 +3507,9 @@ const Center = forwardRef(({ align = "center", justify = "center", className, ..
3474
3507
  ...props
3475
3508
  }));
3476
3509
  Center.displayName = "Center";
3477
- //#endregion
3478
- //#region src/charts/private/svg-empty-state/svg-empty-state.module.scss
3510
+ // src/charts/private/svg-empty-state/svg-empty-state.module.scss
3479
3511
  var svg_empty_state_module_default = { "svg-empty-state": "a8ccharts-udGPVq-svg-empty-state" };
3480
- //#endregion
3481
- //#region src/charts/private/svg-empty-state/svg-empty-state.tsx
3512
+ // src/charts/private/svg-empty-state/svg-empty-state.tsx
3482
3513
  /**
3483
3514
  * Renders empty-state text inside an SVG using foreignObject so that the
3484
3515
  * message wraps onto multiple lines instead of being clipped.
@@ -3505,8 +3536,7 @@ const SvgEmptyState = ({ x, y, width, height, children }) => {
3505
3536
  })
3506
3537
  });
3507
3538
  };
3508
- //#endregion
3509
- //#region src/charts/private/svg-empty-state/get-all-hidden-message.ts
3539
+ // src/charts/private/svg-empty-state/get-all-hidden-message.ts
3510
3540
  /**
3511
3541
  * Return the empty-state message for a chart whose items are all hidden.
3512
3542
  *
@@ -3526,8 +3556,7 @@ const getAllHiddenMessage = (interactive, itemType) => {
3526
3556
  if (interactive) return _x("All series are hidden. Click legend items to show data.", "chart empty state: interactive series", "jetpack-charts");
3527
3557
  return _x("All series are hidden.", "chart empty state: series", "jetpack-charts");
3528
3558
  };
3529
- //#endregion
3530
- //#region src/charts/private/time-axis.ts
3559
+ // src/charts/private/time-axis.ts
3531
3560
  const X_TICK_WIDTH = 60;
3532
3561
  const getCurveType = (type, smoothing) => {
3533
3562
  if (!type) return smoothing ? curveCatmullRom : curveLinear;
@@ -3784,8 +3813,7 @@ const guessOptimalNumTicks = (data, chartWidth, tickFormatter) => {
3784
3813
  }
3785
3814
  return Math.max(1, secondBestGuess);
3786
3815
  };
3787
- //#endregion
3788
- //#region src/charts/private/time-axis-options.ts
3816
+ // src/charts/private/time-axis-options.ts
3789
3817
  const toDateDomain = (domain) => domain ? [new Date(domain[0]), new Date(domain[1])] : void 0;
3790
3818
  /**
3791
3819
  * The x-axis options for a continuous time chart, shared by LineChart and AreaChart.
@@ -3820,8 +3848,7 @@ const buildTimeAxisOptions = ({ dataSorted, width, axisOptions, scaleDomain, zoo
3820
3848
  ...numTicks === void 0 ? {} : { numTicks }
3821
3849
  };
3822
3850
  };
3823
- //#endregion
3824
- //#region src/charts/private/whole-number-ticks.tsx
3851
+ // src/charts/private/whole-number-ticks.tsx
3825
3852
  /**
3826
3853
  * Whether every reading in the series is a whole number.
3827
3854
  *
@@ -3868,15 +3895,13 @@ const WholeNumberTicks = ({ axis, numTicks, enabled, children }) => {
3868
3895
  const tickValues = enabled ? getWholeNumberTickValues(axis === "x" ? xScale : yScale, numTicks) : void 0;
3869
3896
  return /* @__PURE__ */ jsx(Fragment$1, { children: children(tickValues) });
3870
3897
  };
3871
- //#endregion
3872
- //#region src/charts/private/with-responsive/with-responsive.module.scss
3898
+ // src/charts/private/with-responsive/with-responsive.module.scss
3873
3899
  var with_responsive_module_default = {
3874
3900
  "container": "a8ccharts-sP1gHa-container",
3875
3901
  "content": "a8ccharts-sP1gHa-content",
3876
3902
  "isContained": "a8ccharts-sP1gHa-isContained"
3877
3903
  };
3878
- //#endregion
3879
- //#region src/charts/private/with-responsive/with-responsive.tsx
3904
+ // src/charts/private/with-responsive/with-responsive.tsx
3880
3905
  /**
3881
3906
  * A higher-order component that provides responsive dimensions
3882
3907
  * to the wrapped chart component using useParentSize from `@visx/responsive`.
@@ -3955,14 +3980,12 @@ function withResponsive(WrappedComponent) {
3955
3980
  });
3956
3981
  };
3957
3982
  }
3958
- //#endregion
3959
- //#region src/charts/private/x-zoom/x-zoom.module.scss
3983
+ // src/charts/private/x-zoom/x-zoom.module.scss
3960
3984
  var x_zoom_module_default = {
3961
3985
  "x-zoom__reset": "a8ccharts-ur9dWW-x-zoom__reset",
3962
3986
  "x-zoom__selection": "a8ccharts-ur9dWW-x-zoom__selection"
3963
3987
  };
3964
- //#endregion
3965
- //#region src/charts/private/x-zoom/x-zoom.tsx
3988
+ // src/charts/private/x-zoom/x-zoom.tsx
3966
3989
  const MIN_DRAG_PIXELS = 6;
3967
3990
  /**
3968
3991
  * Drag-to-zoom state + pointer handlers for an XY chart. Designed to be
@@ -4145,11 +4168,9 @@ function ZoomResetButton({ onClick }) {
4145
4168
  })
4146
4169
  });
4147
4170
  }
4148
- //#endregion
4149
- //#region src/charts/private/xy-plot/xy-plot.module.scss
4171
+ // src/charts/private/xy-plot/xy-plot.module.scss
4150
4172
  var xy_plot_module_default = { "xy-plot": "a8ccharts-bIyhgG-xy-plot" };
4151
- //#endregion
4152
- //#region src/charts/line-chart/line-chart.module.scss
4173
+ // src/charts/line-chart/line-chart.module.scss
4153
4174
  var line_chart_module_default = {
4154
4175
  "line-chart": "a8ccharts-inuQka-line-chart",
4155
4176
  "line-chart__annotation-label": "a8ccharts-inuQka-line-chart__annotation-label",
@@ -4251,8 +4272,7 @@ const LineChartAnnotationLabelWithPopover = ({ title, subtitle, renderLabel, ren
4251
4272
  })]
4252
4273
  });
4253
4274
  };
4254
- //#endregion
4255
- //#region src/charts/line-chart/private/line-chart-annotations-overlay.tsx
4275
+ // src/charts/line-chart/private/line-chart-annotations-overlay.tsx
4256
4276
  const LineChartAnnotationsOverlay = ({ children }) => {
4257
4277
  const { chartRef, chartWidth, chartHeight } = useChartInstanceContext();
4258
4278
  const [scales, setScales] = useState(null);
@@ -4335,8 +4355,7 @@ const LineChartAnnotationsOverlay = ({ children }) => {
4335
4355
  })
4336
4356
  });
4337
4357
  };
4338
- //#endregion
4339
- //#region src/charts/line-chart/private/line-chart-annotation.tsx
4358
+ // src/charts/line-chart/private/line-chart-annotation.tsx
4340
4359
  const ANNOTATION_MAX_WIDTH = 125;
4341
4360
  const ANNOTATION_INIT_HEIGHT = 100;
4342
4361
  const getLabelPosition = ({ subjectType, x, xMax, y, yMin, yMax, maxWidth, height }) => {
@@ -4529,8 +4548,7 @@ const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", s
4529
4548
  ]
4530
4549
  }) });
4531
4550
  };
4532
- //#endregion
4533
- //#region src/charts/line-chart/private/line-chart-glyph.tsx
4551
+ // src/charts/line-chart/private/line-chart-glyph.tsx
4534
4552
  const toNumber$1 = (val) => {
4535
4553
  const num = typeof val === "number" ? val : parseFloat(val);
4536
4554
  return isNaN(num) ? void 0 : num;
@@ -4560,8 +4578,7 @@ const LineChartGlyph = ({ data, index, color, glyphStyle, renderGlyph, accessors
4560
4578
  position
4561
4579
  });
4562
4580
  };
4563
- //#endregion
4564
- //#region src/charts/line-chart/private/nearest-pointer-events.tsx
4581
+ // src/charts/line-chart/private/nearest-pointer-events.tsx
4565
4582
  /**
4566
4583
  * Find the datum nearest a pointer along x, in one series sorted by x.
4567
4584
  *
@@ -4660,8 +4677,7 @@ function NearestPointerEvents({ onPointerDown, onPointerMove, onPointerUp }) {
4660
4677
  useEventEmitter("pointerup", handlers.pointerup);
4661
4678
  return null;
4662
4679
  }
4663
- //#endregion
4664
- //#region src/charts/line-chart/line-chart.tsx
4680
+ // src/charts/line-chart/line-chart.tsx
4665
4681
  const defaultRenderGlyph = (props) => {
4666
4682
  return /* @__PURE__ */ createElement(DefaultGlyph, {
4667
4683
  ...props,
@@ -4812,6 +4828,8 @@ const LineChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartI
4812
4828
  isVisible: !hiddenSeries.has(series.label)
4813
4829
  }));
4814
4830
  }, [dataSorted, hiddenSeries]);
4831
+ const visibleSeries = useMemo(() => dataSorted.filter((series) => isSeriesVisible(series.label)), [dataSorted, isSeriesVisible]);
4832
+ const visibleSeriesKey = useMemo(() => JSON.stringify(visibleSeries.map((series) => series.label)), [visibleSeries]);
4815
4833
  const allSeriesHidden = useMemo(() => {
4816
4834
  return seriesWithVisibility.every(({ isVisible }) => !isVisible);
4817
4835
  }, [seriesWithVisibility]);
@@ -4841,22 +4859,23 @@ const LineChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartI
4841
4859
  isSeriesVisible,
4842
4860
  options?.yScale
4843
4861
  ]);
4844
- const activateSelectedPoint = useCallback((index) => {
4845
- const series = dataSorted[0];
4846
- const datum = series?.data[index];
4847
- if (series && datum) onDatumActivate?.({
4848
- datum,
4849
- index,
4850
- key: series.label
4862
+ const stops = useMemo(() => getKeyboardStops(visibleSeries), [visibleSeries]);
4863
+ const activateSelectedPoint = useCallback((stop) => {
4864
+ const match = stops.membersAt(stop)[0];
4865
+ if (match) onDatumActivate?.({
4866
+ datum: match.datum,
4867
+ index: match.index,
4868
+ key: match.series.label
4851
4869
  });
4852
- }, [dataSorted, onDatumActivate]);
4870
+ }, [stops, onDatumActivate]);
4853
4871
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
4854
4872
  selectedIndex,
4855
4873
  setSelectedIndex,
4856
4874
  isNavigating,
4857
4875
  setIsNavigating,
4858
4876
  chartRef,
4859
- totalPoints: dataSorted[0]?.data.length || 0,
4877
+ totalPoints: stops.count,
4878
+ visibleSeriesKey,
4860
4879
  onActivate: activateSelectedPoint,
4861
4880
  preventTooltipScroll: tooltipPlacement === "below-axis"
4862
4881
  });
@@ -4875,7 +4894,7 @@ const LineChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartI
4875
4894
  }
4876
4895
  return min <= max ? [min, max] : void 0;
4877
4896
  }, [dataSorted, isSeriesVisible]);
4878
- const hasWholeNumberValues = useMemo(() => hasOnlyWholeNumbers(dataSorted.filter((series) => isSeriesVisible(series.label))), [dataSorted, isSeriesVisible]);
4897
+ const hasWholeNumberValues = useMemo(() => hasOnlyWholeNumbers(visibleSeries), [visibleSeries]);
4879
4898
  const chartOptions = useMemo(() => {
4880
4899
  const fallbackYDomain = getFallbackYDomain(visibleReadingExtent, options?.yScale?.type === "log");
4881
4900
  return {
@@ -4921,11 +4940,7 @@ const LineChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartI
4921
4940
  formatting,
4922
4941
  isSeriesVisible
4923
4942
  ]);
4924
- const bucketInfo = useMemo(() => getBucketInfo(dataSorted.filter((series) => isSeriesVisible(series.label)), options?.axis?.x?.tickResolution), [
4925
- dataSorted,
4926
- isSeriesVisible,
4927
- options?.axis?.x?.tickResolution
4928
- ]);
4943
+ const bucketInfo = useMemo(() => getBucketInfo(visibleSeries, options?.axis?.x?.tickResolution), [visibleSeries, options?.axis?.x?.tickResolution]);
4929
4944
  const tooltipRenderGlyph = useMemo(() => {
4930
4945
  return (props) => {
4931
4946
  const seriesIndex = dataSorted.findIndex((series) => series.label === props.key || series.data.includes(props.datum));
@@ -5155,7 +5170,7 @@ const LineChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartI
5155
5170
  selectedIndex,
5156
5171
  tooltipRef,
5157
5172
  keyboardFocusedClassName: line_chart_module_default["line-chart__tooltip--keyboard-focused"],
5158
- series: dataSorted
5173
+ series: visibleSeries
5159
5174
  }),
5160
5175
  /* @__PURE__ */ jsx(LineChartScalesRef, {
5161
5176
  chartRef: internalChartRef,
@@ -5193,15 +5208,13 @@ const LineChartResponsive = attachSubComponents(withResponsive(LineChartWithProv
5193
5208
  AnnotationsOverlay: LineChartAnnotationsOverlay,
5194
5209
  Annotation: LineChartAnnotation
5195
5210
  });
5196
- //#endregion
5197
- //#region src/charts/area-chart/area-chart.module.scss
5211
+ // src/charts/area-chart/area-chart.module.scss
5198
5212
  var area_chart_module_default = {
5199
5213
  "area-chart": "a8ccharts-jlynaq-area-chart",
5200
5214
  "area-chart--animated": "a8ccharts-jlynaq-area-chart--animated",
5201
5215
  "rise": "a8ccharts-jlynaq-rise"
5202
5216
  };
5203
- //#endregion
5204
- //#region src/charts/area-chart/private/validate-data.ts
5217
+ // src/charts/area-chart/private/validate-data.ts
5205
5218
  /**
5206
5219
  * Up-front data validation. Returns a localised error message when the chart
5207
5220
  * cannot safely render, otherwise `null`. Catches the cases that would
@@ -5217,8 +5230,7 @@ const validateData$3 = (data) => {
5217
5230
  if (data.some((series) => series.data.some((point) => isInvalidReading(point.value, { allowMissing: false }) || "date" in point && point.date && isNaN(point.date.getTime())))) return __("Invalid data", "jetpack-charts");
5218
5231
  return null;
5219
5232
  };
5220
- //#endregion
5221
- //#region src/charts/area-chart/private/overlays.tsx
5233
+ // src/charts/area-chart/private/overlays.tsx
5222
5234
  const AreaChartScalesRef = ({ chartRef, width, height, margin }) => {
5223
5235
  const context = useContext(DataContext);
5224
5236
  useImperativeHandle(chartRef, () => ({
@@ -5283,8 +5295,7 @@ const HoverGlyphs = ({ visibleSeries, stacked, stackOffset, getElementStyles, st
5283
5295
  children: circles
5284
5296
  });
5285
5297
  };
5286
- //#endregion
5287
- //#region src/charts/area-chart/area-chart.tsx
5298
+ // src/charts/area-chart/area-chart.tsx
5288
5299
  const AreaChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
5289
5300
  const legendInteractive = legend.interactive ?? false;
5290
5301
  const legendShape = legend.shape ?? "rect";
@@ -5330,19 +5341,22 @@ const AreaChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartI
5330
5341
  }));
5331
5342
  }, [dataSorted, hiddenSeries]);
5332
5343
  const allSeriesHidden = useMemo(() => seriesWithVisibility.every(({ isVisible }) => !isVisible), [seriesWithVisibility]);
5333
- const hasWholeNumberValues = useMemo(() => (!stacked || stackOffset === "none") && hasOnlyWholeNumbers(dataSorted.filter((series) => isSeriesVisible(series.label))), [
5334
- dataSorted,
5335
- isSeriesVisible,
5344
+ const visibleDataSeries = useMemo(() => dataSorted.filter((series) => isSeriesVisible(series.label)), [dataSorted, isSeriesVisible]);
5345
+ const hasWholeNumberValues = useMemo(() => (!stacked || stackOffset === "none") && hasOnlyWholeNumbers(visibleDataSeries), [
5346
+ visibleDataSeries,
5336
5347
  stacked,
5337
5348
  stackOffset
5338
5349
  ]);
5350
+ const visibleSeriesKey = useMemo(() => JSON.stringify(visibleDataSeries.map((series) => series.label)), [visibleDataSeries]);
5351
+ const stops = useMemo(() => getKeyboardStops(visibleDataSeries), [visibleDataSeries]);
5339
5352
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
5340
5353
  selectedIndex,
5341
5354
  setSelectedIndex,
5342
5355
  isNavigating,
5343
5356
  setIsNavigating,
5344
5357
  chartRef,
5345
- totalPoints: dataSorted[0]?.data.length || 0
5358
+ totalPoints: stops.count,
5359
+ visibleSeriesKey
5346
5360
  });
5347
5361
  const fixedYDomain = useMemo(() => {
5348
5362
  if (rescaleYOnVisibilityChange || !dataSorted.length || !dataSorted[0].data.length || stacked && stackOffset !== "none") return;
@@ -5630,7 +5644,7 @@ const AreaChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartI
5630
5644
  selectedIndex,
5631
5645
  tooltipRef,
5632
5646
  keyboardFocusedClassName: area_chart_module_default["area-chart__tooltip--keyboard-focused"],
5633
- series: dataSorted
5647
+ series: visibleDataSeries
5634
5648
  }), /* @__PURE__ */ jsx(HoverGlyphs, {
5635
5649
  visibleSeries,
5636
5650
  stacked,
@@ -5666,8 +5680,7 @@ const AreaChartWithProvider = forwardRef((props, ref) => {
5666
5680
  AreaChartWithProvider.displayName = "AreaChart";
5667
5681
  const AreaChart = attachSubComponents(AreaChartWithProvider, { Legend });
5668
5682
  const AreaChartResponsive = attachSubComponents(withResponsive(AreaChartWithProvider), { Legend });
5669
- //#endregion
5670
- //#region src/charts/bar-chart/bar-chart.module.scss
5683
+ // src/charts/bar-chart/bar-chart.module.scss
5671
5684
  var bar_chart_module_default = {
5672
5685
  "bar-chart": "a8ccharts-97yN9W-bar-chart",
5673
5686
  "bar-chart--animated": "a8ccharts-97yN9W-bar-chart--animated",
@@ -5675,8 +5688,7 @@ var bar_chart_module_default = {
5675
5688
  "rise": "a8ccharts-97yN9W-rise",
5676
5689
  "stretch": "a8ccharts-97yN9W-stretch"
5677
5690
  };
5678
- //#endregion
5679
- //#region src/charts/bar-chart/private/truncated-tick-component.tsx
5691
+ // src/charts/bar-chart/private/truncated-tick-component.tsx
5680
5692
  /**
5681
5693
  * Get the bandwidth of a scale
5682
5694
  *
@@ -5790,8 +5802,7 @@ const createTruncatedTickComponent = (axis) => (props) => {
5790
5802
  */
5791
5803
  const TruncatedXTickComponent = createTruncatedTickComponent("x");
5792
5804
  const TruncatedYTickComponent = createTruncatedTickComponent("y");
5793
- //#endregion
5794
- //#region src/charts/bar-chart/private/value-domain.ts
5805
+ // src/charts/bar-chart/private/value-domain.ts
5795
5806
  /** Domain for a value axis that has nothing to scale, so its ticks still read 0 to 1. */
5796
5807
  const EMPTY_DOMAIN = [0, 1];
5797
5808
  /**
@@ -5815,13 +5826,6 @@ const getBarValue = (point) => {
5815
5826
  */
5816
5827
  const isBarRendered = (point) => Number.isFinite(getBarValue(point));
5817
5828
  /**
5818
- * How many bars visx renders for a list of points.
5819
- *
5820
- * @param points - Data points handed to visx.
5821
- * @return The number of points that get a bar.
5822
- */
5823
- const countRenderedBars = (points) => points.filter(isBarRendered).length;
5824
- /**
5825
5829
  * An explicit domain for the value scale, or null to let visx fit one to the data.
5826
5830
  *
5827
5831
  * Zero goes into the domain here rather than through the scale's `zero` flag: visx applies
@@ -5849,8 +5853,7 @@ const getValueScaleDomain = (data, includeZero, isSeriesRendered) => {
5849
5853
  if (includeZero) return [Math.min(0, min), Math.max(0, max)];
5850
5854
  return null;
5851
5855
  };
5852
- //#endregion
5853
- //#region src/charts/bar-chart/private/use-bar-chart-options.ts
5856
+ // src/charts/bar-chart/private/use-bar-chart-options.ts
5854
5857
  /** Outer padding of the category band scale (space at the chart edges). */
5855
5858
  const BASE_BAND_PADDING = .2;
5856
5859
  /** Inner padding of the category band scale (the base gap between ticks). */
@@ -6066,8 +6069,7 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
6066
6069
  isSeriesRendered
6067
6070
  ]);
6068
6071
  }
6069
- //#endregion
6070
- //#region src/charts/bar-chart/private/band-scale.ts
6072
+ // src/charts/bar-chart/private/band-scale.ts
6071
6073
  /**
6072
6074
  * Match the inner slot scale used by visx BarGroup.
6073
6075
  * @param keys - Visible primary series keys.
@@ -6080,11 +6082,9 @@ const createGroupScale = (keys, bandwidth, padding) => scaleBand({
6080
6082
  range: [0, bandwidth],
6081
6083
  padding
6082
6084
  });
6083
- //#endregion
6084
- //#region src/charts/bar-chart/private/comparison-bars-geometry.ts
6085
+ // src/charts/bar-chart/private/comparison-bars-geometry.ts
6085
6086
  /**
6086
- * Mirrors visx's getScaleBaseline so comparison shadows and primary bars
6087
- * share the same baseline, including out-of-domain zero values.
6087
+ * Mirrors visx's getScaleBaseline so bars drawn outside visx share the baseline of its bars, including out-of-domain zero values.
6088
6088
  *
6089
6089
  * @param {ValueScale} scale - The continuous value scale.
6090
6090
  * @return {number} The baseline output position in pixels.
@@ -6098,41 +6098,37 @@ function getValueScaleBaseline(scale) {
6098
6098
  return Number.isFinite(maybeZero) ? Math.max(maybeZero, minOutput) : minOutput;
6099
6099
  }
6100
6100
  /**
6101
- * Compute the rect for a comparison "shadow" bar, centered on the paired
6102
- * primary bar slot and scaled by `widthFactor`.
6103
- *
6104
- * @param {object} params - Geometry inputs.
6105
- * @param {boolean} params.horizontal - True for a horizontal bar chart, false for vertical.
6106
- * @param {number} params.bandPosition - bandScale(category): start px of the category band.
6107
- * @param {number} params.slotOffset - groupScale(primaryKey): offset of the primary slot within the band.
6108
- * @param {number} params.slotThickness - groupScale.bandwidth(): primary bar thickness in px.
6109
- * @param {number} params.valuePosition - valueScale(value): output px for the bar's data value.
6110
- * @param {number} params.baseline - getValueScaleBaseline(valueScale): zero-line output px.
6111
- * @param {number} params.widthFactor - Shadow thickness multiplier, e.g. 1.5 for 150% width.
6112
- * @return {ComparisonRect} The {x, y, width, height} of the shadow rect.
6113
- */
6114
- function computeComparisonRect(params) {
6115
- const { horizontal, bandPosition, slotOffset, slotThickness, valuePosition, baseline, widthFactor } = params;
6116
- const slotStart = bandPosition + slotOffset;
6117
- const shadowThickness = slotThickness * widthFactor;
6118
- const shadowStart = slotStart + slotThickness / 2 - shadowThickness / 2;
6119
- const valueStart = Math.min(valuePosition, baseline);
6120
- const valueLength = Math.abs(baseline - valuePosition);
6101
+ * Compute the rect that spans `valuePositions` along the value axis, centered on a slot and scaled by `widthFactor` across it.
6102
+ *
6103
+ * @param {object} params - Geometry inputs.
6104
+ * @param {boolean} params.horizontal - True for a horizontal bar chart, false for vertical.
6105
+ * @param {number} params.bandPosition - bandScale(category): start px of the category band.
6106
+ * @param {number} params.slotOffset - groupScale(slotKey): offset of the slot within the band.
6107
+ * @param {number} params.slotThickness - groupScale.bandwidth(): slot thickness in px.
6108
+ * @param {number[]} params.valuePositions - Output px the rect must reach, such as the baseline and each bar's value.
6109
+ * @param {number} params.widthFactor - Thickness multiplier, e.g. 0.5 for half the slot.
6110
+ * @return {SlotRect} The {x, y, width, height} of the rect.
6111
+ */
6112
+ function computeSlotRect(params) {
6113
+ const { horizontal, bandPosition, slotOffset, slotThickness, valuePositions, widthFactor } = params;
6114
+ const thickness = slotThickness * widthFactor;
6115
+ const start = bandPosition + slotOffset + slotThickness / 2 - thickness / 2;
6116
+ const valueStart = Math.min(...valuePositions);
6117
+ const valueLength = Math.max(...valuePositions) - valueStart;
6121
6118
  if (horizontal) return {
6122
6119
  x: valueStart,
6123
- y: shadowStart,
6120
+ y: start,
6124
6121
  width: valueLength,
6125
- height: shadowThickness
6122
+ height: thickness
6126
6123
  };
6127
6124
  return {
6128
- x: shadowStart,
6125
+ x: start,
6129
6126
  y: valueStart,
6130
- width: shadowThickness,
6127
+ width: thickness,
6131
6128
  height: valueLength
6132
6129
  };
6133
6130
  }
6134
- //#endregion
6135
- //#region src/charts/bar-chart/private/classified-bar-series.tsx
6131
+ // src/charts/bar-chart/private/classified-bar-series.tsx
6136
6132
  const ClassifiedBarSeries = ({ barClassName, primaryKeys, groupPadding, ...props }) => {
6137
6133
  const { xScale, yScale, horizontal } = useContext(DataContext);
6138
6134
  const bandScale = horizontal ? yScale : xScale;
@@ -6176,6 +6172,515 @@ const ClassifiedBarSeries = ({ barClassName, primaryKeys, groupPadding, ...props
6176
6172
  renderGlyph
6177
6173
  });
6178
6174
  };
6175
+ // src/charts/bar-chart/private/slotted-bar-series.tsx
6176
+ const SlottedBarSeries = ({ barClassName, slotKeys, slotKey, thickness, groupPadding, ...props }) => {
6177
+ const { xScale, yScale, horizontal } = useContext(DataContext);
6178
+ const bandScale = horizontal ? yScale : xScale;
6179
+ const valueScale = horizontal ? xScale : yScale;
6180
+ const groupScale = useMemo(() => createGroupScale(slotKeys, bandScale?.bandwidth?.() ?? 0, groupPadding), [
6181
+ slotKeys,
6182
+ bandScale,
6183
+ groupPadding
6184
+ ]);
6185
+ const renderGlyph = useCallback(({ datum, x, y, color }) => {
6186
+ if (!bandScale?.bandwidth || !valueScale) return null;
6187
+ const baseline = getValueScaleBaseline(valueScale);
6188
+ const rect = computeSlotRect({
6189
+ horizontal: !!horizontal,
6190
+ bandPosition: Number(bandScale((horizontal ? props.yAccessor : props.xAccessor)(datum))),
6191
+ slotOffset: groupScale(slotKey) ?? 0,
6192
+ slotThickness: groupScale.bandwidth(),
6193
+ valuePositions: [baseline, horizontal ? x : y],
6194
+ widthFactor: thickness
6195
+ });
6196
+ return /* @__PURE__ */ jsx("rect", {
6197
+ ...rect,
6198
+ className: clsx("visx-bar", barClassName?.(datum)),
6199
+ fill: color
6200
+ });
6201
+ }, [
6202
+ bandScale,
6203
+ valueScale,
6204
+ horizontal,
6205
+ groupScale,
6206
+ slotKey,
6207
+ thickness,
6208
+ props.xAccessor,
6209
+ props.yAccessor,
6210
+ barClassName
6211
+ ]);
6212
+ return /* @__PURE__ */ jsx(GlyphSeries, {
6213
+ ...props,
6214
+ enableEvents: false,
6215
+ renderGlyph
6216
+ });
6217
+ };
6218
+ // src/charts/private/selected-ring/selected-ring.module.scss
6219
+ var selected_ring_module_default = {
6220
+ "selected-ring__contrast": "a8ccharts-gx1_AW-selected-ring__contrast",
6221
+ "selected-ring__edge": "a8ccharts-gx1_AW-selected-ring__edge",
6222
+ "selected-ring__far": "a8ccharts-gx1_AW-selected-ring__far",
6223
+ "selected-ring__focus": "a8ccharts-gx1_AW-selected-ring__focus"
6224
+ };
6225
+ // src/charts/private/selected-ring/selected-ring.tsx
6226
+ const TONES = {
6227
+ inside: ["contrast", "focus"],
6228
+ outside: ["focus", "contrast"]
6229
+ };
6230
+ const EVERYWHERE = "M-1e6 -1e6H1e6V1e6H-1e6Z";
6231
+ /**
6232
+ * Encodes `value` for an SVG id, losslessly so distinct chart ids never share a clip path.
6233
+ *
6234
+ * @param value - Any string.
6235
+ * @return `value` with each character outside `[A-Za-z0-9-]` written as `_<hex code point>_`.
6236
+ */
6237
+ const toSvgId = (value) => Array.from(value, (character) => /[A-Za-z0-9-]/.test(character) ? character : `_${character.codePointAt(0)?.toString(16)}_`).join("");
6238
+ /**
6239
+ * Marks a selected shape with a focus-colored ring and a contrast ring, so it reads against any fill.
6240
+ *
6241
+ * @param {SelectedRingProps} props - Component props
6242
+ * @return {JSX.Element} The ring
6243
+ */
6244
+ const SelectedRing = ({ chartId, d, side, testId }) => {
6245
+ const clipId = `selected-ring-clip-${toSvgId(chartId)}`;
6246
+ const clipPath = `url(#${clipId})`;
6247
+ const [far, edge] = TONES[side];
6248
+ return /* @__PURE__ */ jsxs("g", {
6249
+ pointerEvents: "none",
6250
+ "aria-hidden": "true",
6251
+ children: [
6252
+ /* @__PURE__ */ jsx("clipPath", {
6253
+ id: clipId,
6254
+ children: side === "inside" ? /* @__PURE__ */ jsx("path", { d }) : /* @__PURE__ */ jsx("path", {
6255
+ d: `${EVERYWHERE} ${d}`,
6256
+ clipRule: "evenodd"
6257
+ })
6258
+ }),
6259
+ /* @__PURE__ */ jsx("path", {
6260
+ d,
6261
+ clipPath,
6262
+ className: clsx(selected_ring_module_default["selected-ring__far"], selected_ring_module_default[`selected-ring__${far}`])
6263
+ }),
6264
+ /* @__PURE__ */ jsx("path", {
6265
+ d,
6266
+ clipPath,
6267
+ className: clsx(selected_ring_module_default["selected-ring__edge"], selected_ring_module_default[`selected-ring__${edge}`])
6268
+ })
6269
+ ]
6270
+ });
6271
+ };
6272
+ // src/charts/bar-chart/private/selected-bar-ring.tsx
6273
+ /**
6274
+ * Marks the keyboard-selected slot with a two-tone ring outside its edge, so the ring reads against any bar color.
6275
+ *
6276
+ * @param props - Component props.
6277
+ * @param props.chartId - The chart's id, which keeps the clip path unique on a page with several charts.
6278
+ * @param props.selected - The selected slot.
6279
+ * @param props.slotKeys - The slots, in the order the band lays them out.
6280
+ * @param props.groupPadding - The padding between slots.
6281
+ * @param props.horizontal - Whether the bars run horizontally.
6282
+ * @param props.xAccessor - Reads a datum's x value.
6283
+ * @param props.yAccessor - Reads a datum's y value.
6284
+ * @return The ring, or nothing when the slot has no position.
6285
+ */
6286
+ const SelectedBarRing = ({ chartId, selected, slotKeys, groupPadding, horizontal, xAccessor, yAccessor }) => {
6287
+ const context = useContext(DataContext);
6288
+ const bandScale = horizontal ? context?.yScale : context?.xScale;
6289
+ const valueScale = horizontal ? context?.xScale : context?.yScale;
6290
+ const bandwidth = bandScale?.bandwidth?.() ?? 0;
6291
+ const [category] = selected.data;
6292
+ if (!bandScale || !valueScale || !bandwidth || !category) return null;
6293
+ const groupScale = createGroupScale(slotKeys, bandwidth, groupPadding);
6294
+ const slotOffset = groupScale(selected.slotKey);
6295
+ const bandPosition = Number(bandScale((horizontal ? yAccessor : xAccessor)(category)));
6296
+ if (slotOffset == null || !Number.isFinite(bandPosition)) return null;
6297
+ const valuePositions = selected.data.map((datum) => Number(valueScale(Number((horizontal ? xAccessor : yAccessor)(datum)))));
6298
+ const rect = computeSlotRect({
6299
+ horizontal,
6300
+ bandPosition,
6301
+ slotOffset,
6302
+ slotThickness: groupScale.bandwidth(),
6303
+ valuePositions: [getValueScaleBaseline(valueScale), ...valuePositions],
6304
+ widthFactor: selected.thickness
6305
+ });
6306
+ return /* @__PURE__ */ jsx(SelectedRing, {
6307
+ chartId,
6308
+ d: `M${rect.x} ${rect.y}h${rect.width}v${rect.height}h${-rect.width}Z`,
6309
+ side: "outside",
6310
+ testId: "bar-chart-selected-ring"
6311
+ });
6312
+ };
6313
+ // src/charts/bar-chart/private/bar-layer.tsx
6314
+ /**
6315
+ * Draws the bars in front of any comparison shadows, slotted for an overlap layout and grouped by visx otherwise, then the selected bar's ring.
6316
+ *
6317
+ * @param props - Component props.
6318
+ * @param props.chartId - The chart's id, which keeps the ring's clip path unique on a page with several charts.
6319
+ * @param props.overlapSlots - The overlap layout, or undefined for a grouped one.
6320
+ * @param props.selectedBar - The bar or slot the keyboard has selected.
6321
+ * @param props.primaryEntries - The visible primary series, for a grouped layout.
6322
+ * @param props.primaryKeys - Their labels, in group order.
6323
+ * @param props.getBarBackground - Reads a series' bar fill.
6324
+ * @param props.barClassName - Adds classes to a bar.
6325
+ * @param props.groupPadding - The padding between the bars of a category.
6326
+ * @param props.horizontal - Whether the bars run horizontally.
6327
+ * @param props.xAccessor - Reads a datum's x value.
6328
+ * @param props.yAccessor - Reads a datum's y value.
6329
+ * @return The bars.
6330
+ */
6331
+ const BarLayer = ({ chartId, overlapSlots, selectedBar, primaryEntries, primaryKeys, getBarBackground, barClassName, groupPadding, horizontal, xAccessor, yAccessor }) => {
6332
+ let bars;
6333
+ if (overlapSlots) bars = /* @__PURE__ */ jsx("g", {
6334
+ className: "visx-bar-group",
6335
+ children: overlapSlots.entries.map(({ series, index }) => /* @__PURE__ */ jsx(SlottedBarSeries, {
6336
+ dataKey: series.label,
6337
+ data: series.data,
6338
+ xAccessor,
6339
+ yAccessor,
6340
+ colorAccessor: getBarBackground(index),
6341
+ barClassName,
6342
+ slotKeys: overlapSlots.slotKeys,
6343
+ slotKey: overlapSlots.slotOf(series.label),
6344
+ thickness: overlapSlots.thicknessOf(series.label),
6345
+ groupPadding
6346
+ }, series.label))
6347
+ });
6348
+ else if (barClassName) bars = /* @__PURE__ */ jsx("g", {
6349
+ className: "visx-bar-group",
6350
+ children: primaryEntries.map(({ series, index }) => /* @__PURE__ */ jsx(ClassifiedBarSeries, {
6351
+ dataKey: series.label,
6352
+ data: series.data,
6353
+ xAccessor,
6354
+ yAccessor,
6355
+ colorAccessor: getBarBackground(index),
6356
+ barClassName,
6357
+ primaryKeys,
6358
+ groupPadding
6359
+ }, series.label))
6360
+ });
6361
+ else bars = /* @__PURE__ */ jsx(BarGroup, {
6362
+ padding: groupPadding,
6363
+ children: primaryEntries.map(({ series, index }) => /* @__PURE__ */ jsx(BarSeries, {
6364
+ dataKey: series.label,
6365
+ data: series.data,
6366
+ xAccessor,
6367
+ yAccessor,
6368
+ colorAccessor: getBarBackground(index)
6369
+ }, series.label))
6370
+ });
6371
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [bars, selectedBar && /* @__PURE__ */ jsx(SelectedBarRing, {
6372
+ chartId,
6373
+ selected: selectedBar,
6374
+ slotKeys: overlapSlots?.slotKeys ?? primaryKeys,
6375
+ groupPadding,
6376
+ horizontal,
6377
+ xAccessor,
6378
+ yAccessor
6379
+ })] });
6380
+ };
6381
+ // src/charts/bar-chart/private/slot-tooltip-members.tsx
6382
+ /**
6383
+ * Fills the tooltip with every bar of the keyboard-selected category, and only those.
6384
+ *
6385
+ * The last member shown becomes the tooltip's nearest datum, so the caller lists the keyboard stop last.
6386
+ *
6387
+ * @param props - Component props.
6388
+ * @param props.members - The category's bars, the keyboard stop last.
6389
+ * @return No visual content.
6390
+ */
6391
+ function SlotTooltipMembers({ members }) {
6392
+ const { showTooltip, updateTooltip } = useContext(TooltipContext) ?? {};
6393
+ useEffect(() => {
6394
+ updateTooltip?.((state) => ({
6395
+ ...state,
6396
+ tooltipData: void 0
6397
+ }));
6398
+ members.forEach((member) => showTooltip?.(member));
6399
+ }, [members]);
6400
+ return null;
6401
+ }
6402
+ // src/charts/bar-chart/private/overlap-bars.ts
6403
+ const PRIMARY_SLOT = "primary";
6404
+ const COMPARISON_SLOT = "comparison";
6405
+ const OVERLAP_SLOTS = [PRIMARY_SLOT, COMPARISON_SLOT];
6406
+ const SEARCH_STEPS = 16;
6407
+ /**
6408
+ * Places each series of an overlap layout: primaries in one slot and comparisons in the other, with each `group` a layer, back to front in order of first appearance.
6409
+ *
6410
+ * @param series - The series to place.
6411
+ * @return Each series' placement, by index.
6412
+ */
6413
+ function getOverlapPlacements(series) {
6414
+ const layerByGroup = /* @__PURE__ */ new Map();
6415
+ series.forEach(({ group }) => {
6416
+ if (!layerByGroup.has(group)) layerByGroup.set(group, layerByGroup.size);
6417
+ });
6418
+ const layerCount = layerByGroup.size;
6419
+ return series.map((item) => {
6420
+ const layer = layerByGroup.get(item.group) ?? 0;
6421
+ return {
6422
+ slot: item.options?.type === "comparison" ? COMPARISON_SLOT : PRIMARY_SLOT,
6423
+ layer,
6424
+ thickness: layerCount > 1 ? 1 - layer * .5 / (layerCount - 1) : 1
6425
+ };
6426
+ });
6427
+ }
6428
+ /**
6429
+ * Blends `from` toward `to` and returns the blend nearest the point where `passes` changes, on its passing side.
6430
+ *
6431
+ * @param from - Where the blend starts.
6432
+ * @param to - Where the blend ends.
6433
+ * @param passes - A test that changes at most once along the blend.
6434
+ * @return A hex color.
6435
+ */
6436
+ function blendToEdge(from, to, passes) {
6437
+ const passesAtStart = passes(from);
6438
+ let low = 0;
6439
+ let high = 1;
6440
+ for (let step = 0; step < SEARCH_STEPS; step++) {
6441
+ const middle = (low + high) / 2;
6442
+ if (passes(mixHexColors(from, to, middle)) === passesAtStart) low = middle;
6443
+ else high = middle;
6444
+ }
6445
+ return mixHexColors(from, to, passesAtStart ? low : high);
6446
+ }
6447
+ const awayFrom = (background) => contrastRatio(background, "#000000") >= contrastRatio(background, "#ffffff") ? "#000000" : "#ffffff";
6448
+ const opposite = (end) => end === "#000000" ? "#ffffff" : "#000000";
6449
+ /**
6450
+ * The tone of a color nearest the background that keeps 3:1 against it.
6451
+ *
6452
+ * @param color - A hex color.
6453
+ * @param background - The hex chart background.
6454
+ * @return A hex color.
6455
+ */
6456
+ function tintWithContrast(color, background) {
6457
+ const passes = (tone) => contrastRatio(tone, background) >= 3;
6458
+ return blendToEdge(color, passes(color) ? background : awayFrom(background), passes);
6459
+ }
6460
+ /**
6461
+ * The tone of a color nearest the original that keeps 3:1 against the bar it is drawn over, on the far side of that bar from the background when that side can reach 3:1.
6462
+ *
6463
+ * @param color - A hex color.
6464
+ * @param under - The hex color of the bar behind it.
6465
+ * @param background - The hex chart background.
6466
+ * @return A hex color.
6467
+ */
6468
+ function shadeWithContrast(color, under, background) {
6469
+ const passes = (tone) => contrastRatio(tone, under) >= 3;
6470
+ const start = contrastRatio(color, background) > contrastRatio(under, background) ? color : under;
6471
+ if (passes(start)) return start;
6472
+ const away = awayFrom(background);
6473
+ return blendToEdge(start, passes(away) ? away : opposite(away), passes);
6474
+ }
6475
+ /**
6476
+ * The colors an overlap layout fills its series with: per slot, the back layer tinted to 3:1 against the background and each layer in front shaded to 3:1 against the one behind. A slot whose front-most series has its own `stroke` keeps its colors as given.
6477
+ *
6478
+ * @param series - Every series of the chart.
6479
+ * @param colors - Each series' resolved color.
6480
+ * @param slotColors - The palette color of each slot, primaries first.
6481
+ * @param background - The hex chart background.
6482
+ * @return Each series' fill, by index.
6483
+ */
6484
+ function getOverlapBarColors(series, colors, slotColors, background) {
6485
+ const placements = getOverlapPlacements(series);
6486
+ const barColors = [...colors];
6487
+ OVERLAP_SLOTS.forEach((slot, slotIndex) => {
6488
+ const members = placements.flatMap((placement, index) => placement.slot === slot ? [{
6489
+ index,
6490
+ layer: placement.layer
6491
+ }] : []);
6492
+ if (!members.length) return;
6493
+ const layers = [...new Set(members.map(({ layer }) => layer))].sort((a, b) => a - b);
6494
+ const back = members.find(({ layer }) => layer === layers[0]);
6495
+ const base = back && series[back.index].options?.stroke ? colors[back.index] : slotColors[slotIndex];
6496
+ if (members.some(({ index, layer }) => layer === layers[layers.length - 1] && !!series[index].options?.stroke) || !isValidHexColor(base) || !isValidHexColor(background)) return;
6497
+ const toneByLayer = /* @__PURE__ */ new Map();
6498
+ let under;
6499
+ layers.forEach((layer) => {
6500
+ const tone = under === void 0 ? tintWithContrast(base, background) : shadeWithContrast(base, under, background);
6501
+ toneByLayer.set(layer, tone);
6502
+ under = tone;
6503
+ });
6504
+ members.forEach(({ index, layer }) => {
6505
+ barColors[index] = toneByLayer.get(layer) ?? colors[index];
6506
+ });
6507
+ });
6508
+ return barColors;
6509
+ }
6510
+ // src/charts/bar-chart/private/use-overlap-slots.ts
6511
+ /**
6512
+ * Whether two data points sit at the same category: the same label, or the same date.
6513
+ *
6514
+ * @param point - A data point.
6515
+ * @param category - The data point naming the category.
6516
+ * @return Whether `point` is at `category`.
6517
+ */
6518
+ function isSameCategory(point, category) {
6519
+ return category.label ? point.label === category.label : !!category.date && !!point.date && point.date.getTime() === category.date.getTime();
6520
+ }
6521
+ /**
6522
+ * Lays out the visible series of an overlap layout in slots and layers.
6523
+ *
6524
+ * @param enabled - Whether the chart draws an overlap layout.
6525
+ * @param seriesWithVisibility - Every series, with its index and whether it is visible.
6526
+ * @return The layout, or undefined when the chart is not an overlap layout.
6527
+ */
6528
+ function useOverlapSlots(enabled, seriesWithVisibility) {
6529
+ return useMemo(() => {
6530
+ if (!enabled) return;
6531
+ const visible = seriesWithVisibility.filter(({ isVisible }) => isVisible);
6532
+ const placements = getOverlapPlacements(visible.map(({ series }) => series));
6533
+ const placed = visible.map(({ series, index }, position) => ({
6534
+ entry: {
6535
+ series,
6536
+ index
6537
+ },
6538
+ placement: placements[position]
6539
+ })).sort((a, b) => a.placement.layer - b.placement.layer);
6540
+ const entries = placed.map(({ entry }) => entry);
6541
+ const placementByKey = new Map(placed.map(({ entry, placement }) => [entry.series.label, placement]));
6542
+ const slotOf = (key) => placementByKey.get(key)?.slot ?? "primary";
6543
+ const thicknessOf = (key) => placementByKey.get(key)?.thickness ?? 1;
6544
+ const hasPrimaries = seriesWithVisibility.some(({ series }) => series.options?.type !== "comparison");
6545
+ const showsComparisons = entries.some(({ series }) => slotOf(series.label) === COMPARISON_SLOT);
6546
+ const slotKeys = [...hasPrimaries ? [PRIMARY_SLOT] : [], ...showsComparisons ? [COMPARISON_SLOT] : []];
6547
+ return {
6548
+ entries,
6549
+ keys: entries.map(({ series }) => series.label),
6550
+ slotKeys,
6551
+ slotOf,
6552
+ thicknessOf,
6553
+ positions: getStopPositions(entries, slotKeys, slotOf)
6554
+ };
6555
+ }, [enabled, seriesWithVisibility]);
6556
+ }
6557
+ /**
6558
+ * The category a data point sits at, as a key two points share when `isSameCategory` holds.
6559
+ *
6560
+ * @param point - A data point.
6561
+ * @return The category key.
6562
+ */
6563
+ function categoryKey(point) {
6564
+ return point.label ? `label:${point.label}` : `date:${point.date?.getTime()}`;
6565
+ }
6566
+ /**
6567
+ * Lists a stop for every slot at every category that slot draws a bar at, category by category.
6568
+ *
6569
+ * @param entries - The visible series, back to front.
6570
+ * @param slotKeys - The slots, in band order.
6571
+ * @param slotOf - The slot a series sits in.
6572
+ * @return The positions, in keyboard order.
6573
+ */
6574
+ function getStopPositions(entries, slotKeys, slotOf) {
6575
+ const categories = /* @__PURE__ */ new Map();
6576
+ entries.forEach(({ series }) => {
6577
+ series.data.forEach((datum, dataPointIndex) => {
6578
+ const key = categoryKey(datum);
6579
+ const slots = categories.get(key) ?? /* @__PURE__ */ new Map();
6580
+ categories.set(key, slots);
6581
+ if (!isBarRendered(datum)) return;
6582
+ const slotKey = slotOf(series.label);
6583
+ slots.set(slotKey, {
6584
+ slotKey,
6585
+ key: series.label,
6586
+ datum,
6587
+ dataPointIndex
6588
+ });
6589
+ });
6590
+ });
6591
+ const ordered = [...categories.values()].filter((slots) => slots.size > 0);
6592
+ if (ordered.every((slots) => [...slots.values()].every(({ datum }) => datum.label == null && datum.date))) {
6593
+ const time = (slots) => [...slots.values()][0].datum.date?.getTime() ?? 0;
6594
+ ordered.sort((a, b) => time(a) - time(b));
6595
+ }
6596
+ return ordered.flatMap((slots) => slotKeys.flatMap((slotKey) => {
6597
+ const position = slots.get(slotKey);
6598
+ return position ? [position] : [];
6599
+ }));
6600
+ }
6601
+ /**
6602
+ * Every bar at a category, each with its own data point index, the keyboard stop last.
6603
+ *
6604
+ * @param entries - The visible series.
6605
+ * @param category - The data point naming the category.
6606
+ * @param stopKey - The series the keyboard stop is on.
6607
+ * @return The category's bars; the last one shown becomes the tooltip's nearest datum.
6608
+ */
6609
+ function membersAt(entries, category, stopKey) {
6610
+ const members = entries.flatMap(({ series }) => {
6611
+ const index = series.data.findIndex((point) => isSameCategory(point, category));
6612
+ return index === -1 ? [] : [{
6613
+ key: series.label,
6614
+ datum: series.data[index],
6615
+ index
6616
+ }];
6617
+ });
6618
+ return [...members.filter(({ key }) => key !== stopKey), ...members.filter(({ key }) => key === stopKey)];
6619
+ }
6620
+ /**
6621
+ * The slot a keyboard index selects, and every bar at its category.
6622
+ *
6623
+ * @param slots - The overlap layout.
6624
+ * @param selectedIndex - The keyboard index into `slots.positions`.
6625
+ * @return The selected slot, or undefined when nothing is selected or drawn there.
6626
+ */
6627
+ function getSelectedSlot(slots, selectedIndex) {
6628
+ const position = selectedIndex === void 0 ? void 0 : slots?.positions[selectedIndex];
6629
+ if (!slots || !position) return;
6630
+ const { slotKey, key: stopKey, datum: category } = position;
6631
+ const members = membersAt(slots.entries, category, stopKey);
6632
+ const slotBars = members.filter(({ key, datum }) => isBarRendered(datum) && slots.slotOf(key) === slotKey);
6633
+ return {
6634
+ data: slotBars.map(({ datum }) => datum),
6635
+ slotKey,
6636
+ thickness: Math.max(...slotBars.map(({ key }) => slots.thicknessOf(key))),
6637
+ members
6638
+ };
6639
+ }
6640
+ // src/charts/bar-chart/private/use-bar-stops.ts
6641
+ /**
6642
+ * Lays out the keyboard stops: one per slot and category in an overlap layout, else one per primary bar, series-major within each category.
6643
+ *
6644
+ * @param overlapSlots - The overlap layout, or undefined for a grouped one.
6645
+ * @param primaryEntries - The visible primary series.
6646
+ * @return The stops.
6647
+ */
6648
+ function useBarStops(overlapSlots, primaryEntries) {
6649
+ const series = useMemo(() => overlapSlots ? [] : primaryEntries.map((entry) => entry.series), [overlapSlots, primaryEntries]);
6650
+ return {
6651
+ count: overlapSlots?.positions.length ?? Math.max(0, ...series.map((item) => item.data.length)) * series.length,
6652
+ key: useMemo(() => JSON.stringify(overlapSlots?.keys ?? series.map((item) => item.label)), [overlapSlots, series]),
6653
+ barAt: useCallback((stop) => {
6654
+ if (overlapSlots) {
6655
+ const position = overlapSlots.positions[stop];
6656
+ return position && {
6657
+ datum: position.datum,
6658
+ index: position.dataPointIndex,
6659
+ key: position.key
6660
+ };
6661
+ }
6662
+ if (!series.length) return;
6663
+ const index = Math.floor(stop / series.length);
6664
+ const item = series[stop % series.length];
6665
+ const datum = item?.data[index];
6666
+ return item && datum ? {
6667
+ datum,
6668
+ index,
6669
+ key: item.label
6670
+ } : void 0;
6671
+ }, [overlapSlots, series]),
6672
+ stopOf: useCallback((seriesKey, dataPointIndex) => {
6673
+ if (overlapSlots) {
6674
+ const hovered = overlapSlots.entries.find((entry) => entry.series.label === seriesKey)?.series.data[dataPointIndex];
6675
+ const slotKey = overlapSlots.slotOf(seriesKey);
6676
+ const stop = hovered ? overlapSlots.positions.findIndex((position) => position.slotKey === slotKey && isSameCategory(position.datum, hovered)) : -1;
6677
+ return stop >= 0 ? stop : void 0;
6678
+ }
6679
+ const seriesIndex = series.findIndex((item) => item.label === seriesKey);
6680
+ return seriesIndex >= 0 ? dataPointIndex * series.length + seriesIndex : void 0;
6681
+ }, [overlapSlots, series])
6682
+ };
6683
+ }
6179
6684
  /**
6180
6685
  * Upper clamp on the computed group padding, so bars can never collapse to zero width
6181
6686
  * even at very large `widthFactor` values.
@@ -6186,8 +6691,7 @@ const MAX_GROUP_PADDING = .9;
6186
6691
  * gap between ticks. `0.75` = a 25% reduction of the tick-gap padding.
6187
6692
  */
6188
6693
  const COMPARISON_TICK_GAP_FACTOR = .75;
6189
- //#endregion
6190
- //#region src/charts/bar-chart/private/comparison-bars.tsx
6694
+ // src/charts/bar-chart/private/comparison-bars.tsx
6191
6695
  const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizontal, xAccessor, yAccessor, getElementStyles, resolveFill }) => {
6192
6696
  const context = useContext(DataContext);
6193
6697
  const xScale = context?.xScale;
@@ -6211,9 +6715,7 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
6211
6715
  data: series,
6212
6716
  index
6213
6717
  });
6214
- const opacity = barStyles?.opacity ?? .5;
6215
6718
  const widthFactor = barStyles?.widthFactor ?? 1.5;
6216
- const fill = resolveFill(entry);
6217
6719
  series.data.forEach((datum, i) => {
6218
6720
  const bandPosition = Number(bandScale(bandAccessor(datum)));
6219
6721
  const valuePosition = Number(valueScale(Number(valueAccessor(datum))));
@@ -6221,13 +6723,12 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
6221
6723
  if (process.env.NODE_ENV !== "production" && !Number.isFinite(bandPosition)) console.warn(`[Charts] ComparisonBars: datum key "${String(bandAccessor(datum))}" did not match any primary category. Shadow will not be rendered. Ensure comparison series data uses the same label/date keys as the primary series.`);
6222
6724
  return;
6223
6725
  }
6224
- const rect = computeComparisonRect({
6726
+ const rect = computeSlotRect({
6225
6727
  horizontal,
6226
6728
  bandPosition,
6227
6729
  slotOffset,
6228
6730
  slotThickness,
6229
- valuePosition,
6230
- baseline,
6731
+ valuePositions: [baseline, valuePosition],
6231
6732
  widthFactor
6232
6733
  });
6233
6734
  rects.push(/* @__PURE__ */ jsx("rect", {
@@ -6236,8 +6737,8 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
6236
6737
  y: rect.y,
6237
6738
  width: rect.width,
6238
6739
  height: rect.height,
6239
- fill,
6240
- opacity
6740
+ fill: resolveFill(entry, datum),
6741
+ opacity: barStyles?.opacity
6241
6742
  }, `${index}-${i}`));
6242
6743
  });
6243
6744
  });
@@ -6249,8 +6750,7 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
6249
6750
  children: rects
6250
6751
  });
6251
6752
  };
6252
- //#endregion
6253
- //#region src/charts/bar-chart/private/band-highlight.tsx
6753
+ // src/charts/bar-chart/private/band-highlight.tsx
6254
6754
  /**
6255
6755
  * Render and report the active band using the chart’s registered scales.
6256
6756
  * @param root0 - Highlight options.
@@ -6309,8 +6809,7 @@ function BandHighlight({ visible, horizontal, onChange }) {
6309
6809
  fill: CATALOG_POINTERS.surfaceSecondary
6310
6810
  });
6311
6811
  }
6312
- //#endregion
6313
- //#region src/charts/bar-chart/private/band-tooltip.tsx
6812
+ // src/charts/bar-chart/private/band-tooltip.tsx
6314
6813
  /**
6315
6814
  * Find a category by its painted center, including outer padding and reversed ranges.
6316
6815
  * @param data - Registered series points.
@@ -6336,7 +6835,8 @@ function nearestBandIndex(data, accessor, scale, position) {
6336
6835
  /**
6337
6836
  * Correct visx's range-based band inversion, which omits outer padding.
6338
6837
  * @param root0 - Registered series selection.
6339
- * @param root0.keys - Visible series keys.
6838
+ * @param root0.keys - Visible series keys, back to front where bars overlap.
6839
+ * @param root0.slots - Where series share slots: the slots of a category, in the order the band lays them out, and the slot each series sits in.
6340
6840
  * @param root0.groupPadding - Padding between grouped bars.
6341
6841
  * @param root0.withTooltips - Whether pointer events update the tooltip.
6342
6842
  * @param root0.onPointerDown - Receives the corrected pointer-down datum.
@@ -6344,12 +6844,13 @@ function nearestBandIndex(data, accessor, scale, position) {
6344
6844
  * @param root0.onPointerMove - Receives the corrected datum under a moving pointer.
6345
6845
  * @return No visual content.
6346
6846
  */
6347
- function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointerUp, onPointerMove }) {
6847
+ function BandTooltip({ keys, slots, groupPadding, withTooltips, onPointerDown, onPointerUp, onPointerMove }) {
6348
6848
  const { xScale, yScale, dataRegistry, horizontal } = useContext(DataContext);
6349
- const { showTooltip } = useContext(TooltipContext);
6849
+ const { showTooltip, hideTooltip, updateTooltip } = useContext(TooltipContext);
6350
6850
  const scale = horizontal ? yScale : xScale;
6351
6851
  const bandwidth = scale?.bandwidth?.() ?? 0;
6352
- const groupScale = useMemo(() => createGroupScale(keys, bandwidth, groupPadding), [
6852
+ const groupScale = useMemo(() => createGroupScale(slots?.keys ?? keys, bandwidth, groupPadding), [
6853
+ slots,
6353
6854
  keys,
6354
6855
  bandwidth,
6355
6856
  groupPadding
@@ -6357,18 +6858,21 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
6357
6858
  const getSelections = useCallback((params) => {
6358
6859
  const point = params?.svgPoint;
6359
6860
  if (!point || !scale?.bandwidth) return [];
6861
+ const position = horizontal ? point.y : point.x;
6360
6862
  const selections = [];
6863
+ const bands = [];
6361
6864
  for (const key of keys) {
6362
6865
  const entry = dataRegistry?.get(key);
6363
6866
  if (!entry) continue;
6364
6867
  const accessor = horizontal ? entry.yAccessor : entry.xAccessor;
6365
- const index = nearestBandIndex(entry.data, accessor, scale, horizontal ? point.y : point.x);
6868
+ const index = nearestBandIndex(entry.data, accessor, scale, position);
6366
6869
  if (index < 0) continue;
6367
6870
  const datum = entry.data[index];
6368
- const start = Number(scale(accessor(datum))) + Number(groupScale(key));
6871
+ const band = Number(scale(accessor(datum)));
6872
+ const start = band + Number(groupScale(slots ? slots.slotOf(key) : key));
6369
6873
  const end = start + groupScale.step();
6370
- const position = horizontal ? point.y : point.x;
6371
6874
  const distance = position >= start && position <= end ? 0 : Math.abs(position - (start + end) / 2);
6875
+ bands.push(band);
6372
6876
  selections.push({
6373
6877
  event: params.event,
6374
6878
  key,
@@ -6379,12 +6883,16 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
6379
6883
  distanceY: horizontal ? distance : 0
6380
6884
  });
6381
6885
  }
6382
- return selections;
6886
+ if (!slots) return selections;
6887
+ const bandDistance = (band) => Math.abs(band + scale.bandwidth() / 2 - position);
6888
+ const hoveredBand = bands.reduce((best, band) => best === void 0 || bandDistance(band) < bandDistance(best) ? band : best, void 0);
6889
+ return selections.filter((_, index) => bands[index] === hoveredBand);
6383
6890
  }, [
6384
6891
  scale,
6385
6892
  dataRegistry,
6386
6893
  horizontal,
6387
6894
  keys,
6895
+ slots,
6388
6896
  groupScale
6389
6897
  ]);
6390
6898
  const handlePointer = useCallback((params) => {
@@ -6394,25 +6902,159 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
6394
6902
  return !best || distance(selection) <= distance(best) ? selection : best;
6395
6903
  }, void 0);
6396
6904
  if (params?.event.type === "pointermove" && nearest) onPointerMove?.(nearest);
6397
- if (withTooltips && params?.event.type !== "pointerup") selections.forEach(showTooltip);
6905
+ if (withTooltips && params?.event.type !== "pointerup") {
6906
+ if (slots) updateTooltip((state) => ({
6907
+ ...state,
6908
+ tooltipData: void 0
6909
+ }));
6910
+ selections.forEach(showTooltip);
6911
+ }
6398
6912
  const callback = params?.event.type === "pointerdown" ? onPointerDown : onPointerUp;
6399
6913
  if (params?.event.type === "pointermove" || !callback) return;
6400
6914
  if (nearest) callback(nearest);
6401
6915
  }, [
6402
6916
  getSelections,
6403
6917
  withTooltips,
6918
+ slots,
6404
6919
  showTooltip,
6920
+ updateTooltip,
6405
6921
  onPointerDown,
6406
6922
  onPointerUp,
6407
6923
  onPointerMove
6408
6924
  ]);
6409
6925
  useEventEmitter("pointermove", withTooltips ? handlePointer : void 0);
6926
+ useEventEmitter("pointerout", withTooltips && slots ? hideTooltip : void 0);
6410
6927
  useEventEmitter("pointerdown", handlePointer);
6411
6928
  useEventEmitter("pointerup", onPointerUp ? handlePointer : void 0);
6412
6929
  return null;
6413
6930
  }
6414
- //#endregion
6415
- //#region src/charts/bar-chart/bar-chart.tsx
6931
+ // src/charts/bar-chart/private/grouped-bars.ts
6932
+ /**
6933
+ * Pairs each comparison series with the primary of its `group`, or with the only primary.
6934
+ *
6935
+ * @param series - The chart's series.
6936
+ * @return The index of each paired comparison's primary, keyed by the comparison's index.
6937
+ */
6938
+ function getComparisonPrimaries(series) {
6939
+ const primaries = series.flatMap((item, index) => item.options?.type === "comparison" ? [] : [{
6940
+ group: item.group,
6941
+ index
6942
+ }]);
6943
+ const primaryByGroup = new Map(primaries.map(({ group, index }) => [group, index]));
6944
+ const onlyPrimary = primaries.length === 1 ? primaries[0].index : void 0;
6945
+ const pairs = /* @__PURE__ */ new Map();
6946
+ series.forEach((item, index) => {
6947
+ const primary = item.options?.type === "comparison" ? primaryByGroup.get(item.group) ?? onlyPrimary : void 0;
6948
+ if (primary !== void 0) pairs.set(index, primary);
6949
+ });
6950
+ return pairs;
6951
+ }
6952
+ /**
6953
+ * The colors a grouped bar chart fills its series with. A comparison takes the lightest tint of its primary's color that keeps 3:1 against the background, and the primary in front of it the shade that keeps 3:1 against that tint. A primary with its own `stroke` keeps it, and a comparison with its own, different `stroke` keeps the pair's colors as given.
6954
+ *
6955
+ * @param series - The chart's series.
6956
+ * @param colors - Each series' resolved color.
6957
+ * @param background - The hex chart background.
6958
+ * @return Each series' fill, by index.
6959
+ */
6960
+ function getGroupedBarColors(series, colors, background) {
6961
+ const barColors = [...colors];
6962
+ getComparisonPrimaries(series).forEach((primary, comparison) => {
6963
+ const base = colors[primary];
6964
+ if (!!series[comparison].options?.stroke && colors[comparison].toLowerCase() !== base.toLowerCase() || !isValidHexColor(base) || !isValidHexColor(background)) return;
6965
+ const tint = tintWithContrast(base, background);
6966
+ barColors[comparison] = tint;
6967
+ if (!series[primary].options?.stroke) barColors[primary] = shadeWithContrast(base, tint, background);
6968
+ });
6969
+ return barColors;
6970
+ }
6971
+ // src/charts/bar-chart/use-bar-colors.ts
6972
+ /**
6973
+ * Reads the colors `BarChart` fills series with, so a custom tooltip or legend can match the bars.
6974
+ *
6975
+ * @return A function from series and a layout to each series' bar color, by index.
6976
+ */
6977
+ function useBarColors() {
6978
+ const { getElementStyles, backgroundColor = BACKGROUND_FALLBACK, theme } = useGlobalChartsContext();
6979
+ const comparisonOpacity = theme.barChart?.barStyles?.comparison?.opacity;
6980
+ return useCallback((series, { layout = "grouped" } = {}) => {
6981
+ const colors = series.map((data, index) => getElementStyles({
6982
+ data,
6983
+ index
6984
+ }).color);
6985
+ if (layout !== "overlap") return comparisonOpacity === void 0 ? getGroupedBarColors(series, colors, backgroundColor) : colors;
6986
+ return getOverlapBarColors(series, colors, [getElementStyles({ index: 0 }).color, getElementStyles({ index: 1 }).color], backgroundColor);
6987
+ }, [
6988
+ getElementStyles,
6989
+ backgroundColor,
6990
+ comparisonOpacity
6991
+ ]);
6992
+ }
6993
+ // src/charts/bar-chart/use-bar-legend-items.ts
6994
+ const isComparison = (data, label) => data.some((series) => series.label === label && series.options?.type === "comparison");
6995
+ /**
6996
+ * Applies what a bar layout changes about a legend: overlap lists every primary before every comparison, as it draws their slots, with every swatch opaque like its bar; a collapsed grouped item takes its comparison's tint, the wider bar that frames the group.
6997
+ *
6998
+ * @param data - The series the items were built from.
6999
+ * @param items - The legend items.
7000
+ * @param layout - The layout the bars are drawn in.
7001
+ * @param seriesColors - Each series' bar color, by index.
7002
+ * @return The legend items.
7003
+ */
7004
+ function forBarLayout(data, items, layout, seriesColors) {
7005
+ if (layout === "overlap") {
7006
+ const opaque = items.map((item) => item.shapeStyle ? {
7007
+ ...item,
7008
+ shapeStyle: {
7009
+ ...item.shapeStyle,
7010
+ opacity: void 0
7011
+ }
7012
+ } : item);
7013
+ const seriesItems = opaque.filter((item) => item.seriesLabels);
7014
+ const firstLabel = (item) => item.seriesLabels?.[0];
7015
+ return [
7016
+ ...seriesItems.filter((item) => !isComparison(data, firstLabel(item))),
7017
+ ...seriesItems.filter((item) => isComparison(data, firstLabel(item))),
7018
+ ...opaque.filter((item) => !item.seriesLabels)
7019
+ ];
7020
+ }
7021
+ return items.map((item) => {
7022
+ if ((item.seriesLabels?.length ?? 0) < 2) return item;
7023
+ const comparisonIndex = data.findIndex((series) => series.options?.type === "comparison" && item.seriesLabels?.includes(series.label));
7024
+ return comparisonIndex === -1 ? item : {
7025
+ ...item,
7026
+ color: seriesColors[comparisonIndex]
7027
+ };
7028
+ });
7029
+ }
7030
+ /**
7031
+ * Builds legend items that match `BarChart`'s bars in a layout, for a custom legend or a chart's own.
7032
+ *
7033
+ * @param data - The series the chart draws.
7034
+ * @param options - The legend options, and the layout the bars are drawn in.
7035
+ * @param legendShape - The shape of the legend swatches.
7036
+ * @return The legend items.
7037
+ */
7038
+ function useBarLegendItems(data, options = {}, legendShape) {
7039
+ const { layout } = options;
7040
+ const getBarColors = useBarColors();
7041
+ const seriesColors = useMemo(() => layout ? getBarColors(data, { layout }) : void 0, [
7042
+ data,
7043
+ layout,
7044
+ getBarColors
7045
+ ]);
7046
+ const items = useChartLegendItems(data, useMemo(() => ({
7047
+ ...options,
7048
+ seriesColors
7049
+ }), [options, seriesColors]), legendShape);
7050
+ return useMemo(() => layout && seriesColors ? forBarLayout(data, items, layout, seriesColors) : items, [
7051
+ data,
7052
+ items,
7053
+ layout,
7054
+ seriesColors
7055
+ ]);
7056
+ }
7057
+ // src/charts/bar-chart/bar-chart.tsx
6416
7058
  const validateData$2 = (data) => {
6417
7059
  if (!data?.length) return __("No data available", "jetpack-charts");
6418
7060
  if (data.some((series) => series.data.some((point) => isInvalidReading(point.value, { allowMissing: true }) || !point.label && (!("date" in point && point.date) || isNaN(point.date.getTime()))))) return __("Invalid data", "jetpack-charts");
@@ -6423,13 +7065,14 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ jsx("div", {
6423
7065
  className: bar_chart_module_default["bar-chart__tooltip-row"],
6424
7066
  children: sprintf(__("%1$s: %2$s", "jetpack-charts"), label, value)
6425
7067
  });
6426
- const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, tooltipPlacement, tooltipAnchorTop, tooltipStyle, barClassName, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, withBandHighlight = false, onBandHighlightChange, defaultHiddenSeries, animation, children, gap = "md", onPointerDown, onPointerUp, onDatumActivate }) => {
7068
+ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, tooltipPlacement, tooltipAnchorTop, tooltipStyle, barClassName, options = {}, orientation = "vertical", layout = "grouped", withPatterns = false, showZeroValues = false, withBandHighlight = false, onBandHighlightChange, defaultHiddenSeries, animation, children, gap = "md", onPointerDown, onPointerUp, onDatumActivate }) => {
6427
7069
  if (!withTooltips && (withBandHighlight || onBandHighlightChange)) warnOnce("bar-chart-band-highlight-without-tooltips", "BarChart: withBandHighlight and onBandHighlightChange require withTooltips.");
6428
7070
  const legendInteractive = legend.interactive ?? false;
6429
7071
  const legendCollapseGroups = legend.collapseGroups ?? false;
6430
7072
  const legendComparisonItem = legend.comparisonItem ?? false;
6431
7073
  const legendComparisonSeriesItems = legend.comparisonSeriesItems;
6432
7074
  const horizontal = orientation === "horizontal";
7075
+ const isOverlap = layout === "overlap";
6433
7076
  const chartId = useChartId(providedChartId);
6434
7077
  const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
6435
7078
  const isSeriesVisible = useCallback((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
@@ -6439,16 +7082,25 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6439
7082
  enabled: showZeroValues,
6440
7083
  valueAxisLength: horizontal ? width : height
6441
7084
  });
6442
- const legendItems = useChartLegendItems(dataSorted, useMemo(() => ({
7085
+ const legendItems = useBarLegendItems(dataSorted, useMemo(() => ({
6443
7086
  collapseGroups: legendCollapseGroups,
6444
7087
  comparisonItem: legendComparisonItem,
6445
- comparisonSeriesItems: legendComparisonSeriesItems
7088
+ comparisonSeriesItems: legendComparisonSeriesItems,
7089
+ layout
6446
7090
  }), [
6447
7091
  legendCollapseGroups,
6448
7092
  legendComparisonItem,
6449
- legendComparisonSeriesItems
7093
+ legendComparisonSeriesItems,
7094
+ layout
6450
7095
  ]));
6451
- const { getElementStyles } = useGlobalChartsContext();
7096
+ const { getElementStyles, theme: providerTheme } = useGlobalChartsContext();
7097
+ const getBarColors = useBarColors();
7098
+ const barColors = useMemo(() => getBarColors(dataSorted, { layout }), [
7099
+ layout,
7100
+ getBarColors,
7101
+ dataSorted
7102
+ ]);
7103
+ if (providerTheme.barChart?.barStyles?.comparison?.opacity !== void 0) warnOnce("bar-chart-comparison-opacity", "BarChart: theme.barChart.barStyles.comparison.opacity is deprecated. Comparison bars are opaque; leave it unset.");
6452
7104
  const isSeriesRendered = useCallback((series) => isSeriesVisible(series.label), [isSeriesVisible]);
6453
7105
  const hasWholeNumberValues = useMemo(() => hasOnlyWholeNumbers(dataSorted.filter(isSeriesRendered)), [dataSorted, isSeriesRendered]);
6454
7106
  const chartOptions = useBarChartOptions(dataWithVisibleZeros, horizontal, options, isSeriesRendered);
@@ -6477,37 +7129,30 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6477
7129
  return seriesWithVisibility.every(({ isVisible }) => !isVisible);
6478
7130
  }, [seriesWithVisibility]);
6479
7131
  const primaryEntries = useMemo(() => seriesWithVisibility.filter(({ isVisible, series }) => isVisible && series.options?.type !== "comparison"), [seriesWithVisibility]);
6480
- const totalPoints = Math.max(0, ...primaryEntries.map((e) => e.series.data.length)) * primaryEntries.length;
7132
+ const overlapSlots = useOverlapSlots(isOverlap, seriesWithVisibility);
6481
7133
  const primaryKeys = useMemo(() => primaryEntries.map(({ series }) => series.label), [primaryEntries]);
6482
- const primarySeries = useMemo(() => primaryEntries.map(({ series }) => series), [primaryEntries]);
6483
- const activateSelectedBar = useCallback((index) => {
6484
- const primaryCount = primaryEntries.length;
6485
- if (!primaryCount) return;
6486
- const dataPointIndex = Math.floor(index / primaryCount);
6487
- const series = primaryEntries[index % primaryCount]?.series;
6488
- const datum = series?.data[dataPointIndex];
6489
- if (series && datum) onDatumActivate?.({
6490
- datum,
6491
- index: dataPointIndex,
6492
- key: series.label
6493
- });
6494
- }, [primaryEntries, onDatumActivate]);
6495
- const visibleSeriesKey = useMemo(() => JSON.stringify(primaryKeys), [primaryKeys]);
7134
+ const stops = useBarStops(overlapSlots, primaryEntries);
7135
+ const { barAt, stopOf } = stops;
7136
+ const activateSelectedBar = useCallback((stop) => {
7137
+ const bar = barAt(stop);
7138
+ if (bar) onDatumActivate?.(bar);
7139
+ }, [barAt, onDatumActivate]);
6496
7140
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown, onChartPointerMove } = useKeyboardNavigation({
6497
7141
  selectedIndex,
6498
7142
  setSelectedIndex,
6499
7143
  isNavigating,
6500
7144
  setIsNavigating,
6501
7145
  chartRef,
6502
- totalPoints,
7146
+ totalPoints: stops.count,
6503
7147
  onActivate: activateSelectedBar,
6504
- visibleSeriesKey
7148
+ visibleSeriesKey: stops.key
6505
7149
  });
6506
7150
  const handlePointerMove = useCallback(({ key, index }) => {
6507
- const seriesIndex = primaryKeys.indexOf(key);
6508
- if (seriesIndex >= 0) onChartPointerMove(index * primaryKeys.length + seriesIndex);
6509
- }, [primaryKeys, onChartPointerMove]);
7151
+ const stop = stopOf(key, index);
7152
+ if (stop !== void 0) onChartPointerMove(stop);
7153
+ }, [stopOf, onChartPointerMove]);
6510
7154
  const comparisonEntries = useMemo(() => {
7155
+ if (isOverlap) return [];
6511
7156
  const primaryByGroup = new Map(primaryEntries.map(({ series, index }) => [series.group, {
6512
7157
  label: series.label,
6513
7158
  index
@@ -6523,12 +7168,12 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6523
7168
  entries.push({
6524
7169
  series,
6525
7170
  index,
6526
- primaryKey: primary.label,
6527
- primaryIndex: primary.index
7171
+ primaryKey: primary.label
6528
7172
  });
6529
7173
  });
6530
7174
  return entries;
6531
7175
  }, [
7176
+ isOverlap,
6532
7177
  seriesWithVisibility,
6533
7178
  primaryEntries,
6534
7179
  primaryKeys
@@ -6568,33 +7213,39 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6568
7213
  chartOptions.yScale,
6569
7214
  horizontal
6570
7215
  ]);
6571
- const getBarBackground = useCallback((index) => (datum) => withPatterns ? `url(#${getPatternId(chartId, index)})` : datum.color ?? getElementStyles({
6572
- data: dataSorted[index],
6573
- index
6574
- }).color, [
7216
+ const getBarBackground = useCallback((index) => (datum) => withPatterns ? `url(#${getPatternId(chartId, index)})` : datum.color ?? barColors[index] ?? "", [
6575
7217
  withPatterns,
6576
- getElementStyles,
6577
- dataSorted,
7218
+ barColors,
6578
7219
  chartId
6579
7220
  ]);
6580
- const resolveComparisonFill = useCallback((entry) => withPatterns ? `url(#${getPatternId(chartId, entry.primaryIndex)})` : getElementStyles({
6581
- data: entry.series,
6582
- index: entry.index
6583
- }).color, [
7221
+ const resolveComparisonFill = useCallback((entry, datum) => withPatterns ? `url(#${getPatternId(chartId, entry.index)})` : datum.color ?? barColors[entry.index] ?? "", [
6584
7222
  withPatterns,
6585
7223
  chartId,
6586
- getElementStyles
7224
+ barColors
6587
7225
  ]);
7226
+ const renderOverlapTooltip = useCallback(({ tooltipData }) => {
7227
+ const nearestDatum = tooltipData?.nearestDatum?.datum;
7228
+ if (!nearestDatum) return null;
7229
+ const datumByKey = tooltipData?.datumByKey ?? {};
7230
+ const categoryLabel = chartOptions.tooltip.labelFormatter(nearestDatum.label || (nearestDatum.date ? nearestDatum.date.getTime() : 0), 0, []);
7231
+ return /* @__PURE__ */ jsxs("div", {
7232
+ className: bar_chart_module_default["bar-chart__tooltip"],
7233
+ children: [/* @__PURE__ */ jsx("div", {
7234
+ className: bar_chart_module_default["bar-chart__tooltip-header"],
7235
+ children: categoryLabel
7236
+ }), dataSorted.flatMap(({ label }) => {
7237
+ const datum = datumByKey[label]?.datum;
7238
+ return datum ? [/* @__PURE__ */ jsx(Fragment, { children: renderTooltipRow(label, formatReading(datum.value)) }, label)] : [];
7239
+ })]
7240
+ });
7241
+ }, [chartOptions.tooltip, dataSorted]);
6588
7242
  const renderDefaultTooltip = useCallback(({ tooltipData }) => {
6589
7243
  const nearestDatum = tooltipData?.nearestDatum?.datum;
6590
7244
  if (!nearestDatum) return null;
6591
7245
  const primaryKey = tooltipData?.nearestDatum?.key;
6592
7246
  const categoryLabel = chartOptions.tooltip.labelFormatter(nearestDatum.label || (nearestDatum.date ? nearestDatum.date.getTime() : 0), 0, []);
6593
7247
  const comparisonEntry = comparisonEntries.find((entry) => entry.primaryKey === primaryKey);
6594
- const comparisonDatum = comparisonEntry?.series.data.find((point) => {
6595
- const p = point;
6596
- return nearestDatum.label != null ? p.label === nearestDatum.label : !!nearestDatum.date && !!p.date && p.date.getTime() === nearestDatum.date.getTime();
6597
- });
7248
+ const comparisonDatum = comparisonEntry?.series.data.find((point) => isSameCategory(point, nearestDatum));
6598
7249
  if (comparisonEntry && comparisonDatum) return /* @__PURE__ */ jsxs("div", {
6599
7250
  className: bar_chart_module_default["bar-chart__tooltip"],
6600
7251
  children: [
@@ -6659,26 +7310,35 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6659
7310
  }
6660
7311
  `;
6661
7312
  }, [chartId]);
6662
- const createKeyboardHighlightStyle = useCallback(() => {
6663
- if (selectedIndex === void 0) return "";
6664
- const primaryCount = primaryEntries.length;
6665
- if (!primaryCount) return "";
6666
- const dataPointIndex = Math.floor(selectedIndex / primaryCount);
6667
- const seriesIndex = selectedIndex % primaryCount;
6668
- const seriesData = primaryEntries[seriesIndex]?.series;
6669
- const datum = seriesData?.data[dataPointIndex];
6670
- if (!seriesData || !datum || !isBarRendered(datum)) return "";
6671
- const domBarIndex = primaryEntries.slice(0, seriesIndex).reduce((count, { series }) => count + countRenderedBars(series.data), 0) + countRenderedBars(seriesData.data.slice(0, dataPointIndex));
6672
- return `
6673
- .bar-chart[data-chart-id="bar-chart-${chartId}"] .visx-bar-group .visx-bar:nth-child(${domBarIndex + 1}) {
6674
- stroke: #005fcc;
6675
- stroke-width: 2px;
6676
- }
6677
- `;
7313
+ const overlapBandSlots = useMemo(() => overlapSlots && {
7314
+ keys: overlapSlots.slotKeys,
7315
+ slotOf: overlapSlots.slotOf
7316
+ }, [overlapSlots]);
7317
+ const selectedSlot = useMemo(() => getSelectedSlot(overlapSlots, selectedIndex), [overlapSlots, selectedIndex]);
7318
+ const selectedGroup = useMemo(() => {
7319
+ const stop = overlapSlots || selectedIndex === void 0 ? void 0 : barAt(selectedIndex);
7320
+ if (!stop) return;
7321
+ return membersAt(primaryEntries, stop.datum, stop.key);
6678
7322
  }, [
7323
+ overlapSlots,
6679
7324
  selectedIndex,
6680
- primaryEntries,
6681
- chartId
7325
+ barAt,
7326
+ primaryEntries
7327
+ ]);
7328
+ const selectedMembers = selectedSlot?.members ?? selectedGroup;
7329
+ const selectedBar = useMemo(() => {
7330
+ if (overlapSlots) return selectedSlot;
7331
+ const bar = selectedIndex === void 0 ? void 0 : barAt(selectedIndex);
7332
+ return bar && isBarRendered(bar.datum) ? {
7333
+ data: [bar.datum],
7334
+ slotKey: bar.key,
7335
+ thickness: 1
7336
+ } : void 0;
7337
+ }, [
7338
+ overlapSlots,
7339
+ selectedSlot,
7340
+ selectedIndex,
7341
+ barAt
6682
7342
  ]);
6683
7343
  const error = validateData$2(dataSorted);
6684
7344
  const isDataValid = !error;
@@ -6699,7 +7359,6 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6699
7359
  children: error
6700
7360
  });
6701
7361
  const gridVisibility = gridVisibilityProp ?? chartOptions.gridVisibility;
6702
- const highlightedBarStyle = createKeyboardHighlightStyle();
6703
7362
  const legendPosition = legend.position ?? "bottom";
6704
7363
  const legendElement = showLegend && /* @__PURE__ */ jsx(Legend, {
6705
7364
  orientation: legend.orientation ?? "horizontal",
@@ -6779,14 +7438,7 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6779
7438
  tickValues: (horizontal ? void 0 : valueTicks) ?? chartOptions.axis.y.tickValues
6780
7439
  })] })
6781
7440
  }),
6782
- withPatterns && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("defs", { children: dataSorted.map((seriesData, index) => renderPattern(index, getElementStyles({
6783
- data: seriesData,
6784
- index
6785
- }).color)) }), /* @__PURE__ */ jsx("style", { children: dataSorted.map((seriesData, index) => createPatternBorderStyle(index, getElementStyles({
6786
- data: seriesData,
6787
- index
6788
- }).color)) })] }),
6789
- highlightedBarStyle && /* @__PURE__ */ jsx("style", { children: highlightedBarStyle }),
7441
+ withPatterns && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("defs", { children: barColors.map((color, index) => renderPattern(index, color)) }), /* @__PURE__ */ jsx("style", { children: barColors.map((color, index) => createPatternBorderStyle(index, color)) })] }),
6790
7442
  allSeriesHidden ? /* @__PURE__ */ jsx(SvgEmptyState, {
6791
7443
  x: width / 2,
6792
7444
  y: chartHeight / 2,
@@ -6804,30 +7456,22 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6804
7456
  getElementStyles,
6805
7457
  resolveFill: resolveComparisonFill
6806
7458
  }),
6807
- barClassName ? /* @__PURE__ */ jsx("g", {
6808
- className: "visx-bar-group",
6809
- children: primaryEntries.map(({ series: seriesData, index }) => /* @__PURE__ */ jsx(ClassifiedBarSeries, {
6810
- dataKey: seriesData.label,
6811
- data: seriesData.data,
6812
- xAccessor: chartOptions.accessors.xAccessor,
6813
- yAccessor: chartOptions.accessors.yAccessor,
6814
- colorAccessor: getBarBackground(index),
6815
- barClassName,
6816
- primaryKeys,
6817
- groupPadding
6818
- }, seriesData.label))
6819
- }) : /* @__PURE__ */ jsx(BarGroup, {
6820
- padding: groupPadding,
6821
- children: primaryEntries.map(({ series: seriesData, index }) => /* @__PURE__ */ jsx(BarSeries, {
6822
- dataKey: seriesData?.label,
6823
- data: seriesData.data,
6824
- yAccessor: chartOptions.accessors.yAccessor,
6825
- xAccessor: chartOptions.accessors.xAccessor,
6826
- colorAccessor: getBarBackground(index)
6827
- }, seriesData?.label))
7459
+ /* @__PURE__ */ jsx(BarLayer, {
7460
+ chartId,
7461
+ overlapSlots,
7462
+ selectedBar,
7463
+ primaryEntries,
7464
+ primaryKeys,
7465
+ getBarBackground,
7466
+ barClassName,
7467
+ groupPadding,
7468
+ horizontal,
7469
+ xAccessor: chartOptions.accessors.xAccessor,
7470
+ yAccessor: chartOptions.accessors.yAccessor
6828
7471
  }),
6829
7472
  (withTooltips || onPointerDown || onPointerUp) && /* @__PURE__ */ jsx(BandTooltip, {
6830
- keys: primaryKeys,
7473
+ keys: overlapSlots?.keys ?? primaryKeys,
7474
+ slots: overlapBandSlots,
6831
7475
  groupPadding,
6832
7476
  withTooltips,
6833
7477
  onPointerDown,
@@ -6844,6 +7488,7 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6844
7488
  ...!horizontal && valueTicks ? { tickValues: valueTicks } : {}
6845
7489
  })] })
6846
7490
  }),
7491
+ withTooltips && selectedMembers && /* @__PURE__ */ jsx(SlotTooltipMembers, { members: selectedMembers }),
6847
7492
  withTooltips && /* @__PURE__ */ jsx(XYChartTooltip, {
6848
7493
  tooltipPlacement,
6849
7494
  tooltipAnchorTop,
@@ -6851,11 +7496,10 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6851
7496
  detectBounds: true,
6852
7497
  snapTooltipToDatumX: true,
6853
7498
  snapTooltipToDatumY: true,
6854
- renderTooltip: renderTooltip || renderDefaultTooltip,
7499
+ renderTooltip: renderTooltip || (isOverlap ? renderOverlapTooltip : renderDefaultTooltip),
6855
7500
  selectedIndex,
6856
7501
  tooltipRef,
6857
7502
  keyboardFocusedClassName: bar_chart_module_default["bar-chart__tooltip--keyboard-focused"],
6858
- series: primarySeries,
6859
7503
  mode: "individual"
6860
7504
  })
6861
7505
  ]
@@ -6873,8 +7517,7 @@ const BarChartWithProvider = (props) => {
6873
7517
  BarChartWithProvider.displayName = "BarChart";
6874
7518
  const BarChart = attachSubComponents(BarChartWithProvider, { Legend });
6875
7519
  const BarChartResponsive = attachSubComponents(withResponsive(BarChartWithProvider), { Legend });
6876
- //#endregion
6877
- //#region src/charts/bar-list-chart/bar-list-chart.tsx
7520
+ // src/charts/bar-list-chart/bar-list-chart.tsx
6878
7521
  /**
6879
7522
  * Get the bandwidth of a scale
6880
7523
  * @param scale - The scale to get the bandwidth of
@@ -7043,8 +7686,7 @@ const BarListChart = (props) => {
7043
7686
  };
7044
7687
  BarListChart.displayName = "BarListChart";
7045
7688
  const BarListChartResponsive = withResponsive(BarListChart);
7046
- //#endregion
7047
- //#region src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss
7689
+ // src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss
7048
7690
  var conversion_funnel_chart_module_default = {
7049
7691
  "bar-container": "a8ccharts-mGEVca-bar-container",
7050
7692
  "change-indicator": "a8ccharts-mGEVca-change-indicator",
@@ -7063,83 +7705,131 @@ var conversion_funnel_chart_module_default = {
7063
7705
  "main-rate": "a8ccharts-mGEVca-main-rate",
7064
7706
  "step-label": "a8ccharts-mGEVca-step-label",
7065
7707
  "step-rate": "a8ccharts-mGEVca-step-rate",
7708
+ "step-text": "a8ccharts-mGEVca-step-text",
7066
7709
  "stretch": "a8ccharts-mGEVca-stretch",
7067
- "tooltip-content": "a8ccharts-mGEVca-tooltip-content",
7068
- "tooltip-title": "a8ccharts-mGEVca-tooltip-title"
7710
+ "tooltip-label": "a8ccharts-mGEVca-tooltip-label",
7711
+ "tooltip-title": "a8ccharts-mGEVca-tooltip-title",
7712
+ "tooltip-value": "a8ccharts-mGEVca-tooltip-value"
7069
7713
  };
7070
- //#endregion
7071
- //#region src/charts/conversion-funnel-chart/private/use-funnel-selection.ts
7714
+ // src/charts/conversion-funnel-chart/private/use-funnel-selection.ts
7072
7715
  /**
7073
- * Custom hook to manage funnel bar selection state and interactions
7716
+ * Custom hook to manage funnel step selection state
7074
7717
  * @param hideTooltip - Function to hide tooltip when selection is cleared
7075
- * @return Object containing selection state and event handlers
7718
+ * @return Object containing selection state and its setters
7076
7719
  */
7077
7720
  const useFunnelSelection = (hideTooltip) => {
7078
- const [clickedStep, setClickedStep] = useState(null);
7721
+ const [selectedStepId, setSelectedStepId] = useState(null);
7079
7722
  return {
7080
- clickedStep,
7081
- handleBarClick: useCallback((stepId) => {
7082
- if (clickedStep === stepId) {
7083
- setClickedStep(null);
7084
- hideTooltip?.();
7085
- } else setClickedStep(stepId);
7086
- }, [clickedStep, hideTooltip]),
7087
- handleBarKeyDown: useCallback((stepId, event) => {
7088
- if (event.key === "Enter" || event.key === " ") {
7089
- event.preventDefault();
7090
- if (clickedStep === stepId) {
7091
- setClickedStep(null);
7092
- hideTooltip?.();
7093
- } else setClickedStep(stepId);
7094
- } else if (event.key === "Escape") {
7095
- event.preventDefault();
7096
- setClickedStep(null);
7097
- hideTooltip?.();
7098
- }
7099
- }, [clickedStep, hideTooltip]),
7723
+ selectedStepId,
7724
+ selectStep: setSelectedStepId,
7100
7725
  clearSelection: useCallback(() => {
7101
- setClickedStep(null);
7726
+ setSelectedStepId(null);
7102
7727
  hideTooltip?.();
7103
7728
  }, [hideTooltip]),
7104
7729
  getStepState: useCallback((stepId) => ({
7105
- isClicked: clickedStep === stepId,
7106
- isBlurred: clickedStep !== null && clickedStep !== stepId
7107
- }), [clickedStep])
7730
+ isSelected: selectedStepId === stepId,
7731
+ isBlurred: selectedStepId !== null && selectedStepId !== stepId
7732
+ }), [selectedStepId])
7108
7733
  };
7109
7734
  };
7110
- //#endregion
7111
- //#region src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx
7735
+ // src/charts/conversion-funnel-chart/private/use-roving-step-focus.ts
7736
+ const getTargetIndex = (key, index, count, isRtl) => {
7737
+ const nextKey = isRtl ? "ArrowLeft" : "ArrowRight";
7738
+ const previousKey = isRtl ? "ArrowRight" : "ArrowLeft";
7739
+ switch (key) {
7740
+ case nextKey: return Math.min(index + 1, count - 1);
7741
+ case previousKey: return Math.max(index - 1, 0);
7742
+ case "Home": return 0;
7743
+ case "End": return count - 1;
7744
+ default: return;
7745
+ }
7746
+ };
7747
+ /**
7748
+ * Roving tabindex for the funnel steps: one Tab stop, and arrow keys, Home and End move focus between steps.
7749
+ *
7750
+ * @param stepIds - Step ids in visual order.
7751
+ * @param selectedStepId - The selected step, which holds the Tab stop until any step has taken focus.
7752
+ * @return Tab index, element registration, focus and key handlers for each step.
7753
+ */
7754
+ const useRovingStepFocus = (stepIds, selectedStepId) => {
7755
+ const [focusedStepId, setFocusedStepId] = useState(null);
7756
+ const stepElements = useRef(/* @__PURE__ */ new Map());
7757
+ const tabStopId = [focusedStepId, selectedStepId].find((id) => id !== null && stepIds.includes(id)) ?? stepIds[0];
7758
+ return {
7759
+ getTabIndex: useCallback((stepId) => stepId === tabStopId ? 0 : -1, [tabStopId]),
7760
+ setStepElement: useCallback((stepId, element) => {
7761
+ if (element) stepElements.current.set(stepId, element);
7762
+ else stepElements.current.delete(stepId);
7763
+ }, []),
7764
+ handleStepFocus: useCallback((stepId) => {
7765
+ setFocusedStepId(stepId);
7766
+ }, []),
7767
+ handleNavigationKey: useCallback((stepId, event) => {
7768
+ if (event.altKey || event.ctrlKey || event.metaKey) return false;
7769
+ const isRtl = getComputedStyle(event.currentTarget).direction === "rtl";
7770
+ const targetIndex = getTargetIndex(event.key, stepIds.indexOf(stepId), stepIds.length, isRtl);
7771
+ if (targetIndex === void 0) return false;
7772
+ event.preventDefault();
7773
+ stepElements.current.get(stepIds[targetIndex])?.focus();
7774
+ return true;
7775
+ }, [stepIds])
7776
+ };
7777
+ };
7778
+ // src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx
7112
7779
  /** Chart height (px) below which the funnel tightens its gaps so the bars keep their room. */
7113
7780
  const SHORT_CHART_HEIGHT = 180;
7781
+ const formatStepCount = (count) => sprintf(_n("%s item", "%s items", count, "jetpack-charts"), formatMetricValue(count));
7782
+ const getDefaultTooltipContent = (step) => ({
7783
+ value: step.count === void 0 ? void 0 : formatStepCount(step.count),
7784
+ rows: [{
7785
+ label: __("Conversion rate", "jetpack-charts"),
7786
+ value: formatPercentage(step.rate)
7787
+ }]
7788
+ });
7789
+ const getDefaultTooltipIds = (tooltipId, { rows = [] }) => ({
7790
+ value: `${tooltipId}-value`,
7791
+ rows: rows.map((_, index) => ({
7792
+ label: `${tooltipId}-row-${index}-label`,
7793
+ value: `${tooltipId}-row-${index}-value`
7794
+ }))
7795
+ });
7796
+ const getDefaultTooltipDescriptionIds = (tooltipId, content) => {
7797
+ const ids = getDefaultTooltipIds(tooltipId, content);
7798
+ return [...content.value ? [ids.value] : [], ...ids.rows.flatMap((row) => [row.label, row.value])].join(" ");
7799
+ };
7114
7800
  /**
7115
7801
  * Internal ConversionFunnelChart component with chart registration
7116
7802
  *
7117
- * @param props - Component props
7118
- * @param props.chartId - Optional unique identifier for the chart
7119
- * @param props.mainRate - Main conversion rate to highlight
7120
- * @param props.changeIndicator - Change indicator (e.g., +2%, -1.5%)
7121
- * @param props.steps - Array of funnel steps
7122
- * @param props.loading - Whether the chart is in loading state
7123
- * @param props.animation - Whether to show chart animation on initial render or not
7124
- * @param props.className - Additional CSS class name
7125
- * @param props.ariaLabel - Accessible name for the chart group
7126
- * @param props.height - Height of the chart container. Falls back to style.height if set, otherwise defaults to "100%".
7127
- * @param props.style - Custom styling
7128
- * @param props.renderStepLabel - Custom render function for step labels
7129
- * @param props.renderStepRate - Custom render function for step rates
7130
- * @param props.renderMainMetric - Custom render function for the entire main metric section
7131
- * @param props.renderTooltip - Custom render function for tooltip content
7132
- * @param props.tooltipStyle - Inline styles merged over the tooltip box defaults
7803
+ * @param props - Component props
7804
+ * @param props.chartId - Optional unique identifier for the chart
7805
+ * @param props.mainRate - Main conversion rate to highlight
7806
+ * @param props.changeIndicator - Change indicator (e.g., +2%, -1.5%)
7807
+ * @param props.steps - Array of funnel steps
7808
+ * @param props.loading - Whether the chart is in loading state
7809
+ * @param props.animation - Whether to show chart animation on initial render or not
7810
+ * @param props.className - Additional CSS class name
7811
+ * @param props.ariaLabel - Accessible name for the chart group
7812
+ * @param props.height - Height of the chart container. Falls back to style.height if set, otherwise defaults to "100%".
7813
+ * @param props.style - Custom styling
7814
+ * @param props.renderStepLabel - Custom render function for step labels
7815
+ * @param props.renderStepRate - Custom render function for step rates
7816
+ * @param props.renderMainMetric - Custom render function for the entire main metric section
7817
+ * @param props.renderTooltip - Custom render function for tooltip content
7818
+ * @param props.tooltipStyle - Inline styles merged over the tooltip box defaults
7819
+ * @param props.getTooltipContent - What each step's tooltip shows, which screen readers also read
7133
7820
  * @return JSX element representing the conversion funnel chart
7134
7821
  */
7135
- const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loading = false, animation, className, ariaLabel, chartId: providedChartId, height, style, renderStepLabel, renderStepRate, renderMainMetric, renderTooltip, tooltipStyle }) => {
7822
+ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loading = false, animation, className, ariaLabel, chartId: providedChartId, height, style, renderStepLabel, renderStepRate, renderMainMetric, renderTooltip, tooltipStyle, getTooltipContent = getDefaultTooltipContent }) => {
7136
7823
  const chartId = useChartId(providedChartId);
7137
7824
  const { getElementStyles, isColorPaletteResolved } = useGlobalChartsContext();
7138
7825
  const chartRef = useRef(null);
7139
- const selectedBarRef = useRef(null);
7826
+ const isPointerFocusRef = useRef(false);
7140
7827
  const [scopeNode, setScopeNode] = useState(null);
7828
+ const tooltipId = useId();
7141
7829
  const { tooltipData, tooltipLeft, tooltipTop, tooltipOpen, showTooltip, hideTooltip } = useTooltip();
7142
- const { handleBarClick, handleBarKeyDown, clearSelection, getStepState } = useFunnelSelection(hideTooltip);
7830
+ const { selectedStepId, selectStep, clearSelection, getStepState } = useFunnelSelection(hideTooltip);
7831
+ const stepIds = useMemo(() => steps?.map((step) => step.id) ?? [], [steps]);
7832
+ const { getTabIndex, setStepElement, handleStepFocus, handleNavigationKey } = useRovingStepFocus(stepIds, selectedStepId);
7143
7833
  const standaloneScopeClass = useStandaloneScopeClass();
7144
7834
  const [measureChartRef, , chartHeight] = useElementSize();
7145
7835
  const gap = chartHeight > 0 && chartHeight < SHORT_CHART_HEIGHT ? "sm" : "xl";
@@ -7148,94 +7838,71 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
7148
7838
  setScopeNode(node);
7149
7839
  measureChartRef(node);
7150
7840
  }, [measureChartRef]);
7151
- const clearSelectionAndRef = useCallback(() => {
7152
- clearSelection();
7153
- selectedBarRef.current = null;
7154
- hideTooltip();
7155
- }, [clearSelection, hideTooltip]);
7156
- const showTooltipAt = useCallback((step, x, y) => {
7157
- showTooltip({
7841
+ const selectStepAt = useCallback((step, x, y) => {
7842
+ selectStep(step.id);
7843
+ const bounds = chartRef.current?.getBoundingClientRect();
7844
+ if (bounds) showTooltip({
7158
7845
  tooltipData: step,
7159
- tooltipLeft: x,
7160
- tooltipTop: y - 10
7846
+ tooltipLeft: x - bounds.left,
7847
+ tooltipTop: y - bounds.top - 10
7161
7848
  });
7162
- }, [showTooltip]);
7163
- const getMouseTooltipCoords = useCallback((event) => {
7164
- const bounds = chartRef.current?.getBoundingClientRect();
7165
- if (!bounds) return null;
7166
- return {
7167
- x: event.clientX - bounds.left,
7168
- y: event.clientY - bounds.top
7169
- };
7170
- }, []);
7171
- const getKeyboardTooltipCoords = useCallback((event) => {
7172
- const bounds = chartRef.current?.getBoundingClientRect();
7173
- if (!bounds) return null;
7174
- const rect = event.currentTarget.getBoundingClientRect();
7175
- return {
7176
- x: rect.left + rect.width / 2 - bounds.left,
7177
- y: rect.top - bounds.top
7178
- };
7179
- }, []);
7180
- const handleStepInteraction = useCallback((step, event, interactionType) => {
7181
- selectedBarRef.current = event.currentTarget;
7182
- const { isClicked } = getStepState(step.id);
7183
- if (isClicked) {
7184
- if (interactionType === "click") handleBarClick(step.id);
7185
- else handleBarKeyDown(step.id, event);
7186
- return;
7187
- }
7188
- if (interactionType === "click") {
7189
- handleBarClick(step.id);
7190
- const coords = getMouseTooltipCoords(event);
7191
- if (coords) showTooltipAt(step, coords.x, coords.y);
7192
- } else {
7193
- handleBarKeyDown(step.id, event);
7194
- const coords = getKeyboardTooltipCoords(event);
7195
- if (coords) showTooltipAt(step, coords.x, coords.y);
7196
- }
7197
- }, [
7198
- getStepState,
7199
- handleBarClick,
7200
- handleBarKeyDown,
7201
- showTooltipAt,
7202
- getMouseTooltipCoords,
7203
- getKeyboardTooltipCoords
7204
- ]);
7849
+ }, [selectStep, showTooltip]);
7850
+ const selectStepAtElement = useCallback((step, element) => {
7851
+ const rect = element.getBoundingClientRect();
7852
+ selectStepAt(step, rect.left + rect.width / 2, rect.top);
7853
+ }, [selectStepAt]);
7205
7854
  const stepHandlers = useMemo(() => {
7206
7855
  const handlers = /* @__PURE__ */ new Map();
7207
7856
  steps.forEach((step) => {
7857
+ const onPointerDown = () => {
7858
+ isPointerFocusRef.current = true;
7859
+ };
7208
7860
  const onClick = (event) => {
7209
- event.stopPropagation();
7210
- handleStepInteraction(step, event, "click");
7861
+ isPointerFocusRef.current = false;
7862
+ selectStepAt(step, event.clientX, event.clientY);
7863
+ };
7864
+ const onFocus = (event) => {
7865
+ handleStepFocus(step.id);
7866
+ if (isPointerFocusRef.current) isPointerFocusRef.current = false;
7867
+ else if (step.id !== selectedStepId) selectStepAtElement(step, event.currentTarget);
7211
7868
  };
7212
7869
  const onKeyDown = (event) => {
7213
- if (event.key === "Enter" || event.key === " ") handleStepInteraction(step, event, "keyboard");
7214
- else {
7215
- selectedBarRef.current = event.currentTarget;
7216
- handleBarKeyDown(step.id, event);
7870
+ isPointerFocusRef.current = false;
7871
+ if (handleNavigationKey(step.id, event)) return;
7872
+ if (event.key === "Enter" || event.key === " ") {
7873
+ event.preventDefault();
7874
+ selectStepAtElement(step, event.currentTarget);
7875
+ } else if (event.key === "Escape" && selectedStepId !== null) {
7876
+ event.preventDefault();
7877
+ clearSelection();
7217
7878
  }
7218
7879
  };
7880
+ const ref = (element) => setStepElement(step.id, element);
7219
7881
  handlers.set(step.id, {
7882
+ onPointerDown,
7220
7883
  onClick,
7221
- onKeyDown
7884
+ onKeyDown,
7885
+ onFocus,
7886
+ ref
7222
7887
  });
7223
7888
  });
7224
7889
  return handlers;
7225
7890
  }, [
7226
7891
  steps,
7227
- handleStepInteraction,
7228
- handleBarKeyDown
7892
+ selectedStepId,
7893
+ selectStepAt,
7894
+ selectStepAtElement,
7895
+ clearSelection,
7896
+ handleNavigationKey,
7897
+ handleStepFocus,
7898
+ setStepElement
7229
7899
  ]);
7230
- useEffect(() => {
7231
- const handleDocumentClick = (event) => {
7232
- if (selectedBarRef.current && !selectedBarRef.current.contains(event.target)) clearSelectionAndRef();
7233
- };
7234
- document.addEventListener("mousedown", handleDocumentClick);
7235
- return () => {
7236
- document.removeEventListener("mousedown", handleDocumentClick);
7237
- };
7238
- }, [clearSelectionAndRef]);
7900
+ const handleStepsBlur = useCallback((event) => {
7901
+ if (!event.currentTarget.contains(event.relatedTarget) && document.hasFocus()) {
7902
+ isPointerFocusRef.current = false;
7903
+ clearSelection();
7904
+ }
7905
+ }, [clearSelection]);
7239
7906
  const resolvedHeight = height ?? style?.height ?? "100%";
7240
7907
  const { color: barColor } = getElementStyles ? getElementStyles({ index: 0 }) : { color: "#000000" };
7241
7908
  const isPositiveChange = changeIndicator?.startsWith("+");
@@ -7247,18 +7914,41 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
7247
7914
  className: clsx(conversion_funnel_chart_module_default["change-indicator"], conversion_funnel_chart_module_default[isPositiveChange ? "change-indicator--positive" : "change-indicator--negative"]),
7248
7915
  children: changeIndicator
7249
7916
  })] });
7250
- const renderDefaultTooltip = (step) => /* @__PURE__ */ jsxs(Stack, {
7251
- direction: "column",
7252
- align: "flex-start",
7253
- gap: "xs",
7254
- children: [/* @__PURE__ */ jsx("div", {
7255
- className: conversion_funnel_chart_module_default["tooltip-title"],
7256
- children: step.label
7257
- }), /* @__PURE__ */ jsxs("div", {
7258
- className: conversion_funnel_chart_module_default["tooltip-content"],
7259
- children: [formatPercentage(step.rate), ` • ${step.count ?? "no"} items`]
7260
- })]
7261
- });
7917
+ const renderDefaultTooltip = (step, content) => {
7918
+ const ids = getDefaultTooltipIds(tooltipId, content);
7919
+ return /* @__PURE__ */ jsxs(Stack, {
7920
+ direction: "column",
7921
+ gap: "xs",
7922
+ children: [/* @__PURE__ */ jsxs(Stack, {
7923
+ direction: "row",
7924
+ justify: "space-between",
7925
+ align: "baseline",
7926
+ gap: "sm",
7927
+ children: [/* @__PURE__ */ jsx("span", {
7928
+ className: conversion_funnel_chart_module_default["tooltip-title"],
7929
+ children: step.label
7930
+ }), content.value && /* @__PURE__ */ jsx("span", {
7931
+ id: ids.value,
7932
+ className: conversion_funnel_chart_module_default["tooltip-value"],
7933
+ children: content.value
7934
+ })]
7935
+ }), content.rows?.map((row, index) => /* @__PURE__ */ jsxs(Stack, {
7936
+ direction: "row",
7937
+ justify: "space-between",
7938
+ align: "baseline",
7939
+ gap: "sm",
7940
+ children: [/* @__PURE__ */ jsx("span", {
7941
+ id: ids.rows[index].label,
7942
+ className: conversion_funnel_chart_module_default["tooltip-label"],
7943
+ children: row.label
7944
+ }), /* @__PURE__ */ jsx("span", {
7945
+ id: ids.rows[index].value,
7946
+ className: conversion_funnel_chart_module_default["tooltip-value"],
7947
+ children: row.value
7948
+ })]
7949
+ }, row.label))]
7950
+ });
7951
+ };
7262
7952
  const isDataValid = Boolean(steps && steps.length > 0);
7263
7953
  const chartMetadata = useMemo(() => ({
7264
7954
  mainRate,
@@ -7293,6 +7983,26 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
7293
7983
  })
7294
7984
  });
7295
7985
  const maxRate = Math.max(...steps.map((step) => step.rate));
7986
+ const tooltipStep = tooltipOpen ? tooltipData : void 0;
7987
+ let tooltipContent = null;
7988
+ let tooltipDescriptionIds = "";
7989
+ if (tooltipStep) {
7990
+ const index = steps.findIndex((s) => s.id === tooltipStep.id);
7991
+ const content = getTooltipContent(tooltipStep, index);
7992
+ if (renderTooltip) {
7993
+ tooltipContent = renderTooltip({
7994
+ step: tooltipStep,
7995
+ index,
7996
+ content,
7997
+ top: tooltipTop,
7998
+ left: tooltipLeft
7999
+ });
8000
+ tooltipDescriptionIds = tooltipId;
8001
+ } else {
8002
+ tooltipContent = renderDefaultTooltip(tooltipStep, content);
8003
+ tooltipDescriptionIds = getDefaultTooltipDescriptionIds(tooltipId, content);
8004
+ }
8005
+ }
7296
8006
  return /* @__PURE__ */ jsxs(Stack, {
7297
8007
  direction: "column",
7298
8008
  gap,
@@ -7321,10 +8031,16 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
7321
8031
  direction: "row",
7322
8032
  align: "flex-end",
7323
8033
  gap: "lg",
8034
+ role: "listbox",
8035
+ "aria-label": __("Funnel steps", "jetpack-charts"),
8036
+ "aria-orientation": "horizontal",
8037
+ onBlur: handleStepsBlur,
7324
8038
  className: conversion_funnel_chart_module_default["funnel-container"],
7325
8039
  children: steps.map((step, index) => {
7326
8040
  const barHeight = step.rate / maxRate * 100;
7327
- const { isBlurred } = getStepState(step.id);
8041
+ const { isSelected, isBlurred } = getStepState(step.id);
8042
+ const handlers = stepHandlers.get(step.id);
8043
+ const isDescribedByTooltip = Boolean(tooltipContent && tooltipDescriptionIds) && tooltipStep?.id === step.id;
7328
8044
  return /* @__PURE__ */ jsxs(Stack, {
7329
8045
  direction: "column",
7330
8046
  className: clsx(conversion_funnel_chart_module_default["funnel-step"], isColorPaletteResolved && conversion_funnel_chart_module_default["funnel-step--animated"], isBlurred && conversion_funnel_chart_module_default["funnel-step--blurred"]),
@@ -7332,6 +8048,8 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
7332
8048
  children: [/* @__PURE__ */ jsxs(Stack, {
7333
8049
  direction: "column",
7334
8050
  gap: "xs",
8051
+ className: conversion_funnel_chart_module_default["step-text"],
8052
+ "aria-hidden": "true",
7335
8053
  children: [renderStepLabel ? renderStepLabel({
7336
8054
  step,
7337
8055
  index,
@@ -7351,11 +8069,16 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
7351
8069
  direction: "column",
7352
8070
  justify: "flex-end",
7353
8071
  className: conversion_funnel_chart_module_default["bar-container"],
7354
- onClick: stepHandlers.get(step.id)?.onClick,
7355
- onKeyDown: stepHandlers.get(step.id)?.onKeyDown,
7356
- role: "button",
7357
- tabIndex: isBlurred ? -1 : 0,
7358
- "aria-label": step.label,
8072
+ ref: handlers?.ref,
8073
+ onPointerDown: handlers?.onPointerDown,
8074
+ onClick: handlers?.onClick,
8075
+ onKeyDown: handlers?.onKeyDown,
8076
+ onFocus: handlers?.onFocus,
8077
+ role: "option",
8078
+ tabIndex: getTabIndex(step.id),
8079
+ "aria-label": sprintf(__("%1$s, %2$s", "jetpack-charts"), step.label, formatPercentage(step.rate)),
8080
+ "aria-selected": isSelected,
8081
+ "aria-describedby": isDescribedByTooltip ? tooltipDescriptionIds : void 0,
7359
8082
  children: /* @__PURE__ */ jsx("div", {
7360
8083
  className: clsx(conversion_funnel_chart_module_default["funnel-bar"], { [conversion_funnel_chart_module_default["funnel-bar--animated"]]: animation && !loading && !prefersReducedMotion }),
7361
8084
  style: {
@@ -7367,25 +8090,17 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
7367
8090
  }, step.id);
7368
8091
  })
7369
8092
  })]
7370
- }), tooltipOpen && tooltipData && (() => {
7371
- const tooltipContent = renderTooltip ? renderTooltip({
7372
- step: tooltipData,
7373
- index: steps.findIndex((s) => s.id === tooltipData.id),
7374
- top: tooltipTop,
7375
- left: tooltipLeft
7376
- }) : renderDefaultTooltip(tooltipData);
7377
- if (!tooltipContent) return null;
7378
- return /* @__PURE__ */ jsx(BoundedTooltip, {
7379
- top: tooltipTop,
7380
- left: tooltipLeft,
7381
- className: standaloneScopeClass,
7382
- style: tooltipStyle,
7383
- children: /* @__PURE__ */ jsx("div", {
7384
- role: "tooltip",
7385
- children: tooltipContent
7386
- })
7387
- });
7388
- })()]
8093
+ }), tooltipContent && /* @__PURE__ */ jsx(BoundedTooltip, {
8094
+ top: tooltipTop,
8095
+ left: tooltipLeft,
8096
+ className: standaloneScopeClass,
8097
+ style: tooltipStyle,
8098
+ children: /* @__PURE__ */ jsx("div", {
8099
+ id: tooltipId,
8100
+ "aria-hidden": "true",
8101
+ children: tooltipContent
8102
+ })
8103
+ })]
7389
8104
  });
7390
8105
  };
7391
8106
  /**
@@ -7399,8 +8114,7 @@ const ConversionFunnelChartWithProvider = (props) => {
7399
8114
  return /* @__PURE__ */ jsx(GlobalChartsProvider, { children: /* @__PURE__ */ jsx(ConversionFunnelChartInternal, { ...props }) });
7400
8115
  };
7401
8116
  ConversionFunnelChartWithProvider.displayName = "ConversionFunnelChart";
7402
- //#endregion
7403
- //#region src/utils/sanitize-html.ts
8117
+ // src/utils/sanitize-html.ts
7404
8118
  /**
7405
8119
  * External dependencies
7406
8120
  */
@@ -7448,11 +8162,9 @@ function sanitizeHtml(html) {
7448
8162
  ]
7449
8163
  });
7450
8164
  }
7451
- //#endregion
7452
- //#region src/charts/geo-chart/geo-chart.module.scss
8165
+ // src/charts/geo-chart/geo-chart.module.scss
7453
8166
  var geo_chart_module_default = { "container": "a8ccharts-8hS2IW-container" };
7454
- //#endregion
7455
- //#region src/charts/geo-chart/private/use-tooltip-position.ts
8167
+ // src/charts/geo-chart/private/use-tooltip-position.ts
7456
8168
  const TOOLTIP_CLASS = "google-visualization-tooltip";
7457
8169
  const isTooltip = (node) => node instanceof HTMLElement && node.classList.contains(TOOLTIP_CLASS);
7458
8170
  /**
@@ -7536,8 +8248,7 @@ function useTooltipPosition(containerRef) {
7536
8248
  };
7537
8249
  }, [containerRef]);
7538
8250
  }
7539
- //#endregion
7540
- //#region src/charts/geo-chart/geo-chart.tsx
8251
+ // src/charts/geo-chart/geo-chart.tsx
7541
8252
  /**
7542
8253
  * External dependencies
7543
8254
  */
@@ -7757,8 +8468,7 @@ const GeoChartWithProvider = (props) => {
7757
8468
  };
7758
8469
  GeoChartWithProvider.displayName = "GeoChart";
7759
8470
  const GeoChartResponsive = withResponsive(GeoChartWithProvider);
7760
- //#endregion
7761
- //#region src/types.ts
8471
+ // src/types.ts
7762
8472
  let GoogleDataTableColumnRoleType = /* @__PURE__ */ function(GoogleDataTableColumnRoleType) {
7763
8473
  GoogleDataTableColumnRoleType["annotation"] = "annotation";
7764
8474
  GoogleDataTableColumnRoleType["annotationText"] = "annotationText";
@@ -7838,8 +8548,7 @@ const pickLabelTextColor = (fill, roles, defaultRole) => {
7838
8548
  if (!isValidHexColor(fillHex)) return defaultRole;
7839
8549
  return pickLabelTextColorForFill(hexToRgb(fillHex), roles, defaultRole);
7840
8550
  };
7841
- //#endregion
7842
- //#region src/charts/heatmap-chart/heatmap-chart.module.scss
8551
+ // src/charts/heatmap-chart/heatmap-chart.module.scss
7843
8552
  var heatmap_chart_module_default = {
7844
8553
  "heatmap-chart": "a8ccharts-O3YMOW-heatmap-chart",
7845
8554
  "heatmap-chart__cell": "a8ccharts-O3YMOW-heatmap-chart__cell",
@@ -7870,8 +8579,7 @@ var heatmap_chart_module_default = {
7870
8579
  "heatmap-chart__row-label": "a8ccharts-O3YMOW-heatmap-chart__row-label",
7871
8580
  "heatmap-chart--height-capped": "a8ccharts-O3YMOW-heatmap-chart--height-capped"
7872
8581
  };
7873
- //#endregion
7874
- //#region src/charts/heatmap-chart/private/use-heatmap-colors.ts
8582
+ // src/charts/heatmap-chart/private/use-heatmap-colors.ts
7875
8583
  const isPresent = (value) => value !== null && value !== void 0 && !isNaN(value);
7876
8584
  /**
7877
8585
  * Get the min and max values from heatmap data, ignoring null/NaN and the zeros
@@ -7950,12 +8658,10 @@ const getHeatmapScale = (primary, background, emptyCell) => {
7950
8658
  high
7951
8659
  };
7952
8660
  };
7953
- //#endregion
7954
- //#region src/charts/heatmap-chart/private/heatmap-context.ts
8661
+ // src/charts/heatmap-chart/private/heatmap-context.ts
7955
8662
  /** Shared by the chart and legend without importing back from `heatmap-chart.tsx`. */
7956
8663
  const HeatmapContext = createContext(null);
7957
- //#endregion
7958
- //#region src/charts/heatmap-chart/private/heatmap-legend.tsx
8664
+ // src/charts/heatmap-chart/private/heatmap-legend.tsx
7959
8665
  const HeatmapLegend = ({ steps = 5, variant = "swatches", lessLabel, moreLabel }) => {
7960
8666
  const context = useContext(HeatmapContext);
7961
8667
  const { legend } = useGlobalChartsTheme();
@@ -7999,8 +8705,7 @@ const HeatmapLegend = ({ steps = 5, variant = "swatches", lessLabel, moreLabel }
7999
8705
  ]
8000
8706
  });
8001
8707
  };
8002
- //#endregion
8003
- //#region src/charts/heatmap-chart/private/column-groups.ts
8708
+ // src/charts/heatmap-chart/private/column-groups.ts
8004
8709
  const isSpan = (span) => Number.isInteger(span) && span > 0;
8005
8710
  /**
8006
8711
  * Validates column groups and lays them over the columns.
@@ -8106,8 +8811,7 @@ const wrapColumnGroups = (layout, bands) => {
8106
8811
  slotSpans
8107
8812
  };
8108
8813
  };
8109
- //#endregion
8110
- //#region src/charts/heatmap-chart/private/fit-compact-cells.ts
8814
+ // src/charts/heatmap-chart/private/fit-compact-cells.ts
8111
8815
  /**
8112
8816
  * Largest whole-pixel square cell, and the band count giving it, for a compact grid in a box.
8113
8817
  *
@@ -8145,8 +8849,7 @@ const fitCompactCells = (input) => {
8145
8849
  cellSize: minCellSize
8146
8850
  };
8147
8851
  };
8148
- //#endregion
8149
- //#region src/charts/heatmap-chart/private/keyboard-navigation.ts
8852
+ // src/charts/heatmap-chart/private/keyboard-navigation.ts
8150
8853
  const ARROW_KEYS = [
8151
8854
  "ArrowLeft",
8152
8855
  "ArrowRight",
@@ -8276,8 +8979,7 @@ const stepCalendarCell = (grid, blocks, from, key) => {
8276
8979
  const before = page.slice(0, at === -1 ? page.length : at).reverse();
8277
8980
  return firstSelectable(grid, after) ?? firstSelectable(grid, before);
8278
8981
  };
8279
- //#endregion
8280
- //#region src/charts/heatmap-chart/heatmap-chart.tsx
8982
+ // src/charts/heatmap-chart/heatmap-chart.tsx
8281
8983
  const CELL_VALUE_MODIFIER = {
8282
8984
  label: void 0,
8283
8985
  "label-inverse": heatmap_chart_module_default["heatmap-chart__cell-value--inverse"],
@@ -8732,8 +9434,7 @@ const HeatmapChartResponsiveInner = (props) => /* @__PURE__ */ jsx(HeatmapChartW
8732
9434
  });
8733
9435
  HeatmapChartResponsiveInner.displayName = "HeatmapChart";
8734
9436
  const HeatmapChartResponsive = attachSubComponents(withResponsive(HeatmapChartResponsiveInner), { Legend: HeatmapLegend });
8735
- //#endregion
8736
- //#region src/charts/heatmap-chart/private/civil-day.ts
9437
+ // src/charts/heatmap-chart/private/civil-day.ts
8737
9438
  const DAY_MS = 864e5;
8738
9439
  const KEY_OPTIONS = {
8739
9440
  calendar: "gregory",
@@ -8882,8 +9583,7 @@ const startOfCivilWeek = (date, weekStartsOn) => {
8882
9583
  * @return Columns needed to cover the span, inclusive.
8883
9584
  */
8884
9585
  const civilWeekSpan = (from, to, weekStartsOn) => Math.floor((startOfCivilWeek(to, weekStartsOn).getTime() - startOfCivilWeek(from, weekStartsOn).getTime()) / (7 * DAY_MS)) + 1;
8885
- //#endregion
8886
- //#region src/charts/heatmap-chart/build-calendar-data.ts
9586
+ // src/charts/heatmap-chart/build-calendar-data.ts
8887
9587
  /** Rows that get a weekday label (Mon, Wed, Fri with a Monday week start). */
8888
9588
  const LABELLED_ROWS = [
8889
9589
  0,
@@ -8985,8 +9685,7 @@ const buildCalendarHeatmapData = (series, options = {}) => {
8985
9685
  rowLabels
8986
9686
  };
8987
9687
  };
8988
- //#endregion
8989
- //#region src/charts/heatmap-chart/build-month-calendar-data.ts
9688
+ // src/charts/heatmap-chart/build-month-calendar-data.ts
8990
9689
  /** Week rows per month, fixed so every month block is the same height. */
8991
9690
  const MONTH_ROWS = 6;
8992
9691
  const DAYS_PER_WEEK = 7;
@@ -9068,8 +9767,7 @@ const buildMonthCalendarHeatmapData = (valueByDay, range, options = {}) => {
9068
9767
  columnGroups
9069
9768
  };
9070
9769
  };
9071
- //#endregion
9072
- //#region src/charts/heatmap-chart/use-calendar-heatmap-data.ts
9770
+ // src/charts/heatmap-chart/use-calendar-heatmap-data.ts
9073
9771
  /**
9074
9772
  * `buildCalendarHeatmapData` with the locale and zone taken from
9075
9773
  * `GlobalChartsProvider` where the caller names neither.
@@ -9104,8 +9802,7 @@ const useCalendarHeatmapData = (series, options = {}) => {
9104
9802
  gridEnd
9105
9803
  ]);
9106
9804
  };
9107
- //#endregion
9108
- //#region src/charts/heatmap-chart/use-month-calendar-heatmap-data.ts
9805
+ // src/charts/heatmap-chart/use-month-calendar-heatmap-data.ts
9109
9806
  /**
9110
9807
  * `buildMonthCalendarHeatmapData` with the locale taken from `GlobalChartsProvider`
9111
9808
  * where the caller names none.
@@ -9134,8 +9831,7 @@ const useMonthCalendarHeatmapData = (valueByDay, range, options = {}) => {
9134
9831
  weekStartsOn
9135
9832
  ]);
9136
9833
  };
9137
- //#endregion
9138
- //#region ../../../node_modules/.pnpm/is-plain-object@5.1.0/node_modules/is-plain-object/dist/is-plain-object.mjs
9834
+ // ../../../node_modules/.pnpm/is-plain-object@5.1.0/node_modules/is-plain-object/dist/is-plain-object.mjs
9139
9835
  /*!
9140
9836
  * is-plain-object <https://github.com/jonschlinkert/is-plain-object>
9141
9837
  *
@@ -9155,16 +9851,14 @@ function isPlainObject(o) {
9155
9851
  if (prot.hasOwnProperty("isPrototypeOf") === false) return false;
9156
9852
  return true;
9157
9853
  }
9158
- //#endregion
9159
- //#region ../../../node_modules/.pnpm/lower-case@2.0.2/node_modules/lower-case/dist.es2015/index.js
9854
+ // ../../../node_modules/.pnpm/lower-case@2.0.2/node_modules/lower-case/dist.es2015/index.js
9160
9855
  /**
9161
9856
  * Lower case as a function.
9162
9857
  */
9163
9858
  function lowerCase(str) {
9164
9859
  return str.toLowerCase();
9165
9860
  }
9166
- //#endregion
9167
- //#region ../../../node_modules/.pnpm/no-case@3.0.4/node_modules/no-case/dist.es2015/index.js
9861
+ // ../../../node_modules/.pnpm/no-case@3.0.4/node_modules/no-case/dist.es2015/index.js
9168
9862
  var DEFAULT_SPLIT_REGEXP = [/([a-z0-9])([A-Z])/g, /([A-Z])([A-Z][a-z])/g];
9169
9863
  var DEFAULT_STRIP_REGEXP = /[^A-Z0-9]+/gi;
9170
9864
  /**
@@ -9189,23 +9883,19 @@ function replace$1(input, re, value) {
9189
9883
  return input.replace(re, value);
9190
9884
  }, input);
9191
9885
  }
9192
- //#endregion
9193
- //#region ../../../node_modules/.pnpm/dot-case@3.0.4/node_modules/dot-case/dist.es2015/index.js
9886
+ // ../../../node_modules/.pnpm/dot-case@3.0.4/node_modules/dot-case/dist.es2015/index.js
9194
9887
  function dotCase(input, options) {
9195
9888
  if (options === void 0) options = {};
9196
9889
  return noCase(input, __assign({ delimiter: "." }, options));
9197
9890
  }
9198
- //#endregion
9199
- //#region ../../../node_modules/.pnpm/param-case@3.0.4/node_modules/param-case/dist.es2015/index.js
9891
+ // ../../../node_modules/.pnpm/param-case@3.0.4/node_modules/param-case/dist.es2015/index.js
9200
9892
  function paramCase(input, options) {
9201
9893
  if (options === void 0) options = {};
9202
9894
  return dotCase(input, __assign({ delimiter: "-" }, options));
9203
9895
  }
9204
- //#endregion
9205
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.57.0/node_modules/@wordpress/warning/build-module/utils.mjs
9896
+ // ../../../node_modules/.pnpm/@wordpress+warning@3.57.0/node_modules/@wordpress/warning/build-module/utils.mjs
9206
9897
  var logged = /* @__PURE__ */ new Set();
9207
- //#endregion
9208
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.57.0/node_modules/@wordpress/warning/build-module/index.mjs
9898
+ // ../../../node_modules/.pnpm/@wordpress+warning@3.57.0/node_modules/@wordpress/warning/build-module/index.mjs
9209
9899
  function isDev() {
9210
9900
  return globalThis.SCRIPT_DEBUG === true;
9211
9901
  }
@@ -9218,8 +9908,7 @@ function warning(message) {
9218
9908
  } catch {}
9219
9909
  logged.add(message);
9220
9910
  }
9221
- //#endregion
9222
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
9911
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
9223
9912
  function useUpdateEffect(effect, deps) {
9224
9913
  const mountedRef = useRef$1(false);
9225
9914
  useEffect$1(() => {
@@ -9231,8 +9920,7 @@ function useUpdateEffect(effect, deps) {
9231
9920
  }, []);
9232
9921
  }
9233
9922
  var use_update_effect_default = useUpdateEffect;
9234
- //#endregion
9235
- //#region ../../../node_modules/.pnpm/@emotion+sheet@1.4.0/node_modules/@emotion/sheet/dist/emotion-sheet.esm.js
9923
+ // ../../../node_modules/.pnpm/@emotion+sheet@1.4.0/node_modules/@emotion/sheet/dist/emotion-sheet.esm.js
9236
9924
  function sheetForTag(tag) {
9237
9925
  if (tag.sheet) return tag.sheet;
9238
9926
  /* istanbul ignore next */
@@ -9294,8 +9982,7 @@ var StyleSheet = /*#__PURE__*/ function() {
9294
9982
  };
9295
9983
  return StyleSheet;
9296
9984
  }();
9297
- //#endregion
9298
- //#region ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Enum.js
9985
+ // ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Enum.js
9299
9986
  var MS = "-ms-";
9300
9987
  var MOZ = "-moz-";
9301
9988
  var WEBKIT = "-webkit-";
@@ -9305,8 +9992,7 @@ var DECLARATION = "decl";
9305
9992
  var IMPORT = "@import";
9306
9993
  var KEYFRAMES = "@keyframes";
9307
9994
  var LAYER = "@layer";
9308
- //#endregion
9309
- //#region ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Utility.js
9995
+ // ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Utility.js
9310
9996
  /**
9311
9997
  * @param {number}
9312
9998
  * @return {number}
@@ -9409,8 +10095,7 @@ function append(value, array) {
9409
10095
  function combine(array, callback) {
9410
10096
  return array.map(callback).join("");
9411
10097
  }
9412
- //#endregion
9413
- //#region ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Tokenizer.js
10098
+ // ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Tokenizer.js
9414
10099
  var line = 1;
9415
10100
  var column = 1;
9416
10101
  var length = 0;
@@ -9596,8 +10281,7 @@ function identifier(index) {
9596
10281
  while (!token(peek())) next();
9597
10282
  return slice(index, position);
9598
10283
  }
9599
- //#endregion
9600
- //#region ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Parser.js
10284
+ // ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Parser.js
9601
10285
  /**
9602
10286
  * @param {string} value
9603
10287
  * @return {object[]}
@@ -9750,8 +10434,7 @@ function comment(value, root, parent) {
9750
10434
  function declaration(value, root, parent, length) {
9751
10435
  return node(value, root, parent, DECLARATION, substr(value, 0, length), substr(value, length + 1, -1), length);
9752
10436
  }
9753
- //#endregion
9754
- //#region ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Serializer.js
10437
+ // ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Serializer.js
9755
10438
  /**
9756
10439
  * @param {object[]} children
9757
10440
  * @param {function} callback
@@ -9781,8 +10464,7 @@ function stringify(element, index, children, callback) {
9781
10464
  }
9782
10465
  return strlen(children = serialize(element.children, callback)) ? element.return = element.value + "{" + children + "}" : "";
9783
10466
  }
9784
- //#endregion
9785
- //#region ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Middleware.js
10467
+ // ../../../node_modules/.pnpm/stylis@4.2.0/node_modules/stylis/src/Middleware.js
9786
10468
  /**
9787
10469
  * @param {function[]} collection
9788
10470
  * @return {function}
@@ -9806,8 +10488,7 @@ function rulesheet(callback) {
9806
10488
  }
9807
10489
  };
9808
10490
  }
9809
- //#endregion
9810
- //#region ../../../node_modules/.pnpm/@emotion+memoize@0.9.0/node_modules/@emotion/memoize/dist/emotion-memoize.esm.js
10491
+ // ../../../node_modules/.pnpm/@emotion+memoize@0.9.0/node_modules/@emotion/memoize/dist/emotion-memoize.esm.js
9811
10492
  function memoize(fn) {
9812
10493
  var cache = Object.create(null);
9813
10494
  return function(arg) {
@@ -9815,8 +10496,7 @@ function memoize(fn) {
9815
10496
  return cache[arg];
9816
10497
  };
9817
10498
  }
9818
- //#endregion
9819
- //#region ../../../node_modules/.pnpm/@emotion+cache@11.14.0/node_modules/@emotion/cache/dist/emotion-cache.browser.esm.js
10499
+ // ../../../node_modules/.pnpm/@emotion+cache@11.14.0/node_modules/@emotion/cache/dist/emotion-cache.browser.esm.js
9820
10500
  var identifierWithPointTracking = function identifierWithPointTracking(begin, points, index) {
9821
10501
  var previous = 0;
9822
10502
  var character = 0;
@@ -10042,8 +10722,7 @@ var createCache = function createCache(options) {
10042
10722
  cache.sheet.hydrate(nodesToHydrate);
10043
10723
  return cache;
10044
10724
  };
10045
- //#endregion
10046
- //#region ../../../node_modules/.pnpm/@emotion+utils@1.4.2/node_modules/@emotion/utils/dist/emotion-utils.browser.esm.js
10725
+ // ../../../node_modules/.pnpm/@emotion+utils@1.4.2/node_modules/@emotion/utils/dist/emotion-utils.browser.esm.js
10047
10726
  function getRegisteredStyles$1(registered, registeredStyles, classNames) {
10048
10727
  var rawClassName = "";
10049
10728
  classNames.split(" ").forEach(function(className) {
@@ -10067,8 +10746,7 @@ var insertStyles = function insertStyles(cache, serialized, isStringTag) {
10067
10746
  } while (current !== void 0);
10068
10747
  }
10069
10748
  };
10070
- //#endregion
10071
- //#region ../../../node_modules/.pnpm/@emotion+hash@0.9.2/node_modules/@emotion/hash/dist/emotion-hash.esm.js
10749
+ // ../../../node_modules/.pnpm/@emotion+hash@0.9.2/node_modules/@emotion/hash/dist/emotion-hash.esm.js
10072
10750
  function murmur2(str) {
10073
10751
  var h = 0;
10074
10752
  var k, i = 0, len = str.length;
@@ -10089,8 +10767,7 @@ function murmur2(str) {
10089
10767
  h = (h & 65535) * 1540483477 + ((h >>> 16) * 59797 << 16);
10090
10768
  return ((h ^ h >>> 15) >>> 0).toString(36);
10091
10769
  }
10092
- //#endregion
10093
- //#region ../../../node_modules/.pnpm/@emotion+unitless@0.10.0/node_modules/@emotion/unitless/dist/emotion-unitless.esm.js
10770
+ // ../../../node_modules/.pnpm/@emotion+unitless@0.10.0/node_modules/@emotion/unitless/dist/emotion-unitless.esm.js
10094
10771
  var unitlessKeys = {
10095
10772
  animationIterationCount: 1,
10096
10773
  aspectRatio: 1,
@@ -10140,8 +10817,7 @@ var unitlessKeys = {
10140
10817
  strokeOpacity: 1,
10141
10818
  strokeWidth: 1
10142
10819
  };
10143
- //#endregion
10144
- //#region ../../../node_modules/.pnpm/@emotion+serialize@1.3.3/node_modules/@emotion/serialize/dist/emotion-serialize.esm.js
10820
+ // ../../../node_modules/.pnpm/@emotion+serialize@1.3.3/node_modules/@emotion/serialize/dist/emotion-serialize.esm.js
10145
10821
  var isDevelopment = false;
10146
10822
  var hyphenateRegex = /[A-Z]|^ms/g;
10147
10823
  var animationRegex = /_EMO_([^_]+?)_([^]*?)_EMO_/g;
@@ -10266,16 +10942,14 @@ function serializeStyles(args, registered, mergedProps) {
10266
10942
  };
10267
10943
  }
10268
10944
  React["useInsertionEffect"] && React["useInsertionEffect"] || React.useLayoutEffect;
10269
- //#endregion
10270
- //#region ../../../node_modules/.pnpm/@emotion+react@11.14.0_@types+react@18.3.31_react@18.3.1/node_modules/@emotion/react/dist/emotion-element-f0de968e.browser.esm.js
10945
+ // ../../../node_modules/.pnpm/@emotion+react@11.14.0_@types+react@19.3.0_react@19.3.0/node_modules/@emotion/react/dist/emotion-element-f0de968e.browser.esm.js
10271
10946
  var EmotionCacheContext = /* #__PURE__ */ React.createContext(typeof HTMLElement !== "undefined" ? /* #__PURE__ */ createCache({ key: "css" }) : null);
10272
10947
  EmotionCacheContext.Provider;
10273
10948
  var __unsafe_useEmotionCache = function useEmotionCache() {
10274
10949
  return useContext(EmotionCacheContext);
10275
10950
  };
10276
10951
  ({}).hasOwnProperty;
10277
- //#endregion
10278
- //#region ../../../node_modules/.pnpm/@emotion+css@11.13.5/node_modules/@emotion/css/create-instance/dist/emotion-css-create-instance.esm.js
10952
+ // ../../../node_modules/.pnpm/@emotion+css@11.13.5/node_modules/@emotion/css/create-instance/dist/emotion-css-create-instance.esm.js
10279
10953
  function insertWithoutScoping(cache, serialized) {
10280
10954
  if (cache.inserted[serialized.name] === void 0) return cache.insert("", serialized, cache.sheet, true);
10281
10955
  }
@@ -10360,8 +11034,7 @@ var classnames = function classnames(args) {
10360
11034
  }
10361
11035
  return cls;
10362
11036
  };
10363
- //#endregion
10364
- //#region ../../../node_modules/.pnpm/@emotion+css@11.13.5/node_modules/@emotion/css/dist/emotion-css.esm.js
11037
+ // ../../../node_modules/.pnpm/@emotion+css@11.13.5/node_modules/@emotion/css/dist/emotion-css.esm.js
10365
11038
  var _createEmotion = createEmotion({ key: "css" });
10366
11039
  _createEmotion.flush;
10367
11040
  _createEmotion.hydrate;
@@ -10373,8 +11046,7 @@ _createEmotion.keyframes;
10373
11046
  _createEmotion.css;
10374
11047
  _createEmotion.sheet;
10375
11048
  _createEmotion.cache;
10376
- //#endregion
10377
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
11049
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
10378
11050
  var isSerializedStyles = (o) => typeof o !== "undefined" && o !== null && ["name", "styles"].every((p) => typeof o[p] !== "undefined");
10379
11051
  var useCx = () => {
10380
11052
  const cache = __unsafe_useEmotionCache();
@@ -10389,8 +11061,7 @@ var useCx = () => {
10389
11061
  }));
10390
11062
  }, [cache]);
10391
11063
  };
10392
- //#endregion
10393
- //#region ../../../node_modules/.pnpm/memize@2.1.1/node_modules/memize/dist/index.js
11064
+ // ../../../node_modules/.pnpm/memize@2.1.1/node_modules/memize/dist/index.js
10394
11065
  /**
10395
11066
  * Memize options object.
10396
11067
  *
@@ -10480,8 +11151,7 @@ function memize(fn, options) {
10480
11151
  };
10481
11152
  return memoized;
10482
11153
  }
10483
- //#endregion
10484
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
11154
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
10485
11155
  var import_es6 = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
10486
11156
  module.exports = function equal(a, b) {
10487
11157
  if (a === b) return true;
@@ -10548,19 +11218,16 @@ var BaseContextSystemProvider = ({ children, value }) => {
10548
11218
  });
10549
11219
  };
10550
11220
  memo(BaseContextSystemProvider);
10551
- //#endregion
10552
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/constants.mjs
11221
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/constants.mjs
10553
11222
  var COMPONENT_NAMESPACE = "data-wp-component";
10554
11223
  var CONNECTED_NAMESPACE = "data-wp-c16t";
10555
11224
  var CONNECT_STATIC_NAMESPACE = "__contextSystemKey__";
10556
- //#endregion
10557
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
11225
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
10558
11226
  function getStyledClassName(namespace) {
10559
11227
  return `components-${paramCase(namespace)}`;
10560
11228
  }
10561
11229
  var getStyledClassNameFromKey = memize(getStyledClassName);
10562
- //#endregion
10563
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/context-connect.mjs
11230
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/context-connect.mjs
10564
11231
  function contextConnect(Component, namespace) {
10565
11232
  return _contextConnect(Component, namespace, { forwardsRef: true });
10566
11233
  }
@@ -10576,16 +11243,14 @@ function _contextConnect(Component, namespace, options) {
10576
11243
  selector: `.${getStyledClassNameFromKey(namespace)}`
10577
11244
  });
10578
11245
  }
10579
- //#endregion
10580
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/utils.mjs
11246
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/utils.mjs
10581
11247
  function getNamespace(componentName) {
10582
11248
  return { [COMPONENT_NAMESPACE]: componentName };
10583
11249
  }
10584
11250
  function getConnectedNamespace() {
10585
11251
  return { [CONNECTED_NAMESPACE]: true };
10586
11252
  }
10587
- //#endregion
10588
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
11253
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
10589
11254
  function useContextSystem(props, namespace) {
10590
11255
  const contextSystemProps = useComponentsContext();
10591
11256
  if (typeof namespace === "undefined") globalThis.SCRIPT_DEBUG === true && warning("useContextSystem: Please provide a namespace");
@@ -10606,8 +11271,7 @@ function useContextSystem(props, namespace) {
10606
11271
  finalComponentProps.className = classes;
10607
11272
  return finalComponentProps;
10608
11273
  }
10609
- //#endregion
10610
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
11274
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
10611
11275
  var visuallyHidden = {
10612
11276
  border: 0,
10613
11277
  clip: "rect(1px, 1px, 1px, 1px)",
@@ -10622,8 +11286,7 @@ var visuallyHidden = {
10622
11286
  wordWrap: "normal",
10623
11287
  wordBreak: "normal"
10624
11288
  };
10625
- //#endregion
10626
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
11289
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
10627
11290
  var customAttributeRegExp = /^(data|aria|x)-/i;
10628
11291
  var eventHandlerRegExp = /^on[A-Z]/;
10629
11292
  var svgElementNames = new Set(`animate animateMotion animateTransform circle clipPath defs desc ellipse
@@ -10712,8 +11375,7 @@ function UnforwardedPolymorphicElement({ as, ...props }, ref) {
10712
11375
  });
10713
11376
  }
10714
11377
  var PolymorphicElement = forwardRef$1(UnforwardedPolymorphicElement);
10715
- //#endregion
10716
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/view/component.mjs
11378
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/view/component.mjs
10717
11379
  function UnforwardedView({ css, ...restProps }, ref) {
10718
11380
  return /* @__PURE__ */ jsx(PolymorphicElement, {
10719
11381
  ref,
@@ -10721,8 +11383,7 @@ function UnforwardedView({ css, ...restProps }, ref) {
10721
11383
  });
10722
11384
  }
10723
11385
  var component_default$2 = Object.assign(forwardRef$1(UnforwardedView), { selector: ".components-view" });
10724
- //#endregion
10725
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
11386
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
10726
11387
  function UnconnectedVisuallyHidden(props, forwardedRef) {
10727
11388
  const { style: styleProp, ...contextProps } = useContextSystem(props, "VisuallyHidden");
10728
11389
  return /* @__PURE__ */ jsx(component_default$2, {
@@ -10736,8 +11397,7 @@ function UnconnectedVisuallyHidden(props, forwardedRef) {
10736
11397
  });
10737
11398
  }
10738
11399
  var component_default$1 = contextConnect(UnconnectedVisuallyHidden, "VisuallyHidden");
10739
- //#endregion
10740
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
11400
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
10741
11401
  var breakpoints = [
10742
11402
  "40em",
10743
11403
  "52em",
@@ -10775,8 +11435,7 @@ function useResponsiveValue(values, options = {}) {
10775
11435
  const array = values || [];
10776
11436
  return array[index >= array.length ? array.length - 1 : index];
10777
11437
  }
10778
- //#endregion
10779
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/grid/utils.mjs
11438
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/grid/utils.mjs
10780
11439
  var ALIGNMENTS = {
10781
11440
  bottom: {
10782
11441
  alignItems: "flex-end",
@@ -10823,8 +11482,7 @@ var ALIGNMENTS = {
10823
11482
  function getAlignmentProps(alignment) {
10824
11483
  return alignment ? ALIGNMENTS[alignment] ?? {} : {};
10825
11484
  }
10826
- //#endregion
10827
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/grid/hook.mjs
11485
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/grid/hook.mjs
10828
11486
  var STYLE_HASH_ATTRIBUTE = "data-wp-hash";
10829
11487
  function getRuntime() {
10830
11488
  const globalScope = globalThis;
@@ -10968,8 +11626,7 @@ function useGrid(props) {
10968
11626
  }
10969
11627
  };
10970
11628
  }
10971
- //#endregion
10972
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/grid/component.mjs
11629
+ // ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/grid/component.mjs
10973
11630
  function UnconnectedGrid(props, forwardedRef) {
10974
11631
  const gridProps = useGrid(props);
10975
11632
  return /* @__PURE__ */ jsx(component_default$2, {
@@ -10978,8 +11635,7 @@ function UnconnectedGrid(props, forwardedRef) {
10978
11635
  });
10979
11636
  }
10980
11637
  var component_default = contextConnect(UnconnectedGrid, "Grid");
10981
- //#endregion
10982
- //#region src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts
11638
+ // src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts
10983
11639
  /**
10984
11640
  * Fractional layout rounding can put a row's bottom a hair past the container's
10985
11641
  * without any visible clipping. Allow that much slack before hiding a row.
@@ -11062,8 +11718,7 @@ function useFittedRowCount(enabled, rowCount, data) {
11062
11718
  isMeasurable
11063
11719
  };
11064
11720
  }
11065
- //#endregion
11066
- //#region src/charts/leaderboard-chart/hooks/use-leaderboard-legend-items.ts
11721
+ // src/charts/leaderboard-chart/hooks/use-leaderboard-legend-items.ts
11067
11722
  /**
11068
11723
  * Hook to create legend items from leaderboard data
11069
11724
  * @param root0 - Configuration object
@@ -11111,11 +11766,12 @@ function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withCom
11111
11766
  withOverlayLabel
11112
11767
  ]);
11113
11768
  }
11114
- //#endregion
11115
- //#region src/charts/leaderboard-chart/leaderboard-chart.module.scss
11769
+ // src/charts/leaderboard-chart/leaderboard-chart.module.scss
11116
11770
  var leaderboard_chart_module_default = {
11117
11771
  "bar": "a8ccharts-GovfoW-bar",
11118
11772
  "bar--animated": "a8ccharts-GovfoW-bar--animated",
11773
+ "bar--negative": "a8ccharts-GovfoW-bar--negative",
11774
+ "barTrack": "a8ccharts-GovfoW-barTrack",
11119
11775
  "barWithLabelContainer": "a8ccharts-GovfoW-barWithLabelContainer",
11120
11776
  "chevron": "a8ccharts-GovfoW-chevron",
11121
11777
  "deltaPlaceholder": "a8ccharts-GovfoW-deltaPlaceholder",
@@ -11142,8 +11798,7 @@ var leaderboard_chart_module_default = {
11142
11798
  "stretch": "a8ccharts-GovfoW-stretch",
11143
11799
  "valueContainer": "a8ccharts-GovfoW-valueContainer"
11144
11800
  };
11145
- //#endregion
11146
- //#region src/charts/leaderboard-chart/leaderboard-chart.tsx
11801
+ // src/charts/leaderboard-chart/leaderboard-chart.tsx
11147
11802
  const ROW_GAP = "var(--a8c-charts-dimension-leaderboard-row-gap, 12px)";
11148
11803
  const COLUMN_GAP = "var(--a8c-charts-dimension-leaderboard-column-gap, 4px)";
11149
11804
  /** Indexed by `Math.sign( delta ) + 1`, so [negative, neutral, positive]. */
@@ -11203,27 +11858,43 @@ const BarLabel = ({ label }) => /* @__PURE__ */ jsx(Fragment$1, { children: type
11203
11858
  className: leaderboard_chart_module_default.label,
11204
11859
  children: label
11205
11860
  }) : label });
11206
- const BarWithLabel = ({ entry, withComparison, withOverlayLabel, primaryColor, secondaryColor, animation, isPrimaryVisible = true, isComparisonVisible = true }) => {
11861
+ const Bar = ({ share, color, animation, divergingScale }) => {
11862
+ const isNegative = share < 0;
11863
+ const sideMax = divergingScale?.[isNegative ? "negative" : "positive"];
11864
+ const width = sideMax ? Math.abs(share) / sideMax * 100 : Math.abs(share);
11865
+ const bar = /* @__PURE__ */ jsx("div", {
11866
+ className: clsx(leaderboard_chart_module_default.bar, {
11867
+ [leaderboard_chart_module_default["bar--animated"]]: animation,
11868
+ [leaderboard_chart_module_default["bar--negative"]]: divergingScale && isNegative
11869
+ }),
11870
+ style: {
11871
+ width: getBarWidth(width),
11872
+ backgroundColor: color
11873
+ }
11874
+ });
11875
+ if (!divergingScale) return bar;
11876
+ return /* @__PURE__ */ jsx("div", {
11877
+ className: leaderboard_chart_module_default.barTrack,
11878
+ style: { gridTemplateColumns: `${divergingScale.negative}fr ${divergingScale.positive}fr` },
11879
+ children: bar
11880
+ });
11881
+ };
11882
+ const BarWithLabel = ({ entry, withComparison, withOverlayLabel, primaryColor, secondaryColor, animation, divergingScale, isPrimaryVisible = true, isComparisonVisible = true }) => {
11207
11883
  const showComparisonBar = withComparison && !withOverlayLabel && isComparisonVisible;
11884
+ const bars = /* @__PURE__ */ jsxs(Fragment$1, { children: [isPrimaryVisible && /* @__PURE__ */ jsx(Bar, {
11885
+ share: entry.currentShare,
11886
+ color: primaryColor,
11887
+ animation,
11888
+ divergingScale
11889
+ }), showComparisonBar && hasPreviousValue(entry) && /* @__PURE__ */ jsx(Bar, {
11890
+ share: entry.previousShare,
11891
+ color: secondaryColor,
11892
+ animation,
11893
+ divergingScale
11894
+ })] });
11208
11895
  return /* @__PURE__ */ jsxs("div", {
11209
11896
  className: clsx(leaderboard_chart_module_default.barWithLabelContainer, { [leaderboard_chart_module_default["is-overlay"]]: withOverlayLabel }),
11210
- children: [
11211
- /* @__PURE__ */ jsx(BarLabel, { label: entry.label }),
11212
- isPrimaryVisible && /* @__PURE__ */ jsx("div", {
11213
- className: clsx(leaderboard_chart_module_default.bar, { [leaderboard_chart_module_default["bar--animated"]]: animation }),
11214
- style: {
11215
- width: getBarWidth(entry.currentShare),
11216
- backgroundColor: primaryColor
11217
- }
11218
- }),
11219
- showComparisonBar && hasPreviousValue(entry) && /* @__PURE__ */ jsx("div", {
11220
- className: clsx(leaderboard_chart_module_default.bar, { [leaderboard_chart_module_default["bar--animated"]]: animation }),
11221
- style: {
11222
- width: getBarWidth(entry.previousShare),
11223
- backgroundColor: secondaryColor
11224
- }
11225
- })
11226
- ]
11897
+ children: [/* @__PURE__ */ jsx(BarLabel, { label: entry.label }), bars]
11227
11898
  });
11228
11899
  };
11229
11900
  /**
@@ -11308,6 +11979,26 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
11308
11979
  withComparison,
11309
11980
  withOverlayLabel
11310
11981
  ]);
11982
+ const divergingScale = useMemo(() => {
11983
+ const withComparisonBars = withComparison && !withOverlayLabel;
11984
+ const scale = {
11985
+ negative: 0,
11986
+ positive: 0
11987
+ };
11988
+ for (const entry of data ?? []) {
11989
+ const shares = [entry.currentShare];
11990
+ if (withComparisonBars && hasPreviousValue(entry)) shares.push(entry.previousShare);
11991
+ for (const share of shares) {
11992
+ const side = share < 0 ? "negative" : "positive";
11993
+ scale[side] = Math.max(scale[side], Math.abs(share));
11994
+ }
11995
+ }
11996
+ return scale.negative > 0 ? scale : void 0;
11997
+ }, [
11998
+ data,
11999
+ withComparison,
12000
+ withOverlayLabel
12001
+ ]);
11311
12002
  const isDataValid = Boolean(data && data.length > 0);
11312
12003
  const chartMetadata = useMemo(() => ({
11313
12004
  withComparison,
@@ -11323,6 +12014,8 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
11323
12014
  const prefersReducedMotion = usePrefersReducedMotion();
11324
12015
  const { contentRef, fittedCount, isMeasurable } = useFittedRowCount(fitRows && !allSeriesHidden, data?.length ?? 0, data);
11325
12016
  const shouldFitRows = fitRows && isMeasurable;
12017
+ const showComparisonColumn = withComparison && isComparisonVisible;
12018
+ const templateColumns = showComparisonColumn && isPrimaryVisible ? "minmax(0, 1fr) auto auto" : "minmax(0, 1fr) auto";
11326
12019
  if (!data || data.length === 0) return /* @__PURE__ */ jsx(ChartInstanceContext.Provider, {
11327
12020
  value: { chartId },
11328
12021
  children: /* @__PURE__ */ jsx(ChartLayout, {
@@ -11387,13 +12080,12 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
11387
12080
  }) : /* @__PURE__ */ jsx(component_default, {
11388
12081
  role: "list",
11389
12082
  "aria-label": ariaLabel ?? __("Leaderboard chart", "jetpack-charts"),
11390
- templateColumns: "minmax(0, 1fr) auto",
12083
+ templateColumns,
11391
12084
  rowGap: ROW_GAP,
11392
12085
  columnGap: COLUMN_GAP,
11393
12086
  "data-leaderboard-grid": true,
11394
12087
  children: data.map((entry, rowIndex) => {
11395
12088
  const rowStyle = shouldFitRows && rowIndex >= fittedCount ? { visibility: "hidden" } : void 0;
11396
- const showComparisonColumn = withComparison && isComparisonVisible;
11397
12089
  const hasPreviousPeriodValue = hasPreviousValue(entry);
11398
12090
  const hasDelta = hasDeltaValue(entry);
11399
12091
  const showComparisonValue = showComparisonColumn && hasDelta;
@@ -11408,13 +12100,12 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
11408
12100
  withOverlayLabel,
11409
12101
  primaryColor: resolvedPrimaryColor,
11410
12102
  secondaryColor: resolvedSecondaryColor,
12103
+ divergingScale,
11411
12104
  isPrimaryVisible,
11412
12105
  isComparisonVisible,
11413
12106
  animation: animation && !loading && !prefersReducedMotion
11414
12107
  })
11415
- }), /* @__PURE__ */ jsxs(Stack, {
11416
- direction: "row",
11417
- gap: "xs",
12108
+ }), /* @__PURE__ */ jsxs("div", {
11418
12109
  className: clsx(leaderboard_chart_module_default.valueContainer, { [leaderboard_chart_module_default.overlayLabel]: withOverlayLabel }),
11419
12110
  children: [
11420
12111
  isPrimaryVisible && /* @__PURE__ */ jsx(Text$1, { children: valueFormatter(entry.currentValue) }),
@@ -11495,8 +12186,7 @@ const LeaderboardChartWithProvider = (props) => {
11495
12186
  LeaderboardChartWithProvider.displayName = "LeaderboardChart";
11496
12187
  const LeaderboardChart = attachSubComponents(LeaderboardChartWithProvider, { Legend });
11497
12188
  const LeaderboardChartResponsive = attachSubComponents(withResponsive(LeaderboardChartWithProvider), { Legend });
11498
- //#endregion
11499
- //#region src/charts/private/pie-keyboard-navigation/order-arcs-for-navigation.ts
12189
+ // src/charts/private/pie-keyboard-navigation/order-arcs-for-navigation.ts
11500
12190
  /**
11501
12191
  * Orders arcs the way they appear: clockwise from 12 o'clock, which for a semi-circle is left to right.
11502
12192
  *
@@ -11504,15 +12194,13 @@ const LeaderboardChartResponsive = attachSubComponents(withResponsive(Leaderboar
11504
12194
  * @return A copy of `arcs` sorted by angular midpoint.
11505
12195
  */
11506
12196
  const orderArcsForNavigation = (arcs) => [...arcs].sort((a, b) => a.startAngle + a.endAngle - (b.startAngle + b.endAngle));
11507
- //#endregion
11508
- //#region src/charts/private/pie-keyboard-navigation/pie-selection-announcement.tsx
12197
+ // src/charts/private/pie-keyboard-navigation/pie-selection-announcement.tsx
11509
12198
  const PieSelectionAnnouncement = ({ data }) => /* @__PURE__ */ jsx(VisuallyHidden, {
11510
12199
  role: "status",
11511
12200
  "aria-atomic": "true",
11512
12201
  children: data && /* @__PURE__ */ jsx(LabelValueContent, { data })
11513
12202
  });
11514
- //#endregion
11515
- //#region src/charts/private/pie-keyboard-navigation/pie-tooltip.tsx
12203
+ // src/charts/private/pie-keyboard-navigation/pie-tooltip.tsx
11516
12204
  const TOOLTIP_BOX_STYLE = { zIndex: 3 };
11517
12205
  const PieTooltip = ({ top, left, selectedIndex, tooltipRef, children }) => {
11518
12206
  const standaloneScopeClass = useStandaloneScopeClass();
@@ -11534,8 +12222,7 @@ const PieTooltip = ({ top, left, selectedIndex, tooltipRef, children }) => {
11534
12222
  }, `chart-tooltip-${selectedIndex}`)
11535
12223
  });
11536
12224
  };
11537
- //#endregion
11538
- //#region src/charts/private/pie-keyboard-navigation/pie-selection-output.tsx
12225
+ // src/charts/private/pie-keyboard-navigation/pie-selection-output.tsx
11539
12226
  /**
11540
12227
  * Reports the selected segment: a focused tooltip for a keyboard selection, a pointer tooltip on hover, or a screen reader announcement without tooltips.
11541
12228
  *
@@ -11575,53 +12262,7 @@ const PieSelectionOutput = ({ withTooltips, selectedIndex, selectedData, keyboar
11575
12262
  })
11576
12263
  ] });
11577
12264
  };
11578
- //#endregion
11579
- //#region src/charts/private/pie-keyboard-navigation/pie-keyboard-navigation.module.scss
11580
- var pie_keyboard_navigation_module_default = {
11581
- "selected-ring__contrast": "a8ccharts-V2HDKq-selected-ring__contrast",
11582
- "selected-ring__focus": "a8ccharts-V2HDKq-selected-ring__focus"
11583
- };
11584
- //#endregion
11585
- //#region src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx
11586
- /**
11587
- * Encodes `value` for an SVG id, losslessly so distinct chart ids never share a clip path.
11588
- *
11589
- * @param value - Any string.
11590
- * @return `value` with each character outside `[A-Za-z0-9-]` written as `_<hex code point>_`.
11591
- */
11592
- const toSvgId = (value) => Array.from(value, (character) => /[A-Za-z0-9-]/.test(character) ? character : `_${character.codePointAt(0)?.toString(16)}_`).join("");
11593
- /**
11594
- * Marks the selected segment with a two-tone ring drawn inside its edge, so the SVG edge cannot cut it off.
11595
- *
11596
- * @param {SelectedSegmentRingProps} props - Component props
11597
- * @return {JSX.Element} The ring
11598
- */
11599
- const SelectedSegmentRing = ({ chartId, d }) => {
11600
- const clipId = `pie-selected-ring-clip-${toSvgId(chartId)}`;
11601
- const clipPath = `url(#${clipId})`;
11602
- return /* @__PURE__ */ jsxs("g", {
11603
- pointerEvents: "none",
11604
- "aria-hidden": "true",
11605
- children: [
11606
- /* @__PURE__ */ jsx("clipPath", {
11607
- id: clipId,
11608
- children: /* @__PURE__ */ jsx("path", { d })
11609
- }),
11610
- /* @__PURE__ */ jsx("path", {
11611
- d,
11612
- clipPath,
11613
- className: pie_keyboard_navigation_module_default["selected-ring__contrast"]
11614
- }),
11615
- /* @__PURE__ */ jsx("path", {
11616
- d,
11617
- clipPath,
11618
- className: pie_keyboard_navigation_module_default["selected-ring__focus"]
11619
- })
11620
- ]
11621
- });
11622
- };
11623
- //#endregion
11624
- //#region src/charts/private/pie-keyboard-navigation/use-pie-keyboard-navigation.ts
12265
+ // src/charts/private/pie-keyboard-navigation/use-pie-keyboard-navigation.ts
11625
12266
  const usePieKeyboardNavigation = ({ navigationArcs, withTooltips, tooltipOffsetX, tooltipOffsetY }) => {
11626
12267
  const chartRef = useRef(null);
11627
12268
  const svgRef = useRef(null);
@@ -11701,8 +12342,7 @@ const usePieKeyboardNavigation = ({ navigationArcs, withTooltips, tooltipOffsetX
11701
12342
  }
11702
12343
  };
11703
12344
  };
11704
- //#endregion
11705
- //#region src/charts/private/radial-wipe-animation/radial-wipe-animation.tsx
12345
+ // src/charts/private/radial-wipe-animation/radial-wipe-animation.tsx
11706
12346
  /**
11707
12347
  * Renders a SVG mask that creates a radial wipe animation effect.
11708
12348
  *
@@ -11739,8 +12379,7 @@ function RadialWipeAnimation({ id, radius, innerRadius = 0, durationMs = 1e3, wi
11739
12379
  })
11740
12380
  });
11741
12381
  }
11742
- //#endregion
11743
- //#region src/charts/pie-chart/pie-chart.module.scss
12382
+ // src/charts/pie-chart/pie-chart.module.scss
11744
12383
  var pie_chart_module_default = {
11745
12384
  "pie-chart": "a8ccharts-gnszbG-pie-chart",
11746
12385
  "pie-chart__label-plate": "a8ccharts-gnszbG-pie-chart__label-plate",
@@ -11751,8 +12390,7 @@ var pie_chart_module_default = {
11751
12390
  "pie-chart__plot": "a8ccharts-gnszbG-pie-chart__plot",
11752
12391
  "pie-chart--responsive": "a8ccharts-gnszbG-pie-chart--responsive"
11753
12392
  };
11754
- //#endregion
11755
- //#region src/charts/pie-chart/pie-chart.tsx
12393
+ // src/charts/pie-chart/pie-chart.tsx
11756
12394
  /**
11757
12395
  * Default tooltip renderer for pie charts.
11758
12396
  * Renders the default `label: value` tooltip content for the hovered segment.
@@ -11996,9 +12634,11 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
11996
12634
  selectedArc && /* @__PURE__ */ jsx(Group, {
11997
12635
  top: centerY,
11998
12636
  left: centerX,
11999
- children: /* @__PURE__ */ jsx(SelectedSegmentRing, {
12637
+ children: /* @__PURE__ */ jsx(SelectedRing, {
12000
12638
  chartId,
12001
- d: path(selectedArc) || ""
12639
+ d: path(selectedArc) || "",
12640
+ side: "inside",
12641
+ testId: "pie-selected-ring"
12002
12642
  })
12003
12643
  }),
12004
12644
  !allSegmentsHidden && /* @__PURE__ */ jsxs(Group, {
@@ -12033,8 +12673,7 @@ const PieChartResponsive = attachSubComponents(withResponsive(PieChartWithProvid
12033
12673
  SVG: ChartSVG,
12034
12674
  HTML: ChartHTML
12035
12675
  });
12036
- //#endregion
12037
- //#region src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss
12676
+ // src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss
12038
12677
  var pie_semi_circle_chart_module_default = {
12039
12678
  "label": "a8ccharts-YtTOxW-label",
12040
12679
  "note": "a8ccharts-YtTOxW-note",
@@ -12042,8 +12681,7 @@ var pie_semi_circle_chart_module_default = {
12042
12681
  "pie-semi-circle-chart__plot": "a8ccharts-YtTOxW-pie-semi-circle-chart__plot",
12043
12682
  "pie-semi-circle-chart--responsive": "a8ccharts-YtTOxW-pie-semi-circle-chart--responsive"
12044
12683
  };
12045
- //#endregion
12046
- //#region src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx
12684
+ // src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx
12047
12685
  /**
12048
12686
  * Default tooltip renderer for semi-circle pie charts.
12049
12687
  * Renders the default `label: value` tooltip content for the hovered segment.
@@ -12235,9 +12873,11 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
12235
12873
  selectedArc && /* @__PURE__ */ jsx(Group, {
12236
12874
  top: height,
12237
12875
  left: width / 2,
12238
- children: /* @__PURE__ */ jsx(SelectedSegmentRing, {
12876
+ children: /* @__PURE__ */ jsx(SelectedRing, {
12239
12877
  chartId,
12240
- d: path(selectedArc) || ""
12878
+ d: path(selectedArc) || "",
12879
+ side: "inside",
12880
+ testId: "pie-selected-ring"
12241
12881
  })
12242
12882
  }),
12243
12883
  !allSegmentsHidden && /* @__PURE__ */ jsxs(Group, {
@@ -12284,14 +12924,12 @@ const PieSemiCircleChartResponsive = attachSubComponents(withResponsive(PieSemiC
12284
12924
  SVG: ChartSVG,
12285
12925
  HTML: ChartHTML
12286
12926
  });
12287
- //#endregion
12288
- //#region src/charts/sparkline/sparkline.module.scss
12927
+ // src/charts/sparkline/sparkline.module.scss
12289
12928
  var sparkline_module_default = {
12290
12929
  "sparkline": "a8ccharts-GtgAVa-sparkline",
12291
12930
  "sparkline--empty": "a8ccharts-GtgAVa-sparkline--empty"
12292
12931
  };
12293
- //#endregion
12294
- //#region src/charts/sparkline/sparkline.tsx
12932
+ // src/charts/sparkline/sparkline.tsx
12295
12933
  const DEFAULT_WIDTH = 100;
12296
12934
  const DEFAULT_HEIGHT = 40;
12297
12935
  /**
@@ -12433,8 +13071,7 @@ SparklineUnresponsive.displayName = "SparklineUnresponsive";
12433
13071
  * Responsive Sparkline chart component
12434
13072
  */
12435
13073
  const Sparkline = withResponsive(SparklineUnresponsive);
12436
- //#endregion
12437
- //#region src/components/trend-indicator/trend-indicator.module.scss
13074
+ // src/components/trend-indicator/trend-indicator.module.scss
12438
13075
  var trend_indicator_module_default = {
12439
13076
  "trend-indicator": "a8ccharts-zR7F7G-trend-indicator",
12440
13077
  "trend-indicator__icon": "a8ccharts-zR7F7G-trend-indicator__icon",
@@ -12443,8 +13080,7 @@ var trend_indicator_module_default = {
12443
13080
  "trend-indicator--neutral": "a8ccharts-zR7F7G-trend-indicator--neutral",
12444
13081
  "trend-indicator--up": "a8ccharts-zR7F7G-trend-indicator--up"
12445
13082
  };
12446
- //#endregion
12447
- //#region src/components/trend-indicator/trend-indicator.tsx
13083
+ // src/components/trend-indicator/trend-indicator.tsx
12448
13084
  const DIRECTION_LABELS = {
12449
13085
  up: "Increase",
12450
13086
  down: "Decrease",
@@ -12487,7 +13123,6 @@ function TrendIndicator({ direction, value, className, style, showIcon = true })
12487
13123
  })]
12488
13124
  });
12489
13125
  }
12490
- //#endregion
12491
- export { AccessibleTooltip, AreaChartResponsive as AreaChart, AreaChart as AreaChartUnresponsive, BarChartResponsive as BarChart, BarChart as BarChartUnresponsive, BarListChartResponsive as BarListChart, BarListChart as BarListChartUnresponsive, BaseTooltip, ConversionFunnelChartWithProvider as ConversionFunnelChart, GeoChartResponsive as GeoChart, GeoChartWithProvider as GeoChartUnresponsive, GlobalChartsContext, GlobalChartsProvider, GlobalChartsProvider as ThemeProvider, GoogleDataTableColumnRoleType, HeatmapChartResponsive as HeatmapChart, HeatmapChart as HeatmapChartUnresponsive, LeaderboardChartResponsive as LeaderboardChart, LeaderboardChart as LeaderboardChartUnresponsive, Legend, LineChartResponsive as LineChart, LineChart as LineChartUnresponsive, PieChartResponsive as PieChart, PieChart as PieChartUnresponsive, PieSemiCircleChartResponsive as PieSemiCircleChart, PieSemiCircleChart as PieSemiCircleChartUnresponsive, Sparkline, SparklineUnresponsive, TooltipBox, TrendIndicator, XYChartTooltip, buildCalendarHeatmapData, buildMonthCalendarHeatmapData, defaultTheme, formatMetricValue, formatPercentage, getBucketInfo, getColorDistance, hexToRgba, isValidHexColor, lightenHexColor, mergeThemes, mixHexColors, normalizeColorToHex, parseAsLocalDate, parseHslString, prefersLightText, relativeLuminance, resolveCssVariable, useCalendarHeatmapData, useChartFormatting, useChartLegendItems, useChartRegistration, useChartScopeElement, useGlobalChartsContext, useGlobalChartsTheme, useLeaderboardLegendItems, useMonthCalendarHeatmapData, validateHexColor };
13126
+ export { AccessibleTooltip, AreaChartResponsive as AreaChart, AreaChart as AreaChartUnresponsive, BarChartResponsive as BarChart, BarChart as BarChartUnresponsive, BarListChartResponsive as BarListChart, BarListChart as BarListChartUnresponsive, BaseTooltip, ConversionFunnelChartWithProvider as ConversionFunnelChart, GeoChartResponsive as GeoChart, GeoChartWithProvider as GeoChartUnresponsive, GlobalChartsContext, GlobalChartsProvider, GlobalChartsProvider as ThemeProvider, GoogleDataTableColumnRoleType, HeatmapChartResponsive as HeatmapChart, HeatmapChart as HeatmapChartUnresponsive, LeaderboardChartResponsive as LeaderboardChart, LeaderboardChart as LeaderboardChartUnresponsive, Legend, LineChartResponsive as LineChart, LineChart as LineChartUnresponsive, PieChartResponsive as PieChart, PieChart as PieChartUnresponsive, PieSemiCircleChartResponsive as PieSemiCircleChart, PieSemiCircleChart as PieSemiCircleChartUnresponsive, Sparkline, SparklineUnresponsive, TooltipBox, TrendIndicator, XYChartTooltip, buildCalendarHeatmapData, buildMonthCalendarHeatmapData, defaultTheme, formatMetricValue, formatPercentage, getBucketInfo, getColorDistance, hexToRgba, isValidHexColor, lightenHexColor, mergeThemes, mixHexColors, normalizeColorToHex, parseAsLocalDate, parseHslString, prefersLightText, relativeLuminance, resolveCssVariable, useBarColors, useBarLegendItems, useCalendarHeatmapData, useChartFormatting, useChartLegendItems, useChartRegistration, useChartScopeElement, useGlobalChartsContext, useGlobalChartsTheme, useLeaderboardLegendItems, useMonthCalendarHeatmapData, validateHexColor };
12492
13127
 
12493
13128
  //# sourceMappingURL=index.js.map