@automattic/charts 3.1.1 → 4.0.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 (116) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/TOKENS.md +80 -46
  3. package/dist/index.cjs +1224 -662
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.css +72 -23
  6. package/dist/index.d.cts +230 -162
  7. package/dist/index.d.ts +230 -162
  8. package/dist/index.js +1224 -663
  9. package/dist/index.js.map +1 -1
  10. package/package.json +10 -10
  11. package/src/charts/area-chart/area-chart.tsx +39 -25
  12. package/src/charts/area-chart/test/area-chart.test.tsx +4 -33
  13. package/src/charts/area-chart/test/tick-placement.test.tsx +55 -0
  14. package/src/charts/area-chart/test/tooltip-resolution-branches.test.tsx +86 -0
  15. package/src/charts/area-chart/test/tooltip-resolution.test.tsx +69 -0
  16. package/src/charts/area-chart/types.ts +4 -7
  17. package/src/charts/bar-chart/bar-chart.tsx +38 -11
  18. package/src/charts/bar-chart/private/test/use-bar-chart-options-formatting.test.tsx +69 -0
  19. package/src/charts/bar-chart/private/use-bar-chart-options.ts +27 -15
  20. package/src/charts/bar-chart/test/bar-chart-pointer-events.test.tsx +67 -0
  21. package/src/charts/bar-chart/test/bar-chart.test.tsx +79 -0
  22. package/src/charts/bar-list-chart/bar-list-chart.tsx +45 -2
  23. package/src/charts/bar-list-chart/test/bar-list-chart.test.tsx +38 -0
  24. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +33 -23
  25. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +138 -182
  26. package/src/charts/conversion-funnel-chart/test/conversion-funnel-chart.test.tsx +46 -0
  27. package/src/charts/geo-chart/geo-chart.module.scss +1 -0
  28. package/src/charts/geo-chart/geo-chart.tsx +5 -10
  29. package/src/charts/geo-chart/test/geo-chart.test.tsx +2 -2
  30. package/src/charts/heatmap-chart/heatmap-chart.module.scss +21 -16
  31. package/src/charts/heatmap-chart/heatmap-chart.tsx +45 -28
  32. package/src/charts/heatmap-chart/private/build-calendar-data.ts +3 -0
  33. package/src/charts/heatmap-chart/private/heatmap-legend.tsx +11 -4
  34. package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +51 -12
  35. package/src/charts/heatmap-chart/types.ts +2 -2
  36. package/src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts +2 -2
  37. package/src/charts/leaderboard-chart/hooks/use-leaderboard-legend-items.ts +3 -5
  38. package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +31 -19
  39. package/src/charts/leaderboard-chart/leaderboard-chart.tsx +27 -17
  40. package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +14 -0
  41. package/src/charts/leaderboard-chart/test/use-leaderboard-legend-items.test.tsx +2 -9
  42. package/src/charts/line-chart/line-chart.module.scss +10 -18
  43. package/src/charts/line-chart/line-chart.tsx +109 -25
  44. package/src/charts/line-chart/private/line-chart-annotation.tsx +18 -19
  45. package/src/charts/line-chart/test/default-tooltip-formatting.test.tsx +74 -0
  46. package/src/charts/line-chart/test/line-chart.test.tsx +74 -3
  47. package/src/charts/line-chart/test/tick-placement.test.tsx +84 -0
  48. package/src/charts/line-chart/test/tooltip-resolution.test.tsx +140 -0
  49. package/src/charts/line-chart/types.ts +4 -1
  50. package/src/charts/pie-chart/pie-chart.module.scss +13 -1
  51. package/src/charts/pie-chart/pie-chart.tsx +30 -40
  52. package/src/charts/pie-chart/test/pie-chart.test.tsx +56 -0
  53. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +8 -3
  54. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +23 -37
  55. package/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx +50 -0
  56. package/src/charts/private/chart-layout/chart-layout.module.scss +3 -2
  57. package/src/charts/private/svg-empty-state/svg-empty-state.module.scss +1 -1
  58. package/src/charts/private/test/axis-formatting.test.tsx +85 -0
  59. package/src/charts/private/test/time-axis-formatting.test.ts +109 -0
  60. package/src/charts/private/test/time-axis-options.test.ts +267 -0
  61. package/src/charts/private/test/time-axis-tick-invariants.test.ts +153 -0
  62. package/src/charts/private/test/time-axis-tick-values.test.ts +280 -0
  63. package/src/charts/private/test/time-axis.test.ts +9 -69
  64. package/src/charts/private/time-axis-options.ts +106 -0
  65. package/src/charts/private/time-axis.ts +318 -136
  66. package/src/charts/private/x-zoom/x-zoom.module.scss +3 -3
  67. package/src/charts/private/xy-plot/xy-plot.module.scss +8 -0
  68. package/src/charts/sparkline/sparkline.tsx +9 -3
  69. package/src/charts/sparkline/test/sparkline.test.tsx +29 -56
  70. package/src/charts/sparkline/types.ts +2 -2
  71. package/src/components/legend/private/base-legend.module.scss +9 -5
  72. package/src/components/tooltip/accessible-tooltip.tsx +38 -10
  73. package/src/components/tooltip/base-tooltip.module.scss +3 -3
  74. package/src/components/tooltip/private/bounded-tooltip.tsx +172 -0
  75. package/src/components/tooltip/test/accessible-tooltip.test.tsx +67 -0
  76. package/src/components/tooltip/test/bounded-tooltip.test.tsx +129 -0
  77. package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +279 -0
  78. package/src/components/tooltip/xy-chart-tooltip.tsx +288 -0
  79. package/src/components/trend-indicator/trend-indicator.module.scss +2 -2
  80. package/src/hooks/test/use-xychart-theme-style-reads.test.tsx +17 -10
  81. package/src/hooks/test/use-xychart-theme.test.tsx +63 -5
  82. package/src/hooks/use-xychart-theme.ts +33 -23
  83. package/src/index.ts +4 -0
  84. package/src/providers/chart-context/global-charts-provider.tsx +64 -54
  85. package/src/providers/chart-context/hooks/use-chart-formatting.ts +22 -0
  86. package/src/providers/chart-context/index.ts +1 -0
  87. package/src/providers/chart-context/private/catalog-pointers.ts +42 -0
  88. package/src/providers/chart-context/private/series-palette.ts +35 -0
  89. package/src/providers/chart-context/test/catalog-pointers.test.ts +61 -0
  90. package/src/providers/chart-context/test/chart-context.test.tsx +177 -792
  91. package/src/providers/chart-context/test/use-global-charts-theme.test.tsx +7 -18
  92. package/src/providers/chart-context/themes.ts +7 -47
  93. package/src/providers/chart-context/types.ts +3 -0
  94. package/src/styles/chart-scope.scss +63 -58
  95. package/src/styles/test/chart-scope.test.ts +25 -40
  96. package/src/test-utils/series-fixtures.ts +31 -0
  97. package/src/types.ts +103 -73
  98. package/src/utils/bucket-info.ts +87 -0
  99. package/src/utils/color-utils.ts +0 -27
  100. package/src/utils/date-formatting.ts +174 -0
  101. package/src/utils/get-styles.ts +7 -1
  102. package/src/utils/index.ts +3 -0
  103. package/src/utils/test/bucket-info.test.ts +185 -0
  104. package/src/utils/test/color-utils.test.ts +1 -48
  105. package/src/utils/test/date-formatting.test.ts +89 -0
  106. package/src/utils/test/get-styles.test.ts +10 -7
  107. package/src/utils/test/merge-themes.test.ts +18 -46
  108. package/src/visx/types.ts +29 -2
  109. package/src/hooks/test/use-xychart-theme-provider-override.test.tsx +0 -28
  110. package/src/providers/chart-context/private/theme-override-vars.ts +0 -87
  111. package/src/providers/chart-context/private/with-catalog-pointers.ts +0 -50
  112. package/src/providers/chart-context/test/sparse-theme.test.ts +0 -35
  113. package/src/providers/chart-context/test/theme-override-vars-guard.test.tsx +0 -29
  114. package/src/providers/chart-context/test/theme-override-vars.test.ts +0 -118
  115. package/src/providers/chart-context/test/with-catalog-pointers.test.ts +0 -47
  116. package/src/styles/test/wpds-fallbacks.test.ts +0 -131
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { getStringWidth as getStringWidth$1 } from "./visx/text/index.js";
2
2
  import { formatNumber, formatNumberCompact } from "@automattic/number-formatters";
3
- import { AnimatedAreaSeries, AnimatedAreaStack, AreaSeries, Axis, BarGroup, BarSeries, DataContext, Grid, Tooltip, TooltipContext, XYChart, buildChartTheme } from "@visx/xychart";
3
+ import { AnimatedAreaSeries, AnimatedAreaStack, AreaSeries, Axis, BarGroup, BarSeries, DataContext, Grid, TooltipContext, XYChart, buildChartTheme } from "@visx/xychart";
4
4
  import { __, _x, sprintf } from "@wordpress/i18n";
5
5
  import clsx from "clsx";
6
6
  import * as React from "react";
@@ -10,11 +10,12 @@ import { addDays, differenceInCalendarWeeks, differenceInHours, differenceInYear
10
10
  import { Text, getStringWidth } from "@visx/text";
11
11
  import deepmerge from "deepmerge";
12
12
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
- import { useTooltip, useTooltipInPortal } from "@visx/tooltip";
13
+ import { Tooltip, defaultStyles, useTooltip } from "@visx/tooltip";
14
14
  import { createScale, getTicks, scaleBand, scaleOrdinal, scaleTime } from "@visx/scale";
15
15
  import { Group } from "@visx/group";
16
16
  import { LegendItem, LegendLabel, LegendOrdinal, LegendShape } from "@visx/legend";
17
17
  import { IconButton, Stack, Text as Text$1 } from "@wordpress/ui";
18
+ import { createPortal } from "react-dom";
18
19
  import { LinearGradient } from "@visx/gradient";
19
20
  import { curveCatmullRom, curveLinear, curveMonotoneX } from "@visx/curve";
20
21
  import { useParentSize } from "@visx/responsive";
@@ -45,7 +46,7 @@ var __copyProps = (to, from, except, desc) => {
45
46
  }
46
47
  return to;
47
48
  };
48
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
49
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
49
50
  value: mod,
50
51
  enumerable: true
51
52
  }) : target, mod));
@@ -172,6 +173,39 @@ const parseAsLocalDate = (dateString) => {
172
173
  return /* @__PURE__ */ new Date(NaN);
173
174
  };
174
175
  //#endregion
176
+ //#region src/utils/bucket-info.ts
177
+ const getInstant = (point) => {
178
+ if (!point) return;
179
+ if ("date" in point && point.date) return point.date.getTime();
180
+ if ("dateString" in point && point.dateString) return parseAsLocalDate(point.dateString).getTime();
181
+ };
182
+ const getPointSpacingInHours = (data) => {
183
+ return data.reduce((spacing, datom) => {
184
+ const instants = [...new Set(datom.data.map((point) => getInstant(point)))].filter((time) => time !== void 0 && Number.isFinite(time)).sort((a, b) => a - b);
185
+ return instants.reduce((seriesSpacing, time, index) => index === 0 ? seriesSpacing : Math.min(seriesSpacing, Math.abs(differenceInHours(time, instants[index - 1]))), spacing);
186
+ }, Number.POSITIVE_INFINITY);
187
+ };
188
+ const SUB_DAILY_SPACING_HOURS = 23;
189
+ const MONTHLY_SPACING_HOURS = 672;
190
+ const getBucketResolution = (data, tickResolution) => {
191
+ if (tickResolution) return tickResolution === "week" ? "day" : tickResolution;
192
+ const spacingInHours = getPointSpacingInHours(data);
193
+ if (spacingInHours < SUB_DAILY_SPACING_HOURS) return "hour";
194
+ if (!Number.isFinite(spacingInHours) || spacingInHours < MONTHLY_SPACING_HOURS) return "day";
195
+ return spacingInHours < 12 * MONTHLY_SPACING_HOURS ? "month" : "year";
196
+ };
197
+ /**
198
+ * How this data was classified, for consumers that render their own labels.
199
+ *
200
+ * @param data - The series the chart draws, in any order, dated by `date` or `dateString`.
201
+ * @param tickResolution - Caller-declared bucket resolution, when known.
202
+ * @return The declared or inferred bucket, and the resolution formats key on.
203
+ */
204
+ const getBucketInfo = (data, tickResolution) => ({
205
+ bucket: tickResolution ?? getBucketResolution(data),
206
+ displayResolution: getBucketResolution(data, tickResolution)
207
+ });
208
+ //#endregion
175
209
  //#region src/utils/format-metric-value.ts
176
210
  /**
177
211
  * Format a numeric metric value based on type, precision and scale.
@@ -250,7 +284,8 @@ const formatPercentage = (value) => {
250
284
  */
251
285
  const getLongestTickWidth = (ticks, formatTick, labelStyle) => {
252
286
  const formattedTicks = ticks.map((tick) => formatTick(tick, 0, []));
253
- return getStringWidth(formattedTicks.reduce((longest, current) => longest.length >= current.length ? longest : current, formattedTicks[0]), labelStyle);
287
+ const longestTick = formattedTicks.reduce((longest, current) => longest.length >= current.length ? longest : current, formattedTicks[0]);
288
+ return getStringWidth(longestTick, labelStyle);
254
289
  };
255
290
  //#endregion
256
291
  //#region src/utils/get-styles.ts
@@ -383,20 +418,6 @@ const parseHslString = (hslString) => {
383
418
  ];
384
419
  };
385
420
  /**
386
- * Parse an RGB string like 'rgb(255, 0, 0)' into a hex color.
387
- *
388
- * @deprecated Use normalizeColorToHex() instead, which handles all color formats including rgb() and rgba().
389
- * @param rgbString - RGB color string (not RGBA)
390
- * @return hex color string or null if invalid
391
- */
392
- const parseRgbString = (rgbString) => {
393
- const lower = rgbString.toLowerCase().trim();
394
- if (!lower.startsWith("rgb(") || lower.startsWith("rgba(")) return null;
395
- const parsed = color(lower);
396
- if (!parsed) return null;
397
- return parsed.formatHex();
398
- };
399
- /**
400
421
  * Normalize any CSS color value to a hex color string.
401
422
  * Handles hex, HSL, HSLA, RGB, RGBA, named CSS colors, and CSS variables.
402
423
  *
@@ -624,6 +645,112 @@ const resolveFontSize = (val) => {
624
645
  }
625
646
  };
626
647
  //#endregion
648
+ //#region src/utils/date-formatting.ts
649
+ const formatters = /* @__PURE__ */ new Map();
650
+ const getFormatter$1 = (options, { locale, timeZone }) => {
651
+ const key = `${locale ?? ""}|${timeZone ?? ""}|${JSON.stringify(options)}`;
652
+ let formatter = formatters.get(key);
653
+ if (!formatter) {
654
+ formatter = new Intl.DateTimeFormat(locale, {
655
+ ...options,
656
+ timeZone
657
+ });
658
+ formatters.set(key, formatter);
659
+ }
660
+ return formatter;
661
+ };
662
+ const warned = /* @__PURE__ */ new Set();
663
+ const warnOnce = (key, message) => {
664
+ if (warned.has(key) || process.env.NODE_ENV === "production") return;
665
+ warned.add(key);
666
+ console.warn(`[Charts] ${message}`);
667
+ };
668
+ const isUsable = (formatting) => {
669
+ try {
670
+ Intl.DateTimeFormat(formatting.locale, { timeZone: formatting.timeZone });
671
+ return true;
672
+ } catch {
673
+ return false;
674
+ }
675
+ };
676
+ const toBcp47 = (locale) => locale?.replace(/_/g, "-");
677
+ /**
678
+ * Repairs a host locale for `Intl`, dropping either half it still cannot use.
679
+ *
680
+ * Every formatter below is built during render, so an unusable string would throw
681
+ * where React unmounts the whole host tree rather than one chart. WordPress hands
682
+ * out the likeliest one either way: `timezone_string` is `''` on a site set to a
683
+ * raw UTC offset, and has no repair.
684
+ *
685
+ * @param formatting - Locale and time zone as the host supplied them.
686
+ * @return The usable halves, each `undefined` where the host's value was not.
687
+ */
688
+ const sanitizeFormatting = (formatting) => {
689
+ const repaired = {
690
+ ...formatting,
691
+ locale: toBcp47(formatting.locale)
692
+ };
693
+ if (isUsable(repaired)) return repaired;
694
+ const locale = isUsable({ locale: repaired.locale }) ? repaired.locale : void 0;
695
+ const timeZone = isUsable({ timeZone: repaired.timeZone }) ? repaired.timeZone : void 0;
696
+ if (locale === void 0 && formatting.locale !== void 0) warnOnce(`locale:${formatting.locale}`, `locale ${JSON.stringify(formatting.locale)} is not a BCP-47 tag Intl accepts, so dates render in the browser's locale.`);
697
+ if (timeZone === void 0 && formatting.timeZone !== void 0) warnOnce(`timeZone:${formatting.timeZone}`, `timeZone ${JSON.stringify(formatting.timeZone)} is not a zone Intl accepts, so dates render in the browser's zone. Pass an IANA name or a UTC offset such as "+05:30".`);
698
+ return {
699
+ locale,
700
+ timeZone
701
+ };
702
+ };
703
+ /**
704
+ * A date formatter bound to the host's locale and time zone.
705
+ *
706
+ * @param options - Date-part options, as for `Intl.DateTimeFormat`.
707
+ * @param formatting - Host locale and time zone; either half absent leaves the runtime's own.
708
+ * @return Formatter taking a timestamp or a `Date`.
709
+ */
710
+ const createDateFormatter = (options, formatting) => {
711
+ return (value) => {
712
+ if (!Number.isFinite(Number(value))) return new Date(Number(value)).toLocaleDateString();
713
+ return getFormatter$1(options, formatting).format(value);
714
+ };
715
+ };
716
+ const CLOCK_OPTIONS = {
717
+ month: "numeric",
718
+ hour: "numeric",
719
+ hourCycle: "h23"
720
+ };
721
+ const NO_FIELDS = {
722
+ month: NaN,
723
+ hour: NaN
724
+ };
725
+ /**
726
+ * A clock reading calendar fields in the host's time zone.
727
+ *
728
+ * `Date`'s own getters read the browser's zone, so a chart viewed from another
729
+ * country anchors its ticks to the wrong midnights and the wrong January
730
+ * without this.
731
+ *
732
+ * @param timeZone - IANA zone name; absent leaves the runtime's own.
733
+ * @return Reader taking a `Date`.
734
+ */
735
+ const createZonedClock = (timeZone) => {
736
+ if (!timeZone) return (date) => ({
737
+ month: date.getMonth() + 1,
738
+ hour: date.getHours()
739
+ });
740
+ return (date) => {
741
+ if (!Number.isFinite(Number(date))) return NO_FIELDS;
742
+ const parts = getFormatter$1(CLOCK_OPTIONS, {
743
+ locale: "en-US",
744
+ timeZone
745
+ }).formatToParts(date);
746
+ const read = (type) => Number(parts.find((part) => part.type === type)?.value);
747
+ return {
748
+ month: read("month"),
749
+ hour: read("hour")
750
+ };
751
+ };
752
+ };
753
+ //#endregion
627
754
  //#region src/providers/chart-scope/chart-scope-context.ts
628
755
  /**
629
756
  * The nearest chart element, published so JS token resolution reads the same CSS cascade that paints the chart's own colours.
@@ -760,140 +887,40 @@ const getChartColor = (index, colorCache) => {
760
887
  const fallbackLightness = BASE_LIGHTNESS + index % LIGHTNESS_VARIATION_STEPS * LIGHTNESS_INCREMENT;
761
888
  return hsl(Math.round(fallbackHue), fallbackSaturation / 100, fallbackLightness / 100).formatHex();
762
889
  };
763
- //#endregion
764
- //#region src/providers/chart-context/private/theme-override-vars.ts
765
- const ROLE_FOR_FIELD = [
766
- ["--a8c-charts-color-background", (theme) => theme.backgroundColor],
767
- ["--a8c-charts-color-grid", (theme) => theme.gridStyles?.stroke],
768
- ["--a8c-charts-color-axis", (theme) => theme.xAxisLineStyles?.stroke],
769
- ["--a8c-charts-color-tick", (theme) => theme.xTickLineStyles?.stroke],
770
- ["--a8c-charts-color-label-axis", (theme) => theme.svgLabelSmall?.fill]
771
- ];
772
890
  /**
773
- * The variable a `theme` prop override is published as, one layer outside the role itself.
891
+ * The catalog role holding one series-palette slot.
774
892
  *
775
- * `chart-scope.scss` declares each of these roles as `var(<role>-theme, <catalog default>)`, so an override that is invalid at computed-value time — a fallback-less `var()` naming a token the host never set — only invalidates this variable, and the role still resolves its mapped `--wpds-*` token. Published as the role directly, such a value took the role and every bare `var(<role>)` read site down with it.
776
- *
777
- * @param role - The catalog role being overridden.
778
- * @return The custom property carrying the consumer's value.
893
+ * @param slot - The one-based slot number.
894
+ * @return The role name.
779
895
  */
780
- const themeLayerVar = (role) => `${role}-theme`;
781
- ROLE_FOR_FIELD.map(([role]) => role);
782
- /**
783
- * Whether a value reads the role it is being published for, which would make the catalog entry depend on itself.
784
- *
785
- * The dependency runs `<role>: var(<role>-theme, …)`, so a value naming `<role>` closes a cycle through the catalog entry. CSS marks every custom property in a cycle invalid at computed-value time — the role's own fallback is *not* used, so the token resolves to nothing and every chart loses that colour. Verified in Chrome: `--role-theme: var(--role, blue); --role: var(--role-theme, green)` leaves `--role` empty, not `green`.
786
- *
787
- * Three things this has to get right, none of which a plain substring test gets:
788
- *
789
- * `var()` permits whitespace after the opening paren, so testing for `var(<role>` misses `var( --a8c-charts-color-grid, … )`.
790
- *
791
- * Several roles are prefixes of others (`--a8c-charts-color-label` of `--a8c-charts-color-label-secondary`), and every role is a prefix of its own theme layer. A prefix match would read a legitimate cross-role pointer as a self-reference and silently drop the override, so the role must be followed by something that cannot continue an identifier — `-` included. A value naming `<role>-theme` is therefore not treated as a self-reference: that cycle is confined to the theme layer, which leaves the role free to fall back to the catalog default.
792
- *
793
- * `resolveCssVariable` also accepts a bare custom-property name with no `var()` wrapper (`--a8c-charts-color-grid`), so that is legal `theme` input too. It forms no cycle — custom properties accept arbitrary token streams — so it survives as a literal string and drops silently at the use site instead of falling back to the catalog default.
794
- *
795
- * @param value - The consumer's value for the field.
796
- * @param role - The catalog role this value would override.
797
- * @return True when the value reads `role`, so it must not be published.
798
- */
799
- const readsOwnRole = (value, role) => value.trim() === role || new RegExp(`var\\(\\s*${role.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`).test(value);
800
- /**
801
- * Maps a sparse consumer theme onto the theme-layer variables its overridden catalog roles read, so CSS-painted and JS-resolved colours read one source.
802
- *
803
- * A value that reads the role it would override is left unpublished — that is the default theme's own pointer surviving `mergeThemes`, and publishing it would invalidate the role. The role is still reported in `roles`: `withCatalogPointers` restores its theme field to the catalog pointer either way, so CSS and visx agree on the catalog default rather than visx painting a literal CSS never sees.
804
- *
805
- * @param theme - The consumer's `theme` prop, before merging with the default theme.
806
- * @return The wrapper's custom properties, and the roles the consumer overrode.
807
- */
808
- const themeOverrideVars = (theme) => {
809
- if (!theme) return {
810
- vars: {},
811
- roles: []
812
- };
813
- const vars = {};
814
- const roles = [];
815
- for (const [role, read] of ROLE_FOR_FIELD) {
816
- const value = read(theme);
817
- if (typeof value !== "string" || value === "") continue;
818
- roles.push(role);
819
- if (!readsOwnRole(value, role)) vars[themeLayerVar(role)] = value;
820
- }
821
- return {
822
- vars,
823
- roles
824
- };
825
- };
896
+ const seriesRole = (slot) => `--a8c-charts-color-series-${slot}`;
897
+ const SERIES_SLOT_1_FALLBACK = "#3858e9";
898
+ /** The catalog pointer for every slot, in slot order. */
899
+ const SERIES_PALETTE_POINTERS = Array.from({ length: 5 }, (_, index) => index === 0 ? `var(${seriesRole(1)}, ${SERIES_SLOT_1_FALLBACK})` : `var(${seriesRole(index + 1)})`);
826
900
  //#endregion
827
901
  //#region src/providers/chart-context/themes.ts
828
902
  /**
829
- * Default theme configuration
903
+ * Default theme configuration: the shape and spacing a consumer can override.
904
+ *
905
+ * For a color, set the matching `--a8c-charts-color-*` role in CSS instead; the `var()`
906
+ * chains that deliver those live in `private/catalog-pointers.ts`.
830
907
  */
831
908
  const defaultTheme = {
832
- backgroundColor: "var(--a8c-charts-color-background, #fff)",
833
- labelBackgroundColor: "transparent",
834
- labelTextColor: "var(--a8c-charts-color-label-on-fill, #FFFFFF)",
835
- colors: [
836
- "#98C8DF",
837
- "#006DAB",
838
- "#A6DC80",
839
- "#1F9828",
840
- "#FF8C8F"
841
- ],
842
- gridStyles: {
843
- stroke: "var(--a8c-charts-color-grid, #dbdbdb)",
844
- strokeWidth: 1
845
- },
909
+ gridStyles: { strokeWidth: 1 },
846
910
  tickLength: 4,
847
- gridColor: "",
848
- gridColorDark: "",
849
- xTickLineStyles: {
850
- stroke: "var(--a8c-charts-color-tick, #dbdbdb)",
851
- strokeWidth: 1
852
- },
853
- xAxisLineStyles: {
854
- stroke: "var(--a8c-charts-color-axis, #dbdbdb)",
855
- strokeWidth: 1
856
- },
911
+ xTickLineStyles: { strokeWidth: 1 },
912
+ xAxisLineStyles: { strokeWidth: 1 },
857
913
  legend: {
858
- labelStyles: { color: "var(--a8c-charts-color-label, #1e1e1e)" },
914
+ labelStyles: {},
859
915
  containerStyles: {},
860
916
  shapeStyles: []
861
917
  },
862
918
  seriesLineStyles: [],
863
919
  glyphs: [],
864
- svgLabelSmall: {
865
- fill: "var(--a8c-charts-color-label-axis, #1e1e1e)",
866
- fontFamily: "inherit"
867
- },
920
+ svgLabelSmall: { fontFamily: "inherit" },
868
921
  svgLabelBig: { fontFamily: "inherit" },
869
- annotationStyles: {
870
- label: {
871
- anchorLineStroke: "var(--a8c-charts-color-annotation, #1e1e1e)",
872
- backgroundFill: "var(--a8c-charts-color-surface, #fff)"
873
- },
874
- connector: { stroke: "var(--a8c-charts-color-annotation, #1e1e1e)" },
875
- circleSubject: {
876
- stroke: "transparent",
877
- fill: "var(--a8c-charts-color-annotation, #1e1e1e)",
878
- radius: 5
879
- }
880
- },
881
- geoChart: { featureFillColor: "var(--a8c-charts-color-surface-secondary, #f4f4f4)" },
882
- leaderboardChart: {
883
- rowGap: 12,
884
- columnGap: 4,
885
- labelSpacing: "xs",
886
- deltaColors: [
887
- "var(--a8c-charts-color-trend-down, #cc1818)",
888
- "var(--a8c-charts-color-trend-neutral, #707070)",
889
- "var(--a8c-charts-color-trend-up, #008030)"
890
- ]
891
- },
892
- conversionFunnelChart: {
893
- backgroundColor: "var(--a8c-charts-color-surface-secondary, #f4f4f4)",
894
- positiveChangeColor: "var(--a8c-charts-color-trend-up, #008030)",
895
- negativeChangeColor: "var(--a8c-charts-color-trend-down, #cc1818)"
896
- },
922
+ annotationStyles: {},
923
+ leaderboardChart: { labelSpacing: "xs" },
897
924
  lineChart: { lineStyles: { comparison: {
898
925
  strokeDasharray: "4 4",
899
926
  strokeLinecap: "square"
@@ -917,52 +944,9 @@ const defaultTheme = {
917
944
  }
918
945
  };
919
946
  //#endregion
920
- //#region src/providers/chart-context/private/with-catalog-pointers.ts
921
- const CATALOG_RESTORE_FOR_ROLE = {
922
- "--a8c-charts-color-background": () => ({ backgroundColor: defaultTheme.backgroundColor }),
923
- "--a8c-charts-color-grid": (theme) => ({ gridStyles: {
924
- ...theme.gridStyles,
925
- stroke: defaultTheme.gridStyles.stroke
926
- } }),
927
- "--a8c-charts-color-axis": (theme) => ({ xAxisLineStyles: {
928
- ...theme.xAxisLineStyles,
929
- stroke: defaultTheme.xAxisLineStyles.stroke
930
- } }),
931
- "--a8c-charts-color-tick": (theme) => ({ xTickLineStyles: {
932
- ...theme.xTickLineStyles,
933
- stroke: defaultTheme.xTickLineStyles.stroke
934
- } }),
935
- "--a8c-charts-color-label-axis": (theme) => ({ svgLabelSmall: {
936
- ...theme.svgLabelSmall,
937
- fill: defaultTheme.svgLabelSmall.fill
938
- } })
939
- };
940
- /**
941
- * Restores the mapped theme fields of `overriddenRoles` to their catalog pointer, so the theme-layer variable `themeOverrideVars` writes on the provider wrapper is the only carrier for an overridden role — CSS and the JS bridge (`useXYChartTheme`) then resolve it through the same cascade instead of visx reading a baked literal that can disagree with a closer CSS override.
942
- *
943
- * This covers roles `themeOverrideVars` deliberately left unpublished as well as the ones it published. A value that reads its own role is not publishable, but leaving visx the consumer's literal is what the whole mechanism exists to prevent: `theme={ { gridStyles: { stroke: 'var(--brand, var(--a8c-charts-color-grid, red))' } } }` would have CSS paint the catalog default while visx painted `--brand`.
944
- *
945
- * Every field outside the five mapped roles is left exactly as `merged` provided it: those aren't published as theme-layer vars, so rewriting them would erase a consumer's override with no replacement carrier.
946
- *
947
- * @param merged - The consumer theme merged onto `defaultTheme` (`mergeThemes` output).
948
- * @param overriddenRoles - The catalog roles the consumer overrode, from `themeOverrideVars`.
949
- * @return A new theme object; `merged` is not mutated.
950
- */
951
- const withCatalogPointers = (merged, overriddenRoles) => {
952
- let result = merged;
953
- for (const role of overriddenRoles) {
954
- const restore = CATALOG_RESTORE_FOR_ROLE[role];
955
- if (restore) result = {
956
- ...result,
957
- ...restore(result)
958
- };
959
- }
960
- return result;
961
- };
962
- //#endregion
963
947
  //#region src/providers/chart-context/global-charts-provider.tsx
964
948
  const GlobalChartsContext = createContext(null);
965
- const GlobalChartsProvider = ({ children, theme }) => {
949
+ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
966
950
  const [charts, setCharts] = useState(() => /* @__PURE__ */ new Map());
967
951
  const [hiddenSeries, setHiddenSeries] = useState(() => /* @__PURE__ */ new Map());
968
952
  const seededCharts = useRef(/* @__PURE__ */ new Set());
@@ -972,11 +956,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
972
956
  wrapperRef.current = node;
973
957
  setScopeNode(node);
974
958
  }, []);
975
- const { vars: overrideVars, roles: overriddenRoles } = useMemo(() => themeOverrideVars(theme), [theme]);
976
- const providerTheme = useMemo(() => {
977
- if (!theme) return defaultTheme;
978
- return withCatalogPointers(mergeThemes(defaultTheme, theme), overriddenRoles);
979
- }, [theme, overriddenRoles]);
959
+ const providerTheme = useMemo(() => theme ? mergeThemes(defaultTheme, theme) : defaultTheme, [theme]);
980
960
  const [colorCache, setColorCache] = useState(() => ({
981
961
  colors: [],
982
962
  hues: [],
@@ -987,29 +967,26 @@ const GlobalChartsProvider = ({ children, theme }) => {
987
967
  const [isColorPaletteResolved, setIsColorPaletteResolved] = useState(false);
988
968
  useLayoutEffect(() => {
989
969
  setIsColorPaletteResolved(false);
990
- const { colors } = providerTheme;
991
970
  const resolvedColors = [];
992
971
  const hues = [];
993
972
  const existingHslColors = [];
994
973
  let minHue = 360;
995
974
  let maxHue = 0;
996
- if (Array.isArray(colors)) {
997
- for (const color of colors) if (color && typeof color === "string") {
998
- const normalizedColor = normalizeColorToHex(color, wrapperRef.current, resolveCssVariable);
999
- if (normalizedColor.startsWith("#")) {
1000
- resolvedColors.push(normalizedColor);
1001
- const hslColor = hsl(normalizedColor);
1002
- if (!isNaN(hslColor.h)) {
1003
- const hslTuple = [
1004
- hslColor.h,
1005
- hslColor.s * 100,
1006
- hslColor.l * 100
1007
- ];
1008
- hues.push(hslTuple[0]);
1009
- existingHslColors.push(hslTuple);
1010
- minHue = Math.min(minHue, hslTuple[0]);
1011
- maxHue = Math.max(maxHue, hslTuple[0]);
1012
- }
975
+ for (const color of SERIES_PALETTE_POINTERS) {
976
+ const normalizedColor = normalizeColorToHex(color, wrapperRef.current, resolveCssVariable);
977
+ if (normalizedColor.startsWith("#")) {
978
+ resolvedColors.push(normalizedColor);
979
+ const hslColor = hsl(normalizedColor);
980
+ if (!isNaN(hslColor.h)) {
981
+ const hslTuple = [
982
+ hslColor.h,
983
+ hslColor.s * 100,
984
+ hslColor.l * 100
985
+ ];
986
+ hues.push(hslTuple[0]);
987
+ existingHslColors.push(hslTuple);
988
+ minHue = Math.min(minHue, hslTuple[0]);
989
+ maxHue = Math.max(maxHue, hslTuple[0]);
1013
990
  }
1014
991
  }
1015
992
  }
@@ -1020,14 +997,15 @@ const GlobalChartsProvider = ({ children, theme }) => {
1020
997
  minHue,
1021
998
  maxHue
1022
999
  });
1023
- }, [providerTheme]);
1000
+ }, []);
1024
1001
  useEffect(() => {
1025
1002
  if (colorCache.colors.length > 0) setIsColorPaletteResolved(true);
1026
1003
  }, [colorCache]);
1027
1004
  const [groupToColorMap, setGroupToColorMap] = useState(() => /* @__PURE__ */ new Map());
1005
+ const paletteKey = colorCache.colors.join(",");
1028
1006
  useEffect(() => {
1029
1007
  setGroupToColorMap(/* @__PURE__ */ new Map());
1030
- }, [providerTheme.colors]);
1008
+ }, [paletteKey]);
1031
1009
  const registerChart = useCallback((id, data) => {
1032
1010
  setCharts((prev) => new Map(prev).set(id, data));
1033
1011
  }, []);
@@ -1111,12 +1089,17 @@ const GlobalChartsProvider = ({ children, theme }) => {
1111
1089
  const set = hiddenSeries.get(chartId);
1112
1090
  return set ? new Set(set) : /* @__PURE__ */ new Set();
1113
1091
  }, [hiddenSeries]);
1092
+ const formatting = useMemo(() => sanitizeFormatting({
1093
+ locale,
1094
+ timeZone
1095
+ }), [locale, timeZone]);
1114
1096
  const value = useMemo(() => ({
1115
1097
  charts,
1116
1098
  registerChart,
1117
1099
  unregisterChart,
1118
1100
  getChartData,
1119
1101
  theme: providerTheme,
1102
+ formatting,
1120
1103
  getElementStyles,
1121
1104
  toggleSeriesVisibility,
1122
1105
  setSeriesVisibility,
@@ -1132,6 +1115,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
1132
1115
  unregisterChart,
1133
1116
  getChartData,
1134
1117
  providerTheme,
1118
+ formatting,
1135
1119
  getElementStyles,
1136
1120
  toggleSeriesVisibility,
1137
1121
  setSeriesVisibility,
@@ -1147,10 +1131,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
1147
1131
  children: /* @__PURE__ */ jsx("div", {
1148
1132
  ref: setWrapperNode,
1149
1133
  className: CHART_SCOPE_CLASS,
1150
- style: {
1151
- display: "contents",
1152
- ...overrideVars
1153
- },
1134
+ style: { display: "contents" },
1154
1135
  children: /* @__PURE__ */ jsx(ChartScopeContext.Provider, {
1155
1136
  value: scopeNode,
1156
1137
  children
@@ -1214,6 +1195,39 @@ const useDeepMemo = (value) => {
1214
1195
  return ref.current;
1215
1196
  };
1216
1197
  //#endregion
1198
+ //#region src/providers/chart-context/private/catalog-pointers.ts
1199
+ const CATALOG_POINTERS = {
1200
+ background: "var(--a8c-charts-color-background, #fff)",
1201
+ labelAxis: "var(--a8c-charts-color-label-axis, #1e1e1e)",
1202
+ grid: "var(--a8c-charts-color-grid, #dbdbdb)",
1203
+ axisX: "var(--a8c-charts-color-axis-x, #dbdbdb)",
1204
+ tickX: "var(--a8c-charts-color-tick-x, #dbdbdb)",
1205
+ axisY: "var(--a8c-charts-color-axis-y, none)",
1206
+ tickY: "var(--a8c-charts-color-tick-y, none)",
1207
+ annotation: "var(--a8c-charts-color-annotation, #1e1e1e)",
1208
+ surface: "var(--a8c-charts-color-surface, #fff)",
1209
+ surfaceSecondary: "var(--a8c-charts-color-surface-secondary, #f4f4f4)",
1210
+ trendUp: "var(--a8c-charts-color-trend-up, #008030)",
1211
+ trendDown: "var(--a8c-charts-color-trend-down, #cc1818)",
1212
+ series: SERIES_PALETTE_POINTERS
1213
+ };
1214
+ /**
1215
+ * The annotation parts visx paints, in the shape `@visx/annotation` takes. `radius`
1216
+ * rides along as the base the theme and the per-datum styles merge onto.
1217
+ */
1218
+ const ANNOTATION_POINTERS = {
1219
+ label: {
1220
+ anchorLineStroke: CATALOG_POINTERS.annotation,
1221
+ backgroundFill: CATALOG_POINTERS.surface
1222
+ },
1223
+ connector: { stroke: CATALOG_POINTERS.annotation },
1224
+ circleSubject: {
1225
+ stroke: "transparent",
1226
+ fill: CATALOG_POINTERS.annotation,
1227
+ radius: 5
1228
+ }
1229
+ };
1230
+ //#endregion
1217
1231
  //#region src/providers/chart-scope/use-chart-scope-element.ts
1218
1232
  /**
1219
1233
  * Returns the nearest chart scope element, or `null` when rendered outside any scope (and during SSR, before the ref is attached).
@@ -1240,26 +1254,33 @@ const useXYChartTheme = (data) => {
1240
1254
  return useMemo(() => {
1241
1255
  const resolve = createCssVariableResolver(scopeElement);
1242
1256
  const resolveColor = (value) => value ? resolve(value) ?? value : value;
1243
- const seriesColors = JSON.parse(seriesColorKey);
1257
+ const paletteColors = [...JSON.parse(seriesColorKey), ...CATALOG_POINTERS.series].map((color) => resolveColor(color)).filter((color) => Boolean(color) && !color.includes("var("));
1258
+ const resolvedLabelColor = resolveColor(CATALOG_POINTERS.labelAxis);
1259
+ const htmlLabelColor = resolvedLabelColor ? normalizeColorToHex(resolvedLabelColor) || resolvedLabelColor : resolvedLabelColor;
1244
1260
  return buildChartTheme({
1245
1261
  ...theme,
1246
- colors: [...seriesColors, ...theme.colors ?? []],
1247
- backgroundColor: resolveColor(theme.backgroundColor),
1248
- gridStyles: theme.gridStyles && {
1262
+ gridColor: "",
1263
+ gridColorDark: "",
1264
+ colors: paletteColors,
1265
+ backgroundColor: resolveColor(CATALOG_POINTERS.background),
1266
+ htmlLabel: htmlLabelColor ? { color: htmlLabelColor } : void 0,
1267
+ gridStyles: {
1249
1268
  ...theme.gridStyles,
1250
- stroke: resolveColor(theme.gridStyles.stroke)
1269
+ stroke: CATALOG_POINTERS.grid
1251
1270
  },
1252
- xAxisLineStyles: theme.xAxisLineStyles && {
1271
+ xAxisLineStyles: {
1253
1272
  ...theme.xAxisLineStyles,
1254
- stroke: resolveColor(theme.xAxisLineStyles.stroke)
1273
+ stroke: CATALOG_POINTERS.axisX
1255
1274
  },
1256
- xTickLineStyles: theme.xTickLineStyles && {
1275
+ xTickLineStyles: {
1257
1276
  ...theme.xTickLineStyles,
1258
- stroke: resolveColor(theme.xTickLineStyles.stroke)
1277
+ stroke: CATALOG_POINTERS.tickX
1259
1278
  },
1260
- svgLabelSmall: theme.svgLabelSmall && {
1279
+ yAxisLineStyles: { stroke: CATALOG_POINTERS.axisY },
1280
+ yTickLineStyles: { stroke: CATALOG_POINTERS.tickY },
1281
+ svgLabelSmall: {
1261
1282
  ...theme.svgLabelSmall,
1262
- fill: resolveColor(theme.svgLabelSmall.fill)
1283
+ fill: CATALOG_POINTERS.labelAxis
1263
1284
  }
1264
1285
  });
1265
1286
  }, [
@@ -1357,11 +1378,12 @@ const useChartMargin = (height, options, data, theme, horizontal = false) => {
1357
1378
  if (options.axis?.y?.tickValues?.length) return options.axis.y.tickValues;
1358
1379
  const minY = Math.min(...allDataPoints.map((d) => d.value));
1359
1380
  const maxY = Math.max(...allDataPoints.map((d) => d.value));
1360
- return getTicks(createScale({
1381
+ const yScale = createScale({
1361
1382
  ...options.yScale,
1362
1383
  domain: [minY, maxY],
1363
1384
  range: [height, 0]
1364
- }), options.axis?.y?.numTicks);
1385
+ });
1386
+ return getTicks(yScale, options.axis?.y?.numTicks);
1365
1387
  }, [
1366
1388
  options,
1367
1389
  data,
@@ -1486,7 +1508,7 @@ const MIN_PIXEL_SIZE = 3;
1486
1508
  /**
1487
1509
  * Pixel size for zero-value bars (1px less than near-zero to be visually distinguishable).
1488
1510
  */
1489
- const ZERO_PIXEL_SIZE = MIN_PIXEL_SIZE - 1;
1511
+ const ZERO_PIXEL_SIZE = 2;
1490
1512
  const useZeroValueDisplay = (data, options = { enabled: false }) => {
1491
1513
  const { enabled, valueAxisLength } = options;
1492
1514
  return useMemo(() => {
@@ -1664,6 +1686,21 @@ const useGlobalChartsTheme = () => {
1664
1686
  return useContext(GlobalChartsContext)?.theme ?? defaultTheme;
1665
1687
  };
1666
1688
  //#endregion
1689
+ //#region src/providers/chart-context/hooks/use-chart-formatting.ts
1690
+ const RUNTIME_FORMATTING = {};
1691
+ /**
1692
+ * The locale and time zone dates are rendered in, as set on `GlobalChartsProvider`.
1693
+ *
1694
+ * Falls back to the runtime's own rather than throwing: charts render outside a
1695
+ * provider, and a host that sets neither gets exactly the browser-default
1696
+ * behavior this package had before the context existed.
1697
+ *
1698
+ * @return The host's formatting context.
1699
+ */
1700
+ const useChartFormatting = () => {
1701
+ return useContext(GlobalChartsContext)?.formatting ?? RUNTIME_FORMATTING;
1702
+ };
1703
+ //#endregion
1667
1704
  //#region src/components/legend/utils/value-or-identity.ts
1668
1705
  /**
1669
1706
  * Returns an object's value if defined, or the object.
@@ -2101,9 +2138,311 @@ const BaseTooltip = ({ data, top, left, component: Component = DefaultTooltipCon
2101
2138
  });
2102
2139
  };
2103
2140
  //#endregion
2141
+ //#region src/components/tooltip/private/bounded-tooltip.tsx
2142
+ const DEFAULT_OFFSET = 10;
2143
+ const isClipping = (element) => {
2144
+ const { overflow, overflowX, overflowY } = getComputedStyle(element);
2145
+ return [
2146
+ overflow,
2147
+ overflowX,
2148
+ overflowY
2149
+ ].some((value) => value && value !== "visible");
2150
+ };
2151
+ const findClippingAncestor = (wrapper) => {
2152
+ let element = wrapper.parentElement;
2153
+ while (element && element !== document.body) {
2154
+ if (isClipping(element)) return element;
2155
+ element = element.parentElement;
2156
+ }
2157
+ return null;
2158
+ };
2159
+ /**
2160
+ * Where the box goes, in wrapper coordinates. Below and to the right of the
2161
+ * anchor; flipped to the other side when that side clips less, as visx's
2162
+ * `TooltipWithBounds` decides it; then clamped so the box stays inside
2163
+ * `bounds` whenever it fits at all.
2164
+ *
2165
+ * @param params - Anchor, offsets, the box size and the bounds to keep inside.
2166
+ * @param params.left - Anchor x, in wrapper coordinates.
2167
+ * @param params.top - Anchor y, in wrapper coordinates.
2168
+ * @param params.offsetLeft - Gap between the anchor and the box, horizontally.
2169
+ * @param params.offsetTop - Gap between the anchor and the box, vertically.
2170
+ * @param params.box - Rendered size of the box.
2171
+ * @param params.bounds - Edges the box must keep inside, in wrapper coordinates.
2172
+ * @return The box's top-left corner, rounded to whole pixels.
2173
+ */
2174
+ const getBoundedPosition = ({ left, top, offsetLeft, offsetTop, box, bounds }) => {
2175
+ const rightX = left + offsetLeft;
2176
+ const leftX = left - offsetLeft - box.width;
2177
+ const rightOverflow = rightX + box.width - bounds.right;
2178
+ const leftOverflow = bounds.left - leftX;
2179
+ let x = rightOverflow > 0 && rightOverflow > leftOverflow ? leftX : rightX;
2180
+ const downY = top + offsetTop;
2181
+ const upY = top - offsetTop - box.height;
2182
+ const downOverflow = downY + box.height - bounds.bottom;
2183
+ const upOverflow = bounds.top - upY;
2184
+ let y = downOverflow > 0 && downOverflow > upOverflow ? upY : downY;
2185
+ x = Math.min(Math.max(x, bounds.left), Math.max(bounds.left, bounds.right - box.width));
2186
+ y = Math.min(Math.max(y, bounds.top), Math.max(bounds.top, bounds.bottom - box.height));
2187
+ return {
2188
+ x: Math.round(x),
2189
+ y: Math.round(y)
2190
+ };
2191
+ };
2192
+ /**
2193
+ * visx's `Tooltip`, positioned like its `TooltipWithBounds` but kept inside the
2194
+ * nearest clipping ancestor — or the viewport when there is none — rather than
2195
+ * inside its own parent. Rendered in-tree, a tooltip's parent is the chart
2196
+ * wrapper, which is often narrower than the box; measuring against the parent
2197
+ * alone would let the box spill into an `overflow: hidden` card and be cut off.
2198
+ *
2199
+ * Re-measures on every render, so a box whose content changes width between
2200
+ * two hovers is placed for its current size.
2201
+ *
2202
+ * @param props - visx `Tooltip` props.
2203
+ * @param props.left - Anchor x, in wrapper coordinates.
2204
+ * @param props.top - Anchor y, in wrapper coordinates.
2205
+ * @param props.offsetLeft - Gap between the anchor and the box, horizontally.
2206
+ * @param props.offsetTop - Gap between the anchor and the box, vertically.
2207
+ * @param props.style - Box styles; visx's defaults unless `unstyled`.
2208
+ * @param props.unstyled - Skip `style` and leave the box bare.
2209
+ * @param props.children - Box content.
2210
+ * @return The tooltip box.
2211
+ */
2212
+ const BoundedTooltip = ({ left = 0, top = 0, offsetLeft = DEFAULT_OFFSET, offsetTop = DEFAULT_OFFSET, style = defaultStyles, unstyled = false, children, ...rest }) => {
2213
+ const nodeRef = useRef(null);
2214
+ const clipRef = useRef(null);
2215
+ const [position, setPosition] = useState(null);
2216
+ useLayoutEffect(() => {
2217
+ const node = nodeRef.current;
2218
+ const wrapper = node?.parentElement;
2219
+ if (!node || !wrapper) return;
2220
+ if (clipRef.current?.wrapper !== wrapper) clipRef.current = {
2221
+ wrapper,
2222
+ clip: findClippingAncestor(wrapper)
2223
+ };
2224
+ const own = node.getBoundingClientRect();
2225
+ const wrapperRect = wrapper.getBoundingClientRect();
2226
+ const clipRect = clipRef.current.clip?.getBoundingClientRect() ?? {
2227
+ left: 0,
2228
+ top: 0,
2229
+ right: window.innerWidth,
2230
+ bottom: window.innerHeight
2231
+ };
2232
+ const next = getBoundedPosition({
2233
+ left,
2234
+ top,
2235
+ offsetLeft,
2236
+ offsetTop,
2237
+ box: {
2238
+ width: own.width,
2239
+ height: own.height
2240
+ },
2241
+ bounds: {
2242
+ left: clipRect.left - wrapperRect.left,
2243
+ top: clipRect.top - wrapperRect.top,
2244
+ right: clipRect.right - wrapperRect.left,
2245
+ bottom: clipRect.bottom - wrapperRect.top
2246
+ }
2247
+ });
2248
+ setPosition((current) => current && current.x === next.x && current.y === next.y ? current : next);
2249
+ }, [
2250
+ left,
2251
+ top,
2252
+ offsetLeft,
2253
+ offsetTop,
2254
+ children
2255
+ ]);
2256
+ const x = position?.x ?? left + offsetLeft;
2257
+ const y = position?.y ?? top + offsetTop;
2258
+ return /* @__PURE__ */ jsx(Tooltip, {
2259
+ ref: nodeRef,
2260
+ style: {
2261
+ position: "absolute",
2262
+ left: 0,
2263
+ top: 0,
2264
+ transform: `translate(${x}px, ${y}px)`,
2265
+ ...!unstyled && style
2266
+ },
2267
+ ...rest,
2268
+ children
2269
+ });
2270
+ };
2271
+ //#endregion
2272
+ //#region src/components/tooltip/xy-chart-tooltip.tsx
2273
+ const CROSSHAIR_STROKE_WIDTH = 1.5;
2274
+ const DEFAULT_GLYPH_RADIUS = 4;
2275
+ const FALLBACK_COLOR = "#222";
2276
+ const DEFAULT_TOOLTIP_Z_INDEX = 3;
2277
+ const PORTAL_OPTIONS = /* @__PURE__ */ new Set([
2278
+ "scroll",
2279
+ "debounce",
2280
+ "resizeObserverPolyfill"
2281
+ ]);
2282
+ const isValidNumber = (value) => typeof value === "number" && Number.isFinite(value);
2283
+ const scaleBandwidth = (scale) => {
2284
+ const bandwidth = scale?.bandwidth;
2285
+ return typeof bandwidth === "function" ? Number(bandwidth()) : 0;
2286
+ };
2287
+ const DefaultGlyph$1 = ({ x, y, size, color, glyphStyle, seriesKey }) => {
2288
+ const { theme } = useContext(DataContext) || {};
2289
+ return /* @__PURE__ */ jsx("circle", {
2290
+ cx: x,
2291
+ cy: y,
2292
+ r: size,
2293
+ fill: color,
2294
+ stroke: theme?.backgroundColor,
2295
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
2296
+ paintOrder: "fill",
2297
+ ...glyphStyle
2298
+ });
2299
+ };
2300
+ const defaultRenderGlyph$1 = ({ key, ...props }) => /* @__PURE__ */ jsx(DefaultGlyph$1, {
2301
+ seriesKey: key,
2302
+ ...props
2303
+ }, key);
2304
+ const XyChartTooltipContent = ({ tooltipContext, container, renderTooltip, renderGlyph = defaultRenderGlyph$1, glyphStyle, snapTooltipToDatumX = false, snapTooltipToDatumY = false, showVerticalCrosshair = false, showHorizontalCrosshair = false, showDatumGlyph = false, showSeriesGlyphs = false, verticalCrosshairStyle, horizontalCrosshairStyle, detectBounds = true, zIndex = DEFAULT_TOOLTIP_Z_INDEX, ...rest }) => {
2305
+ const tooltipProps = Object.fromEntries(Object.entries(rest).filter(([key]) => !PORTAL_OPTIONS.has(key)));
2306
+ const { colorScale, theme, innerHeight = 0, innerWidth = 0, margin, xScale, yScale, dataRegistry } = useContext(DataContext) || {};
2307
+ const tooltipContent = renderTooltip ? renderTooltip({
2308
+ ...tooltipContext,
2309
+ colorScale
2310
+ }) : null;
2311
+ if (tooltipContent == null) return null;
2312
+ const scaleX = xScale;
2313
+ const scaleY = yScale;
2314
+ const getDatumLeftTop = (key, datum) => {
2315
+ const entry = dataRegistry?.get(key);
2316
+ return {
2317
+ left: scaleX && entry?.xAccessor ? Number(scaleX(entry.xAccessor(datum))) + scaleBandwidth(xScale) / 2 : void 0,
2318
+ top: scaleY && entry?.yAccessor ? Number(scaleY(entry.yAccessor(datum))) + scaleBandwidth(yScale) / 2 : void 0
2319
+ };
2320
+ };
2321
+ const nearestDatum = tooltipContext.tooltipData?.nearestDatum;
2322
+ let { tooltipLeft, tooltipTop } = tooltipContext;
2323
+ if (nearestDatum && (snapTooltipToDatumX || snapTooltipToDatumY)) {
2324
+ const { left, top } = getDatumLeftTop(nearestDatum.key, nearestDatum.datum);
2325
+ if (snapTooltipToDatumX && isValidNumber(left)) tooltipLeft = left;
2326
+ if (snapTooltipToDatumY && isValidNumber(top)) tooltipTop = top;
2327
+ }
2328
+ const glyphs = [];
2329
+ if (showDatumGlyph || showSeriesGlyphs) {
2330
+ const size = Number(glyphStyle?.radius ?? DEFAULT_GLYPH_RADIUS);
2331
+ const labelColor = theme?.htmlLabel?.color ?? FALLBACK_COLOR;
2332
+ const fallbackColor = showSeriesGlyphs ? labelColor : theme?.gridStyles?.stroke ?? labelColor;
2333
+ let entries = [];
2334
+ if (showSeriesGlyphs) entries = Object.values(tooltipContext.tooltipData?.datumByKey ?? {});
2335
+ else if (nearestDatum) entries = [nearestDatum];
2336
+ for (const { key, datum, index } of entries) {
2337
+ const { left, top } = getDatumLeftTop(key, datum);
2338
+ if (!isValidNumber(left) || !isValidNumber(top)) continue;
2339
+ glyphs.push(/* @__PURE__ */ jsx("g", {
2340
+ className: "visx-tooltip-glyph",
2341
+ transform: `translate(${left}, ${top})`,
2342
+ children: renderGlyph({
2343
+ key,
2344
+ color: colorScale?.(key) ?? fallbackColor,
2345
+ datum,
2346
+ index,
2347
+ size,
2348
+ x: 0,
2349
+ y: 0,
2350
+ glyphStyle,
2351
+ isNearestDatum: nearestDatum?.key === key
2352
+ })
2353
+ }, key));
2354
+ }
2355
+ }
2356
+ const crosshairStroke = theme?.gridStyles?.stroke ?? theme?.htmlLabel?.color ?? FALLBACK_COLOR;
2357
+ const marginTop = margin?.top ?? 0;
2358
+ const marginLeft = margin?.left ?? 0;
2359
+ const TooltipComponent = detectBounds ? BoundedTooltip : Tooltip;
2360
+ const boxStyle = {
2361
+ ...defaultStyles,
2362
+ zIndex,
2363
+ background: theme?.backgroundColor ?? "white",
2364
+ boxShadow: `0 1px 2px ${theme?.htmlLabel?.color ? `${theme.htmlLabel.color}55` : "#22222255"}`,
2365
+ ...theme?.htmlLabel
2366
+ };
2367
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("g", {
2368
+ className: "visx-tooltip-overlay",
2369
+ pointerEvents: "none",
2370
+ children: [
2371
+ showVerticalCrosshair && isValidNumber(tooltipLeft) && /* @__PURE__ */ jsx("line", {
2372
+ className: "visx-crosshair visx-crosshair-vertical",
2373
+ x1: tooltipLeft,
2374
+ x2: tooltipLeft,
2375
+ y1: marginTop,
2376
+ y2: marginTop + innerHeight,
2377
+ stroke: crosshairStroke,
2378
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
2379
+ ...verticalCrosshairStyle
2380
+ }),
2381
+ showHorizontalCrosshair && isValidNumber(tooltipTop) && /* @__PURE__ */ jsx("line", {
2382
+ className: "visx-crosshair visx-crosshair-horizontal",
2383
+ x1: marginLeft,
2384
+ x2: marginLeft + innerWidth,
2385
+ y1: tooltipTop,
2386
+ y2: tooltipTop,
2387
+ stroke: crosshairStroke,
2388
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
2389
+ ...horizontalCrosshairStyle
2390
+ }),
2391
+ glyphs
2392
+ ]
2393
+ }), container && createPortal(/* @__PURE__ */ jsx(TooltipComponent, {
2394
+ left: tooltipLeft,
2395
+ top: tooltipTop,
2396
+ style: boxStyle,
2397
+ applyPositionStyle: true,
2398
+ ...tooltipProps,
2399
+ children: tooltipContent
2400
+ }), container)] });
2401
+ };
2402
+ /**
2403
+ * In-tree replacement for `@visx/xychart`'s `Tooltip`.
2404
+ *
2405
+ * visx renders its tooltip box, glyphs and crosshairs through portals appended
2406
+ * to `document.body`. That puts them in the page's root stacking context, where
2407
+ * they paint above every sticky or fixed element that lives inside a nested
2408
+ * stacking context, and no z-index on that element can change the order. This
2409
+ * component draws the glyphs and crosshairs straight into the chart SVG and
2410
+ * renders the tooltip box into the SVG's parent element, so all of them stack
2411
+ * as ordinary descendants of the chart.
2412
+ *
2413
+ * Render it as a child of `XYChart`. The element wrapping that `XYChart` must
2414
+ * be `position: relative` with the SVG at its origin, and `isolation: isolate`:
2415
+ * the box is placed with the SVG-local coordinates visx reports, and the
2416
+ * isolation keeps the box's `zIndex` from competing with page chrome. The box
2417
+ * flips and clamps to stay inside the nearest ancestor that clips its overflow
2418
+ * (or the viewport), so it may extend past the chart wrapper but is never cut
2419
+ * off unless that ancestor is smaller than the box itself.
2420
+ *
2421
+ * @param props - visx's `Tooltip` options. `scroll`, `debounce` and `resizeObserverPolyfill` are accepted and ignored.
2422
+ * @return An anchor in the SVG, plus the overlay and the tooltip box while the tooltip is open.
2423
+ */
2424
+ const XyChartTooltip = (props) => {
2425
+ const tooltipContext = useContext(TooltipContext);
2426
+ const [container, setContainer] = useState(null);
2427
+ const anchorRef = useCallback((node) => {
2428
+ setContainer(node?.ownerSVGElement?.parentElement ?? null);
2429
+ }, []);
2430
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("g", { ref: anchorRef }), tooltipContext?.tooltipOpen && /* @__PURE__ */ jsx(XyChartTooltipContent, {
2431
+ ...props,
2432
+ tooltipContext,
2433
+ container
2434
+ })] });
2435
+ };
2436
+ //#endregion
2104
2437
  //#region src/components/tooltip/accessible-tooltip.tsx
2105
- const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocusedClassName, series = [], mode = "group", ...props }) => {
2438
+ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocusedClassName, series = [], mode = "group", verticalCrosshairStyle, horizontalCrosshairStyle, ...props }) => {
2106
2439
  const tooltipContext = useContext(TooltipContext);
2440
+ const scopeElement = useChartScopeElement();
2441
+ const crosshairStroke = useMemo(() => {
2442
+ const stroke = resolveCssVariable(CATALOG_POINTERS.grid, scopeElement);
2443
+ return stroke ? { stroke } : void 0;
2444
+ }, [scopeElement]);
2445
+ const standaloneScopeClass = useStandaloneScopeClass();
2107
2446
  const tooltipData = useMemo(() => {
2108
2447
  if (mode !== "individual") return [];
2109
2448
  if (series.length === 0) return [];
@@ -2164,11 +2503,11 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
2164
2503
  tabIndex: -1,
2165
2504
  role: "tooltip",
2166
2505
  "aria-atomic": "true",
2167
- className: clsx(CHART_SCOPE_CLASS, keyboardFocusedClassName),
2506
+ className: clsx(standaloneScopeClass, keyboardFocusedClassName),
2168
2507
  children: tooltipContent
2169
2508
  }, `chart-tooltip-${selectedIndex}`);
2170
2509
  return /* @__PURE__ */ jsx("div", {
2171
- className: CHART_SCOPE_CLASS,
2510
+ className: standaloneScopeClass,
2172
2511
  role: "tooltip",
2173
2512
  "aria-live": "polite",
2174
2513
  children: tooltipContent
@@ -2178,14 +2517,23 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
2178
2517
  renderTooltip,
2179
2518
  selectedIndex,
2180
2519
  tooltipRef,
2181
- keyboardFocusedClassName
2520
+ keyboardFocusedClassName,
2521
+ standaloneScopeClass
2182
2522
  ]);
2183
- return /* @__PURE__ */ jsx(Tooltip, {
2523
+ return /* @__PURE__ */ jsx(XyChartTooltip, {
2184
2524
  ...props,
2525
+ verticalCrosshairStyle: {
2526
+ ...crosshairStroke,
2527
+ ...verticalCrosshairStyle
2528
+ },
2529
+ horizontalCrosshairStyle: {
2530
+ ...crosshairStroke,
2531
+ ...horizontalCrosshairStyle
2532
+ },
2185
2533
  renderTooltip: focusableRenderTooltip
2186
2534
  });
2187
2535
  };
2188
- const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints }) => {
2536
+ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints, onActivate }) => {
2189
2537
  return {
2190
2538
  tooltipRef: useCallback((element) => {
2191
2539
  if (element && selectedIndex !== void 0) element.focus();
@@ -2226,13 +2574,14 @@ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating,
2226
2574
  setSelectedIndex(void 0);
2227
2575
  setIsNavigating(false);
2228
2576
  chartRef.current?.focus();
2229
- }
2577
+ } else if ((event.key === "Enter" || event.key === " ") && selectedIndex !== void 0) onActivate?.(selectedIndex);
2230
2578
  }, [
2231
2579
  totalPoints,
2232
2580
  selectedIndex,
2233
2581
  setSelectedIndex,
2234
2582
  setIsNavigating,
2235
- chartRef
2583
+ chartRef,
2584
+ onActivate
2236
2585
  ])
2237
2586
  };
2238
2587
  };
@@ -2519,72 +2868,90 @@ const getCurveType = (type, smoothing) => {
2519
2868
  default: return curveLinear;
2520
2869
  }
2521
2870
  };
2522
- const dateTimeFormat = (options) => {
2523
- let formatter;
2524
- return (timestamp) => {
2525
- if (!Number.isFinite(Number(timestamp))) return new Date(timestamp).toLocaleDateString();
2526
- formatter = formatter ?? new Intl.DateTimeFormat(void 0, options);
2527
- return formatter.format(timestamp);
2528
- };
2529
- };
2530
- const formatYearTick = dateTimeFormat({ year: "numeric" });
2531
- const formatDateTick = dateTimeFormat({
2871
+ const YEAR_TICK = { year: "numeric" };
2872
+ const DATE_TICK = {
2532
2873
  month: "short",
2533
2874
  day: "numeric"
2534
- });
2535
- const formatHourTick = dateTimeFormat({
2536
- hour: "numeric",
2537
- hour12: true
2538
- });
2539
- const formatMonthTick = dateTimeFormat({ month: "short" });
2875
+ };
2876
+ const HOUR_TICK = { hour: "numeric" };
2877
+ const MONTH_TICK = { month: "short" };
2540
2878
  const boundaryFormat = (isAnchor, atBoundary, between) => {
2541
2879
  const format = (timestamp) => isAnchor(new Date(timestamp)) ? atBoundary(timestamp) : between(timestamp);
2542
2880
  format.isAnchor = isAnchor;
2543
2881
  return format;
2544
2882
  };
2545
- const formatDateOrHourTick = boundaryFormat((date) => date.getHours() === 0 && date.getMinutes() === 0, formatDateTick, formatHourTick);
2546
- const formatMonthOrYearTick = boundaryFormat((date) => date.getMonth() === 0, formatYearTick, formatMonthTick);
2883
+ const tickFormats = (formatting) => {
2884
+ const clock = createZonedClock(formatting.timeZone);
2885
+ const year = createDateFormatter(YEAR_TICK, formatting);
2886
+ const date = createDateFormatter(DATE_TICK, formatting);
2887
+ const hour = createDateFormatter(HOUR_TICK, formatting);
2888
+ const month = createDateFormatter(MONTH_TICK, formatting);
2889
+ const datedTick = (timestamp) => date(timestamp);
2890
+ datedTick.anchorsAtLabelChange = true;
2891
+ return {
2892
+ year,
2893
+ date: datedTick,
2894
+ dateOrHour: boundaryFormat((value) => clock(value).hour === 0, date, hour),
2895
+ hour,
2896
+ monthOrYear: boundaryFormat((value) => clock(value).month === 1, year, month)
2897
+ };
2898
+ };
2547
2899
  const getSpan = (sortedData) => {
2548
- const bounds = sortedData.map((datom) => [datom.data.at(0)?.date, datom.data.at(-1)?.date]).filter(([first, last]) => first !== void 0 && last !== void 0);
2900
+ const bounds = sortedData.map((datom) => [datom.data.at(0)?.date, datom.data.at(-1)?.date]).filter(([first, last]) => Number.isFinite(Number(first)) && Number.isFinite(Number(last)));
2549
2901
  if (!bounds.length) return null;
2550
2902
  return {
2551
2903
  minX: Math.min(...bounds.map(([first]) => Number(first))),
2552
2904
  maxX: Math.max(...bounds.map(([, last]) => Number(last)))
2553
2905
  };
2554
2906
  };
2555
- const getPointSpacingInHours = (sortedData) => {
2556
- return sortedData.reduce((spacing, datom) => datom.data.reduce((seriesSpacing, point, index) => {
2557
- const previous = datom.data[index - 1];
2558
- if (previous?.date === void 0 || point?.date === void 0) return seriesSpacing;
2559
- return Math.min(seriesSpacing, Math.abs(differenceInHours(point.date, previous.date)));
2560
- }, spacing), Number.POSITIVE_INFINITY);
2561
- };
2562
- const SUB_DAILY_SPACING_HOURS = 23;
2563
- const MONTHLY_SPACING_HOURS = 672;
2564
- const getBucketResolution = (sortedData, tickResolution) => {
2565
- if (tickResolution) return tickResolution === "week" ? "day" : tickResolution;
2566
- const spacingInHours = getPointSpacingInHours(sortedData);
2567
- if (spacingInHours < SUB_DAILY_SPACING_HOURS) return "hour";
2568
- if (!Number.isFinite(spacingInHours) || spacingInHours < MONTHLY_SPACING_HOURS) return "day";
2569
- return spacingInHours < 12 * MONTHLY_SPACING_HOURS ? "month" : "year";
2570
- };
2571
- const getFormatter = (sortedData, tickResolution) => {
2907
+ /**
2908
+ * The most informative tick format for a bucket resolution and the span on screen.
2909
+ *
2910
+ * @param sortedData - Series as returned by `useChartDataTransform`.
2911
+ * @param tickResolution - Caller-declared bucket resolution, when known.
2912
+ * @param formatting - Host locale and time zone.
2913
+ * @param domain - The effective scale domain, or undefined for the data's own extent.
2914
+ * @return The formatter the axis labels its ticks with.
2915
+ */
2916
+ const getFormatter = (sortedData, tickResolution, formatting = {}, domain) => {
2917
+ const format = tickFormats(formatting);
2572
2918
  const resolution = getBucketResolution(sortedData, tickResolution);
2573
- if (resolution === "year") return formatYearTick;
2574
- if (resolution === "month") return formatMonthOrYearTick;
2575
- const span = getSpan(sortedData);
2576
- if (!span) return formatDateTick;
2919
+ if (resolution === "year") return format.year;
2920
+ if (resolution === "month") return format.monthOrYear;
2921
+ const span = domain ? {
2922
+ minX: domain[0].getTime(),
2923
+ maxX: domain[1].getTime()
2924
+ } : getSpan(sortedData);
2925
+ if (!span) return format.date;
2577
2926
  const diffInHours = Math.abs(differenceInHours(span.maxX, span.minX));
2578
2927
  if (resolution === "hour") {
2579
- if (diffInHours <= 24) return formatHourTick;
2580
- if (diffInHours <= 168) return formatDateOrHourTick;
2928
+ if (diffInHours < 24) return format.hour;
2929
+ if (diffInHours <= 168) return format.dateOrHour;
2581
2930
  }
2582
- return Math.abs(differenceInYears(span.maxX, span.minX)) <= 1 ? formatDateTick : formatYearTick;
2931
+ return Math.abs(differenceInYears(span.maxX, span.minX)) <= 1 ? format.date : format.year;
2932
+ };
2933
+ /**
2934
+ * The instants a set of series spans, as a scale domain.
2935
+ *
2936
+ * @param sortedData - Series as returned by `useChartDataTransform`.
2937
+ * @return Earliest and latest dated point, or undefined when nothing is dated.
2938
+ */
2939
+ const getSeriesExtent = (sortedData) => {
2940
+ const span = getSpan(sortedData);
2941
+ return span ? [new Date(span.minX), new Date(span.maxX)] : void 0;
2942
+ };
2943
+ const getAnchorIndices = (domain, tickFormatter, labels) => {
2944
+ if (tickFormatter.anchorsAtLabelChange) return labels.reduce((indices, label, index) => {
2945
+ if (index === 0 || label !== labels[index - 1]) indices.push(index);
2946
+ return indices;
2947
+ }, []);
2948
+ const { isAnchor } = tickFormatter;
2949
+ if (!isAnchor) return [];
2950
+ return domain.reduce((indices, date, index) => {
2951
+ if (isAnchor(date)) indices.push(index);
2952
+ return indices;
2953
+ }, []);
2583
2954
  };
2584
- const getAnchorIndices = (domain, isAnchor) => domain.reduce((indices, date, index) => {
2585
- if (isAnchor(date)) indices.push(index);
2586
- return indices;
2587
- }, []);
2588
2955
  const getCandidateSteps = (length, maxTicks, anchorSpacing) => {
2589
2956
  const steps = /* @__PURE__ */ new Set();
2590
2957
  for (let count = maxTicks; count > 1; count--) {
@@ -2599,26 +2966,25 @@ const getCandidateSteps = (length, maxTicks, anchorSpacing) => {
2599
2966
  return steps;
2600
2967
  };
2601
2968
  /**
2602
- * Tick values for a band time axis.
2969
+ * Tick values sampled by index, for the bar chart's band axis and for evenly
2970
+ * spaced points on a continuous one.
2603
2971
  *
2604
- * visx samples a band domain by index from offset zero, blind to which labels
2605
- * carry a boundary and without collapsing repeats — so the tick that prints the
2606
- * year or the date often isn't sampled at all, and a long series can show the
2607
- * same label twice. Choose the values instead: sweep the evenly spaced
2608
- * candidates, including those that step from anchor to anchor, and keep the one
2609
- * that reaches the most anchors without thinning the axis or putting two
2610
- * identical labels side by side.
2972
+ * visx samples by index from offset zero, blind to which labels carry a boundary
2973
+ * and without collapsing repeats — so the tick that prints the year or the date
2974
+ * often isn't sampled at all, and a long series can show the same label twice.
2975
+ * Choose the values instead: sweep the evenly spaced candidates, including those
2976
+ * that step from anchor to anchor, and keep the one that reaches the most anchors
2977
+ * without thinning the axis or putting two identical labels side by side.
2611
2978
  *
2612
- * @param domain - Band domain, in axis order.
2979
+ * @param domain - Buckets in axis order.
2613
2980
  * @param tickFormatter - Formatter the axis will render these values with.
2614
2981
  * @param maxTicks - Most ticks the axis should carry.
2615
2982
  * @return Values to hand the axis as `tickValues`.
2616
2983
  */
2617
2984
  const getBandTickValues = (domain, tickFormatter, maxTicks) => {
2618
2985
  if (!domain.length) return [];
2619
- const { isAnchor } = tickFormatter;
2620
- const anchorIndices = isAnchor ? getAnchorIndices(domain, isAnchor) : [];
2621
2986
  const domainLabels = domain.map((date) => tickFormatter(date.getTime()));
2987
+ const anchorIndices = getAnchorIndices(domain, tickFormatter, domainLabels);
2622
2988
  const candidates = [];
2623
2989
  const consider = (indices) => {
2624
2990
  if (!indices.length || indices.length > maxTicks) return;
@@ -2630,15 +2996,108 @@ const getBandTickValues = (domain, tickFormatter, maxTicks) => {
2630
2996
  for (let index = offset; index < domain.length; index += step) indices.push(index);
2631
2997
  consider(indices);
2632
2998
  }
2633
- for (let stride = 1; stride <= anchorIndices.length; stride++) consider(anchorIndices.filter((_, position) => position % stride === 0));
2999
+ const minStride = Math.max(1, Math.ceil(anchorIndices.length / maxTicks));
3000
+ for (let stride = minStride; stride <= anchorIndices.length; stride++) {
3001
+ const stepped = [];
3002
+ for (let position = 0; position < anchorIndices.length; position += stride) stepped.push(anchorIndices[position]);
3003
+ consider(stepped);
3004
+ }
2634
3005
  if (!candidates.length) return [domain[0]];
2635
3006
  const densest = candidates.reduce((most, indices) => Math.max(most, indices.length), 0);
2636
- const anchorsIn = (indices) => isAnchor ? indices.filter((index) => isAnchor(domain[index])).length : 0;
3007
+ const anchorIndexSet = new Set(anchorIndices);
3008
+ const anchorsIn = (indices) => indices.filter((index) => anchorIndexSet.has(index)).length;
2637
3009
  return candidates.filter((indices) => indices.length >= densest - 1).reduce((chosen, indices) => {
2638
3010
  const gain = anchorsIn(indices) - anchorsIn(chosen);
2639
3011
  return gain > 0 || gain === 0 && indices.length > chosen.length ? indices : chosen;
2640
3012
  }).map((index) => domain[index]);
2641
3013
  };
3014
+ /**
3015
+ * The most x-axis ticks a chart of this width has room for.
3016
+ *
3017
+ * @param chartWidth - Chart width in pixels.
3018
+ * @return A tick budget of at least one.
3019
+ */
3020
+ const getMaxTicksForWidth = (chartWidth) => Math.max(1, Math.floor(chartWidth / X_TICK_WIDTH));
3021
+ const MIN_GAP_IN_LABELS = .9;
3022
+ const nearestValue = (sorted, target) => {
3023
+ let low = 0;
3024
+ let high = sorted.length - 1;
3025
+ while (low < high) {
3026
+ const middle = Math.floor((low + high) / 2);
3027
+ if (sorted[middle] < target) low = middle + 1;
3028
+ else high = middle;
3029
+ }
3030
+ const above = sorted[low];
3031
+ const below = low > 0 ? sorted[low - 1] : above;
3032
+ return Math.abs(above - target) <= Math.abs(below - target) ? above : below;
3033
+ };
3034
+ const getPositionTickValues = (points, tickFormatter, maxTicks, axis) => {
3035
+ const times = points.map((point) => point.getTime());
3036
+ const { first, last } = axis;
3037
+ if (maxTicks <= 1 || times.length === 1 || first === last) return [points[0]];
3038
+ const step = (last - first) / (maxTicks - 1);
3039
+ const labels = points.map((point) => tickFormatter(point.getTime()));
3040
+ const minGap = (last - first) / maxTicks * MIN_GAP_IN_LABELS;
3041
+ const anchors = getAnchorIndices(points, tickFormatter, labels).map((index) => times[index]);
3042
+ const chosen = [];
3043
+ let lastLabel = null;
3044
+ for (let index = 0; index < maxTicks; index++) {
3045
+ const target = first + index * step;
3046
+ const anchor = anchors.length ? nearestValue(anchors, target) : null;
3047
+ let tick = anchor !== null && Math.abs(anchor - target) <= step / 2 ? anchor : null;
3048
+ if (tick === null) {
3049
+ const point = nearestValue(times, target);
3050
+ tick = Math.abs(point - target) <= step / 2 ? point : target;
3051
+ }
3052
+ const previous = chosen[chosen.length - 1];
3053
+ if (previous !== void 0 && (tick <= previous || tick - previous < minGap)) continue;
3054
+ const label = tickFormatter(tick);
3055
+ if (label === lastLabel) continue;
3056
+ chosen.push(tick);
3057
+ lastLabel = label;
3058
+ }
3059
+ return chosen.map((timestamp) => new Date(timestamp));
3060
+ };
3061
+ const isIndexSamplingUnusable = (ticks, points, maxTicks, axis) => {
3062
+ if (ticks.length < 2 || points.length < 2) return false;
3063
+ const span = axis.last - axis.first;
3064
+ if (!span) return false;
3065
+ return ticks.slice(1).reduce((nearest, tick, index) => Math.min(nearest, tick.getTime() - ticks[index].getTime()), Infinity) < span / maxTicks * MIN_GAP_IN_LABELS || ticks.length < Math.min(maxTicks, points.length) / 3;
3066
+ };
3067
+ /**
3068
+ * Tick values for a continuous time axis, chosen from the points it draws.
3069
+ *
3070
+ * Sampled by index while the points are evenly spaced, which keeps the axis on
3071
+ * the calendar boundaries `getBandTickValues` steers towards, and by position
3072
+ * once they are not, where an index stride crowds one end of the scale.
3073
+ *
3074
+ * Values are taken from the data wherever there is one to take, so a local time
3075
+ * that does not exist cannot become a tick and DST needs no special case. Only a
3076
+ * target stranded in a gap is constructed, as a plain instant.
3077
+ *
3078
+ * @param sortedData - Series as returned by `useChartDataTransform`.
3079
+ * @param domain - The effective scale domain, or undefined for all data.
3080
+ * @param tickFormatter - The formatter the ticks will be labeled with.
3081
+ * @param maxTicks - The most ticks the axis has room for.
3082
+ * @return Tick dates, or null when no series carries a usable date.
3083
+ */
3084
+ const getTimeAxisTickValues = (sortedData, domain, tickFormatter, maxTicks) => {
3085
+ const byTimestamp = /* @__PURE__ */ new Map();
3086
+ for (const series of sortedData) for (const point of series.data) {
3087
+ const date = point.date;
3088
+ if (date instanceof Date && Number.isFinite(date.getTime())) byTimestamp.set(date.getTime(), date);
3089
+ }
3090
+ if (!byTimestamp.size) return null;
3091
+ const min = domain?.[0]?.getTime();
3092
+ const max = domain?.[1]?.getTime();
3093
+ const visible = [...byTimestamp.keys()].sort((a, b) => a - b).filter((timestamp) => (min === void 0 || timestamp >= min) && (max === void 0 || timestamp <= max)).map((timestamp) => byTimestamp.get(timestamp));
3094
+ const axis = {
3095
+ first: min ?? visible[0].getTime(),
3096
+ last: max ?? visible[visible.length - 1].getTime()
3097
+ };
3098
+ const ticks = getBandTickValues(visible, tickFormatter, maxTicks);
3099
+ return isIndexSamplingUnusable(ticks, visible, maxTicks, axis) ? getPositionTickValues(visible, tickFormatter, maxTicks, axis) : ticks;
3100
+ };
2642
3101
  const guessOptimalNumTicks = (data, chartWidth, tickFormatter) => {
2643
3102
  const span = getSpan(data);
2644
3103
  if (!span) return 1;
@@ -2653,7 +3112,43 @@ const guessOptimalNumTicks = (data, chartWidth, tickFormatter) => {
2653
3112
  if (ticks.some((tick, idx) => idx > 0 && tick === ticks[idx - 1])) continue;
2654
3113
  return ticks.length;
2655
3114
  }
2656
- return secondBestGuess;
3115
+ return Math.max(1, secondBestGuess);
3116
+ };
3117
+ //#endregion
3118
+ //#region src/charts/private/time-axis-options.ts
3119
+ const toDateDomain = (domain) => domain ? [new Date(domain[0]), new Date(domain[1])] : void 0;
3120
+ /**
3121
+ * The x-axis options for a continuous time chart, shared by LineChart and AreaChart.
3122
+ *
3123
+ * Kept in one place so the tick-selection logic isn't duplicated between them.
3124
+ *
3125
+ * @param args - Named arguments.
3126
+ * @param args.dataSorted - Series as returned by `useChartDataTransform`.
3127
+ * @param args.width - Chart width in pixels, which bounds how many ticks fit.
3128
+ * @param args.axisOptions - The caller's `options.axis.x`.
3129
+ * @param args.scaleDomain - The caller's `options.xScale.domain`.
3130
+ * @param args.zoomDomain - The chart's current zoom window, when zoomed.
3131
+ * @param args.formatting - Host locale and time zone.
3132
+ * @param args.isSeriesRendered - Whether visx mounts a series, i.e. the legend shows it. Defaults to all of them.
3133
+ * @return Options ready to spread into the chart's `axis.x`; `orientation` is always set.
3134
+ */
3135
+ const buildTimeAxisOptions = ({ dataSorted, width, axisOptions, scaleDomain, zoomDomain, formatting, isSeriesRendered }) => {
3136
+ const { tickResolution, tickFormat, tickValues: callerTickValues, numTicks: callerNumTicks, orientation: callerOrientation, display: callerDisplay, ...rest } = axisOptions ?? {};
3137
+ const rendered = isSeriesRendered ? dataSorted.filter(isSeriesRendered) : dataSorted;
3138
+ const effectiveDomain = zoomDomain ?? toDateDomain(scaleDomain) ?? getSeriesExtent(rendered);
3139
+ const ownFormatter = getFormatter(rendered, tickResolution, formatting, effectiveDomain);
3140
+ const formatter = tickFormat || ownFormatter;
3141
+ const ownTickValues = tickFormat || callerTickValues ? null : getTimeAxisTickValues(rendered, effectiveDomain, ownFormatter, callerNumTicks ?? getMaxTicksForWidth(width));
3142
+ const tickValues = callerTickValues ?? (ownTickValues?.length ? ownTickValues : void 0);
3143
+ const numTicks = callerNumTicks ?? (tickValues ? void 0 : guessOptimalNumTicks(rendered, width, formatter));
3144
+ return {
3145
+ orientation: callerOrientation ?? "bottom",
3146
+ tickFormat: formatter,
3147
+ display: callerDisplay ?? true,
3148
+ ...rest,
3149
+ ...tickValues ? { tickValues } : {},
3150
+ ...numTicks === void 0 ? {} : { numTicks }
3151
+ };
2657
3152
  };
2658
3153
  //#endregion
2659
3154
  //#region src/charts/private/with-responsive/with-responsive.module.scss
@@ -2934,6 +3429,9 @@ function ZoomResetButton({ onClick }) {
2934
3429
  });
2935
3430
  }
2936
3431
  //#endregion
3432
+ //#region src/charts/private/xy-plot/xy-plot.module.scss
3433
+ var xy_plot_module_default = { "xy-plot": "a8ccharts-bIyhgG-xy-plot" };
3434
+ //#endregion
2937
3435
  //#region src/charts/line-chart/line-chart.module.scss
2938
3436
  var line_chart_module_default = {
2939
3437
  "line-chart": "a8ccharts-inuQka-line-chart",
@@ -2999,7 +3497,7 @@ const LineChartAnnotationLabelWithPopover = ({ title, subtitle, renderLabel, ren
2999
3497
  style: {
3000
3498
  width: `44px`,
3001
3499
  height: `44px`,
3002
- transform: `translate(${44 / 2}px, 0)`
3500
+ transform: `translate(22px, 0)`
3003
3501
  },
3004
3502
  "aria-label": title || __("View details", "jetpack-charts"),
3005
3503
  children: renderLabel({
@@ -3174,12 +3672,14 @@ const getVerticalAnchor = (subjectType, isFlippedVertically, y, yMax, height) =>
3174
3672
  };
3175
3673
  const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", styles: datumStyles, testId, renderLabel, renderLabelPopover }) => {
3176
3674
  const providerTheme = useGlobalChartsTheme();
3177
- const scopeElement = useChartScopeElement();
3178
3675
  const { xScale, yScale } = useContext(DataContext) || {};
3179
3676
  const labelRef = useRef(null);
3180
3677
  const [height, setHeight] = useState(null);
3181
- const styles = deepmerge(providerTheme.annotationStyles ?? {}, datumStyles ?? {});
3182
- const resolveColor = (value) => value ? resolveCssVariable(value, scopeElement) ?? value : value;
3678
+ const styles = useMemo(() => deepmerge.all([
3679
+ ANNOTATION_POINTERS,
3680
+ providerTheme.annotationStyles ?? {},
3681
+ datumStyles ?? {}
3682
+ ]), [providerTheme.annotationStyles, datumStyles]);
3183
3683
  useEffect(() => {
3184
3684
  if (labelRef.current?.getBBox) {
3185
3685
  const bbox = labelRef.current.getBBox();
@@ -3265,15 +3765,8 @@ const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", s
3265
3765
  dx,
3266
3766
  dy,
3267
3767
  children: [
3268
- /* @__PURE__ */ jsx(Connector, {
3269
- ...styles?.connector,
3270
- stroke: resolveColor(styles?.connector?.stroke)
3271
- }),
3272
- subjectType === "circle" && /* @__PURE__ */ jsx(CircleSubject, {
3273
- ...styles?.circleSubject,
3274
- fill: resolveColor(styles?.circleSubject?.fill),
3275
- stroke: resolveColor(styles?.circleSubject?.stroke)
3276
- }),
3768
+ /* @__PURE__ */ jsx(Connector, { ...styles?.connector }),
3769
+ subjectType === "circle" && /* @__PURE__ */ jsx(CircleSubject, { ...styles?.circleSubject }),
3277
3770
  subjectType === "line-vertical" && /* @__PURE__ */ jsx(LineSubject, {
3278
3771
  min: yMax,
3279
3772
  max: yMin,
@@ -3307,8 +3800,6 @@ const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", s
3307
3800
  title,
3308
3801
  subtitle,
3309
3802
  ...styles?.label,
3310
- anchorLineStroke: resolveColor(styles?.label?.anchorLineStroke),
3311
- backgroundFill: resolveColor(styles?.label?.backgroundFill),
3312
3803
  ...labelPosition,
3313
3804
  horizontalAnchor: getHorizontalAnchor(subjectType, isFlippedHorizontally),
3314
3805
  verticalAnchor: getVerticalAnchor(subjectType, isFlippedVertically, y, yMax, height ?? ANNOTATION_INIT_HEIGHT)
@@ -3356,16 +3847,33 @@ const toNumber = (val) => {
3356
3847
  const num = typeof val === "number" ? val : parseFloat(val);
3357
3848
  return isNaN(num) ? void 0 : num;
3358
3849
  };
3850
+ const TOOLTIP_DATE = {};
3851
+ const TOOLTIP_FORMAT_BY_RESOLUTION$1 = {
3852
+ hour: {
3853
+ year: "numeric",
3854
+ month: "numeric",
3855
+ day: "numeric",
3856
+ hour: "numeric"
3857
+ },
3858
+ day: TOOLTIP_DATE,
3859
+ month: TOOLTIP_DATE,
3860
+ year: TOOLTIP_DATE
3861
+ };
3862
+ const TooltipDate = ({ date, displayResolution }) => {
3863
+ const formatting = useChartFormatting();
3864
+ const format = useMemo(() => createDateFormatter(TOOLTIP_FORMAT_BY_RESOLUTION$1[displayResolution], formatting), [displayResolution, formatting]);
3865
+ return /* @__PURE__ */ jsx(Fragment$1, { children: date ? format(date) : null });
3866
+ };
3359
3867
  /**
3360
3868
  * Default visx-tooltip render that prints the hovered date as a heading and
3361
3869
  * one row per visible series (label + formatted value), sorted descending by
3362
3870
  * value. Reused by AreaChart, which has the same multi-series shape.
3363
3871
  *
3364
- * @param params - visx `RenderTooltipParams< DataPointDate >`.
3872
+ * @param params - visx `RenderTooltipParams< DataPointDate >`, plus the chart's optional `bucketInfo`.
3365
3873
  * @return Tooltip JSX, or `null` when no datum is hovered.
3366
3874
  */
3367
3875
  const renderDefaultTooltip = (params) => {
3368
- const { tooltipData } = params;
3876
+ const { tooltipData, bucketInfo } = params;
3369
3877
  const nearestDatum = tooltipData?.nearestDatum?.datum;
3370
3878
  if (!nearestDatum) return null;
3371
3879
  const tooltipPoints = Object.entries(tooltipData?.datumByKey || {}).map(([key, { datum }]) => ({
@@ -3376,7 +3884,10 @@ const renderDefaultTooltip = (params) => {
3376
3884
  className: line_chart_module_default["line-chart__tooltip"],
3377
3885
  children: [/* @__PURE__ */ jsx("div", {
3378
3886
  className: line_chart_module_default["line-chart__tooltip-date"],
3379
- children: nearestDatum.date?.toLocaleDateString()
3887
+ children: /* @__PURE__ */ jsx(TooltipDate, {
3888
+ date: nearestDatum.date,
3889
+ displayResolution: bucketInfo?.displayResolution ?? "day"
3890
+ })
3380
3891
  }), tooltipPoints.map((point) => /* @__PURE__ */ jsxs(Stack, {
3381
3892
  direction: "row",
3382
3893
  align: "center",
@@ -3420,14 +3931,14 @@ const LineChartScalesRef = ({ chartRef, width, height, margin }) => {
3420
3931
  ]);
3421
3932
  return null;
3422
3933
  };
3423
- const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, withStartGlyphs = false, withEndGlyphs = false, animation, options = {}, onPointerDown = void 0, onPointerUp = void 0, onPointerMove = void 0, onPointerOut = void 0, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
3934
+ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, withStartGlyphs = false, withEndGlyphs = false, animation, options = {}, onPointerDown = void 0, onPointerUp = void 0, onPointerMove = void 0, onPointerOut = void 0, onDatumActivate = void 0, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
3424
3935
  const legendInteractive = legend.interactive ?? false;
3425
3936
  const legendCollapseGroups = legend.collapseGroups ?? false;
3426
3937
  const legendShape = legend.shape ?? "line";
3427
3938
  const legendPosition = legend.position ?? "bottom";
3428
- const providerTheme = useGlobalChartsTheme();
3939
+ const formatting = useChartFormatting();
3429
3940
  const theme = useXYChartTheme(data);
3430
- const resolvedBackgroundColor = theme.backgroundColor ?? providerTheme.backgroundColor;
3941
+ const resolvedBackgroundColor = theme.backgroundColor;
3431
3942
  const chartId = useChartId(providedChartId);
3432
3943
  const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
3433
3944
  const isSeriesVisible = useCallback((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
@@ -3482,26 +3993,36 @@ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
3482
3993
  }
3483
3994
  return min < max ? [min, max] : void 0;
3484
3995
  }, [rescaleYOnVisibilityChange, dataSorted]);
3996
+ const activateSelectedPoint = useCallback((index) => {
3997
+ const series = dataSorted[0];
3998
+ const datum = series?.data[index];
3999
+ if (series && datum) onDatumActivate?.({
4000
+ datum,
4001
+ index,
4002
+ key: series.label
4003
+ });
4004
+ }, [dataSorted, onDatumActivate]);
3485
4005
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
3486
4006
  selectedIndex,
3487
4007
  setSelectedIndex,
3488
4008
  isNavigating,
3489
4009
  setIsNavigating,
3490
4010
  chartRef,
3491
- totalPoints: dataSorted[0]?.data.length || 0
4011
+ totalPoints: dataSorted[0]?.data.length || 0,
4012
+ onActivate: activateSelectedPoint
3492
4013
  });
3493
4014
  const chartOptions = useMemo(() => {
3494
- const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
3495
- const formatter = tickFormat || getFormatter(dataSorted, tickResolution);
3496
4015
  return {
3497
4016
  axis: {
3498
- x: {
3499
- orientation: "bottom",
3500
- numTicks: guessOptimalNumTicks(dataSorted, width, formatter),
3501
- tickFormat: formatter,
3502
- display: true,
3503
- ...xAxisOptions
3504
- },
4017
+ x: buildTimeAxisOptions({
4018
+ dataSorted,
4019
+ width,
4020
+ axisOptions: options?.axis?.x,
4021
+ scaleDomain: options?.xScale?.domain,
4022
+ zoomDomain: zoom.domain,
4023
+ formatting,
4024
+ isSeriesRendered: (series) => isSeriesVisible(series.label)
4025
+ }),
3505
4026
  y: {
3506
4027
  orientation: "left",
3507
4028
  numTicks: 4,
@@ -3528,7 +4049,14 @@ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
3528
4049
  dataSorted,
3529
4050
  width,
3530
4051
  zoom.domain,
3531
- stableYDomain
4052
+ stableYDomain,
4053
+ formatting,
4054
+ isSeriesVisible
4055
+ ]);
4056
+ const bucketInfo = useMemo(() => getBucketInfo(dataSorted.filter((series) => isSeriesVisible(series.label)), options?.axis?.x?.tickResolution), [
4057
+ dataSorted,
4058
+ isSeriesVisible,
4059
+ options?.axis?.x?.tickResolution
3532
4060
  ]);
3533
4061
  const tooltipRenderGlyph = useMemo(() => {
3534
4062
  return (props) => {
@@ -3552,42 +4080,48 @@ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
3552
4080
  const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme);
3553
4081
  const error = validateData$4(dataSorted);
3554
4082
  const isDataValid = !error;
4083
+ const legendItems = useChartLegendItems(dataSorted, useMemo(() => ({
4084
+ withGlyph: withLegendGlyph,
4085
+ glyphSize: Math.max(0, toNumber(glyphStyle?.radius) ?? 4),
4086
+ collapseGroups: legendCollapseGroups,
4087
+ renderGlyph
4088
+ }), [
4089
+ withLegendGlyph,
4090
+ glyphStyle?.radius,
4091
+ legendCollapseGroups,
4092
+ renderGlyph
4093
+ ]), legendShape);
4094
+ const chartMetadata = useMemo(() => ({
4095
+ withGradientFill,
4096
+ smoothing,
4097
+ curveType,
4098
+ withStartGlyphs,
4099
+ withEndGlyphs,
4100
+ withLegendGlyph
4101
+ }), [
4102
+ withGradientFill,
4103
+ smoothing,
4104
+ curveType,
4105
+ withStartGlyphs,
4106
+ withEndGlyphs,
4107
+ withLegendGlyph
4108
+ ]);
3555
4109
  useChartRegistration({
3556
4110
  chartId,
3557
- legendItems: useChartLegendItems(dataSorted, useMemo(() => ({
3558
- withGlyph: withLegendGlyph,
3559
- glyphSize: Math.max(0, toNumber(glyphStyle?.radius) ?? 4),
3560
- collapseGroups: legendCollapseGroups,
3561
- renderGlyph
3562
- }), [
3563
- withLegendGlyph,
3564
- glyphStyle?.radius,
3565
- legendCollapseGroups,
3566
- renderGlyph
3567
- ]), legendShape),
4111
+ legendItems,
3568
4112
  chartType: "line",
3569
4113
  isDataValid,
3570
- metadata: useMemo(() => ({
3571
- withGradientFill,
3572
- smoothing,
3573
- curveType,
3574
- withStartGlyphs,
3575
- withEndGlyphs,
3576
- withLegendGlyph
3577
- }), [
3578
- withGradientFill,
3579
- smoothing,
3580
- curveType,
3581
- withStartGlyphs,
3582
- withEndGlyphs,
3583
- withLegendGlyph
3584
- ])
4114
+ metadata: chartMetadata
3585
4115
  });
3586
4116
  const prefersReducedMotion = usePrefersReducedMotion();
3587
4117
  const accessors = {
3588
4118
  xAccessor: (d) => d?.date,
3589
4119
  yAccessor: (d) => d?.value
3590
4120
  };
4121
+ const tooltipRenderer = useMemo(() => (params) => renderTooltip({
4122
+ ...params,
4123
+ bucketInfo
4124
+ }), [renderTooltip, bucketInfo]);
3591
4125
  if (error) return /* @__PURE__ */ jsx("div", {
3592
4126
  className: clsx("line-chart", line_chart_module_default["line-chart"]),
3593
4127
  children: error
@@ -3636,7 +4170,7 @@ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
3636
4170
  onBlur: onChartBlur,
3637
4171
  children: chartHeight > 0 && /* @__PURE__ */ jsxs("div", {
3638
4172
  ref: chartRef,
3639
- style: { position: "relative" },
4173
+ className: xy_plot_module_default["xy-plot"],
3640
4174
  children: [zoomable && zoom.domain && /* @__PURE__ */ jsx(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ jsxs(XYChart, {
3641
4175
  theme,
3642
4176
  width,
@@ -3728,7 +4262,7 @@ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
3728
4262
  snapTooltipToDatumX: true,
3729
4263
  snapTooltipToDatumY: true,
3730
4264
  showSeriesGlyphs: true,
3731
- renderTooltip,
4265
+ renderTooltip: tooltipRenderer,
3732
4266
  renderGlyph: tooltipRenderGlyph,
3733
4267
  glyphStyle,
3734
4268
  showVerticalCrosshair: withTooltipCrosshairs?.showVertical,
@@ -3866,12 +4400,11 @@ const HoverGlyphs = ({ visibleSeries, stacked, stackOffset, getElementStyles, st
3866
4400
  };
3867
4401
  //#endregion
3868
4402
  //#region src/charts/area-chart/area-chart.tsx
3869
- const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange, rescaleYOnLegendToggle, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
4403
+ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
3870
4404
  const legendInteractive = legend.interactive ?? false;
3871
4405
  const legendShape = legend.shape ?? "rect";
3872
4406
  const legendPosition = legend.position ?? "bottom";
3873
- const rescaleYOnVisibility = rescaleYOnVisibilityChange ?? rescaleYOnLegendToggle ?? true;
3874
- const providerTheme = useGlobalChartsTheme();
4407
+ const formatting = useChartFormatting();
3875
4408
  const theme = useXYChartTheme(data);
3876
4409
  const chartId = useChartId(providedChartId);
3877
4410
  const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
@@ -3921,7 +4454,7 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
3921
4454
  totalPoints: dataSorted[0]?.data.length || 0
3922
4455
  });
3923
4456
  const fixedYDomain = useMemo(() => {
3924
- if (rescaleYOnVisibility || !dataSorted.length || !dataSorted[0].data.length || stacked && stackOffset !== "none") return;
4457
+ if (rescaleYOnVisibilityChange || !dataSorted.length || !dataSorted[0].data.length || stacked && stackOffset !== "none") return;
3925
4458
  if (stacked) {
3926
4459
  const numPoints = Math.max(...dataSorted.map((s) => s.data.length));
3927
4460
  let posMax = 0;
@@ -3955,20 +4488,19 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
3955
4488
  dataSorted,
3956
4489
  stacked,
3957
4490
  stackOffset,
3958
- rescaleYOnVisibility
4491
+ rescaleYOnVisibilityChange
3959
4492
  ]);
3960
4493
  const chartOptions = useMemo(() => {
3961
- const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
3962
- const formatter = tickFormat || getFormatter(dataSorted, tickResolution);
3963
4494
  return {
3964
4495
  axis: {
3965
- x: {
3966
- orientation: "bottom",
3967
- numTicks: guessOptimalNumTicks(dataSorted, width, formatter),
3968
- tickFormat: formatter,
3969
- display: true,
3970
- ...xAxisOptions
3971
- },
4496
+ x: buildTimeAxisOptions({
4497
+ dataSorted,
4498
+ width,
4499
+ axisOptions: options?.axis?.x,
4500
+ scaleDomain: options?.xScale?.domain,
4501
+ zoomDomain: zoom.domain,
4502
+ formatting
4503
+ }),
3972
4504
  y: {
3973
4505
  orientation: "left",
3974
4506
  numTicks: 4,
@@ -3996,31 +4528,34 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
3996
4528
  width,
3997
4529
  stacked,
3998
4530
  fixedYDomain,
3999
- zoom.domain
4531
+ zoom.domain,
4532
+ formatting
4000
4533
  ]);
4001
4534
  const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme);
4002
4535
  const error = validateData$3(dataSorted);
4003
4536
  const isDataValid = !error;
4537
+ const legendItems = useChartLegendItems(dataSorted, useMemo(() => ({
4538
+ withGlyph: false,
4539
+ glyphSize: 0,
4540
+ collapseGroups: legend.collapseGroups ?? false
4541
+ }), [legend.collapseGroups]), legendShape);
4542
+ const chartMetadata = useMemo(() => ({
4543
+ stacked,
4544
+ stackOffset,
4545
+ smoothing,
4546
+ curveType
4547
+ }), [
4548
+ stacked,
4549
+ stackOffset,
4550
+ smoothing,
4551
+ curveType
4552
+ ]);
4004
4553
  useChartRegistration({
4005
4554
  chartId,
4006
- legendItems: useChartLegendItems(dataSorted, useMemo(() => ({
4007
- withGlyph: false,
4008
- glyphSize: 0,
4009
- collapseGroups: legend.collapseGroups ?? false
4010
- }), [legend.collapseGroups]), legendShape),
4555
+ legendItems,
4011
4556
  chartType: "area",
4012
4557
  isDataValid,
4013
- metadata: useMemo(() => ({
4014
- stacked,
4015
- stackOffset,
4016
- smoothing,
4017
- curveType
4018
- }), [
4019
- stacked,
4020
- stackOffset,
4021
- smoothing,
4022
- curveType
4023
- ])
4558
+ metadata: chartMetadata
4024
4559
  });
4025
4560
  const prefersReducedMotion = usePrefersReducedMotion();
4026
4561
  const animationEnabled = !!animation && !prefersReducedMotion;
@@ -4030,9 +4565,17 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4030
4565
  };
4031
4566
  const zeroYAccessor = useCallback(() => 0, []);
4032
4567
  const visibleLabels = useMemo(() => new Set(seriesWithVisibility.filter((s) => s.isVisible).map((s) => s.series.label)), [seriesWithVisibility]);
4568
+ const bucketInfo = useMemo(() => getBucketInfo(dataSorted.filter((series) => visibleLabels.has(series.label)), options?.axis?.x?.tickResolution), [
4569
+ dataSorted,
4570
+ visibleLabels,
4571
+ options?.axis?.x?.tickResolution
4572
+ ]);
4033
4573
  const filteredRenderTooltip = useCallback((params) => {
4034
4574
  const datumByKey = params?.tooltipData?.datumByKey;
4035
- if (!datumByKey) return renderTooltip(params);
4575
+ if (!datumByKey) return renderTooltip({
4576
+ ...params,
4577
+ bucketInfo
4578
+ });
4036
4579
  const filtered = Object.fromEntries(Object.entries(datumByKey).filter(([key]) => visibleLabels.has(key)));
4037
4580
  if (Object.keys(filtered).length === 0) return null;
4038
4581
  const nearestDatum = params?.tooltipData?.nearestDatum;
@@ -4042,13 +4585,18 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4042
4585
  };
4043
4586
  return renderTooltip({
4044
4587
  ...params,
4588
+ bucketInfo,
4045
4589
  tooltipData: {
4046
4590
  ...params.tooltipData,
4047
4591
  datumByKey: filtered,
4048
4592
  nearestDatum: nextNearest
4049
4593
  }
4050
4594
  });
4051
- }, [renderTooltip, visibleLabels]);
4595
+ }, [
4596
+ renderTooltip,
4597
+ visibleLabels,
4598
+ bucketInfo
4599
+ ]);
4052
4600
  const resolvedFillOpacity = fillOpacity ?? (stacked ? .85 : .4);
4053
4601
  const resolvedWithStroke = withStroke ?? !stacked;
4054
4602
  if (error) return /* @__PURE__ */ jsx("div", {
@@ -4123,7 +4671,7 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4123
4671
  onBlur: onChartBlur,
4124
4672
  children: chartHeight > 0 && /* @__PURE__ */ jsxs("div", {
4125
4673
  ref: chartRef,
4126
- style: { position: "relative" },
4674
+ className: xy_plot_module_default["xy-plot"],
4127
4675
  children: [zoomable && zoom.domain && /* @__PURE__ */ jsx(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ jsxs(XYChart, {
4128
4676
  theme,
4129
4677
  width,
@@ -4179,7 +4727,7 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4179
4727
  stacked,
4180
4728
  stackOffset,
4181
4729
  getElementStyles,
4182
- strokeColor: theme.backgroundColor ?? providerTheme.backgroundColor
4730
+ strokeColor: theme.backgroundColor
4183
4731
  })] }),
4184
4732
  /* @__PURE__ */ jsx(AreaChartScalesRef, {
4185
4733
  chartRef: internalChartRef,
@@ -4347,8 +4895,7 @@ const TOOLTIP_FORMAT_BY_RESOLUTION = {
4347
4895
  year: "numeric",
4348
4896
  month: "long",
4349
4897
  day: "numeric",
4350
- hour: "numeric",
4351
- hour12: true
4898
+ hour: "numeric"
4352
4899
  },
4353
4900
  day: {
4354
4901
  year: "numeric",
@@ -4367,16 +4914,16 @@ const TOOLTIP_FORMAT_BY_RESOLUTION = {
4367
4914
  *
4368
4915
  * @param data - Date-based series, already parsed and sorted by `useChartDataTransform`.
4369
4916
  * @param tickResolution - Caller-declared bucket resolution, when known.
4917
+ * @param formatting - Host locale and time zone.
4370
4918
  * @return Tooltip label formatter.
4371
4919
  */
4372
- const getTooltipFormatter = (data, tickResolution) => {
4373
- if (tickResolution === "week") return (timestamp) => sprintf(__("Week of %s", "jetpack-charts"), new Date(timestamp).toLocaleDateString(void 0, {
4374
- year: "numeric",
4375
- month: "long",
4376
- day: "numeric"
4377
- }));
4920
+ const getTooltipFormatter = (data, tickResolution, formatting) => {
4921
+ if (tickResolution === "week") {
4922
+ const formatDay = createDateFormatter(TOOLTIP_FORMAT_BY_RESOLUTION.day, formatting);
4923
+ return (timestamp) => sprintf(__("Week of %s", "jetpack-charts"), formatDay(timestamp));
4924
+ }
4378
4925
  const format = TOOLTIP_FORMAT_BY_RESOLUTION[getBucketResolution(data, tickResolution)] ?? TOOLTIP_FORMAT_BY_RESOLUTION.day;
4379
- return (timestamp) => new Date(timestamp).toLocaleString(void 0, format);
4926
+ return createDateFormatter(format, formatting);
4380
4927
  };
4381
4928
  const identity = (label) => label;
4382
4929
  const byBucket = (format) => (bucket) => typeof bucket === "string" ? bucket : format(Number(bucket));
@@ -4427,6 +4974,7 @@ const getGroupPadding = (scale) => {
4427
4974
  */
4428
4975
  function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = ALL_RENDERED) {
4429
4976
  const stableOptions = useDeepMemo(options);
4977
+ const formatting = useChartFormatting();
4430
4978
  const axisConfig = useMemo(() => {
4431
4979
  const { labelOverflow: xLabelOverflow, tickResolution: xTickResolution, tickFormat: xTickFormat, ...xAxisOptions } = stableOptions.axis?.x || {};
4432
4980
  const { labelOverflow: yLabelOverflow, tickResolution: yTickResolution, tickFormat: yTickFormat, ...yAxisOptions } = stableOptions.axis?.y || {};
@@ -4453,9 +5001,9 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
4453
5001
  zero: false
4454
5002
  };
4455
5003
  const hasLabels = Boolean(data?.[0]?.data?.[0]?.label);
4456
- const timeTickFormatter = hasLabels ? null : getFormatter(data, tickResolution);
5004
+ const timeTickFormatter = hasLabels ? null : getFormatter(data, tickResolution, formatting);
4457
5005
  const labelFormatter = timeTickFormatter ? byBucket(timeTickFormatter) : identity;
4458
- const tooltipDatumFormatter = hasLabels ? labelFormatter : byBucket(getTooltipFormatter(data, tickResolution));
5006
+ const tooltipDatumFormatter = hasLabels ? labelFormatter : byBucket(getTooltipFormatter(data, tickResolution, formatting));
4459
5007
  const valueFormatter = formatNumberCompact;
4460
5008
  const bandDomain = timeTickFormatter ? getBandDomain(data, isSeriesRendered) : null;
4461
5009
  const valueAccessor = (d) => {
@@ -4491,7 +5039,8 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
4491
5039
  }, [
4492
5040
  data,
4493
5041
  tickResolution,
4494
- isSeriesRendered
5042
+ isSeriesRendered,
5043
+ formatting
4495
5044
  ]);
4496
5045
  return useMemo(() => {
4497
5046
  const { xTickFormat: defaultXTickFormat, yTickFormat: defaultYTickFormat, tooltipLabelFormatter: defaultTooltipLabelFormatter, xAccessor, yAccessor, gridVisibility, xScale: baseXScale, yScale: baseYScale } = defaultOptions[horizontal ? "horizontal" : "vertical"];
@@ -4615,11 +5164,6 @@ function computeComparisonRect(params) {
4615
5164
  };
4616
5165
  }
4617
5166
  /**
4618
- * Fraction of each per-series step left as a gap between bars within a single tick.
4619
- * Larger = more space between adjacent series; the shadow spans `1 - COMPARISON_INNER_GAP` of the step.
4620
- */
4621
- const COMPARISON_INNER_GAP = .1;
4622
- /**
4623
5167
  * Upper clamp on the computed group padding, so bars can never collapse to zero width
4624
5168
  * even at very large `widthFactor` values.
4625
5169
  */
@@ -4707,7 +5251,7 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ jsx("div", {
4707
5251
  className: bar_chart_module_default["bar-chart__tooltip-row"],
4708
5252
  children: sprintf(__("%1$s: %2$s", "jetpack-charts"), label, value)
4709
5253
  });
4710
- const BarChartInternal = ({ data, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, defaultHiddenSeries, animation, children, gap = "md" }) => {
5254
+ const BarChartInternal = ({ data, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, defaultHiddenSeries, animation, children, gap = "md", onPointerDown, onPointerUp, onDatumActivate }) => {
4711
5255
  const legendInteractive = legend.interactive ?? false;
4712
5256
  const legendCollapseGroups = legend.collapseGroups ?? false;
4713
5257
  const horizontal = orientation === "horizontal";
@@ -4734,14 +5278,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4734
5278
  const [selectedIndex, setSelectedIndex] = useState(void 0);
4735
5279
  const [isNavigating, setIsNavigating] = useState(false);
4736
5280
  const primarySeriesForNav = dataWithVisibleZeros.filter((s) => s.options?.type !== "comparison");
4737
- const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
4738
- selectedIndex,
4739
- setSelectedIndex,
4740
- isNavigating,
4741
- setIsNavigating,
4742
- chartRef,
4743
- totalPoints: Math.max(0, ...primarySeriesForNav.map((s) => s.data?.length || 0)) * primarySeriesForNav.length
4744
- });
5281
+ const totalPoints = Math.max(0, ...primarySeriesForNav.map((s) => s.data?.length || 0)) * primarySeriesForNav.length;
4745
5282
  const seriesWithVisibility = useMemo(() => dataWithVisibleZeros.map((series, index) => ({
4746
5283
  series,
4747
5284
  index,
@@ -4753,6 +5290,27 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4753
5290
  const primaryEntries = useMemo(() => seriesWithVisibility.filter(({ isVisible, series }) => isVisible && series.options?.type !== "comparison"), [seriesWithVisibility]);
4754
5291
  const primaryKeys = useMemo(() => primaryEntries.map(({ series }) => series.label), [primaryEntries]);
4755
5292
  const primarySeries = useMemo(() => primaryEntries.map(({ series }) => series), [primaryEntries]);
5293
+ const activateSelectedBar = useCallback((index) => {
5294
+ const primaryCount = primaryEntries.length;
5295
+ if (!primaryCount) return;
5296
+ const dataPointIndex = Math.floor(index / primaryCount);
5297
+ const series = primaryEntries[index % primaryCount]?.series;
5298
+ const datum = series?.data[dataPointIndex];
5299
+ if (series && datum) onDatumActivate?.({
5300
+ datum,
5301
+ index: dataPointIndex,
5302
+ key: series.label
5303
+ });
5304
+ }, [primaryEntries, onDatumActivate]);
5305
+ const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
5306
+ selectedIndex,
5307
+ setSelectedIndex,
5308
+ isNavigating,
5309
+ setIsNavigating,
5310
+ chartRef,
5311
+ totalPoints,
5312
+ onActivate: activateSelectedBar
5313
+ });
4756
5314
  const comparisonEntries = useMemo(() => {
4757
5315
  const primaryByGroup = new Map(primaryEntries.map(({ series, index }) => [series.group, {
4758
5316
  label: series.label,
@@ -4789,7 +5347,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4789
5347
  const groupPadding = useMemo(() => {
4790
5348
  const basePadding = chartOptions.barGroup.padding;
4791
5349
  if (!comparisonWidthFactor || comparisonWidthFactor <= 1) return basePadding;
4792
- const p = 1 - (1 - COMPARISON_INNER_GAP) / comparisonWidthFactor;
5350
+ const p = 1 - .9 / comparisonWidthFactor;
4793
5351
  return Math.min(Math.max(p, basePadding), MAX_GROUP_PADDING);
4794
5352
  }, [chartOptions.barGroup.padding, comparisonWidthFactor]);
4795
5353
  const { xScale, yScale } = useMemo(() => {
@@ -4927,15 +5485,17 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4927
5485
  chartId
4928
5486
  ]);
4929
5487
  const error = validateData$2(dataSorted);
5488
+ const isDataValid = !error;
5489
+ const chartMetadata = useMemo(() => ({
5490
+ orientation,
5491
+ withPatterns
5492
+ }), [orientation, withPatterns]);
4930
5493
  useChartRegistration({
4931
5494
  chartId,
4932
5495
  legendItems,
4933
5496
  chartType: "bar",
4934
- isDataValid: !error,
4935
- metadata: useMemo(() => ({
4936
- orientation,
4937
- withPatterns
4938
- }), [orientation, withPatterns])
5497
+ isDataValid,
5498
+ metadata: chartMetadata
4939
5499
  });
4940
5500
  const prefersReducedMotion = usePrefersReducedMotion();
4941
5501
  if (error) return /* @__PURE__ */ jsx("div", {
@@ -4989,6 +5549,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4989
5549
  onBlur: onChartBlur,
4990
5550
  children: chartHeight > 0 && /* @__PURE__ */ jsx("div", {
4991
5551
  ref: chartRef,
5552
+ className: xy_plot_module_default["xy-plot"],
4992
5553
  children: /* @__PURE__ */ jsxs(XYChart, {
4993
5554
  theme,
4994
5555
  width,
@@ -5000,6 +5561,8 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5000
5561
  xScale,
5001
5562
  yScale,
5002
5563
  horizontal,
5564
+ onPointerDown,
5565
+ onPointerUp,
5003
5566
  pointerEventsDataKey: "nearest",
5004
5567
  children: [
5005
5568
  !allSeriesHidden && /* @__PURE__ */ jsx(Grid, {
@@ -5154,7 +5717,26 @@ const BarListChartInternal = ({ data, width, height, options = {}, margin = {
5154
5717
  right: 20,
5155
5718
  bottom: 0,
5156
5719
  top: 0
5157
- }, ...rest }) => {
5720
+ }, className, ...rest }) => {
5721
+ const { getElementStyles } = useGlobalChartsContext();
5722
+ const tintedData = useMemo(() => {
5723
+ if (data.length > 1) return data;
5724
+ return data.map((series, index) => {
5725
+ if (series.options?.stroke) return series;
5726
+ const { color } = getElementStyles({
5727
+ data: series,
5728
+ index
5729
+ });
5730
+ if (!isValidHexColor(color)) return series;
5731
+ return {
5732
+ ...series,
5733
+ options: {
5734
+ ...series.options,
5735
+ stroke: lightenHexColor(color, .6)
5736
+ }
5737
+ };
5738
+ });
5739
+ }, [data, getElementStyles]);
5158
5740
  const chartOptions = useMemo(() => {
5159
5741
  const isMultiSeries = data.length > 1;
5160
5742
  const defaultYScale = {
@@ -5187,7 +5769,8 @@ const BarListChartInternal = ({ data, width, height, options = {}, margin = {
5187
5769
  return /* @__PURE__ */ jsx(BarChart, {
5188
5770
  orientation: "horizontal",
5189
5771
  gridVisibility: "none",
5190
- data,
5772
+ className: clsx("bar-list-chart", className),
5773
+ data: tintedData,
5191
5774
  width,
5192
5775
  height,
5193
5776
  margin,
@@ -5223,6 +5806,9 @@ const BarListChartResponsive = withResponsive(BarListChart);
5223
5806
  var conversion_funnel_chart_module_default = {
5224
5807
  "bar-container": "a8ccharts-mGEVca-bar-container",
5225
5808
  "change-indicator": "a8ccharts-mGEVca-change-indicator",
5809
+ "change-indicator--negative": "a8ccharts-mGEVca-change-indicator--negative",
5810
+ "change-indicator--positive": "a8ccharts-mGEVca-change-indicator--positive",
5811
+ "conversion-funnel-chart": "a8ccharts-mGEVca-conversion-funnel-chart",
5226
5812
  "conversion-funnel-chart--loading": "a8ccharts-mGEVca-conversion-funnel-chart--loading",
5227
5813
  "empty-state": "a8ccharts-mGEVca-empty-state",
5228
5814
  "funnel-bar": "a8ccharts-mGEVca-funnel-bar",
@@ -5303,23 +5889,14 @@ const useFunnelSelection = (hideTooltip) => {
5303
5889
  */
5304
5890
  const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loading = false, animation, className, chartId: providedChartId, height, style, renderStepLabel, renderStepRate, renderMainMetric, renderTooltip }) => {
5305
5891
  const chartId = useChartId(providedChartId);
5306
- const { conversionFunnelChart: conversionFunnelChartSettings } = useGlobalChartsTheme();
5307
5892
  const { getElementStyles, isColorPaletteResolved } = useGlobalChartsContext();
5308
5893
  const chartRef = useRef(null);
5309
5894
  const selectedBarRef = useRef(null);
5310
5895
  const [scopeNode, setScopeNode] = useState(null);
5311
5896
  const { tooltipData, tooltipLeft, tooltipTop, tooltipOpen, showTooltip, hideTooltip } = useTooltip();
5312
5897
  const { handleBarClick, handleBarKeyDown, clearSelection, getStepState } = useFunnelSelection(hideTooltip);
5313
- const { containerRef: portalContainerRef, TooltipInPortal, containerBounds } = useTooltipInPortal({
5314
- detectBounds: true,
5315
- scroll: true
5316
- });
5317
- const portalContainerRefRef = useRef(portalContainerRef);
5318
- useEffect(() => {
5319
- portalContainerRefRef.current = portalContainerRef;
5320
- }, [portalContainerRef]);
5898
+ const standaloneScopeClass = useStandaloneScopeClass();
5321
5899
  const setChartRef = useCallback((node) => {
5322
- portalContainerRefRef.current(node);
5323
5900
  chartRef.current = node;
5324
5901
  setScopeNode(node);
5325
5902
  }, []);
@@ -5336,30 +5913,22 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5336
5913
  });
5337
5914
  }, [showTooltip]);
5338
5915
  const getMouseTooltipCoords = useCallback((event) => {
5339
- if (containerBounds.width === 0 || containerBounds.height === 0) return null;
5916
+ const bounds = chartRef.current?.getBoundingClientRect();
5917
+ if (!bounds) return null;
5340
5918
  return {
5341
- x: event.clientX - containerBounds.left,
5342
- y: event.clientY - containerBounds.top
5919
+ x: event.clientX - bounds.left,
5920
+ y: event.clientY - bounds.top
5343
5921
  };
5344
- }, [
5345
- containerBounds.width,
5346
- containerBounds.height,
5347
- containerBounds.left,
5348
- containerBounds.top
5349
- ]);
5922
+ }, []);
5350
5923
  const getKeyboardTooltipCoords = useCallback((event) => {
5351
- if (containerBounds.width === 0 || containerBounds.height === 0) return null;
5924
+ const bounds = chartRef.current?.getBoundingClientRect();
5925
+ if (!bounds) return null;
5352
5926
  const rect = event.currentTarget.getBoundingClientRect();
5353
5927
  return {
5354
- x: rect.left + rect.width / 2 - containerBounds.left,
5355
- y: rect.top - containerBounds.top
5928
+ x: rect.left + rect.width / 2 - bounds.left,
5929
+ y: rect.top - bounds.top
5356
5930
  };
5357
- }, [
5358
- containerBounds.width,
5359
- containerBounds.height,
5360
- containerBounds.left,
5361
- containerBounds.top
5362
- ]);
5931
+ }, []);
5363
5932
  const handleStepInteraction = useCallback((step, event, interactionType) => {
5364
5933
  selectedBarRef.current = event.currentTarget;
5365
5934
  const { isClicked } = getStepState(step.id);
@@ -5420,19 +5989,14 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5420
5989
  };
5421
5990
  }, [clearSelectionAndRef]);
5422
5991
  const resolvedHeight = height ?? style?.height ?? "100%";
5423
- const { primaryColor, backgroundColor, positiveChangeColor, negativeChangeColor } = conversionFunnelChartSettings;
5424
- const { color: barColor } = getElementStyles ? getElementStyles({
5425
- index: 0,
5426
- overrideColor: primaryColor
5427
- }) : { color: primaryColor || "#000000" };
5428
- const changeColor = changeIndicator?.startsWith("+") ? positiveChangeColor : negativeChangeColor;
5429
- const barBackgroundColor = backgroundColor || hexToRgba(barColor, .08) || "rgba(0, 0, 0, 0.08)";
5992
+ const { color: barColor } = getElementStyles ? getElementStyles({ index: 0 }) : { color: "#000000" };
5993
+ const isPositiveChange = changeIndicator?.startsWith("+");
5994
+ const changeColor = isPositiveChange ? CATALOG_POINTERS.trendUp : CATALOG_POINTERS.trendDown;
5430
5995
  const renderDefaultMainMetric = () => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
5431
5996
  className: conversion_funnel_chart_module_default["main-rate"],
5432
5997
  children: formatPercentage(mainRate)
5433
5998
  }), changeIndicator && /* @__PURE__ */ jsx("span", {
5434
- className: conversion_funnel_chart_module_default["change-indicator"],
5435
- style: { color: changeColor },
5999
+ className: clsx(conversion_funnel_chart_module_default["change-indicator"], conversion_funnel_chart_module_default[isPositiveChange ? "change-indicator--positive" : "change-indicator--negative"]),
5436
6000
  children: changeIndicator
5437
6001
  })] });
5438
6002
  const renderDefaultTooltip = (step) => /* @__PURE__ */ jsxs(Stack, {
@@ -5448,20 +6012,21 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5448
6012
  })]
5449
6013
  });
5450
6014
  const isDataValid = Boolean(steps && steps.length > 0);
6015
+ const chartMetadata = useMemo(() => ({
6016
+ mainRate,
6017
+ changeIndicator,
6018
+ stepsCount: steps?.length || 0
6019
+ }), [
6020
+ mainRate,
6021
+ changeIndicator,
6022
+ steps?.length
6023
+ ]);
5451
6024
  useChartRegistration({
5452
6025
  chartId,
5453
6026
  legendItems: [],
5454
6027
  chartType: "conversion-funnel",
5455
6028
  isDataValid,
5456
- metadata: useMemo(() => ({
5457
- mainRate,
5458
- changeIndicator,
5459
- stepsCount: steps?.length || 0
5460
- }), [
5461
- mainRate,
5462
- changeIndicator,
5463
- steps?.length
5464
- ])
6029
+ metadata: chartMetadata
5465
6030
  });
5466
6031
  const prefersReducedMotion = usePrefersReducedMotion();
5467
6032
  if (!isDataValid) return /* @__PURE__ */ jsx(Stack, {
@@ -5469,7 +6034,7 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5469
6034
  align: "center",
5470
6035
  justify: "center",
5471
6036
  ref: setScopeNode,
5472
- className: clsx(conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
6037
+ className: clsx("conversion-funnel-chart", conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
5473
6038
  style: {
5474
6039
  ...style,
5475
6040
  height: resolvedHeight
@@ -5480,16 +6045,16 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5480
6045
  })
5481
6046
  });
5482
6047
  const maxRate = Math.max(...steps.map((step) => step.rate));
5483
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Stack, {
6048
+ return /* @__PURE__ */ jsxs(Stack, {
5484
6049
  direction: "column",
5485
6050
  gap: "xl",
5486
6051
  ref: setChartRef,
5487
- className: clsx(conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
6052
+ className: clsx("conversion-funnel-chart", conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
5488
6053
  style: {
5489
6054
  ...style,
5490
6055
  height: resolvedHeight
5491
6056
  },
5492
- children: /* @__PURE__ */ jsxs(ChartScopeContext.Provider, {
6057
+ children: [/* @__PURE__ */ jsxs(ChartScopeContext.Provider, {
5493
6058
  value: scopeNode,
5494
6059
  children: [renderMainMetric ? renderMainMetric({
5495
6060
  mainRate,
@@ -5541,7 +6106,6 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5541
6106
  role: "button",
5542
6107
  tabIndex: isBlurred ? -1 : 0,
5543
6108
  "aria-label": step.label,
5544
- style: { backgroundColor: barBackgroundColor },
5545
6109
  children: /* @__PURE__ */ jsx("div", {
5546
6110
  className: clsx(conversion_funnel_chart_module_default["funnel-bar"], { [conversion_funnel_chart_module_default["funnel-bar--animated"]]: animation && !loading && !prefersReducedMotion }),
5547
6111
  style: {
@@ -5553,23 +6117,23 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5553
6117
  }, step.id);
5554
6118
  })
5555
6119
  })]
5556
- })
5557
- }), tooltipOpen && tooltipData && (() => {
5558
- const tooltipContent = renderTooltip ? renderTooltip({
5559
- step: tooltipData,
5560
- index: steps.findIndex((s) => s.id === tooltipData.id),
5561
- top: tooltipTop,
5562
- left: tooltipLeft,
5563
- className: conversion_funnel_chart_module_default["tooltip-wrapper"]
5564
- }) : renderDefaultTooltip(tooltipData);
5565
- if (!tooltipContent) return null;
5566
- return /* @__PURE__ */ jsx(TooltipInPortal, {
5567
- top: tooltipTop,
5568
- left: tooltipLeft,
5569
- className: clsx("a8c-charts-scope", conversion_funnel_chart_module_default["tooltip-wrapper"]),
5570
- children: tooltipContent
5571
- }, Math.random());
5572
- })()] });
6120
+ }), tooltipOpen && tooltipData && (() => {
6121
+ const tooltipContent = renderTooltip ? renderTooltip({
6122
+ step: tooltipData,
6123
+ index: steps.findIndex((s) => s.id === tooltipData.id),
6124
+ top: tooltipTop,
6125
+ left: tooltipLeft,
6126
+ className: conversion_funnel_chart_module_default["tooltip-wrapper"]
6127
+ }) : renderDefaultTooltip(tooltipData);
6128
+ if (!tooltipContent) return null;
6129
+ return /* @__PURE__ */ jsx(BoundedTooltip, {
6130
+ top: tooltipTop,
6131
+ left: tooltipLeft,
6132
+ className: clsx(standaloneScopeClass, conversion_funnel_chart_module_default["tooltip-wrapper"]),
6133
+ children: tooltipContent
6134
+ });
6135
+ })()]
6136
+ });
5573
6137
  };
5574
6138
  /**
5575
6139
  * ConversionFunnelChart component with provider wrapper
@@ -5715,7 +6279,7 @@ function normalizeGeoChartError(eventArgs) {
5715
6279
  * @return A React component displaying an interactive map with data visualization
5716
6280
  */
5717
6281
  const GeoChartInternal = ({ className, data, width, height, region = "world", resolution = "countries", onError, renderPlaceholder }) => {
5718
- const { getElementStyles, theme: { geoChart: { featureFillColor }, backgroundColor } } = useGlobalChartsContext();
6282
+ const { getElementStyles } = useGlobalChartsContext();
5719
6283
  const scopeElement = useChartScopeElement();
5720
6284
  const containerRef = useRef(null);
5721
6285
  const reportedErrorIdsRef = useRef(/* @__PURE__ */ new Set());
@@ -5749,8 +6313,8 @@ const GeoChartInternal = ({ className, data, width, height, region = "world", re
5749
6313
  });
5750
6314
  const fullColorHex = getElementStyles({ index: 0 }).color;
5751
6315
  const lightColorHex = lightenHexColor(fullColorHex, .8);
5752
- const backgroundColorHex = normalizeColorToHex(backgroundColor, scopeElement, resolveCssVariable) || DEFAULT_BACKGROUND_COLOR;
5753
- const defaultFillColorHex = normalizeColorToHex(featureFillColor, scopeElement, resolveCssVariable) || DEFAULT_FEATURE_FILL_COLOR;
6316
+ const backgroundColorHex = normalizeColorToHex(CATALOG_POINTERS.background, scopeElement, resolveCssVariable) || DEFAULT_BACKGROUND_COLOR;
6317
+ const defaultFillColorHex = normalizeColorToHex(CATALOG_POINTERS.surfaceSecondary, scopeElement, resolveCssVariable) || DEFAULT_FEATURE_FILL_COLOR;
5754
6318
  const sanitizedData = useMemo(() => {
5755
6319
  if (data.length === 0) return {
5756
6320
  data,
@@ -5811,8 +6375,7 @@ const GeoChartInternal = ({ className, data, width, height, region = "world", re
5811
6375
  className: clsx("geo-chart", geo_chart_module_default.container, className),
5812
6376
  style: {
5813
6377
  width,
5814
- height,
5815
- backgroundColor
6378
+ height
5816
6379
  },
5817
6380
  children: /* @__PURE__ */ jsx(Chart, {
5818
6381
  chartType: "GeoChart",
@@ -5862,6 +6425,7 @@ var heatmap_chart_module_default = {
5862
6425
  "heatmap-chart__grid": "a8ccharts-O3YMOW-heatmap-chart__grid",
5863
6426
  "heatmap-chart__grid--compact": "a8ccharts-O3YMOW-heatmap-chart__grid--compact",
5864
6427
  "heatmap-chart__grid--height-capped": "a8ccharts-O3YMOW-heatmap-chart__grid--height-capped",
6428
+ "heatmap-chart__legend-label": "a8ccharts-O3YMOW-heatmap-chart__legend-label",
5865
6429
  "heatmap-chart__legend-swatch": "a8ccharts-O3YMOW-heatmap-chart__legend-swatch",
5866
6430
  "heatmap-chart__row": "a8ccharts-O3YMOW-heatmap-chart__row",
5867
6431
  "heatmap-chart__row-label": "a8ccharts-O3YMOW-heatmap-chart__row-label",
@@ -6004,7 +6568,7 @@ const HeatmapContext = createContext(null);
6004
6568
  //#region src/charts/heatmap-chart/private/heatmap-legend.tsx
6005
6569
  const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6006
6570
  const context = useContext(HeatmapContext);
6007
- const { legend, backgroundColor } = useGlobalChartsTheme();
6571
+ const { legend } = useGlobalChartsTheme();
6008
6572
  if (!context) return null;
6009
6573
  const { primaryColorHex } = context;
6010
6574
  const labelStyle = legend.labelStyles;
@@ -6015,6 +6579,7 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6015
6579
  children: [
6016
6580
  /* @__PURE__ */ jsx(Text$1, {
6017
6581
  variant: "body-sm",
6582
+ className: heatmap_chart_module_default["heatmap-chart__legend-label"],
6018
6583
  style: labelStyle,
6019
6584
  children: lessLabel ?? __("Less", "jetpack-charts")
6020
6585
  }),
@@ -6028,7 +6593,6 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6028
6593
  className: heatmap_chart_module_default["heatmap-chart__legend-swatch"],
6029
6594
  style: {
6030
6595
  "--a8c-charts-color-heatmap-primary": primaryColorHex,
6031
- "--a8c-charts-color-heatmap-background": backgroundColor,
6032
6596
  "--a8c-charts-heatmap-cell-intensity": intensity
6033
6597
  }
6034
6598
  }, index);
@@ -6036,6 +6600,7 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6036
6600
  }),
6037
6601
  /* @__PURE__ */ jsx(Text$1, {
6038
6602
  variant: "body-sm",
6603
+ className: heatmap_chart_module_default["heatmap-chart__legend-label"],
6039
6604
  style: labelStyle,
6040
6605
  children: moreLabel ?? __("More", "jetpack-charts")
6041
6606
  })
@@ -6053,19 +6618,16 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6053
6618
  const { nonLegendChildren } = useChartChildren(children, "HeatmapChart");
6054
6619
  const [selectedIndex, setSelectedIndex] = useState();
6055
6620
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, showTooltip, hideTooltip } = useTooltip();
6056
- const { containerRef, containerBounds, TooltipInPortal } = useTooltipInPortal({
6057
- detectBounds: true,
6058
- scroll: true
6059
- });
6060
- const containerBoundsRef = useRef(containerBounds);
6061
- containerBoundsRef.current = containerBounds;
6621
+ const standaloneScopeClass = useStandaloneScopeClass();
6622
+ const containerRef = useRef(null);
6623
+ const getTooltipOrigin = useCallback(() => containerRef.current?.closest(`[data-chart-id="heatmap-chart-${chartId}"]`)?.getBoundingClientRect() ?? null, [chartId]);
6062
6624
  const { color: primaryColorHex } = getElementStyles({
6063
6625
  index: 0,
6064
- overrideColor: primaryColor || heatmapChartSettings.primaryColor
6626
+ overrideColor: primaryColor
6065
6627
  });
6066
- const chartBackgroundHex = normalizeColorToHex(theme.backgroundColor, scopeElement, resolveCssVariable);
6628
+ const chartBackgroundHex = normalizeColorToHex(CATALOG_POINTERS.background, scopeElement, resolveCssVariable);
6067
6629
  const primaryHex = normalizeColorToHex(primaryColorHex);
6068
- const cellHasLightText = (intensity) => isValidHexColor(primaryHex) && isValidHexColor(chartBackgroundHex) && prefersLightText(mixHexColors(primaryHex, chartBackgroundHex, 1 - (CELL_MIX_FLOOR + (1 - CELL_MIX_FLOOR) * intensity)));
6630
+ const cellHasLightText = (intensity) => isValidHexColor(primaryHex) && isValidHexColor(chartBackgroundHex) && prefersLightText(mixHexColors(primaryHex, chartBackgroundHex, 1 - (CELL_MIX_FLOOR + .85 * intensity)));
6069
6631
  const extent = useMemo(() => getValueExtent(data), [data]);
6070
6632
  const heatmapContext = useMemo(() => ({
6071
6633
  extent,
@@ -6139,19 +6701,21 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6139
6701
  ]);
6140
6702
  const handleCellMouseMove = useCallback((event) => {
6141
6703
  if (!withTooltips) return;
6704
+ const origin = getTooltipOrigin();
6705
+ if (!origin) return;
6142
6706
  const target = event.currentTarget;
6143
6707
  const columnIndex = Number(target.dataset.column);
6144
6708
  const rowIndex = Number(target.dataset.row);
6145
- const bounds = containerBoundsRef.current;
6146
6709
  showTooltip({
6147
- tooltipLeft: event.clientX - bounds.left,
6148
- tooltipTop: event.clientY - bounds.top,
6710
+ tooltipLeft: event.clientX - origin.left,
6711
+ tooltipTop: event.clientY - origin.top,
6149
6712
  tooltipData: buildTooltipData(columnIndex, rowIndex)
6150
6713
  });
6151
6714
  }, [
6152
6715
  withTooltips,
6153
6716
  showTooltip,
6154
- buildTooltipData
6717
+ buildTooltipData,
6718
+ getTooltipOrigin
6155
6719
  ]);
6156
6720
  const handleCellMouseLeave = useCallback(() => {
6157
6721
  if (withTooltips && selectedIndex === void 0) hideTooltip();
@@ -6162,13 +6726,14 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6162
6726
  ]);
6163
6727
  useEffect(() => {
6164
6728
  if (!withTooltips || selectedIndex === void 0) return;
6729
+ const origin = getTooltipOrigin();
6730
+ if (!origin) return;
6165
6731
  const col = Math.floor(selectedIndex / rows);
6166
6732
  const row = selectedIndex % rows;
6167
6733
  const rect = (typeof document !== "undefined" ? document.getElementById(`${chartId}-cell-${col}-${row}`) : null)?.getBoundingClientRect();
6168
- const bounds = containerBoundsRef.current;
6169
6734
  showTooltip({
6170
- tooltipLeft: rect ? rect.left + rect.width / 2 - bounds.left : 0,
6171
- tooltipTop: rect ? rect.top + rect.height / 2 - bounds.top : 0,
6735
+ tooltipLeft: rect ? rect.left + rect.width / 2 - origin.left : 0,
6736
+ tooltipTop: rect ? rect.top + rect.height / 2 - origin.top : 0,
6172
6737
  tooltipData: buildTooltipData(col, row)
6173
6738
  });
6174
6739
  }, [
@@ -6177,7 +6742,8 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6177
6742
  rows,
6178
6743
  chartId,
6179
6744
  buildTooltipData,
6180
- showTooltip
6745
+ showTooltip,
6746
+ getTooltipOrigin
6181
6747
  ]);
6182
6748
  const defaultRenderTooltip = useCallback((info) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("strong", { children: info.cellLabel || `${info.columnLabel ?? ""} ${info.rowLabel ?? ""}`.trim() }), /* @__PURE__ */ jsx("div", { children: info.value === null ? __("No data", "jetpack-charts") : formatNumber(info.value) })] }), []);
6183
6749
  if (!columns || !rows) return /* @__PURE__ */ jsx(Center, {
@@ -6195,7 +6761,6 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6195
6761
  const rowTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
6196
6762
  const gridStyle = {
6197
6763
  "--a8c-charts-color-heatmap-primary": primaryColorHex,
6198
- "--a8c-charts-color-heatmap-background": theme.backgroundColor,
6199
6764
  gridTemplateColumns: `auto repeat(${columns}, ${columnTrack})`,
6200
6765
  gridTemplateRows: `auto repeat(${rows}, ${rowTrack})`
6201
6766
  };
@@ -6293,11 +6858,11 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6293
6858
  })]
6294
6859
  }, `row-${rowIndex}`);
6295
6860
  })]
6296
- }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ jsx(TooltipInPortal, {
6861
+ }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ jsx(BoundedTooltip, {
6297
6862
  top: tooltipTop,
6298
6863
  left: tooltipLeft,
6299
6864
  children: /* @__PURE__ */ jsx("div", {
6300
- className: "a8c-charts-scope",
6865
+ className: standaloneScopeClass,
6301
6866
  role: "tooltip",
6302
6867
  tabIndex: -1,
6303
6868
  children: (renderTooltip ?? defaultRenderTooltip)(tooltipData)
@@ -6321,7 +6886,7 @@ const HeatmapChartResponsiveInner = (props) => /* @__PURE__ */ jsx(HeatmapChartW
6321
6886
  HeatmapChartResponsiveInner.displayName = "HeatmapChart";
6322
6887
  const HeatmapChartResponsive = attachSubComponents(withResponsive(HeatmapChartResponsiveInner), { Legend: HeatmapLegend });
6323
6888
  //#endregion
6324
- //#region ../../../node_modules/.pnpm/is-plain-object@5.0.0/node_modules/is-plain-object/dist/is-plain-object.mjs
6889
+ //#region ../../../node_modules/.pnpm/is-plain-object@5.1.0/node_modules/is-plain-object/dist/is-plain-object.mjs
6325
6890
  /*!
6326
6891
  * is-plain-object <https://github.com/jonschlinkert/is-plain-object>
6327
6892
  *
@@ -6388,7 +6953,7 @@ function paramCase(input, options) {
6388
6953
  return dotCase(input, __assign({ delimiter: "-" }, options));
6389
6954
  }
6390
6955
  //#endregion
6391
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
6956
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
6392
6957
  function useUpdateEffect(effect, deps) {
6393
6958
  const mountedRef = useRef$1(false);
6394
6959
  useEffect$1(() => {
@@ -6420,10 +6985,11 @@ var StyleSheet = /*#__PURE__*/ function() {
6420
6985
  var _this = this;
6421
6986
  this._insertTag = function(tag) {
6422
6987
  var before;
6423
- if (_this.tags.length === 0) if (_this.insertionPoint) before = _this.insertionPoint.nextSibling;
6424
- else if (_this.prepend) before = _this.container.firstChild;
6425
- else before = _this.before;
6426
- else before = _this.tags[_this.tags.length - 1].nextSibling;
6988
+ if (_this.tags.length === 0) {
6989
+ if (_this.insertionPoint) before = _this.insertionPoint.nextSibling;
6990
+ else if (_this.prepend) before = _this.container.firstChild;
6991
+ else before = _this.before;
6992
+ } else before = _this.tags[_this.tags.length - 1].nextSibling;
6427
6993
  _this.container.insertBefore(tag, before);
6428
6994
  _this.tags.push(tag);
6429
6995
  };
@@ -6742,9 +7308,7 @@ function delimiter(type) {
6742
7308
  case 40:
6743
7309
  if (type === 41) delimiter(type);
6744
7310
  break;
6745
- case 92:
6746
- next();
6747
- break;
7311
+ case 92: next();
6748
7312
  }
6749
7313
  return position;
6750
7314
  }
@@ -7720,14 +8284,12 @@ function handleInterpolation(mergedProps, registered, interpolation) {
7720
8284
  return serializedStyles.styles + ";";
7721
8285
  }
7722
8286
  return createStringFromObject(mergedProps, registered, interpolation);
7723
- case "function":
7724
- if (mergedProps !== void 0) {
7725
- var previousCursor = cursor;
7726
- var result = interpolation(mergedProps);
7727
- cursor = previousCursor;
7728
- return handleInterpolation(mergedProps, registered, result);
7729
- }
7730
- break;
8287
+ case "function": if (mergedProps !== void 0) {
8288
+ var previousCursor = cursor;
8289
+ var result = interpolation(mergedProps);
8290
+ cursor = previousCursor;
8291
+ return handleInterpolation(mergedProps, registered, result);
8292
+ }
7731
8293
  }
7732
8294
  var asString = interpolation;
7733
8295
  if (registered == null) return asString;
@@ -7961,7 +8523,7 @@ _createEmotion.css;
7961
8523
  _createEmotion.sheet;
7962
8524
  _createEmotion.cache;
7963
8525
  //#endregion
7964
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
8526
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
7965
8527
  var isSerializedStyles = (o) => typeof o !== "undefined" && o !== null && ["name", "styles"].every((p) => typeof o[p] !== "undefined");
7966
8528
  var useCx = () => {
7967
8529
  const cache = __unsafe_useEmotionCache();
@@ -8068,7 +8630,7 @@ function memize(fn, options) {
8068
8630
  return memoized;
8069
8631
  }
8070
8632
  //#endregion
8071
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/utils/colors-values.mjs
8633
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/utils/colors-values.mjs
8072
8634
  var white = "#fff";
8073
8635
  var GRAY = {
8074
8636
  900: "#1e1e1e",
@@ -8147,7 +8709,7 @@ var COLORS = Object.freeze({
8147
8709
  ui: UI
8148
8710
  });
8149
8711
  //#endregion
8150
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/utils/config-values.mjs
8712
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/utils/config-values.mjs
8151
8713
  var CONTROL_HEIGHT = "36px";
8152
8714
  var CONTROL_PROPS = {
8153
8715
  controlPaddingX: 12,
@@ -8162,9 +8724,6 @@ var CONTROL_PROPS = {
8162
8724
  };
8163
8725
  var config_values_default = Object.assign({}, CONTROL_PROPS, {
8164
8726
  colorDivider: "rgba(0, 0, 0, 0.1)",
8165
- colorScrollbarThumb: "rgba(0, 0, 0, 0.2)",
8166
- colorScrollbarThumbHover: "rgba(0, 0, 0, 0.5)",
8167
- colorScrollbarTrack: "rgba(0, 0, 0, 0.04)",
8168
8727
  elevationIntensity: 1,
8169
8728
  radiusXSmall: "1px",
8170
8729
  radiusSmall: "2px",
@@ -8175,7 +8734,6 @@ var config_values_default = Object.assign({}, CONTROL_PROPS, {
8175
8734
  borderWidth: "1px",
8176
8735
  borderWidthFocus: "1.5px",
8177
8736
  borderWidthTab: "4px",
8178
- spinnerSize: 16,
8179
8737
  fontSize: "13px",
8180
8738
  fontSizeH1: "calc(2.44 * 13px)",
8181
8739
  fontSizeH2: "calc(1.95 * 13px)",
@@ -8252,10 +8810,10 @@ var require_es6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8252
8810
  };
8253
8811
  }));
8254
8812
  //#endregion
8255
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.52.0/node_modules/@wordpress/warning/build-module/utils.mjs
8813
+ //#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/utils.mjs
8256
8814
  var logged = /* @__PURE__ */ new Set();
8257
8815
  //#endregion
8258
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.52.0/node_modules/@wordpress/warning/build-module/index.mjs
8816
+ //#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/index.mjs
8259
8817
  function isDev() {
8260
8818
  return globalThis.SCRIPT_DEBUG === true;
8261
8819
  }
@@ -8269,7 +8827,7 @@ function warning(message) {
8269
8827
  logged.add(message);
8270
8828
  }
8271
8829
  //#endregion
8272
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
8830
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
8273
8831
  var import_es6 = /* @__PURE__ */ __toESM(require_es6(), 1);
8274
8832
  var ComponentsContext = createContext$1(
8275
8833
  /** @type {Record<string, any>} */
@@ -8296,18 +8854,18 @@ var BaseContextSystemProvider = ({ children, value }) => {
8296
8854
  };
8297
8855
  memo(BaseContextSystemProvider);
8298
8856
  //#endregion
8299
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/context/constants.mjs
8857
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/context/constants.mjs
8300
8858
  var COMPONENT_NAMESPACE = "data-wp-component";
8301
8859
  var CONNECTED_NAMESPACE = "data-wp-c16t";
8302
8860
  var CONNECT_STATIC_NAMESPACE = "__contextSystemKey__";
8303
8861
  //#endregion
8304
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
8862
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
8305
8863
  function getStyledClassName(namespace) {
8306
8864
  return `components-${paramCase(namespace)}`;
8307
8865
  }
8308
8866
  var getStyledClassNameFromKey = memize(getStyledClassName);
8309
8867
  //#endregion
8310
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/context/context-connect.mjs
8868
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/context/context-connect.mjs
8311
8869
  function contextConnect(Component, namespace) {
8312
8870
  return _contextConnect(Component, namespace, { forwardsRef: true });
8313
8871
  }
@@ -8324,7 +8882,7 @@ function _contextConnect(Component, namespace, options) {
8324
8882
  });
8325
8883
  }
8326
8884
  //#endregion
8327
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/context/utils.mjs
8885
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/context/utils.mjs
8328
8886
  function getNamespace(componentName) {
8329
8887
  return { [COMPONENT_NAMESPACE]: componentName };
8330
8888
  }
@@ -8332,7 +8890,7 @@ function getConnectedNamespace() {
8332
8890
  return { [CONNECTED_NAMESPACE]: true };
8333
8891
  }
8334
8892
  //#endregion
8335
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
8893
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
8336
8894
  function useContextSystem(props, namespace) {
8337
8895
  const contextSystemProps = useComponentsContext();
8338
8896
  if (typeof namespace === "undefined") globalThis.SCRIPT_DEBUG === true && warning("useContextSystem: Please provide a namespace");
@@ -8352,7 +8910,7 @@ function useContextSystem(props, namespace) {
8352
8910
  return finalComponentProps;
8353
8911
  }
8354
8912
  //#endregion
8355
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
8913
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
8356
8914
  var visuallyHidden = {
8357
8915
  border: 0,
8358
8916
  clip: "rect(1px, 1px, 1px, 1px)",
@@ -8368,7 +8926,7 @@ var visuallyHidden = {
8368
8926
  wordBreak: "normal"
8369
8927
  };
8370
8928
  //#endregion
8371
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
8929
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
8372
8930
  var customAttributeRegExp = /^(data|aria|x)-/i;
8373
8931
  var eventHandlerRegExp = /^on[A-Z]/;
8374
8932
  var svgElementNames = new Set(`animate animateMotion animateTransform circle clipPath defs desc ellipse
@@ -8450,14 +9008,15 @@ function filterIntrinsicElementProps(props, element) {
8450
9008
  }
8451
9009
  function UnforwardedPolymorphicElement({ as, ...props }, ref) {
8452
9010
  const Element = as || "div";
9011
+ const forwardedProps = typeof Element === "string" ? filterIntrinsicElementProps(props, Element) : props;
8453
9012
  return /* @__PURE__ */ jsx(Element, {
8454
9013
  ref,
8455
- ...typeof Element === "string" ? filterIntrinsicElementProps(props, Element) : props
9014
+ ...forwardedProps
8456
9015
  });
8457
9016
  }
8458
9017
  var PolymorphicElement = forwardRef$1(UnforwardedPolymorphicElement);
8459
9018
  //#endregion
8460
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/view/component.mjs
9019
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/view/component.mjs
8461
9020
  function UnforwardedView({ css, ...restProps }, ref) {
8462
9021
  return /* @__PURE__ */ jsx(PolymorphicElement, {
8463
9022
  ref,
@@ -8466,7 +9025,7 @@ function UnforwardedView({ css, ...restProps }, ref) {
8466
9025
  }
8467
9026
  var component_default$2 = Object.assign(forwardRef$1(UnforwardedView), { selector: ".components-view" });
8468
9027
  //#endregion
8469
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
9028
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
8470
9029
  function UnconnectedVisuallyHidden(props, forwardedRef) {
8471
9030
  const { style: styleProp, ...contextProps } = useContextSystem(props, "VisuallyHidden");
8472
9031
  return /* @__PURE__ */ jsx(component_default$2, {
@@ -8481,7 +9040,7 @@ function UnconnectedVisuallyHidden(props, forwardedRef) {
8481
9040
  }
8482
9041
  var component_default$1 = contextConnect(UnconnectedVisuallyHidden, "VisuallyHidden");
8483
9042
  //#endregion
8484
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
9043
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
8485
9044
  var breakpoints = [
8486
9045
  "40em",
8487
9046
  "52em",
@@ -8515,7 +9074,7 @@ function useResponsiveValue(values, options = {}) {
8515
9074
  return array[index >= array.length ? array.length - 1 : index];
8516
9075
  }
8517
9076
  //#endregion
8518
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/grid/utils.mjs
9077
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/grid/utils.mjs
8519
9078
  var ALIGNMENTS = {
8520
9079
  bottom: {
8521
9080
  alignItems: "flex-end",
@@ -8563,7 +9122,7 @@ function getAlignmentProps(alignment) {
8563
9122
  return alignment ? ALIGNMENTS[alignment] : {};
8564
9123
  }
8565
9124
  //#endregion
8566
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/grid/hook.mjs
9125
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/grid/hook.mjs
8567
9126
  function useGrid(props) {
8568
9127
  const { align, alignment, className, columnGap, columns = 2, gap = 3, isInline = false, justify, rowGap, rows, templateColumns, templateRows, ...otherProps } = useContextSystem(props, "Grid");
8569
9128
  const column = useResponsiveValue(Array.isArray(columns) ? columns : [columns]);
@@ -8584,7 +9143,7 @@ function useGrid(props) {
8584
9143
  justifyContent: justify,
8585
9144
  verticalAlign: isInline ? "middle" : void 0,
8586
9145
  ...alignmentProps
8587
- }, process.env.NODE_ENV === "production" ? "" : ";label:gridClasses;", process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImhvb2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBdURzQiIsImZpbGUiOiJob29rLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBFeHRlcm5hbCBkZXBlbmRlbmNpZXNcbiAqL1xuaW1wb3J0IHsgY3NzIH0gZnJvbSAnQGVtb3Rpb24vcmVhY3QnO1xuXG4vKipcbiAqIFdvcmRQcmVzcyBkZXBlbmRlbmNpZXNcbiAqL1xuaW1wb3J0IHsgdXNlTWVtbyB9IGZyb20gJ0B3b3JkcHJlc3MvZWxlbWVudCc7XG5cbi8qKlxuICogSW50ZXJuYWwgZGVwZW5kZW5jaWVzXG4gKi9cbmltcG9ydCB0eXBlIHsgV29yZFByZXNzQ29tcG9uZW50UHJvcHMgfSBmcm9tICcuLi9jb250ZXh0JztcbmltcG9ydCB7IHVzZUNvbnRleHRTeXN0ZW0gfSBmcm9tICcuLi9jb250ZXh0JztcbmltcG9ydCB7IGdldEFsaWdubWVudFByb3BzIH0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQgeyB1c2VSZXNwb25zaXZlVmFsdWUgfSBmcm9tICcuLi91dGlscy91c2UtcmVzcG9uc2l2ZS12YWx1ZSc7XG5pbXBvcnQgQ09ORklHIGZyb20gJy4uL3V0aWxzL2NvbmZpZy12YWx1ZXMnO1xuaW1wb3J0IHsgdXNlQ3ggfSBmcm9tICcuLi91dGlscy9ob29rcy91c2UtY3gnO1xuaW1wb3J0IHR5cGUgeyBHcmlkUHJvcHMgfSBmcm9tICcuL3R5cGVzJztcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdXNlR3JpZChcblx0cHJvcHM6IFdvcmRQcmVzc0NvbXBvbmVudFByb3BzPCBHcmlkUHJvcHMsICdkaXYnID5cbikge1xuXHRjb25zdCB7XG5cdFx0YWxpZ24sXG5cdFx0YWxpZ25tZW50LFxuXHRcdGNsYXNzTmFtZSxcblx0XHRjb2x1bW5HYXAsXG5cdFx0Y29sdW1ucyA9IDIsXG5cdFx0Z2FwID0gMyxcblx0XHRpc0lubGluZSA9IGZhbHNlLFxuXHRcdGp1c3RpZnksXG5cdFx0cm93R2FwLFxuXHRcdHJvd3MsXG5cdFx0dGVtcGxhdGVDb2x1bW5zLFxuXHRcdHRlbXBsYXRlUm93cyxcblx0XHQuLi5vdGhlclByb3BzXG5cdH0gPSB1c2VDb250ZXh0U3lzdGVtKCBwcm9wcywgJ0dyaWQnICk7XG5cblx0Y29uc3QgY29sdW1uc0FzQXJyYXkgPSBBcnJheS5pc0FycmF5KCBjb2x1bW5zICkgPyBjb2x1bW5zIDogWyBjb2x1bW5zIF07XG5cdGNvbnN0IGNvbHVtbiA9IHVzZVJlc3BvbnNpdmVWYWx1ZSggY29sdW1uc0FzQXJyYXkgKTtcblx0Y29uc3Qgcm93c0FzQXJyYXkgPSBBcnJheS5pc0FycmF5KCByb3dzICkgPyByb3dzIDogWyByb3dzIF07XG5cdGNvbnN0IHJvdyA9IHVzZVJlc3BvbnNpdmVWYWx1ZSggcm93c0FzQXJyYXkgKTtcblxuXHRjb25zdCBncmlkVGVtcGxhdGVDb2x1bW5zID1cblx0XHR0ZW1wbGF0ZUNvbHVtbnMgfHwgKCAhISBjb2x1bW5zICYmIGByZXBlYXQoICR7IGNvbHVtbiB9LCAxZnIgKWAgKTtcblx0Y29uc3QgZ3JpZFRlbXBsYXRlUm93cyA9XG5cdFx0dGVtcGxhdGVSb3dzIHx8ICggISEgcm93cyAmJiBgcmVwZWF0KCAkeyByb3cgfSwgMWZyIClgICk7XG5cblx0Y29uc3QgY3ggPSB1c2VDeCgpO1xuXG5cdGNvbnN0IGNsYXNzZXMgPSB1c2VNZW1vKCAoKSA9PiB7XG5cdFx0Y29uc3QgYWxpZ25tZW50UHJvcHMgPSBnZXRBbGlnbm1lbnRQcm9wcyggYWxpZ25tZW50ICk7XG5cblx0XHRjb25zdCBncmlkQ2xhc3NlcyA9IGNzcygge1xuXHRcdFx0YWxpZ25JdGVtczogYWxpZ24sXG5cdFx0XHRkaXNwbGF5OiBpc0lubGluZSA/ICdpbmxpbmUtZ3JpZCcgOiAnZ3JpZCcsXG5cdFx0XHRnYXA6IGBjYWxjKCAkeyBDT05GSUcuZ3JpZEJhc2UgfSAqICR7IGdhcCB9IClgLFxuXHRcdFx0Z3JpZFRlbXBsYXRlQ29sdW1uczogZ3JpZFRlbXBsYXRlQ29sdW1ucyB8fCB1bmRlZmluZWQsXG5cdFx0XHRncmlkVGVtcGxhdGVSb3dzOiBncmlkVGVtcGxhdGVSb3dzIHx8IHVuZGVmaW5lZCxcblx0XHRcdGdyaWRSb3dHYXA6IHJvd0dhcCxcblx0XHRcdGdyaWRDb2x1bW5HYXA6IGNvbHVtbkdhcCxcblx0XHRcdGp1c3RpZnlDb250ZW50OiBqdXN0aWZ5LFxuXHRcdFx0dmVydGljYWxBbGlnbjogaXNJbmxpbmUgPyAnbWlkZGxlJyA6IHVuZGVmaW5lZCxcblx0XHRcdC4uLmFsaWdubWVudFByb3BzLFxuXHRcdH0gKTtcblxuXHRcdHJldHVybiBjeCggZ3JpZENsYXNzZXMsIGNsYXNzTmFtZSApO1xuXHR9LCBbXG5cdFx0YWxpZ24sXG5cdFx0YWxpZ25tZW50LFxuXHRcdGNsYXNzTmFtZSxcblx0XHRjb2x1bW5HYXAsXG5cdFx0Y3gsXG5cdFx0Z2FwLFxuXHRcdGdyaWRUZW1wbGF0ZUNvbHVtbnMsXG5cdFx0Z3JpZFRlbXBsYXRlUm93cyxcblx0XHRpc0lubGluZSxcblx0XHRqdXN0aWZ5LFxuXHRcdHJvd0dhcCxcblx0XSApO1xuXG5cdHJldHVybiB7IC4uLm90aGVyUHJvcHMsIGNsYXNzTmFtZTogY2xhc3NlcyB9O1xufVxuIl19 */");
9146
+ }, process.env.NODE_ENV === "production" ? "" : ";label:gridClasses;", process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImhvb2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBNENzQiIsImZpbGUiOiJob29rLnRzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3NzIH0gZnJvbSAnQGVtb3Rpb24vcmVhY3QnO1xuaW1wb3J0IHsgdXNlTWVtbyB9IGZyb20gJ0B3b3JkcHJlc3MvZWxlbWVudCc7XG5pbXBvcnQgdHlwZSB7IFdvcmRQcmVzc0NvbXBvbmVudFByb3BzIH0gZnJvbSAnLi4vY29udGV4dCc7XG5pbXBvcnQgeyB1c2VDb250ZXh0U3lzdGVtIH0gZnJvbSAnLi4vY29udGV4dCc7XG5pbXBvcnQgeyBnZXRBbGlnbm1lbnRQcm9wcyB9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHsgdXNlUmVzcG9uc2l2ZVZhbHVlIH0gZnJvbSAnLi4vdXRpbHMvdXNlLXJlc3BvbnNpdmUtdmFsdWUnO1xuaW1wb3J0IENPTkZJRyBmcm9tICcuLi91dGlscy9jb25maWctdmFsdWVzJztcbmltcG9ydCB7IHVzZUN4IH0gZnJvbSAnLi4vdXRpbHMvaG9va3MvdXNlLWN4JztcbmltcG9ydCB0eXBlIHsgR3JpZFByb3BzIH0gZnJvbSAnLi90eXBlcyc7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHVzZUdyaWQoXG5cdHByb3BzOiBXb3JkUHJlc3NDb21wb25lbnRQcm9wczwgR3JpZFByb3BzLCAnZGl2JyA+XG4pIHtcblx0Y29uc3Qge1xuXHRcdGFsaWduLFxuXHRcdGFsaWdubWVudCxcblx0XHRjbGFzc05hbWUsXG5cdFx0Y29sdW1uR2FwLFxuXHRcdGNvbHVtbnMgPSAyLFxuXHRcdGdhcCA9IDMsXG5cdFx0aXNJbmxpbmUgPSBmYWxzZSxcblx0XHRqdXN0aWZ5LFxuXHRcdHJvd0dhcCxcblx0XHRyb3dzLFxuXHRcdHRlbXBsYXRlQ29sdW1ucyxcblx0XHR0ZW1wbGF0ZVJvd3MsXG5cdFx0Li4ub3RoZXJQcm9wc1xuXHR9ID0gdXNlQ29udGV4dFN5c3RlbSggcHJvcHMsICdHcmlkJyApO1xuXG5cdGNvbnN0IGNvbHVtbnNBc0FycmF5ID0gQXJyYXkuaXNBcnJheSggY29sdW1ucyApID8gY29sdW1ucyA6IFsgY29sdW1ucyBdO1xuXHRjb25zdCBjb2x1bW4gPSB1c2VSZXNwb25zaXZlVmFsdWUoIGNvbHVtbnNBc0FycmF5ICk7XG5cdGNvbnN0IHJvd3NBc0FycmF5ID0gQXJyYXkuaXNBcnJheSggcm93cyApID8gcm93cyA6IFsgcm93cyBdO1xuXHRjb25zdCByb3cgPSB1c2VSZXNwb25zaXZlVmFsdWUoIHJvd3NBc0FycmF5ICk7XG5cblx0Y29uc3QgZ3JpZFRlbXBsYXRlQ29sdW1ucyA9XG5cdFx0dGVtcGxhdGVDb2x1bW5zIHx8ICggISEgY29sdW1ucyAmJiBgcmVwZWF0KCAkeyBjb2x1bW4gfSwgMWZyIClgICk7XG5cdGNvbnN0IGdyaWRUZW1wbGF0ZVJvd3MgPVxuXHRcdHRlbXBsYXRlUm93cyB8fCAoICEhIHJvd3MgJiYgYHJlcGVhdCggJHsgcm93IH0sIDFmciApYCApO1xuXG5cdGNvbnN0IGN4ID0gdXNlQ3goKTtcblxuXHRjb25zdCBjbGFzc2VzID0gdXNlTWVtbyggKCkgPT4ge1xuXHRcdGNvbnN0IGFsaWdubWVudFByb3BzID0gZ2V0QWxpZ25tZW50UHJvcHMoIGFsaWdubWVudCApO1xuXG5cdFx0Y29uc3QgZ3JpZENsYXNzZXMgPSBjc3MoIHtcblx0XHRcdGFsaWduSXRlbXM6IGFsaWduLFxuXHRcdFx0ZGlzcGxheTogaXNJbmxpbmUgPyAnaW5saW5lLWdyaWQnIDogJ2dyaWQnLFxuXHRcdFx0Z2FwOiBgY2FsYyggJHsgQ09ORklHLmdyaWRCYXNlIH0gKiAkeyBnYXAgfSApYCxcblx0XHRcdGdyaWRUZW1wbGF0ZUNvbHVtbnM6IGdyaWRUZW1wbGF0ZUNvbHVtbnMgfHwgdW5kZWZpbmVkLFxuXHRcdFx0Z3JpZFRlbXBsYXRlUm93czogZ3JpZFRlbXBsYXRlUm93cyB8fCB1bmRlZmluZWQsXG5cdFx0XHRncmlkUm93R2FwOiByb3dHYXAsXG5cdFx0XHRncmlkQ29sdW1uR2FwOiBjb2x1bW5HYXAsXG5cdFx0XHRqdXN0aWZ5Q29udGVudDoganVzdGlmeSxcblx0XHRcdHZlcnRpY2FsQWxpZ246IGlzSW5saW5lID8gJ21pZGRsZScgOiB1bmRlZmluZWQsXG5cdFx0XHQuLi5hbGlnbm1lbnRQcm9wcyxcblx0XHR9ICk7XG5cblx0XHRyZXR1cm4gY3goIGdyaWRDbGFzc2VzLCBjbGFzc05hbWUgKTtcblx0fSwgW1xuXHRcdGFsaWduLFxuXHRcdGFsaWdubWVudCxcblx0XHRjbGFzc05hbWUsXG5cdFx0Y29sdW1uR2FwLFxuXHRcdGN4LFxuXHRcdGdhcCxcblx0XHRncmlkVGVtcGxhdGVDb2x1bW5zLFxuXHRcdGdyaWRUZW1wbGF0ZVJvd3MsXG5cdFx0aXNJbmxpbmUsXG5cdFx0anVzdGlmeSxcblx0XHRyb3dHYXAsXG5cdF0gKTtcblxuXHRyZXR1cm4geyAuLi5vdGhlclByb3BzLCBjbGFzc05hbWU6IGNsYXNzZXMgfTtcbn1cbiJdfQ== */");
8588
9147
  return cx(gridClasses, className);
8589
9148
  }, [
8590
9149
  align,
@@ -8605,10 +9164,11 @@ function useGrid(props) {
8605
9164
  };
8606
9165
  }
8607
9166
  //#endregion
8608
- //#region ../../../node_modules/.pnpm/@wordpress+components@37.0.0_@types+react@18.3.28_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@wordpress/components/build-module/grid/component.mjs
9167
+ //#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@types+react-dom@18.3.7_@types+react@18.3.31__@types+react_d122c1aea9dc8a45ffffc1d3c2f19116/node_modules/@wordpress/components/build-module/grid/component.mjs
8609
9168
  function UnconnectedGrid(props, forwardedRef) {
9169
+ const gridProps = useGrid(props);
8610
9170
  return /* @__PURE__ */ jsx(component_default$2, {
8611
- ...useGrid(props),
9171
+ ...gridProps,
8612
9172
  ref: forwardedRef
8613
9173
  });
8614
9174
  }
@@ -8627,8 +9187,8 @@ const SUBPIXEL_TOLERANCE = .5;
8627
9187
  * Counts how many leading rows fit inside the content container.
8628
9188
  *
8629
9189
  * Rows are read from the DOM rather than derived from constants: row height
8630
- * depends on the theme's row gap, label wrapping, and whatever a caller renders
8631
- * as a label, so any restated number would drift.
9190
+ * depends on `--a8c-charts-dimension-leaderboard-row-gap`, label wrapping, and
9191
+ * whatever a caller renders as a label, so any restated number would drift.
8632
9192
  *
8633
9193
  * Every row is a direct grid child marked with `data-row-index`; interactive rows
8634
9194
  * use a button wrapper and non-interactive rows use a div wrapper.
@@ -8713,14 +9273,13 @@ function useFittedRowCount(enabled, rowCount, data) {
8713
9273
  * @return Array of legend items for the leaderboard chart
8714
9274
  */
8715
9275
  function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withComparison = false, withOverlayLabel = false, legendLabels }) {
8716
- const { leaderboardChart: leaderboardChartSettings } = useGlobalChartsTheme();
8717
9276
  const { getElementStyles } = useGlobalChartsContext();
8718
9277
  return useMemo(() => {
8719
9278
  if (!data || data.length === 0) return [];
8720
9279
  const items = [];
8721
9280
  const { color: resolvedPrimaryColor } = getElementStyles({
8722
9281
  index: 0,
8723
- overrideColor: primaryColor || leaderboardChartSettings.primaryColor
9282
+ overrideColor: primaryColor
8724
9283
  });
8725
9284
  items.push({
8726
9285
  label: legendLabels?.primary || __("Current period", "jetpack-charts"),
@@ -8729,7 +9288,7 @@ function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withCom
8729
9288
  if (withComparison && !withOverlayLabel) {
8730
9289
  const { color: resolvedSecondaryColor } = getElementStyles({
8731
9290
  index: 1,
8732
- overrideColor: secondaryColor || leaderboardChartSettings.secondaryColor
9291
+ overrideColor: secondaryColor
8733
9292
  });
8734
9293
  items.push({
8735
9294
  label: legendLabels?.comparison || __("Previous period", "jetpack-charts"),
@@ -8743,7 +9302,6 @@ function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withCom
8743
9302
  secondaryColor,
8744
9303
  withComparison,
8745
9304
  legendLabels,
8746
- leaderboardChartSettings,
8747
9305
  getElementStyles,
8748
9306
  withOverlayLabel
8749
9307
  ]);
@@ -8757,6 +9315,9 @@ var leaderboard_chart_module_default = {
8757
9315
  "chevron": "a8ccharts-GovfoW-chevron",
8758
9316
  "deltaPlaceholder": "a8ccharts-GovfoW-deltaPlaceholder",
8759
9317
  "deltaValue": "a8ccharts-GovfoW-deltaValue",
9318
+ "deltaValue--down": "a8ccharts-GovfoW-deltaValue--down",
9319
+ "deltaValue--neutral": "a8ccharts-GovfoW-deltaValue--neutral",
9320
+ "deltaValue--up": "a8ccharts-GovfoW-deltaValue--up",
8760
9321
  "emptyState": "a8ccharts-GovfoW-emptyState",
8761
9322
  "fitEmptyState": "a8ccharts-GovfoW-fitEmptyState",
8762
9323
  "interactiveRow": "a8ccharts-GovfoW-interactiveRow",
@@ -8775,6 +9336,14 @@ var leaderboard_chart_module_default = {
8775
9336
  };
8776
9337
  //#endregion
8777
9338
  //#region src/charts/leaderboard-chart/leaderboard-chart.tsx
9339
+ const ROW_GAP = "var(--a8c-charts-dimension-leaderboard-row-gap, 12px)";
9340
+ const COLUMN_GAP = "var(--a8c-charts-dimension-leaderboard-column-gap, 4px)";
9341
+ /** Indexed by `Math.sign( delta ) + 1`, so [negative, neutral, positive]. */
9342
+ const DELTA_TREND_CLASS = [
9343
+ "deltaValue--down",
9344
+ "deltaValue--neutral",
9345
+ "deltaValue--up"
9346
+ ];
8778
9347
  /**
8779
9348
  * Default value formatter using formatMetricValue
8780
9349
  *
@@ -8877,15 +9446,15 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8877
9446
  ...legend.shapeStyles
8878
9447
  };
8879
9448
  const { legendChildren, nonLegendChildren } = useChartChildren(children, "LeaderboardChart");
8880
- const { labelSpacing, rowGap, columnGap, primaryColor: settingsPrimaryColor, secondaryColor: settingsSecondaryColor, deltaColors } = leaderboardChartSettings;
9449
+ const { labelSpacing } = leaderboardChartSettings;
8881
9450
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
8882
9451
  const { color: resolvedPrimaryColor } = getElementStyles({
8883
9452
  index: 0,
8884
- overrideColor: primaryColor || settingsPrimaryColor
9453
+ overrideColor: primaryColor
8885
9454
  });
8886
9455
  const { color: resolvedSecondaryColor } = getElementStyles({
8887
9456
  index: 1,
8888
- overrideColor: secondaryColor || settingsSecondaryColor
9457
+ overrideColor: secondaryColor
8889
9458
  });
8890
9459
  const legendItems = useLeaderboardLegendItems({
8891
9460
  data: data || [],
@@ -8920,15 +9489,17 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8920
9489
  withComparison,
8921
9490
  withOverlayLabel
8922
9491
  ]);
9492
+ const isDataValid = Boolean(data && data.length > 0);
9493
+ const chartMetadata = useMemo(() => ({
9494
+ withComparison,
9495
+ withOverlayLabel
9496
+ }), [withComparison, withOverlayLabel]);
8923
9497
  useChartRegistration({
8924
9498
  chartId,
8925
9499
  legendItems,
8926
9500
  chartType: "leaderboard",
8927
- isDataValid: Boolean(data && data.length > 0),
8928
- metadata: useMemo(() => ({
8929
- withComparison,
8930
- withOverlayLabel
8931
- }), [withComparison, withOverlayLabel])
9501
+ isDataValid,
9502
+ metadata: chartMetadata
8932
9503
  });
8933
9504
  const prefersReducedMotion = usePrefersReducedMotion();
8934
9505
  const { contentRef, fittedCount, isMeasurable } = useFittedRowCount(fitRows && !allSeriesHidden, data?.length ?? 0, data);
@@ -8939,7 +9510,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8939
9510
  legendPosition,
8940
9511
  legendElement: false,
8941
9512
  legendChildren,
8942
- className: clsx(leaderboard_chart_module_default.leaderboardChart, {
9513
+ className: clsx("leaderboard-chart", leaderboard_chart_module_default.leaderboardChart, {
8943
9514
  [leaderboard_chart_module_default["leaderboardChart--responsive"]]: !propWidth && !propHeight,
8944
9515
  [leaderboard_chart_module_default["leaderboardChart--loading"]]: loading
8945
9516
  }, className),
@@ -8974,7 +9545,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8974
9545
  legendPosition,
8975
9546
  legendElement,
8976
9547
  legendChildren,
8977
- className: clsx(leaderboard_chart_module_default.leaderboardChart, {
9548
+ className: clsx("leaderboard-chart", leaderboard_chart_module_default.leaderboardChart, {
8978
9549
  [leaderboard_chart_module_default["leaderboardChart--responsive"]]: !propWidth && !propHeight,
8979
9550
  [leaderboard_chart_module_default["leaderboardChart--loading"]]: loading
8980
9551
  }, className),
@@ -8996,8 +9567,8 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8996
9567
  children: getAllHiddenMessage(legendInteractive, "series")
8997
9568
  }) : /* @__PURE__ */ jsx(component_default, {
8998
9569
  templateColumns: "minmax(0, 1fr) auto",
8999
- rowGap,
9000
- columnGap,
9570
+ rowGap: ROW_GAP,
9571
+ columnGap: COLUMN_GAP,
9001
9572
  "data-leaderboard-grid": true,
9002
9573
  children: data.map((entry, rowIndex) => {
9003
9574
  const rowStyle = shouldFitRows && rowIndex >= fittedCount ? { visibility: "hidden" } : void 0;
@@ -9006,8 +9577,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9006
9577
  const hasDelta = hasDeltaValue(entry);
9007
9578
  const showComparisonValue = showComparisonColumn && hasDelta;
9008
9579
  const showComparisonPlaceholder = showComparisonColumn && !hasDelta;
9009
- const colorIndex = showComparisonValue ? Math.sign(entry.delta) + 1 : 1;
9010
- const deltaColor = deltaColors[colorIndex];
9580
+ const deltaTrendClass = leaderboard_chart_module_default[DELTA_TREND_CLASS[showComparisonValue ? Math.sign(entry.delta) + 1 : 1]];
9011
9581
  const rowCells = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Stack, {
9012
9582
  direction: "column",
9013
9583
  gap: labelSpacing,
@@ -9028,13 +9598,11 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9028
9598
  children: [
9029
9599
  isPrimaryVisible && /* @__PURE__ */ jsx(Text$1, { children: valueFormatter(entry.currentValue) }),
9030
9600
  showComparisonValue && /* @__PURE__ */ jsx(Text$1, {
9031
- className: leaderboard_chart_module_default.deltaValue,
9032
- style: { color: deltaColor },
9601
+ className: clsx(leaderboard_chart_module_default.deltaValue, deltaTrendClass),
9033
9602
  children: deltaFormatter(entry.delta)
9034
9603
  }),
9035
9604
  showComparisonPlaceholder && /* @__PURE__ */ jsxs(Text$1, {
9036
- className: clsx(leaderboard_chart_module_default.deltaValue, leaderboard_chart_module_default.deltaPlaceholder),
9037
- style: { color: deltaColor },
9605
+ className: clsx(leaderboard_chart_module_default.deltaValue, leaderboard_chart_module_default.deltaPlaceholder, deltaTrendClass),
9038
9606
  children: [/* @__PURE__ */ jsx("span", {
9039
9607
  "aria-hidden": "true",
9040
9608
  children: "—"
@@ -9118,7 +9686,9 @@ function RadialWipeAnimation({ id, radius, innerRadius = 0, durationMs = 1e3, wi
9118
9686
  //#endregion
9119
9687
  //#region src/charts/pie-chart/pie-chart.module.scss
9120
9688
  var pie_chart_module_default = {
9121
- "pie-chart": "a8ccharts-gnszbG-pie-chart",
9689
+ "pie-chart__label-plate": "a8ccharts-gnszbG-pie-chart__label-plate",
9690
+ "pie-chart__label-text": "a8ccharts-gnszbG-pie-chart__label-text",
9691
+ "pie-chart__plot": "a8ccharts-gnszbG-pie-chart__plot",
9122
9692
  "pie-chart--responsive": "a8ccharts-gnszbG-pie-chart--responsive"
9123
9693
  };
9124
9694
  //#endregion
@@ -9173,18 +9743,16 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9173
9743
  const providerTheme = useGlobalChartsTheme();
9174
9744
  const chartId = useChartId(providedChartId);
9175
9745
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip();
9176
- const { containerRef, TooltipInPortal, containerBounds } = useTooltipInPortal({
9177
- detectBounds: true,
9178
- scroll: true,
9179
- debounce: 0
9180
- });
9746
+ const standaloneScopeClass = useStandaloneScopeClass();
9747
+ const containerRef = useRef(null);
9181
9748
  const onMouseLeave = useCallback(() => {
9182
9749
  if (!withTooltips) return;
9183
9750
  hideTooltip();
9184
9751
  }, [withTooltips, hideTooltip]);
9185
9752
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
9753
+ const dataWithPercentages = useDataWithPercentages(data);
9186
9754
  const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9187
- data: useDataWithPercentages(data),
9755
+ data: dataWithPercentages,
9188
9756
  chartId,
9189
9757
  isSeriesVisible
9190
9758
  });
@@ -9194,20 +9762,21 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9194
9762
  }), [legendValueDisplay]));
9195
9763
  const { isValid, message } = validateData$1(data);
9196
9764
  const { svgChildren, htmlChildren, legendChildren, otherChildren } = useChartChildren(children, "PieChart");
9765
+ const chartMetadata = useMemo(() => ({
9766
+ thickness,
9767
+ gapScale,
9768
+ cornerScale
9769
+ }), [
9770
+ thickness,
9771
+ gapScale,
9772
+ cornerScale
9773
+ ]);
9197
9774
  useChartRegistration({
9198
9775
  chartId,
9199
9776
  legendItems,
9200
9777
  chartType: "pie",
9201
9778
  isDataValid: isValid,
9202
- metadata: useMemo(() => ({
9203
- thickness,
9204
- gapScale,
9205
- cornerScale
9206
- }), [
9207
- thickness,
9208
- gapScale,
9209
- cornerScale
9210
- ])
9779
+ metadata: chartMetadata
9211
9780
  });
9212
9781
  const prefersReducedMotion = usePrefersReducedMotion();
9213
9782
  if (!isValid) return /* @__PURE__ */ jsx("div", {
@@ -9258,19 +9827,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9258
9827
  width: propWidth || void 0,
9259
9828
  height: propHeight || void 0
9260
9829
  },
9261
- trailingContent: /* @__PURE__ */ jsxs(Fragment$1, { children: [
9262
- withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ jsx(TooltipInPortal, {
9263
- top: tooltipTop || 0,
9264
- left: tooltipLeft || 0,
9265
- children: /* @__PURE__ */ jsx("div", {
9266
- className: "a8c-charts-scope",
9267
- role: "tooltip",
9268
- children: renderTooltip({ tooltipData })
9269
- })
9270
- }),
9271
- htmlChildren,
9272
- otherChildren
9273
- ] }),
9830
+ trailingContent: /* @__PURE__ */ jsxs(Fragment$1, { children: [htmlChildren, otherChildren] }),
9274
9831
  children: ({ contentWidth, contentHeight }) => {
9275
9832
  const availableSize = Math.min(contentWidth > 0 ? contentWidth : 300, contentHeight > 0 ? contentHeight : 300);
9276
9833
  const actualSize = size ? Math.min(size, availableSize) : availableSize;
@@ -9283,9 +9840,10 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9283
9840
  const innerRadius = thickness === 0 ? 0 : outerRadius * (1 - thickness);
9284
9841
  const maxCornerRadius = (outerRadius - innerRadius) / 2;
9285
9842
  const cornerRadius = cornerScale ? Math.min(cornerScale * outerRadius, maxCornerRadius) : 0;
9286
- return /* @__PURE__ */ jsx(Center, {
9843
+ return /* @__PURE__ */ jsxs(Center, {
9287
9844
  ref: containerRef,
9288
- children: /* @__PURE__ */ jsxs("svg", {
9845
+ className: pie_chart_module_default["pie-chart__plot"],
9846
+ children: [/* @__PURE__ */ jsxs("svg", {
9289
9847
  viewBox: `0 0 ${width} ${height}`,
9290
9848
  preserveAspectRatio: "xMidYMid meet",
9291
9849
  width,
@@ -9317,11 +9875,12 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9317
9875
  const hasSpaceForLabel = arc.endAngle - arc.startAngle >= .25;
9318
9876
  const handleMouseMove = (event) => {
9319
9877
  if (!withTooltips) return;
9320
- if (containerBounds.width === 0 || containerBounds.height === 0) return;
9878
+ const bounds = containerRef.current?.getBoundingClientRect();
9879
+ if (!bounds) return;
9321
9880
  showTooltip({
9322
9881
  tooltipData: arc.data,
9323
- tooltipLeft: event.clientX - containerBounds.left + tooltipOffsetX,
9324
- tooltipTop: event.clientY - containerBounds.top + tooltipOffsetY
9882
+ tooltipLeft: event.clientX - bounds.left + tooltipOffsetX,
9883
+ tooltipTop: event.clientY - bounds.top + tooltipOffsetY
9325
9884
  });
9326
9885
  };
9327
9886
  const pathProps = {
@@ -9336,30 +9895,28 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9336
9895
  }
9337
9896
  const svgLabelSmall = providerTheme.svgLabelSmall;
9338
9897
  const fontSize = resolveFontSize(svgLabelSmall?.fontSize) ?? 12;
9339
- const estimatedTextWidth = getStringWidth$1(arc.data.label, {
9898
+ const backgroundWidth = getStringWidth$1(arc.data.label, {
9340
9899
  fontSize,
9341
9900
  fontFamily: svgLabelSmall?.fontFamily,
9342
9901
  fontWeight: svgLabelSmall?.fontWeight
9343
- });
9344
- const labelPadding = 6;
9345
- const backgroundWidth = estimatedTextWidth + labelPadding * 2;
9346
- const backgroundHeight = fontSize + labelPadding * 2;
9902
+ }) + 12;
9903
+ const backgroundHeight = fontSize + 12;
9347
9904
  return /* @__PURE__ */ jsxs("g", {
9348
9905
  ...groupProps,
9349
- children: [/* @__PURE__ */ jsx("path", { ...pathProps }), showLabels && hasSpaceForLabel && /* @__PURE__ */ jsxs("g", { children: [providerTheme.labelBackgroundColor && /* @__PURE__ */ jsx("rect", {
9906
+ children: [/* @__PURE__ */ jsx("path", { ...pathProps }), showLabels && hasSpaceForLabel && /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("rect", {
9907
+ className: pie_chart_module_default["pie-chart__label-plate"],
9350
9908
  x: centroidX - backgroundWidth / 2,
9351
9909
  y: centroidY - backgroundHeight / 2,
9352
9910
  width: backgroundWidth,
9353
9911
  height: backgroundHeight,
9354
- fill: providerTheme.labelBackgroundColor,
9355
9912
  rx: 4,
9356
9913
  ry: 4,
9357
9914
  pointerEvents: "none"
9358
9915
  }), /* @__PURE__ */ jsx("text", {
9916
+ className: pie_chart_module_default["pie-chart__label-text"],
9359
9917
  x: centroidX,
9360
9918
  y: centroidY,
9361
9919
  dy: ".33em",
9362
- fill: providerTheme.labelTextColor || "#333",
9363
9920
  fontSize,
9364
9921
  textAnchor: "middle",
9365
9922
  pointerEvents: "none",
@@ -9370,7 +9927,15 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9370
9927
  }
9371
9928
  }), !allSegmentsHidden && svgChildren]
9372
9929
  })]
9373
- })
9930
+ }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ jsx(BoundedTooltip, {
9931
+ top: tooltipTop || 0,
9932
+ left: tooltipLeft || 0,
9933
+ children: /* @__PURE__ */ jsx("div", {
9934
+ className: standaloneScopeClass,
9935
+ role: "tooltip",
9936
+ children: renderTooltip({ tooltipData })
9937
+ })
9938
+ })]
9374
9939
  });
9375
9940
  }
9376
9941
  })
@@ -9397,6 +9962,7 @@ var pie_semi_circle_chart_module_default = {
9397
9962
  "label": "a8ccharts-YtTOxW-label",
9398
9963
  "note": "a8ccharts-YtTOxW-note",
9399
9964
  "pie-semi-circle-chart": "a8ccharts-YtTOxW-pie-semi-circle-chart",
9965
+ "pie-semi-circle-chart__plot": "a8ccharts-YtTOxW-pie-semi-circle-chart__plot",
9400
9966
  "pie-semi-circle-chart--responsive": "a8ccharts-YtTOxW-pie-semi-circle-chart--responsive"
9401
9967
  };
9402
9968
  //#endregion
@@ -9446,23 +10012,17 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9446
10012
  const legendPosition = legend.position ?? "bottom";
9447
10013
  const chartId = useChartId(providedChartId);
9448
10014
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip();
9449
- const { containerRef, TooltipInPortal, containerBounds } = useTooltipInPortal({
9450
- detectBounds: true,
9451
- scroll: true,
9452
- debounce: 0
9453
- });
10015
+ const standaloneScopeClass = useStandaloneScopeClass();
10016
+ const containerRef = useRef(null);
9454
10017
  const handleMouseMove = useCallback((event, arc) => {
9455
- if (containerBounds.width === 0 || containerBounds.height === 0) return;
10018
+ const bounds = containerRef.current?.getBoundingClientRect();
10019
+ if (!bounds) return;
9456
10020
  showTooltip({
9457
10021
  tooltipData: arc.data,
9458
- tooltipLeft: event.clientX - containerBounds.left + tooltipOffsetX,
9459
- tooltipTop: event.clientY - containerBounds.top + tooltipOffsetY
10022
+ tooltipLeft: event.clientX - bounds.left + tooltipOffsetX,
10023
+ tooltipTop: event.clientY - bounds.top + tooltipOffsetY
9460
10024
  });
9461
10025
  }, [
9462
- containerBounds.width,
9463
- containerBounds.height,
9464
- containerBounds.left,
9465
- containerBounds.top,
9466
10026
  showTooltip,
9467
10027
  tooltipOffsetX,
9468
10028
  tooltipOffsetY
@@ -9475,8 +10035,9 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9475
10035
  }, [handleMouseMove]);
9476
10036
  const { isValid, message } = validateData(data);
9477
10037
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
10038
+ const dataWithPercentages = useDataWithPercentages(data);
9478
10039
  const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9479
- data: useDataWithPercentages(data),
10040
+ data: dataWithPercentages,
9480
10041
  chartId,
9481
10042
  isSeriesVisible
9482
10043
  });
@@ -9493,15 +10054,16 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9493
10054
  legendValueDisplay
9494
10055
  }), [legendValueDisplay]));
9495
10056
  const { svgChildren, htmlChildren, legendChildren, otherChildren } = useChartChildren(children, "PieSemiCircleChart");
10057
+ const chartMetadata = useMemo(() => ({
10058
+ thickness,
10059
+ clockwise
10060
+ }), [thickness, clockwise]);
9496
10061
  useChartRegistration({
9497
10062
  chartId,
9498
10063
  legendItems,
9499
10064
  chartType: "pie-semi-circle",
9500
10065
  isDataValid: isValid,
9501
- metadata: useMemo(() => ({
9502
- thickness,
9503
- clockwise
9504
- }), [thickness, clockwise])
10066
+ metadata: chartMetadata
9505
10067
  });
9506
10068
  const prefersReducedMotion = usePrefersReducedMotion();
9507
10069
  const effectiveWidth = propWidth || DEFAULT_WIDTH$1;
@@ -9509,7 +10071,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9509
10071
  const errorWidth = propHeight ? Math.min(propWidth || propHeight * 2, propHeight * 2) : effectiveWidth;
9510
10072
  const errorHeight = errorWidth / 2;
9511
10073
  return /* @__PURE__ */ jsx("div", {
9512
- className: pie_semi_circle_chart_module_default["pie-semi-circle-chart"],
10074
+ className: clsx("pie-semi-circle-chart", pie_semi_circle_chart_module_default["pie-semi-circle-chart"], className),
9513
10075
  children: /* @__PURE__ */ jsx("svg", {
9514
10076
  width: errorWidth,
9515
10077
  height: errorHeight,
@@ -9556,19 +10118,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9556
10118
  width: propWidth || void 0,
9557
10119
  height: propHeight || void 0
9558
10120
  },
9559
- trailingContent: /* @__PURE__ */ jsxs(Fragment$1, { children: [
9560
- withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ jsx(TooltipInPortal, {
9561
- top: tooltipTop || 0,
9562
- left: tooltipLeft || 0,
9563
- children: /* @__PURE__ */ jsx("div", {
9564
- className: "a8c-charts-scope",
9565
- role: "tooltip",
9566
- children: renderTooltip({ tooltipData })
9567
- })
9568
- }),
9569
- htmlChildren,
9570
- otherChildren
9571
- ] }),
10121
+ trailingContent: /* @__PURE__ */ jsxs(Fragment$1, { children: [htmlChildren, otherChildren] }),
9572
10122
  children: ({ contentWidth, contentHeight }) => {
9573
10123
  const availableWidth = contentWidth > 0 ? contentWidth : effectiveWidth;
9574
10124
  const availableHeight = contentHeight > 0 ? contentHeight : propHeight || effectiveWidth / 2;
@@ -9576,9 +10126,10 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9576
10126
  const height = width / 2;
9577
10127
  const radius = height;
9578
10128
  const innerRadius = radius * (1 - thickness);
9579
- return /* @__PURE__ */ jsx(Center, {
10129
+ return /* @__PURE__ */ jsxs(Center, {
9580
10130
  ref: containerRef,
9581
- children: /* @__PURE__ */ jsxs("svg", {
10131
+ className: pie_semi_circle_chart_module_default["pie-semi-circle-chart__plot"],
10132
+ children: [/* @__PURE__ */ jsxs("svg", {
9582
10133
  width,
9583
10134
  height,
9584
10135
  viewBox: `0 0 ${width} ${height}`,
@@ -9636,7 +10187,15 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9636
10187
  !allSegmentsHidden && svgChildren
9637
10188
  ] })
9638
10189
  })]
9639
- })
10190
+ }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ jsx(BoundedTooltip, {
10191
+ top: tooltipTop || 0,
10192
+ left: tooltipLeft || 0,
10193
+ children: /* @__PURE__ */ jsx("div", {
10194
+ className: standaloneScopeClass,
10195
+ role: "tooltip",
10196
+ children: renderTooltip({ tooltipData })
10197
+ })
10198
+ })]
9640
10199
  });
9641
10200
  }
9642
10201
  })
@@ -9692,6 +10251,7 @@ const transformToSeriesData = (data, color, strokeWidth) => {
9692
10251
  };
9693
10252
  const SparklineComponent = forwardRef(({ data, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT, color, strokeWidth: strokeWidthProp, withGradientFill = true, gradient, className, chartId, margin: marginProp, animation }, ref) => {
9694
10253
  const theme = useGlobalChartsTheme();
10254
+ const chartsContext = useContext(GlobalChartsContext);
9695
10255
  const themeStrokeWidth = theme.sparkline?.strokeWidth ?? 1.5;
9696
10256
  const strokeWidth = strokeWidthProp ?? themeStrokeWidth;
9697
10257
  const seriesData = useMemo(() => {
@@ -9745,7 +10305,7 @@ const SparklineComponent = forwardRef(({ data, width = DEFAULT_WIDTH, height = D
9745
10305
  if (data.length === 1) {
9746
10306
  const cx = width / 2;
9747
10307
  const cy = height / 2;
9748
- const resolvedColor = color || "#000000";
10308
+ const resolvedColor = color || chartsContext?.getElementStyles({ index: 0 }).color || "#000000";
9749
10309
  return /* @__PURE__ */ jsx("div", {
9750
10310
  ref,
9751
10311
  className: clsx("sparkline", sparkline_module_default.sparkline, sparkline_module_default["sparkline--single-point"], className),
@@ -9848,8 +10408,9 @@ const Icon$1 = ({ direction }) => {
9848
10408
  */
9849
10409
  function TrendIndicator({ direction, value, className, style, showIcon = true }) {
9850
10410
  const ariaLabel = `${DIRECTION_LABELS[direction]}: ${value}`;
10411
+ const standaloneScopeClass = useStandaloneScopeClass();
9851
10412
  return /* @__PURE__ */ jsxs("span", {
9852
- className: clsx(useStandaloneScopeClass(), trend_indicator_module_default["trend-indicator"], trend_indicator_module_default[`trend-indicator--${direction}`], className),
10413
+ className: clsx(standaloneScopeClass, trend_indicator_module_default["trend-indicator"], trend_indicator_module_default[`trend-indicator--${direction}`], className),
9853
10414
  style,
9854
10415
  "aria-label": ariaLabel,
9855
10416
  children: [showIcon && /* @__PURE__ */ jsx(Icon$1, { direction }), /* @__PURE__ */ jsx("span", {
@@ -9859,6 +10420,6 @@ function TrendIndicator({ direction, value, className, style, showIcon = true })
9859
10420
  });
9860
10421
  }
9861
10422
  //#endregion
9862
- export { AccessibleTooltip, AreaChartResponsive as AreaChart, AreaChart as AreaChartUnresponsive, BarChartResponsive as BarChart, BarChart as BarChartUnresponsive, BarListChartResponsive as BarListChart, BarListChart as BarListChartUnresponsive, BaseTooltip, ConversionFunnelChartWithProvider as ConversionFunnelChart, GeoChartResponsive as GeoChart, GeoChartWithProvider as GeoChartUnresponsive, GlobalChartsContext, GlobalChartsProvider, GlobalChartsProvider as ThemeProvider, GoogleDataTableColumnRoleType, HeatmapChartResponsive as HeatmapChart, HeatmapChart as HeatmapChartUnresponsive, LeaderboardChartResponsive as LeaderboardChart, LeaderboardChart as LeaderboardChartUnresponsive, Legend, LineChartResponsive as LineChart, LineChart as LineChartUnresponsive, PieChartResponsive as PieChart, PieChart as PieChartUnresponsive, PieSemiCircleChartResponsive as PieSemiCircleChart, PieSemiCircleChart as PieSemiCircleChartUnresponsive, Sparkline, SparklineUnresponsive, TrendIndicator, buildCalendarHeatmapData, defaultTheme, formatMetricValue, formatPercentage, getColorDistance, hexToRgba, isValidHexColor, lightenHexColor, mergeThemes, mixHexColors, normalizeColorToHex, parseAsLocalDate, parseHslString, parseRgbString, prefersLightText, relativeLuminance, resolveCssVariable, useChartLegendItems, useChartRegistration, useChartScopeElement, useGlobalChartsContext, useGlobalChartsTheme, useLeaderboardLegendItems, validateHexColor };
10423
+ export { AccessibleTooltip, AreaChartResponsive as AreaChart, AreaChart as AreaChartUnresponsive, BarChartResponsive as BarChart, BarChart as BarChartUnresponsive, BarListChartResponsive as BarListChart, BarListChart as BarListChartUnresponsive, BaseTooltip, ConversionFunnelChartWithProvider as ConversionFunnelChart, GeoChartResponsive as GeoChart, GeoChartWithProvider as GeoChartUnresponsive, GlobalChartsContext, GlobalChartsProvider, GlobalChartsProvider as ThemeProvider, GoogleDataTableColumnRoleType, HeatmapChartResponsive as HeatmapChart, HeatmapChart as HeatmapChartUnresponsive, LeaderboardChartResponsive as LeaderboardChart, LeaderboardChart as LeaderboardChartUnresponsive, Legend, LineChartResponsive as LineChart, LineChart as LineChartUnresponsive, PieChartResponsive as PieChart, PieChart as PieChartUnresponsive, PieSemiCircleChartResponsive as PieSemiCircleChart, PieSemiCircleChart as PieSemiCircleChartUnresponsive, Sparkline, SparklineUnresponsive, TrendIndicator, buildCalendarHeatmapData, defaultTheme, formatMetricValue, formatPercentage, getBucketInfo, getColorDistance, hexToRgba, isValidHexColor, lightenHexColor, mergeThemes, mixHexColors, normalizeColorToHex, parseAsLocalDate, parseHslString, prefersLightText, relativeLuminance, resolveCssVariable, useChartFormatting, useChartLegendItems, useChartRegistration, useChartScopeElement, useGlobalChartsContext, useGlobalChartsTheme, useLeaderboardLegendItems, validateHexColor };
9863
10424
 
9864
10425
  //# sourceMappingURL=index.js.map