@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.cjs CHANGED
@@ -17,7 +17,7 @@ var __copyProps = (to, from, except, desc) => {
17
17
  }
18
18
  return to;
19
19
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
21
21
  value: mod,
22
22
  enumerable: true
23
23
  }) : target, mod));
@@ -28,7 +28,7 @@ let _wordpress_i18n = require("@wordpress/i18n");
28
28
  let clsx = require("clsx");
29
29
  clsx = __toESM(clsx, 1);
30
30
  let react = require("react");
31
- react = __toESM(react, 1);
31
+ react = __toESM(react);
32
32
  let _visx_vendor_d3_color = require("@visx/vendor/d3-color");
33
33
  let date_fns = require("date-fns");
34
34
  let _visx_text = require("@visx/text");
@@ -40,6 +40,7 @@ let _visx_scale = require("@visx/scale");
40
40
  let _visx_group = require("@visx/group");
41
41
  let _visx_legend = require("@visx/legend");
42
42
  let _wordpress_ui = require("@wordpress/ui");
43
+ let react_dom = require("react-dom");
43
44
  let _visx_gradient = require("@visx/gradient");
44
45
  let _visx_curve = require("@visx/curve");
45
46
  let _visx_responsive = require("@visx/responsive");
@@ -174,6 +175,39 @@ const parseAsLocalDate = (dateString) => {
174
175
  return /* @__PURE__ */ new Date(NaN);
175
176
  };
176
177
  //#endregion
178
+ //#region src/utils/bucket-info.ts
179
+ const getInstant = (point) => {
180
+ if (!point) return;
181
+ if ("date" in point && point.date) return point.date.getTime();
182
+ if ("dateString" in point && point.dateString) return parseAsLocalDate(point.dateString).getTime();
183
+ };
184
+ const getPointSpacingInHours = (data) => {
185
+ return data.reduce((spacing, datom) => {
186
+ const instants = [...new Set(datom.data.map((point) => getInstant(point)))].filter((time) => time !== void 0 && Number.isFinite(time)).sort((a, b) => a - b);
187
+ return instants.reduce((seriesSpacing, time, index) => index === 0 ? seriesSpacing : Math.min(seriesSpacing, Math.abs((0, date_fns.differenceInHours)(time, instants[index - 1]))), spacing);
188
+ }, Number.POSITIVE_INFINITY);
189
+ };
190
+ const SUB_DAILY_SPACING_HOURS = 23;
191
+ const MONTHLY_SPACING_HOURS = 672;
192
+ const getBucketResolution = (data, tickResolution) => {
193
+ if (tickResolution) return tickResolution === "week" ? "day" : tickResolution;
194
+ const spacingInHours = getPointSpacingInHours(data);
195
+ if (spacingInHours < SUB_DAILY_SPACING_HOURS) return "hour";
196
+ if (!Number.isFinite(spacingInHours) || spacingInHours < MONTHLY_SPACING_HOURS) return "day";
197
+ return spacingInHours < 12 * MONTHLY_SPACING_HOURS ? "month" : "year";
198
+ };
199
+ /**
200
+ * How this data was classified, for consumers that render their own labels.
201
+ *
202
+ * @param data - The series the chart draws, in any order, dated by `date` or `dateString`.
203
+ * @param tickResolution - Caller-declared bucket resolution, when known.
204
+ * @return The declared or inferred bucket, and the resolution formats key on.
205
+ */
206
+ const getBucketInfo = (data, tickResolution) => ({
207
+ bucket: tickResolution ?? getBucketResolution(data),
208
+ displayResolution: getBucketResolution(data, tickResolution)
209
+ });
210
+ //#endregion
177
211
  //#region src/utils/format-metric-value.ts
178
212
  /**
179
213
  * Format a numeric metric value based on type, precision and scale.
@@ -252,7 +286,8 @@ const formatPercentage = (value) => {
252
286
  */
253
287
  const getLongestTickWidth = (ticks, formatTick, labelStyle) => {
254
288
  const formattedTicks = ticks.map((tick) => formatTick(tick, 0, []));
255
- return (0, _visx_text.getStringWidth)(formattedTicks.reduce((longest, current) => longest.length >= current.length ? longest : current, formattedTicks[0]), labelStyle);
289
+ const longestTick = formattedTicks.reduce((longest, current) => longest.length >= current.length ? longest : current, formattedTicks[0]);
290
+ return (0, _visx_text.getStringWidth)(longestTick, labelStyle);
256
291
  };
257
292
  //#endregion
258
293
  //#region src/utils/get-styles.ts
@@ -385,20 +420,6 @@ const parseHslString = (hslString) => {
385
420
  ];
386
421
  };
387
422
  /**
388
- * Parse an RGB string like 'rgb(255, 0, 0)' into a hex color.
389
- *
390
- * @deprecated Use normalizeColorToHex() instead, which handles all color formats including rgb() and rgba().
391
- * @param rgbString - RGB color string (not RGBA)
392
- * @return hex color string or null if invalid
393
- */
394
- const parseRgbString = (rgbString) => {
395
- const lower = rgbString.toLowerCase().trim();
396
- if (!lower.startsWith("rgb(") || lower.startsWith("rgba(")) return null;
397
- const parsed = (0, _visx_vendor_d3_color.color)(lower);
398
- if (!parsed) return null;
399
- return parsed.formatHex();
400
- };
401
- /**
402
423
  * Normalize any CSS color value to a hex color string.
403
424
  * Handles hex, HSL, HSLA, RGB, RGBA, named CSS colors, and CSS variables.
404
425
  *
@@ -626,6 +647,112 @@ const resolveFontSize = (val) => {
626
647
  }
627
648
  };
628
649
  //#endregion
650
+ //#region src/utils/date-formatting.ts
651
+ const formatters = /* @__PURE__ */ new Map();
652
+ const getFormatter$1 = (options, { locale, timeZone }) => {
653
+ const key = `${locale ?? ""}|${timeZone ?? ""}|${JSON.stringify(options)}`;
654
+ let formatter = formatters.get(key);
655
+ if (!formatter) {
656
+ formatter = new Intl.DateTimeFormat(locale, {
657
+ ...options,
658
+ timeZone
659
+ });
660
+ formatters.set(key, formatter);
661
+ }
662
+ return formatter;
663
+ };
664
+ const warned = /* @__PURE__ */ new Set();
665
+ const warnOnce = (key, message) => {
666
+ if (warned.has(key) || process.env.NODE_ENV === "production") return;
667
+ warned.add(key);
668
+ console.warn(`[Charts] ${message}`);
669
+ };
670
+ const isUsable = (formatting) => {
671
+ try {
672
+ Intl.DateTimeFormat(formatting.locale, { timeZone: formatting.timeZone });
673
+ return true;
674
+ } catch {
675
+ return false;
676
+ }
677
+ };
678
+ const toBcp47 = (locale) => locale?.replace(/_/g, "-");
679
+ /**
680
+ * Repairs a host locale for `Intl`, dropping either half it still cannot use.
681
+ *
682
+ * Every formatter below is built during render, so an unusable string would throw
683
+ * where React unmounts the whole host tree rather than one chart. WordPress hands
684
+ * out the likeliest one either way: `timezone_string` is `''` on a site set to a
685
+ * raw UTC offset, and has no repair.
686
+ *
687
+ * @param formatting - Locale and time zone as the host supplied them.
688
+ * @return The usable halves, each `undefined` where the host's value was not.
689
+ */
690
+ const sanitizeFormatting = (formatting) => {
691
+ const repaired = {
692
+ ...formatting,
693
+ locale: toBcp47(formatting.locale)
694
+ };
695
+ if (isUsable(repaired)) return repaired;
696
+ const locale = isUsable({ locale: repaired.locale }) ? repaired.locale : void 0;
697
+ const timeZone = isUsable({ timeZone: repaired.timeZone }) ? repaired.timeZone : void 0;
698
+ 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.`);
699
+ 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".`);
700
+ return {
701
+ locale,
702
+ timeZone
703
+ };
704
+ };
705
+ /**
706
+ * A date formatter bound to the host's locale and time zone.
707
+ *
708
+ * @param options - Date-part options, as for `Intl.DateTimeFormat`.
709
+ * @param formatting - Host locale and time zone; either half absent leaves the runtime's own.
710
+ * @return Formatter taking a timestamp or a `Date`.
711
+ */
712
+ const createDateFormatter = (options, formatting) => {
713
+ return (value) => {
714
+ if (!Number.isFinite(Number(value))) return new Date(Number(value)).toLocaleDateString();
715
+ return getFormatter$1(options, formatting).format(value);
716
+ };
717
+ };
718
+ const CLOCK_OPTIONS = {
719
+ month: "numeric",
720
+ hour: "numeric",
721
+ hourCycle: "h23"
722
+ };
723
+ const NO_FIELDS = {
724
+ month: NaN,
725
+ hour: NaN
726
+ };
727
+ /**
728
+ * A clock reading calendar fields in the host's time zone.
729
+ *
730
+ * `Date`'s own getters read the browser's zone, so a chart viewed from another
731
+ * country anchors its ticks to the wrong midnights and the wrong January
732
+ * without this.
733
+ *
734
+ * @param timeZone - IANA zone name; absent leaves the runtime's own.
735
+ * @return Reader taking a `Date`.
736
+ */
737
+ const createZonedClock = (timeZone) => {
738
+ if (!timeZone) return (date) => ({
739
+ month: date.getMonth() + 1,
740
+ hour: date.getHours()
741
+ });
742
+ return (date) => {
743
+ if (!Number.isFinite(Number(date))) return NO_FIELDS;
744
+ const parts = getFormatter$1(CLOCK_OPTIONS, {
745
+ locale: "en-US",
746
+ timeZone
747
+ }).formatToParts(date);
748
+ const read = (type) => Number(parts.find((part) => part.type === type)?.value);
749
+ return {
750
+ month: read("month"),
751
+ hour: read("hour")
752
+ };
753
+ };
754
+ };
755
+ //#endregion
629
756
  //#region src/providers/chart-scope/chart-scope-context.ts
630
757
  /**
631
758
  * The nearest chart element, published so JS token resolution reads the same CSS cascade that paints the chart's own colours.
@@ -762,140 +889,40 @@ const getChartColor = (index, colorCache) => {
762
889
  const fallbackLightness = BASE_LIGHTNESS + index % LIGHTNESS_VARIATION_STEPS * LIGHTNESS_INCREMENT;
763
890
  return (0, _visx_vendor_d3_color.hsl)(Math.round(fallbackHue), fallbackSaturation / 100, fallbackLightness / 100).formatHex();
764
891
  };
765
- //#endregion
766
- //#region src/providers/chart-context/private/theme-override-vars.ts
767
- const ROLE_FOR_FIELD = [
768
- ["--a8c-charts-color-background", (theme) => theme.backgroundColor],
769
- ["--a8c-charts-color-grid", (theme) => theme.gridStyles?.stroke],
770
- ["--a8c-charts-color-axis", (theme) => theme.xAxisLineStyles?.stroke],
771
- ["--a8c-charts-color-tick", (theme) => theme.xTickLineStyles?.stroke],
772
- ["--a8c-charts-color-label-axis", (theme) => theme.svgLabelSmall?.fill]
773
- ];
774
892
  /**
775
- * The variable a `theme` prop override is published as, one layer outside the role itself.
893
+ * The catalog role holding one series-palette slot.
776
894
  *
777
- * `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.
778
- *
779
- * @param role - The catalog role being overridden.
780
- * @return The custom property carrying the consumer's value.
895
+ * @param slot - The one-based slot number.
896
+ * @return The role name.
781
897
  */
782
- const themeLayerVar = (role) => `${role}-theme`;
783
- ROLE_FOR_FIELD.map(([role]) => role);
784
- /**
785
- * Whether a value reads the role it is being published for, which would make the catalog entry depend on itself.
786
- *
787
- * 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`.
788
- *
789
- * Three things this has to get right, none of which a plain substring test gets:
790
- *
791
- * `var()` permits whitespace after the opening paren, so testing for `var(<role>` misses `var( --a8c-charts-color-grid, … )`.
792
- *
793
- * 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.
794
- *
795
- * `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.
796
- *
797
- * @param value - The consumer's value for the field.
798
- * @param role - The catalog role this value would override.
799
- * @return True when the value reads `role`, so it must not be published.
800
- */
801
- const readsOwnRole = (value, role) => value.trim() === role || new RegExp(`var\\(\\s*${role.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`).test(value);
802
- /**
803
- * 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.
804
- *
805
- * 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.
806
- *
807
- * @param theme - The consumer's `theme` prop, before merging with the default theme.
808
- * @return The wrapper's custom properties, and the roles the consumer overrode.
809
- */
810
- const themeOverrideVars = (theme) => {
811
- if (!theme) return {
812
- vars: {},
813
- roles: []
814
- };
815
- const vars = {};
816
- const roles = [];
817
- for (const [role, read] of ROLE_FOR_FIELD) {
818
- const value = read(theme);
819
- if (typeof value !== "string" || value === "") continue;
820
- roles.push(role);
821
- if (!readsOwnRole(value, role)) vars[themeLayerVar(role)] = value;
822
- }
823
- return {
824
- vars,
825
- roles
826
- };
827
- };
898
+ const seriesRole = (slot) => `--a8c-charts-color-series-${slot}`;
899
+ const SERIES_SLOT_1_FALLBACK = "#3858e9";
900
+ /** The catalog pointer for every slot, in slot order. */
901
+ const SERIES_PALETTE_POINTERS = Array.from({ length: 5 }, (_, index) => index === 0 ? `var(${seriesRole(1)}, ${SERIES_SLOT_1_FALLBACK})` : `var(${seriesRole(index + 1)})`);
828
902
  //#endregion
829
903
  //#region src/providers/chart-context/themes.ts
830
904
  /**
831
- * Default theme configuration
905
+ * Default theme configuration: the shape and spacing a consumer can override.
906
+ *
907
+ * For a color, set the matching `--a8c-charts-color-*` role in CSS instead; the `var()`
908
+ * chains that deliver those live in `private/catalog-pointers.ts`.
832
909
  */
833
910
  const defaultTheme = {
834
- backgroundColor: "var(--a8c-charts-color-background, #fff)",
835
- labelBackgroundColor: "transparent",
836
- labelTextColor: "var(--a8c-charts-color-label-on-fill, #FFFFFF)",
837
- colors: [
838
- "#98C8DF",
839
- "#006DAB",
840
- "#A6DC80",
841
- "#1F9828",
842
- "#FF8C8F"
843
- ],
844
- gridStyles: {
845
- stroke: "var(--a8c-charts-color-grid, #dbdbdb)",
846
- strokeWidth: 1
847
- },
911
+ gridStyles: { strokeWidth: 1 },
848
912
  tickLength: 4,
849
- gridColor: "",
850
- gridColorDark: "",
851
- xTickLineStyles: {
852
- stroke: "var(--a8c-charts-color-tick, #dbdbdb)",
853
- strokeWidth: 1
854
- },
855
- xAxisLineStyles: {
856
- stroke: "var(--a8c-charts-color-axis, #dbdbdb)",
857
- strokeWidth: 1
858
- },
913
+ xTickLineStyles: { strokeWidth: 1 },
914
+ xAxisLineStyles: { strokeWidth: 1 },
859
915
  legend: {
860
- labelStyles: { color: "var(--a8c-charts-color-label, #1e1e1e)" },
916
+ labelStyles: {},
861
917
  containerStyles: {},
862
918
  shapeStyles: []
863
919
  },
864
920
  seriesLineStyles: [],
865
921
  glyphs: [],
866
- svgLabelSmall: {
867
- fill: "var(--a8c-charts-color-label-axis, #1e1e1e)",
868
- fontFamily: "inherit"
869
- },
922
+ svgLabelSmall: { fontFamily: "inherit" },
870
923
  svgLabelBig: { fontFamily: "inherit" },
871
- annotationStyles: {
872
- label: {
873
- anchorLineStroke: "var(--a8c-charts-color-annotation, #1e1e1e)",
874
- backgroundFill: "var(--a8c-charts-color-surface, #fff)"
875
- },
876
- connector: { stroke: "var(--a8c-charts-color-annotation, #1e1e1e)" },
877
- circleSubject: {
878
- stroke: "transparent",
879
- fill: "var(--a8c-charts-color-annotation, #1e1e1e)",
880
- radius: 5
881
- }
882
- },
883
- geoChart: { featureFillColor: "var(--a8c-charts-color-surface-secondary, #f4f4f4)" },
884
- leaderboardChart: {
885
- rowGap: 12,
886
- columnGap: 4,
887
- labelSpacing: "xs",
888
- deltaColors: [
889
- "var(--a8c-charts-color-trend-down, #cc1818)",
890
- "var(--a8c-charts-color-trend-neutral, #707070)",
891
- "var(--a8c-charts-color-trend-up, #008030)"
892
- ]
893
- },
894
- conversionFunnelChart: {
895
- backgroundColor: "var(--a8c-charts-color-surface-secondary, #f4f4f4)",
896
- positiveChangeColor: "var(--a8c-charts-color-trend-up, #008030)",
897
- negativeChangeColor: "var(--a8c-charts-color-trend-down, #cc1818)"
898
- },
924
+ annotationStyles: {},
925
+ leaderboardChart: { labelSpacing: "xs" },
899
926
  lineChart: { lineStyles: { comparison: {
900
927
  strokeDasharray: "4 4",
901
928
  strokeLinecap: "square"
@@ -919,52 +946,9 @@ const defaultTheme = {
919
946
  }
920
947
  };
921
948
  //#endregion
922
- //#region src/providers/chart-context/private/with-catalog-pointers.ts
923
- const CATALOG_RESTORE_FOR_ROLE = {
924
- "--a8c-charts-color-background": () => ({ backgroundColor: defaultTheme.backgroundColor }),
925
- "--a8c-charts-color-grid": (theme) => ({ gridStyles: {
926
- ...theme.gridStyles,
927
- stroke: defaultTheme.gridStyles.stroke
928
- } }),
929
- "--a8c-charts-color-axis": (theme) => ({ xAxisLineStyles: {
930
- ...theme.xAxisLineStyles,
931
- stroke: defaultTheme.xAxisLineStyles.stroke
932
- } }),
933
- "--a8c-charts-color-tick": (theme) => ({ xTickLineStyles: {
934
- ...theme.xTickLineStyles,
935
- stroke: defaultTheme.xTickLineStyles.stroke
936
- } }),
937
- "--a8c-charts-color-label-axis": (theme) => ({ svgLabelSmall: {
938
- ...theme.svgLabelSmall,
939
- fill: defaultTheme.svgLabelSmall.fill
940
- } })
941
- };
942
- /**
943
- * 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.
944
- *
945
- * 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`.
946
- *
947
- * 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.
948
- *
949
- * @param merged - The consumer theme merged onto `defaultTheme` (`mergeThemes` output).
950
- * @param overriddenRoles - The catalog roles the consumer overrode, from `themeOverrideVars`.
951
- * @return A new theme object; `merged` is not mutated.
952
- */
953
- const withCatalogPointers = (merged, overriddenRoles) => {
954
- let result = merged;
955
- for (const role of overriddenRoles) {
956
- const restore = CATALOG_RESTORE_FOR_ROLE[role];
957
- if (restore) result = {
958
- ...result,
959
- ...restore(result)
960
- };
961
- }
962
- return result;
963
- };
964
- //#endregion
965
949
  //#region src/providers/chart-context/global-charts-provider.tsx
966
950
  const GlobalChartsContext = (0, react.createContext)(null);
967
- const GlobalChartsProvider = ({ children, theme }) => {
951
+ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
968
952
  const [charts, setCharts] = (0, react.useState)(() => /* @__PURE__ */ new Map());
969
953
  const [hiddenSeries, setHiddenSeries] = (0, react.useState)(() => /* @__PURE__ */ new Map());
970
954
  const seededCharts = (0, react.useRef)(/* @__PURE__ */ new Set());
@@ -974,11 +958,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
974
958
  wrapperRef.current = node;
975
959
  setScopeNode(node);
976
960
  }, []);
977
- const { vars: overrideVars, roles: overriddenRoles } = (0, react.useMemo)(() => themeOverrideVars(theme), [theme]);
978
- const providerTheme = (0, react.useMemo)(() => {
979
- if (!theme) return defaultTheme;
980
- return withCatalogPointers(mergeThemes(defaultTheme, theme), overriddenRoles);
981
- }, [theme, overriddenRoles]);
961
+ const providerTheme = (0, react.useMemo)(() => theme ? mergeThemes(defaultTheme, theme) : defaultTheme, [theme]);
982
962
  const [colorCache, setColorCache] = (0, react.useState)(() => ({
983
963
  colors: [],
984
964
  hues: [],
@@ -989,29 +969,26 @@ const GlobalChartsProvider = ({ children, theme }) => {
989
969
  const [isColorPaletteResolved, setIsColorPaletteResolved] = (0, react.useState)(false);
990
970
  (0, react.useLayoutEffect)(() => {
991
971
  setIsColorPaletteResolved(false);
992
- const { colors } = providerTheme;
993
972
  const resolvedColors = [];
994
973
  const hues = [];
995
974
  const existingHslColors = [];
996
975
  let minHue = 360;
997
976
  let maxHue = 0;
998
- if (Array.isArray(colors)) {
999
- for (const color of colors) if (color && typeof color === "string") {
1000
- const normalizedColor = normalizeColorToHex(color, wrapperRef.current, resolveCssVariable);
1001
- if (normalizedColor.startsWith("#")) {
1002
- resolvedColors.push(normalizedColor);
1003
- const hslColor = (0, _visx_vendor_d3_color.hsl)(normalizedColor);
1004
- if (!isNaN(hslColor.h)) {
1005
- const hslTuple = [
1006
- hslColor.h,
1007
- hslColor.s * 100,
1008
- hslColor.l * 100
1009
- ];
1010
- hues.push(hslTuple[0]);
1011
- existingHslColors.push(hslTuple);
1012
- minHue = Math.min(minHue, hslTuple[0]);
1013
- maxHue = Math.max(maxHue, hslTuple[0]);
1014
- }
977
+ for (const color of SERIES_PALETTE_POINTERS) {
978
+ const normalizedColor = normalizeColorToHex(color, wrapperRef.current, resolveCssVariable);
979
+ if (normalizedColor.startsWith("#")) {
980
+ resolvedColors.push(normalizedColor);
981
+ const hslColor = (0, _visx_vendor_d3_color.hsl)(normalizedColor);
982
+ if (!isNaN(hslColor.h)) {
983
+ const hslTuple = [
984
+ hslColor.h,
985
+ hslColor.s * 100,
986
+ hslColor.l * 100
987
+ ];
988
+ hues.push(hslTuple[0]);
989
+ existingHslColors.push(hslTuple);
990
+ minHue = Math.min(minHue, hslTuple[0]);
991
+ maxHue = Math.max(maxHue, hslTuple[0]);
1015
992
  }
1016
993
  }
1017
994
  }
@@ -1022,14 +999,15 @@ const GlobalChartsProvider = ({ children, theme }) => {
1022
999
  minHue,
1023
1000
  maxHue
1024
1001
  });
1025
- }, [providerTheme]);
1002
+ }, []);
1026
1003
  (0, react.useEffect)(() => {
1027
1004
  if (colorCache.colors.length > 0) setIsColorPaletteResolved(true);
1028
1005
  }, [colorCache]);
1029
1006
  const [groupToColorMap, setGroupToColorMap] = (0, react.useState)(() => /* @__PURE__ */ new Map());
1007
+ const paletteKey = colorCache.colors.join(",");
1030
1008
  (0, react.useEffect)(() => {
1031
1009
  setGroupToColorMap(/* @__PURE__ */ new Map());
1032
- }, [providerTheme.colors]);
1010
+ }, [paletteKey]);
1033
1011
  const registerChart = (0, react.useCallback)((id, data) => {
1034
1012
  setCharts((prev) => new Map(prev).set(id, data));
1035
1013
  }, []);
@@ -1113,12 +1091,17 @@ const GlobalChartsProvider = ({ children, theme }) => {
1113
1091
  const set = hiddenSeries.get(chartId);
1114
1092
  return set ? new Set(set) : /* @__PURE__ */ new Set();
1115
1093
  }, [hiddenSeries]);
1094
+ const formatting = (0, react.useMemo)(() => sanitizeFormatting({
1095
+ locale,
1096
+ timeZone
1097
+ }), [locale, timeZone]);
1116
1098
  const value = (0, react.useMemo)(() => ({
1117
1099
  charts,
1118
1100
  registerChart,
1119
1101
  unregisterChart,
1120
1102
  getChartData,
1121
1103
  theme: providerTheme,
1104
+ formatting,
1122
1105
  getElementStyles,
1123
1106
  toggleSeriesVisibility,
1124
1107
  setSeriesVisibility,
@@ -1134,6 +1117,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
1134
1117
  unregisterChart,
1135
1118
  getChartData,
1136
1119
  providerTheme,
1120
+ formatting,
1137
1121
  getElementStyles,
1138
1122
  toggleSeriesVisibility,
1139
1123
  setSeriesVisibility,
@@ -1149,10 +1133,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
1149
1133
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1150
1134
  ref: setWrapperNode,
1151
1135
  className: CHART_SCOPE_CLASS,
1152
- style: {
1153
- display: "contents",
1154
- ...overrideVars
1155
- },
1136
+ style: { display: "contents" },
1156
1137
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartScopeContext.Provider, {
1157
1138
  value: scopeNode,
1158
1139
  children
@@ -1216,6 +1197,39 @@ const useDeepMemo = (value) => {
1216
1197
  return ref.current;
1217
1198
  };
1218
1199
  //#endregion
1200
+ //#region src/providers/chart-context/private/catalog-pointers.ts
1201
+ const CATALOG_POINTERS = {
1202
+ background: "var(--a8c-charts-color-background, #fff)",
1203
+ labelAxis: "var(--a8c-charts-color-label-axis, #1e1e1e)",
1204
+ grid: "var(--a8c-charts-color-grid, #dbdbdb)",
1205
+ axisX: "var(--a8c-charts-color-axis-x, #dbdbdb)",
1206
+ tickX: "var(--a8c-charts-color-tick-x, #dbdbdb)",
1207
+ axisY: "var(--a8c-charts-color-axis-y, none)",
1208
+ tickY: "var(--a8c-charts-color-tick-y, none)",
1209
+ annotation: "var(--a8c-charts-color-annotation, #1e1e1e)",
1210
+ surface: "var(--a8c-charts-color-surface, #fff)",
1211
+ surfaceSecondary: "var(--a8c-charts-color-surface-secondary, #f4f4f4)",
1212
+ trendUp: "var(--a8c-charts-color-trend-up, #008030)",
1213
+ trendDown: "var(--a8c-charts-color-trend-down, #cc1818)",
1214
+ series: SERIES_PALETTE_POINTERS
1215
+ };
1216
+ /**
1217
+ * The annotation parts visx paints, in the shape `@visx/annotation` takes. `radius`
1218
+ * rides along as the base the theme and the per-datum styles merge onto.
1219
+ */
1220
+ const ANNOTATION_POINTERS = {
1221
+ label: {
1222
+ anchorLineStroke: CATALOG_POINTERS.annotation,
1223
+ backgroundFill: CATALOG_POINTERS.surface
1224
+ },
1225
+ connector: { stroke: CATALOG_POINTERS.annotation },
1226
+ circleSubject: {
1227
+ stroke: "transparent",
1228
+ fill: CATALOG_POINTERS.annotation,
1229
+ radius: 5
1230
+ }
1231
+ };
1232
+ //#endregion
1219
1233
  //#region src/providers/chart-scope/use-chart-scope-element.ts
1220
1234
  /**
1221
1235
  * Returns the nearest chart scope element, or `null` when rendered outside any scope (and during SSR, before the ref is attached).
@@ -1242,26 +1256,33 @@ const useXYChartTheme = (data) => {
1242
1256
  return (0, react.useMemo)(() => {
1243
1257
  const resolve = createCssVariableResolver(scopeElement);
1244
1258
  const resolveColor = (value) => value ? resolve(value) ?? value : value;
1245
- const seriesColors = JSON.parse(seriesColorKey);
1259
+ const paletteColors = [...JSON.parse(seriesColorKey), ...CATALOG_POINTERS.series].map((color) => resolveColor(color)).filter((color) => Boolean(color) && !color.includes("var("));
1260
+ const resolvedLabelColor = resolveColor(CATALOG_POINTERS.labelAxis);
1261
+ const htmlLabelColor = resolvedLabelColor ? normalizeColorToHex(resolvedLabelColor) || resolvedLabelColor : resolvedLabelColor;
1246
1262
  return (0, _visx_xychart.buildChartTheme)({
1247
1263
  ...theme,
1248
- colors: [...seriesColors, ...theme.colors ?? []],
1249
- backgroundColor: resolveColor(theme.backgroundColor),
1250
- gridStyles: theme.gridStyles && {
1264
+ gridColor: "",
1265
+ gridColorDark: "",
1266
+ colors: paletteColors,
1267
+ backgroundColor: resolveColor(CATALOG_POINTERS.background),
1268
+ htmlLabel: htmlLabelColor ? { color: htmlLabelColor } : void 0,
1269
+ gridStyles: {
1251
1270
  ...theme.gridStyles,
1252
- stroke: resolveColor(theme.gridStyles.stroke)
1271
+ stroke: CATALOG_POINTERS.grid
1253
1272
  },
1254
- xAxisLineStyles: theme.xAxisLineStyles && {
1273
+ xAxisLineStyles: {
1255
1274
  ...theme.xAxisLineStyles,
1256
- stroke: resolveColor(theme.xAxisLineStyles.stroke)
1275
+ stroke: CATALOG_POINTERS.axisX
1257
1276
  },
1258
- xTickLineStyles: theme.xTickLineStyles && {
1277
+ xTickLineStyles: {
1259
1278
  ...theme.xTickLineStyles,
1260
- stroke: resolveColor(theme.xTickLineStyles.stroke)
1279
+ stroke: CATALOG_POINTERS.tickX
1261
1280
  },
1262
- svgLabelSmall: theme.svgLabelSmall && {
1281
+ yAxisLineStyles: { stroke: CATALOG_POINTERS.axisY },
1282
+ yTickLineStyles: { stroke: CATALOG_POINTERS.tickY },
1283
+ svgLabelSmall: {
1263
1284
  ...theme.svgLabelSmall,
1264
- fill: resolveColor(theme.svgLabelSmall.fill)
1285
+ fill: CATALOG_POINTERS.labelAxis
1265
1286
  }
1266
1287
  });
1267
1288
  }, [
@@ -1359,11 +1380,12 @@ const useChartMargin = (height, options, data, theme, horizontal = false) => {
1359
1380
  if (options.axis?.y?.tickValues?.length) return options.axis.y.tickValues;
1360
1381
  const minY = Math.min(...allDataPoints.map((d) => d.value));
1361
1382
  const maxY = Math.max(...allDataPoints.map((d) => d.value));
1362
- return (0, _visx_scale.getTicks)((0, _visx_scale.createScale)({
1383
+ const yScale = (0, _visx_scale.createScale)({
1363
1384
  ...options.yScale,
1364
1385
  domain: [minY, maxY],
1365
1386
  range: [height, 0]
1366
- }), options.axis?.y?.numTicks);
1387
+ });
1388
+ return (0, _visx_scale.getTicks)(yScale, options.axis?.y?.numTicks);
1367
1389
  }, [
1368
1390
  options,
1369
1391
  data,
@@ -1488,7 +1510,7 @@ const MIN_PIXEL_SIZE = 3;
1488
1510
  /**
1489
1511
  * Pixel size for zero-value bars (1px less than near-zero to be visually distinguishable).
1490
1512
  */
1491
- const ZERO_PIXEL_SIZE = MIN_PIXEL_SIZE - 1;
1513
+ const ZERO_PIXEL_SIZE = 2;
1492
1514
  const useZeroValueDisplay = (data, options = { enabled: false }) => {
1493
1515
  const { enabled, valueAxisLength } = options;
1494
1516
  return (0, react.useMemo)(() => {
@@ -1666,6 +1688,21 @@ const useGlobalChartsTheme = () => {
1666
1688
  return (0, react.useContext)(GlobalChartsContext)?.theme ?? defaultTheme;
1667
1689
  };
1668
1690
  //#endregion
1691
+ //#region src/providers/chart-context/hooks/use-chart-formatting.ts
1692
+ const RUNTIME_FORMATTING = {};
1693
+ /**
1694
+ * The locale and time zone dates are rendered in, as set on `GlobalChartsProvider`.
1695
+ *
1696
+ * Falls back to the runtime's own rather than throwing: charts render outside a
1697
+ * provider, and a host that sets neither gets exactly the browser-default
1698
+ * behavior this package had before the context existed.
1699
+ *
1700
+ * @return The host's formatting context.
1701
+ */
1702
+ const useChartFormatting = () => {
1703
+ return (0, react.useContext)(GlobalChartsContext)?.formatting ?? RUNTIME_FORMATTING;
1704
+ };
1705
+ //#endregion
1669
1706
  //#region src/components/legend/utils/value-or-identity.ts
1670
1707
  /**
1671
1708
  * Returns an object's value if defined, or the object.
@@ -2103,9 +2140,311 @@ const BaseTooltip = ({ data, top, left, component: Component = DefaultTooltipCon
2103
2140
  });
2104
2141
  };
2105
2142
  //#endregion
2143
+ //#region src/components/tooltip/private/bounded-tooltip.tsx
2144
+ const DEFAULT_OFFSET = 10;
2145
+ const isClipping = (element) => {
2146
+ const { overflow, overflowX, overflowY } = getComputedStyle(element);
2147
+ return [
2148
+ overflow,
2149
+ overflowX,
2150
+ overflowY
2151
+ ].some((value) => value && value !== "visible");
2152
+ };
2153
+ const findClippingAncestor = (wrapper) => {
2154
+ let element = wrapper.parentElement;
2155
+ while (element && element !== document.body) {
2156
+ if (isClipping(element)) return element;
2157
+ element = element.parentElement;
2158
+ }
2159
+ return null;
2160
+ };
2161
+ /**
2162
+ * Where the box goes, in wrapper coordinates. Below and to the right of the
2163
+ * anchor; flipped to the other side when that side clips less, as visx's
2164
+ * `TooltipWithBounds` decides it; then clamped so the box stays inside
2165
+ * `bounds` whenever it fits at all.
2166
+ *
2167
+ * @param params - Anchor, offsets, the box size and the bounds to keep inside.
2168
+ * @param params.left - Anchor x, in wrapper coordinates.
2169
+ * @param params.top - Anchor y, in wrapper coordinates.
2170
+ * @param params.offsetLeft - Gap between the anchor and the box, horizontally.
2171
+ * @param params.offsetTop - Gap between the anchor and the box, vertically.
2172
+ * @param params.box - Rendered size of the box.
2173
+ * @param params.bounds - Edges the box must keep inside, in wrapper coordinates.
2174
+ * @return The box's top-left corner, rounded to whole pixels.
2175
+ */
2176
+ const getBoundedPosition = ({ left, top, offsetLeft, offsetTop, box, bounds }) => {
2177
+ const rightX = left + offsetLeft;
2178
+ const leftX = left - offsetLeft - box.width;
2179
+ const rightOverflow = rightX + box.width - bounds.right;
2180
+ const leftOverflow = bounds.left - leftX;
2181
+ let x = rightOverflow > 0 && rightOverflow > leftOverflow ? leftX : rightX;
2182
+ const downY = top + offsetTop;
2183
+ const upY = top - offsetTop - box.height;
2184
+ const downOverflow = downY + box.height - bounds.bottom;
2185
+ const upOverflow = bounds.top - upY;
2186
+ let y = downOverflow > 0 && downOverflow > upOverflow ? upY : downY;
2187
+ x = Math.min(Math.max(x, bounds.left), Math.max(bounds.left, bounds.right - box.width));
2188
+ y = Math.min(Math.max(y, bounds.top), Math.max(bounds.top, bounds.bottom - box.height));
2189
+ return {
2190
+ x: Math.round(x),
2191
+ y: Math.round(y)
2192
+ };
2193
+ };
2194
+ /**
2195
+ * visx's `Tooltip`, positioned like its `TooltipWithBounds` but kept inside the
2196
+ * nearest clipping ancestor — or the viewport when there is none — rather than
2197
+ * inside its own parent. Rendered in-tree, a tooltip's parent is the chart
2198
+ * wrapper, which is often narrower than the box; measuring against the parent
2199
+ * alone would let the box spill into an `overflow: hidden` card and be cut off.
2200
+ *
2201
+ * Re-measures on every render, so a box whose content changes width between
2202
+ * two hovers is placed for its current size.
2203
+ *
2204
+ * @param props - visx `Tooltip` props.
2205
+ * @param props.left - Anchor x, in wrapper coordinates.
2206
+ * @param props.top - Anchor y, in wrapper coordinates.
2207
+ * @param props.offsetLeft - Gap between the anchor and the box, horizontally.
2208
+ * @param props.offsetTop - Gap between the anchor and the box, vertically.
2209
+ * @param props.style - Box styles; visx's defaults unless `unstyled`.
2210
+ * @param props.unstyled - Skip `style` and leave the box bare.
2211
+ * @param props.children - Box content.
2212
+ * @return The tooltip box.
2213
+ */
2214
+ const BoundedTooltip = ({ left = 0, top = 0, offsetLeft = DEFAULT_OFFSET, offsetTop = DEFAULT_OFFSET, style = _visx_tooltip.defaultStyles, unstyled = false, children, ...rest }) => {
2215
+ const nodeRef = (0, react.useRef)(null);
2216
+ const clipRef = (0, react.useRef)(null);
2217
+ const [position, setPosition] = (0, react.useState)(null);
2218
+ (0, react.useLayoutEffect)(() => {
2219
+ const node = nodeRef.current;
2220
+ const wrapper = node?.parentElement;
2221
+ if (!node || !wrapper) return;
2222
+ if (clipRef.current?.wrapper !== wrapper) clipRef.current = {
2223
+ wrapper,
2224
+ clip: findClippingAncestor(wrapper)
2225
+ };
2226
+ const own = node.getBoundingClientRect();
2227
+ const wrapperRect = wrapper.getBoundingClientRect();
2228
+ const clipRect = clipRef.current.clip?.getBoundingClientRect() ?? {
2229
+ left: 0,
2230
+ top: 0,
2231
+ right: window.innerWidth,
2232
+ bottom: window.innerHeight
2233
+ };
2234
+ const next = getBoundedPosition({
2235
+ left,
2236
+ top,
2237
+ offsetLeft,
2238
+ offsetTop,
2239
+ box: {
2240
+ width: own.width,
2241
+ height: own.height
2242
+ },
2243
+ bounds: {
2244
+ left: clipRect.left - wrapperRect.left,
2245
+ top: clipRect.top - wrapperRect.top,
2246
+ right: clipRect.right - wrapperRect.left,
2247
+ bottom: clipRect.bottom - wrapperRect.top
2248
+ }
2249
+ });
2250
+ setPosition((current) => current && current.x === next.x && current.y === next.y ? current : next);
2251
+ }, [
2252
+ left,
2253
+ top,
2254
+ offsetLeft,
2255
+ offsetTop,
2256
+ children
2257
+ ]);
2258
+ const x = position?.x ?? left + offsetLeft;
2259
+ const y = position?.y ?? top + offsetTop;
2260
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_tooltip.Tooltip, {
2261
+ ref: nodeRef,
2262
+ style: {
2263
+ position: "absolute",
2264
+ left: 0,
2265
+ top: 0,
2266
+ transform: `translate(${x}px, ${y}px)`,
2267
+ ...!unstyled && style
2268
+ },
2269
+ ...rest,
2270
+ children
2271
+ });
2272
+ };
2273
+ //#endregion
2274
+ //#region src/components/tooltip/xy-chart-tooltip.tsx
2275
+ const CROSSHAIR_STROKE_WIDTH = 1.5;
2276
+ const DEFAULT_GLYPH_RADIUS = 4;
2277
+ const FALLBACK_COLOR = "#222";
2278
+ const DEFAULT_TOOLTIP_Z_INDEX = 3;
2279
+ const PORTAL_OPTIONS = /* @__PURE__ */ new Set([
2280
+ "scroll",
2281
+ "debounce",
2282
+ "resizeObserverPolyfill"
2283
+ ]);
2284
+ const isValidNumber = (value) => typeof value === "number" && Number.isFinite(value);
2285
+ const scaleBandwidth = (scale) => {
2286
+ const bandwidth = scale?.bandwidth;
2287
+ return typeof bandwidth === "function" ? Number(bandwidth()) : 0;
2288
+ };
2289
+ const DefaultGlyph$1 = ({ x, y, size, color, glyphStyle, seriesKey }) => {
2290
+ const { theme } = (0, react.useContext)(_visx_xychart.DataContext) || {};
2291
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
2292
+ cx: x,
2293
+ cy: y,
2294
+ r: size,
2295
+ fill: color,
2296
+ stroke: theme?.backgroundColor,
2297
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
2298
+ paintOrder: "fill",
2299
+ ...glyphStyle
2300
+ });
2301
+ };
2302
+ const defaultRenderGlyph$1 = ({ key, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultGlyph$1, {
2303
+ seriesKey: key,
2304
+ ...props
2305
+ }, key);
2306
+ 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 }) => {
2307
+ const tooltipProps = Object.fromEntries(Object.entries(rest).filter(([key]) => !PORTAL_OPTIONS.has(key)));
2308
+ const { colorScale, theme, innerHeight = 0, innerWidth = 0, margin, xScale, yScale, dataRegistry } = (0, react.useContext)(_visx_xychart.DataContext) || {};
2309
+ const tooltipContent = renderTooltip ? renderTooltip({
2310
+ ...tooltipContext,
2311
+ colorScale
2312
+ }) : null;
2313
+ if (tooltipContent == null) return null;
2314
+ const scaleX = xScale;
2315
+ const scaleY = yScale;
2316
+ const getDatumLeftTop = (key, datum) => {
2317
+ const entry = dataRegistry?.get(key);
2318
+ return {
2319
+ left: scaleX && entry?.xAccessor ? Number(scaleX(entry.xAccessor(datum))) + scaleBandwidth(xScale) / 2 : void 0,
2320
+ top: scaleY && entry?.yAccessor ? Number(scaleY(entry.yAccessor(datum))) + scaleBandwidth(yScale) / 2 : void 0
2321
+ };
2322
+ };
2323
+ const nearestDatum = tooltipContext.tooltipData?.nearestDatum;
2324
+ let { tooltipLeft, tooltipTop } = tooltipContext;
2325
+ if (nearestDatum && (snapTooltipToDatumX || snapTooltipToDatumY)) {
2326
+ const { left, top } = getDatumLeftTop(nearestDatum.key, nearestDatum.datum);
2327
+ if (snapTooltipToDatumX && isValidNumber(left)) tooltipLeft = left;
2328
+ if (snapTooltipToDatumY && isValidNumber(top)) tooltipTop = top;
2329
+ }
2330
+ const glyphs = [];
2331
+ if (showDatumGlyph || showSeriesGlyphs) {
2332
+ const size = Number(glyphStyle?.radius ?? DEFAULT_GLYPH_RADIUS);
2333
+ const labelColor = theme?.htmlLabel?.color ?? FALLBACK_COLOR;
2334
+ const fallbackColor = showSeriesGlyphs ? labelColor : theme?.gridStyles?.stroke ?? labelColor;
2335
+ let entries = [];
2336
+ if (showSeriesGlyphs) entries = Object.values(tooltipContext.tooltipData?.datumByKey ?? {});
2337
+ else if (nearestDatum) entries = [nearestDatum];
2338
+ for (const { key, datum, index } of entries) {
2339
+ const { left, top } = getDatumLeftTop(key, datum);
2340
+ if (!isValidNumber(left) || !isValidNumber(top)) continue;
2341
+ glyphs.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
2342
+ className: "visx-tooltip-glyph",
2343
+ transform: `translate(${left}, ${top})`,
2344
+ children: renderGlyph({
2345
+ key,
2346
+ color: colorScale?.(key) ?? fallbackColor,
2347
+ datum,
2348
+ index,
2349
+ size,
2350
+ x: 0,
2351
+ y: 0,
2352
+ glyphStyle,
2353
+ isNearestDatum: nearestDatum?.key === key
2354
+ })
2355
+ }, key));
2356
+ }
2357
+ }
2358
+ const crosshairStroke = theme?.gridStyles?.stroke ?? theme?.htmlLabel?.color ?? FALLBACK_COLOR;
2359
+ const marginTop = margin?.top ?? 0;
2360
+ const marginLeft = margin?.left ?? 0;
2361
+ const TooltipComponent = detectBounds ? BoundedTooltip : _visx_tooltip.Tooltip;
2362
+ const boxStyle = {
2363
+ ..._visx_tooltip.defaultStyles,
2364
+ zIndex,
2365
+ background: theme?.backgroundColor ?? "white",
2366
+ boxShadow: `0 1px 2px ${theme?.htmlLabel?.color ? `${theme.htmlLabel.color}55` : "#22222255"}`,
2367
+ ...theme?.htmlLabel
2368
+ };
2369
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
2370
+ className: "visx-tooltip-overlay",
2371
+ pointerEvents: "none",
2372
+ children: [
2373
+ showVerticalCrosshair && isValidNumber(tooltipLeft) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
2374
+ className: "visx-crosshair visx-crosshair-vertical",
2375
+ x1: tooltipLeft,
2376
+ x2: tooltipLeft,
2377
+ y1: marginTop,
2378
+ y2: marginTop + innerHeight,
2379
+ stroke: crosshairStroke,
2380
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
2381
+ ...verticalCrosshairStyle
2382
+ }),
2383
+ showHorizontalCrosshair && isValidNumber(tooltipTop) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
2384
+ className: "visx-crosshair visx-crosshair-horizontal",
2385
+ x1: marginLeft,
2386
+ x2: marginLeft + innerWidth,
2387
+ y1: tooltipTop,
2388
+ y2: tooltipTop,
2389
+ stroke: crosshairStroke,
2390
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
2391
+ ...horizontalCrosshairStyle
2392
+ }),
2393
+ glyphs
2394
+ ]
2395
+ }), container && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipComponent, {
2396
+ left: tooltipLeft,
2397
+ top: tooltipTop,
2398
+ style: boxStyle,
2399
+ applyPositionStyle: true,
2400
+ ...tooltipProps,
2401
+ children: tooltipContent
2402
+ }), container)] });
2403
+ };
2404
+ /**
2405
+ * In-tree replacement for `@visx/xychart`'s `Tooltip`.
2406
+ *
2407
+ * visx renders its tooltip box, glyphs and crosshairs through portals appended
2408
+ * to `document.body`. That puts them in the page's root stacking context, where
2409
+ * they paint above every sticky or fixed element that lives inside a nested
2410
+ * stacking context, and no z-index on that element can change the order. This
2411
+ * component draws the glyphs and crosshairs straight into the chart SVG and
2412
+ * renders the tooltip box into the SVG's parent element, so all of them stack
2413
+ * as ordinary descendants of the chart.
2414
+ *
2415
+ * Render it as a child of `XYChart`. The element wrapping that `XYChart` must
2416
+ * be `position: relative` with the SVG at its origin, and `isolation: isolate`:
2417
+ * the box is placed with the SVG-local coordinates visx reports, and the
2418
+ * isolation keeps the box's `zIndex` from competing with page chrome. The box
2419
+ * flips and clamps to stay inside the nearest ancestor that clips its overflow
2420
+ * (or the viewport), so it may extend past the chart wrapper but is never cut
2421
+ * off unless that ancestor is smaller than the box itself.
2422
+ *
2423
+ * @param props - visx's `Tooltip` options. `scroll`, `debounce` and `resizeObserverPolyfill` are accepted and ignored.
2424
+ * @return An anchor in the SVG, plus the overlay and the tooltip box while the tooltip is open.
2425
+ */
2426
+ const XyChartTooltip = (props) => {
2427
+ const tooltipContext = (0, react.useContext)(_visx_xychart.TooltipContext);
2428
+ const [container, setContainer] = (0, react.useState)(null);
2429
+ const anchorRef = (0, react.useCallback)((node) => {
2430
+ setContainer(node?.ownerSVGElement?.parentElement ?? null);
2431
+ }, []);
2432
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", { ref: anchorRef }), tooltipContext?.tooltipOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XyChartTooltipContent, {
2433
+ ...props,
2434
+ tooltipContext,
2435
+ container
2436
+ })] });
2437
+ };
2438
+ //#endregion
2106
2439
  //#region src/components/tooltip/accessible-tooltip.tsx
2107
- const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocusedClassName, series = [], mode = "group", ...props }) => {
2440
+ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocusedClassName, series = [], mode = "group", verticalCrosshairStyle, horizontalCrosshairStyle, ...props }) => {
2108
2441
  const tooltipContext = (0, react.useContext)(_visx_xychart.TooltipContext);
2442
+ const scopeElement = useChartScopeElement();
2443
+ const crosshairStroke = (0, react.useMemo)(() => {
2444
+ const stroke = resolveCssVariable(CATALOG_POINTERS.grid, scopeElement);
2445
+ return stroke ? { stroke } : void 0;
2446
+ }, [scopeElement]);
2447
+ const standaloneScopeClass = useStandaloneScopeClass();
2109
2448
  const tooltipData = (0, react.useMemo)(() => {
2110
2449
  if (mode !== "individual") return [];
2111
2450
  if (series.length === 0) return [];
@@ -2166,11 +2505,11 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
2166
2505
  tabIndex: -1,
2167
2506
  role: "tooltip",
2168
2507
  "aria-atomic": "true",
2169
- className: (0, clsx.default)(CHART_SCOPE_CLASS, keyboardFocusedClassName),
2508
+ className: (0, clsx.default)(standaloneScopeClass, keyboardFocusedClassName),
2170
2509
  children: tooltipContent
2171
2510
  }, `chart-tooltip-${selectedIndex}`);
2172
2511
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2173
- className: CHART_SCOPE_CLASS,
2512
+ className: standaloneScopeClass,
2174
2513
  role: "tooltip",
2175
2514
  "aria-live": "polite",
2176
2515
  children: tooltipContent
@@ -2180,14 +2519,23 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
2180
2519
  renderTooltip,
2181
2520
  selectedIndex,
2182
2521
  tooltipRef,
2183
- keyboardFocusedClassName
2522
+ keyboardFocusedClassName,
2523
+ standaloneScopeClass
2184
2524
  ]);
2185
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Tooltip, {
2525
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XyChartTooltip, {
2186
2526
  ...props,
2527
+ verticalCrosshairStyle: {
2528
+ ...crosshairStroke,
2529
+ ...verticalCrosshairStyle
2530
+ },
2531
+ horizontalCrosshairStyle: {
2532
+ ...crosshairStroke,
2533
+ ...horizontalCrosshairStyle
2534
+ },
2187
2535
  renderTooltip: focusableRenderTooltip
2188
2536
  });
2189
2537
  };
2190
- const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints }) => {
2538
+ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints, onActivate }) => {
2191
2539
  return {
2192
2540
  tooltipRef: (0, react.useCallback)((element) => {
2193
2541
  if (element && selectedIndex !== void 0) element.focus();
@@ -2228,13 +2576,14 @@ const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating,
2228
2576
  setSelectedIndex(void 0);
2229
2577
  setIsNavigating(false);
2230
2578
  chartRef.current?.focus();
2231
- }
2579
+ } else if ((event.key === "Enter" || event.key === " ") && selectedIndex !== void 0) onActivate?.(selectedIndex);
2232
2580
  }, [
2233
2581
  totalPoints,
2234
2582
  selectedIndex,
2235
2583
  setSelectedIndex,
2236
2584
  setIsNavigating,
2237
- chartRef
2585
+ chartRef,
2586
+ onActivate
2238
2587
  ])
2239
2588
  };
2240
2589
  };
@@ -2521,72 +2870,90 @@ const getCurveType = (type, smoothing) => {
2521
2870
  default: return _visx_curve.curveLinear;
2522
2871
  }
2523
2872
  };
2524
- const dateTimeFormat = (options) => {
2525
- let formatter;
2526
- return (timestamp) => {
2527
- if (!Number.isFinite(Number(timestamp))) return new Date(timestamp).toLocaleDateString();
2528
- formatter = formatter ?? new Intl.DateTimeFormat(void 0, options);
2529
- return formatter.format(timestamp);
2530
- };
2531
- };
2532
- const formatYearTick = dateTimeFormat({ year: "numeric" });
2533
- const formatDateTick = dateTimeFormat({
2873
+ const YEAR_TICK = { year: "numeric" };
2874
+ const DATE_TICK = {
2534
2875
  month: "short",
2535
2876
  day: "numeric"
2536
- });
2537
- const formatHourTick = dateTimeFormat({
2538
- hour: "numeric",
2539
- hour12: true
2540
- });
2541
- const formatMonthTick = dateTimeFormat({ month: "short" });
2877
+ };
2878
+ const HOUR_TICK = { hour: "numeric" };
2879
+ const MONTH_TICK = { month: "short" };
2542
2880
  const boundaryFormat = (isAnchor, atBoundary, between) => {
2543
2881
  const format = (timestamp) => isAnchor(new Date(timestamp)) ? atBoundary(timestamp) : between(timestamp);
2544
2882
  format.isAnchor = isAnchor;
2545
2883
  return format;
2546
2884
  };
2547
- const formatDateOrHourTick = boundaryFormat((date) => date.getHours() === 0 && date.getMinutes() === 0, formatDateTick, formatHourTick);
2548
- const formatMonthOrYearTick = boundaryFormat((date) => date.getMonth() === 0, formatYearTick, formatMonthTick);
2885
+ const tickFormats = (formatting) => {
2886
+ const clock = createZonedClock(formatting.timeZone);
2887
+ const year = createDateFormatter(YEAR_TICK, formatting);
2888
+ const date = createDateFormatter(DATE_TICK, formatting);
2889
+ const hour = createDateFormatter(HOUR_TICK, formatting);
2890
+ const month = createDateFormatter(MONTH_TICK, formatting);
2891
+ const datedTick = (timestamp) => date(timestamp);
2892
+ datedTick.anchorsAtLabelChange = true;
2893
+ return {
2894
+ year,
2895
+ date: datedTick,
2896
+ dateOrHour: boundaryFormat((value) => clock(value).hour === 0, date, hour),
2897
+ hour,
2898
+ monthOrYear: boundaryFormat((value) => clock(value).month === 1, year, month)
2899
+ };
2900
+ };
2549
2901
  const getSpan = (sortedData) => {
2550
- const bounds = sortedData.map((datom) => [datom.data.at(0)?.date, datom.data.at(-1)?.date]).filter(([first, last]) => first !== void 0 && last !== void 0);
2902
+ 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)));
2551
2903
  if (!bounds.length) return null;
2552
2904
  return {
2553
2905
  minX: Math.min(...bounds.map(([first]) => Number(first))),
2554
2906
  maxX: Math.max(...bounds.map(([, last]) => Number(last)))
2555
2907
  };
2556
2908
  };
2557
- const getPointSpacingInHours = (sortedData) => {
2558
- return sortedData.reduce((spacing, datom) => datom.data.reduce((seriesSpacing, point, index) => {
2559
- const previous = datom.data[index - 1];
2560
- if (previous?.date === void 0 || point?.date === void 0) return seriesSpacing;
2561
- return Math.min(seriesSpacing, Math.abs((0, date_fns.differenceInHours)(point.date, previous.date)));
2562
- }, spacing), Number.POSITIVE_INFINITY);
2563
- };
2564
- const SUB_DAILY_SPACING_HOURS = 23;
2565
- const MONTHLY_SPACING_HOURS = 672;
2566
- const getBucketResolution = (sortedData, tickResolution) => {
2567
- if (tickResolution) return tickResolution === "week" ? "day" : tickResolution;
2568
- const spacingInHours = getPointSpacingInHours(sortedData);
2569
- if (spacingInHours < SUB_DAILY_SPACING_HOURS) return "hour";
2570
- if (!Number.isFinite(spacingInHours) || spacingInHours < MONTHLY_SPACING_HOURS) return "day";
2571
- return spacingInHours < 12 * MONTHLY_SPACING_HOURS ? "month" : "year";
2572
- };
2573
- const getFormatter = (sortedData, tickResolution) => {
2909
+ /**
2910
+ * The most informative tick format for a bucket resolution and the span on screen.
2911
+ *
2912
+ * @param sortedData - Series as returned by `useChartDataTransform`.
2913
+ * @param tickResolution - Caller-declared bucket resolution, when known.
2914
+ * @param formatting - Host locale and time zone.
2915
+ * @param domain - The effective scale domain, or undefined for the data's own extent.
2916
+ * @return The formatter the axis labels its ticks with.
2917
+ */
2918
+ const getFormatter = (sortedData, tickResolution, formatting = {}, domain) => {
2919
+ const format = tickFormats(formatting);
2574
2920
  const resolution = getBucketResolution(sortedData, tickResolution);
2575
- if (resolution === "year") return formatYearTick;
2576
- if (resolution === "month") return formatMonthOrYearTick;
2577
- const span = getSpan(sortedData);
2578
- if (!span) return formatDateTick;
2921
+ if (resolution === "year") return format.year;
2922
+ if (resolution === "month") return format.monthOrYear;
2923
+ const span = domain ? {
2924
+ minX: domain[0].getTime(),
2925
+ maxX: domain[1].getTime()
2926
+ } : getSpan(sortedData);
2927
+ if (!span) return format.date;
2579
2928
  const diffInHours = Math.abs((0, date_fns.differenceInHours)(span.maxX, span.minX));
2580
2929
  if (resolution === "hour") {
2581
- if (diffInHours <= 24) return formatHourTick;
2582
- if (diffInHours <= 168) return formatDateOrHourTick;
2930
+ if (diffInHours < 24) return format.hour;
2931
+ if (diffInHours <= 168) return format.dateOrHour;
2583
2932
  }
2584
- return Math.abs((0, date_fns.differenceInYears)(span.maxX, span.minX)) <= 1 ? formatDateTick : formatYearTick;
2933
+ return Math.abs((0, date_fns.differenceInYears)(span.maxX, span.minX)) <= 1 ? format.date : format.year;
2934
+ };
2935
+ /**
2936
+ * The instants a set of series spans, as a scale domain.
2937
+ *
2938
+ * @param sortedData - Series as returned by `useChartDataTransform`.
2939
+ * @return Earliest and latest dated point, or undefined when nothing is dated.
2940
+ */
2941
+ const getSeriesExtent = (sortedData) => {
2942
+ const span = getSpan(sortedData);
2943
+ return span ? [new Date(span.minX), new Date(span.maxX)] : void 0;
2944
+ };
2945
+ const getAnchorIndices = (domain, tickFormatter, labels) => {
2946
+ if (tickFormatter.anchorsAtLabelChange) return labels.reduce((indices, label, index) => {
2947
+ if (index === 0 || label !== labels[index - 1]) indices.push(index);
2948
+ return indices;
2949
+ }, []);
2950
+ const { isAnchor } = tickFormatter;
2951
+ if (!isAnchor) return [];
2952
+ return domain.reduce((indices, date, index) => {
2953
+ if (isAnchor(date)) indices.push(index);
2954
+ return indices;
2955
+ }, []);
2585
2956
  };
2586
- const getAnchorIndices = (domain, isAnchor) => domain.reduce((indices, date, index) => {
2587
- if (isAnchor(date)) indices.push(index);
2588
- return indices;
2589
- }, []);
2590
2957
  const getCandidateSteps = (length, maxTicks, anchorSpacing) => {
2591
2958
  const steps = /* @__PURE__ */ new Set();
2592
2959
  for (let count = maxTicks; count > 1; count--) {
@@ -2601,26 +2968,25 @@ const getCandidateSteps = (length, maxTicks, anchorSpacing) => {
2601
2968
  return steps;
2602
2969
  };
2603
2970
  /**
2604
- * Tick values for a band time axis.
2971
+ * Tick values sampled by index, for the bar chart's band axis and for evenly
2972
+ * spaced points on a continuous one.
2605
2973
  *
2606
- * visx samples a band domain by index from offset zero, blind to which labels
2607
- * carry a boundary and without collapsing repeats — so the tick that prints the
2608
- * year or the date often isn't sampled at all, and a long series can show the
2609
- * same label twice. Choose the values instead: sweep the evenly spaced
2610
- * candidates, including those that step from anchor to anchor, and keep the one
2611
- * that reaches the most anchors without thinning the axis or putting two
2612
- * identical labels side by side.
2974
+ * visx samples by index from offset zero, blind to which labels carry a boundary
2975
+ * and without collapsing repeats — so the tick that prints the year or the date
2976
+ * often isn't sampled at all, and a long series can show the same label twice.
2977
+ * Choose the values instead: sweep the evenly spaced candidates, including those
2978
+ * that step from anchor to anchor, and keep the one that reaches the most anchors
2979
+ * without thinning the axis or putting two identical labels side by side.
2613
2980
  *
2614
- * @param domain - Band domain, in axis order.
2981
+ * @param domain - Buckets in axis order.
2615
2982
  * @param tickFormatter - Formatter the axis will render these values with.
2616
2983
  * @param maxTicks - Most ticks the axis should carry.
2617
2984
  * @return Values to hand the axis as `tickValues`.
2618
2985
  */
2619
2986
  const getBandTickValues = (domain, tickFormatter, maxTicks) => {
2620
2987
  if (!domain.length) return [];
2621
- const { isAnchor } = tickFormatter;
2622
- const anchorIndices = isAnchor ? getAnchorIndices(domain, isAnchor) : [];
2623
2988
  const domainLabels = domain.map((date) => tickFormatter(date.getTime()));
2989
+ const anchorIndices = getAnchorIndices(domain, tickFormatter, domainLabels);
2624
2990
  const candidates = [];
2625
2991
  const consider = (indices) => {
2626
2992
  if (!indices.length || indices.length > maxTicks) return;
@@ -2632,15 +2998,108 @@ const getBandTickValues = (domain, tickFormatter, maxTicks) => {
2632
2998
  for (let index = offset; index < domain.length; index += step) indices.push(index);
2633
2999
  consider(indices);
2634
3000
  }
2635
- for (let stride = 1; stride <= anchorIndices.length; stride++) consider(anchorIndices.filter((_, position) => position % stride === 0));
3001
+ const minStride = Math.max(1, Math.ceil(anchorIndices.length / maxTicks));
3002
+ for (let stride = minStride; stride <= anchorIndices.length; stride++) {
3003
+ const stepped = [];
3004
+ for (let position = 0; position < anchorIndices.length; position += stride) stepped.push(anchorIndices[position]);
3005
+ consider(stepped);
3006
+ }
2636
3007
  if (!candidates.length) return [domain[0]];
2637
3008
  const densest = candidates.reduce((most, indices) => Math.max(most, indices.length), 0);
2638
- const anchorsIn = (indices) => isAnchor ? indices.filter((index) => isAnchor(domain[index])).length : 0;
3009
+ const anchorIndexSet = new Set(anchorIndices);
3010
+ const anchorsIn = (indices) => indices.filter((index) => anchorIndexSet.has(index)).length;
2639
3011
  return candidates.filter((indices) => indices.length >= densest - 1).reduce((chosen, indices) => {
2640
3012
  const gain = anchorsIn(indices) - anchorsIn(chosen);
2641
3013
  return gain > 0 || gain === 0 && indices.length > chosen.length ? indices : chosen;
2642
3014
  }).map((index) => domain[index]);
2643
3015
  };
3016
+ /**
3017
+ * The most x-axis ticks a chart of this width has room for.
3018
+ *
3019
+ * @param chartWidth - Chart width in pixels.
3020
+ * @return A tick budget of at least one.
3021
+ */
3022
+ const getMaxTicksForWidth = (chartWidth) => Math.max(1, Math.floor(chartWidth / X_TICK_WIDTH));
3023
+ const MIN_GAP_IN_LABELS = .9;
3024
+ const nearestValue = (sorted, target) => {
3025
+ let low = 0;
3026
+ let high = sorted.length - 1;
3027
+ while (low < high) {
3028
+ const middle = Math.floor((low + high) / 2);
3029
+ if (sorted[middle] < target) low = middle + 1;
3030
+ else high = middle;
3031
+ }
3032
+ const above = sorted[low];
3033
+ const below = low > 0 ? sorted[low - 1] : above;
3034
+ return Math.abs(above - target) <= Math.abs(below - target) ? above : below;
3035
+ };
3036
+ const getPositionTickValues = (points, tickFormatter, maxTicks, axis) => {
3037
+ const times = points.map((point) => point.getTime());
3038
+ const { first, last } = axis;
3039
+ if (maxTicks <= 1 || times.length === 1 || first === last) return [points[0]];
3040
+ const step = (last - first) / (maxTicks - 1);
3041
+ const labels = points.map((point) => tickFormatter(point.getTime()));
3042
+ const minGap = (last - first) / maxTicks * MIN_GAP_IN_LABELS;
3043
+ const anchors = getAnchorIndices(points, tickFormatter, labels).map((index) => times[index]);
3044
+ const chosen = [];
3045
+ let lastLabel = null;
3046
+ for (let index = 0; index < maxTicks; index++) {
3047
+ const target = first + index * step;
3048
+ const anchor = anchors.length ? nearestValue(anchors, target) : null;
3049
+ let tick = anchor !== null && Math.abs(anchor - target) <= step / 2 ? anchor : null;
3050
+ if (tick === null) {
3051
+ const point = nearestValue(times, target);
3052
+ tick = Math.abs(point - target) <= step / 2 ? point : target;
3053
+ }
3054
+ const previous = chosen[chosen.length - 1];
3055
+ if (previous !== void 0 && (tick <= previous || tick - previous < minGap)) continue;
3056
+ const label = tickFormatter(tick);
3057
+ if (label === lastLabel) continue;
3058
+ chosen.push(tick);
3059
+ lastLabel = label;
3060
+ }
3061
+ return chosen.map((timestamp) => new Date(timestamp));
3062
+ };
3063
+ const isIndexSamplingUnusable = (ticks, points, maxTicks, axis) => {
3064
+ if (ticks.length < 2 || points.length < 2) return false;
3065
+ const span = axis.last - axis.first;
3066
+ if (!span) return false;
3067
+ 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;
3068
+ };
3069
+ /**
3070
+ * Tick values for a continuous time axis, chosen from the points it draws.
3071
+ *
3072
+ * Sampled by index while the points are evenly spaced, which keeps the axis on
3073
+ * the calendar boundaries `getBandTickValues` steers towards, and by position
3074
+ * once they are not, where an index stride crowds one end of the scale.
3075
+ *
3076
+ * Values are taken from the data wherever there is one to take, so a local time
3077
+ * that does not exist cannot become a tick and DST needs no special case. Only a
3078
+ * target stranded in a gap is constructed, as a plain instant.
3079
+ *
3080
+ * @param sortedData - Series as returned by `useChartDataTransform`.
3081
+ * @param domain - The effective scale domain, or undefined for all data.
3082
+ * @param tickFormatter - The formatter the ticks will be labeled with.
3083
+ * @param maxTicks - The most ticks the axis has room for.
3084
+ * @return Tick dates, or null when no series carries a usable date.
3085
+ */
3086
+ const getTimeAxisTickValues = (sortedData, domain, tickFormatter, maxTicks) => {
3087
+ const byTimestamp = /* @__PURE__ */ new Map();
3088
+ for (const series of sortedData) for (const point of series.data) {
3089
+ const date = point.date;
3090
+ if (date instanceof Date && Number.isFinite(date.getTime())) byTimestamp.set(date.getTime(), date);
3091
+ }
3092
+ if (!byTimestamp.size) return null;
3093
+ const min = domain?.[0]?.getTime();
3094
+ const max = domain?.[1]?.getTime();
3095
+ 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));
3096
+ const axis = {
3097
+ first: min ?? visible[0].getTime(),
3098
+ last: max ?? visible[visible.length - 1].getTime()
3099
+ };
3100
+ const ticks = getBandTickValues(visible, tickFormatter, maxTicks);
3101
+ return isIndexSamplingUnusable(ticks, visible, maxTicks, axis) ? getPositionTickValues(visible, tickFormatter, maxTicks, axis) : ticks;
3102
+ };
2644
3103
  const guessOptimalNumTicks = (data, chartWidth, tickFormatter) => {
2645
3104
  const span = getSpan(data);
2646
3105
  if (!span) return 1;
@@ -2655,7 +3114,43 @@ const guessOptimalNumTicks = (data, chartWidth, tickFormatter) => {
2655
3114
  if (ticks.some((tick, idx) => idx > 0 && tick === ticks[idx - 1])) continue;
2656
3115
  return ticks.length;
2657
3116
  }
2658
- return secondBestGuess;
3117
+ return Math.max(1, secondBestGuess);
3118
+ };
3119
+ //#endregion
3120
+ //#region src/charts/private/time-axis-options.ts
3121
+ const toDateDomain = (domain) => domain ? [new Date(domain[0]), new Date(domain[1])] : void 0;
3122
+ /**
3123
+ * The x-axis options for a continuous time chart, shared by LineChart and AreaChart.
3124
+ *
3125
+ * Kept in one place so the tick-selection logic isn't duplicated between them.
3126
+ *
3127
+ * @param args - Named arguments.
3128
+ * @param args.dataSorted - Series as returned by `useChartDataTransform`.
3129
+ * @param args.width - Chart width in pixels, which bounds how many ticks fit.
3130
+ * @param args.axisOptions - The caller's `options.axis.x`.
3131
+ * @param args.scaleDomain - The caller's `options.xScale.domain`.
3132
+ * @param args.zoomDomain - The chart's current zoom window, when zoomed.
3133
+ * @param args.formatting - Host locale and time zone.
3134
+ * @param args.isSeriesRendered - Whether visx mounts a series, i.e. the legend shows it. Defaults to all of them.
3135
+ * @return Options ready to spread into the chart's `axis.x`; `orientation` is always set.
3136
+ */
3137
+ const buildTimeAxisOptions = ({ dataSorted, width, axisOptions, scaleDomain, zoomDomain, formatting, isSeriesRendered }) => {
3138
+ const { tickResolution, tickFormat, tickValues: callerTickValues, numTicks: callerNumTicks, orientation: callerOrientation, display: callerDisplay, ...rest } = axisOptions ?? {};
3139
+ const rendered = isSeriesRendered ? dataSorted.filter(isSeriesRendered) : dataSorted;
3140
+ const effectiveDomain = zoomDomain ?? toDateDomain(scaleDomain) ?? getSeriesExtent(rendered);
3141
+ const ownFormatter = getFormatter(rendered, tickResolution, formatting, effectiveDomain);
3142
+ const formatter = tickFormat || ownFormatter;
3143
+ const ownTickValues = tickFormat || callerTickValues ? null : getTimeAxisTickValues(rendered, effectiveDomain, ownFormatter, callerNumTicks ?? getMaxTicksForWidth(width));
3144
+ const tickValues = callerTickValues ?? (ownTickValues?.length ? ownTickValues : void 0);
3145
+ const numTicks = callerNumTicks ?? (tickValues ? void 0 : guessOptimalNumTicks(rendered, width, formatter));
3146
+ return {
3147
+ orientation: callerOrientation ?? "bottom",
3148
+ tickFormat: formatter,
3149
+ display: callerDisplay ?? true,
3150
+ ...rest,
3151
+ ...tickValues ? { tickValues } : {},
3152
+ ...numTicks === void 0 ? {} : { numTicks }
3153
+ };
2659
3154
  };
2660
3155
  //#endregion
2661
3156
  //#region src/charts/private/with-responsive/with-responsive.module.scss
@@ -2936,6 +3431,9 @@ function ZoomResetButton({ onClick }) {
2936
3431
  });
2937
3432
  }
2938
3433
  //#endregion
3434
+ //#region src/charts/private/xy-plot/xy-plot.module.scss
3435
+ var xy_plot_module_default = { "xy-plot": "a8ccharts-bIyhgG-xy-plot" };
3436
+ //#endregion
2939
3437
  //#region src/charts/line-chart/line-chart.module.scss
2940
3438
  var line_chart_module_default = {
2941
3439
  "line-chart": "a8ccharts-inuQka-line-chart",
@@ -3001,7 +3499,7 @@ const LineChartAnnotationLabelWithPopover = ({ title, subtitle, renderLabel, ren
3001
3499
  style: {
3002
3500
  width: `44px`,
3003
3501
  height: `44px`,
3004
- transform: `translate(${44 / 2}px, 0)`
3502
+ transform: `translate(22px, 0)`
3005
3503
  },
3006
3504
  "aria-label": title || (0, _wordpress_i18n.__)("View details", "jetpack-charts"),
3007
3505
  children: renderLabel({
@@ -3176,12 +3674,14 @@ const getVerticalAnchor = (subjectType, isFlippedVertically, y, yMax, height) =>
3176
3674
  };
3177
3675
  const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", styles: datumStyles, testId, renderLabel, renderLabelPopover }) => {
3178
3676
  const providerTheme = useGlobalChartsTheme();
3179
- const scopeElement = useChartScopeElement();
3180
3677
  const { xScale, yScale } = (0, react.useContext)(_visx_xychart.DataContext) || {};
3181
3678
  const labelRef = (0, react.useRef)(null);
3182
3679
  const [height, setHeight] = (0, react.useState)(null);
3183
- const styles = (0, deepmerge.default)(providerTheme.annotationStyles ?? {}, datumStyles ?? {});
3184
- const resolveColor = (value) => value ? resolveCssVariable(value, scopeElement) ?? value : value;
3680
+ const styles = (0, react.useMemo)(() => deepmerge.default.all([
3681
+ ANNOTATION_POINTERS,
3682
+ providerTheme.annotationStyles ?? {},
3683
+ datumStyles ?? {}
3684
+ ]), [providerTheme.annotationStyles, datumStyles]);
3185
3685
  (0, react.useEffect)(() => {
3186
3686
  if (labelRef.current?.getBBox) {
3187
3687
  const bbox = labelRef.current.getBBox();
@@ -3267,15 +3767,8 @@ const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", s
3267
3767
  dx,
3268
3768
  dy,
3269
3769
  children: [
3270
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.Connector, {
3271
- ...styles?.connector,
3272
- stroke: resolveColor(styles?.connector?.stroke)
3273
- }),
3274
- subjectType === "circle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.CircleSubject, {
3275
- ...styles?.circleSubject,
3276
- fill: resolveColor(styles?.circleSubject?.fill),
3277
- stroke: resolveColor(styles?.circleSubject?.stroke)
3278
- }),
3770
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.Connector, { ...styles?.connector }),
3771
+ subjectType === "circle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.CircleSubject, { ...styles?.circleSubject }),
3279
3772
  subjectType === "line-vertical" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.LineSubject, {
3280
3773
  min: yMax,
3281
3774
  max: yMin,
@@ -3309,8 +3802,6 @@ const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", s
3309
3802
  title,
3310
3803
  subtitle,
3311
3804
  ...styles?.label,
3312
- anchorLineStroke: resolveColor(styles?.label?.anchorLineStroke),
3313
- backgroundFill: resolveColor(styles?.label?.backgroundFill),
3314
3805
  ...labelPosition,
3315
3806
  horizontalAnchor: getHorizontalAnchor(subjectType, isFlippedHorizontally),
3316
3807
  verticalAnchor: getVerticalAnchor(subjectType, isFlippedVertically, y, yMax, height ?? ANNOTATION_INIT_HEIGHT)
@@ -3358,16 +3849,33 @@ const toNumber = (val) => {
3358
3849
  const num = typeof val === "number" ? val : parseFloat(val);
3359
3850
  return isNaN(num) ? void 0 : num;
3360
3851
  };
3852
+ const TOOLTIP_DATE = {};
3853
+ const TOOLTIP_FORMAT_BY_RESOLUTION$1 = {
3854
+ hour: {
3855
+ year: "numeric",
3856
+ month: "numeric",
3857
+ day: "numeric",
3858
+ hour: "numeric"
3859
+ },
3860
+ day: TOOLTIP_DATE,
3861
+ month: TOOLTIP_DATE,
3862
+ year: TOOLTIP_DATE
3863
+ };
3864
+ const TooltipDate = ({ date, displayResolution }) => {
3865
+ const formatting = useChartFormatting();
3866
+ const format = (0, react.useMemo)(() => createDateFormatter(TOOLTIP_FORMAT_BY_RESOLUTION$1[displayResolution], formatting), [displayResolution, formatting]);
3867
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: date ? format(date) : null });
3868
+ };
3361
3869
  /**
3362
3870
  * Default visx-tooltip render that prints the hovered date as a heading and
3363
3871
  * one row per visible series (label + formatted value), sorted descending by
3364
3872
  * value. Reused by AreaChart, which has the same multi-series shape.
3365
3873
  *
3366
- * @param params - visx `RenderTooltipParams< DataPointDate >`.
3874
+ * @param params - visx `RenderTooltipParams< DataPointDate >`, plus the chart's optional `bucketInfo`.
3367
3875
  * @return Tooltip JSX, or `null` when no datum is hovered.
3368
3876
  */
3369
3877
  const renderDefaultTooltip = (params) => {
3370
- const { tooltipData } = params;
3878
+ const { tooltipData, bucketInfo } = params;
3371
3879
  const nearestDatum = tooltipData?.nearestDatum?.datum;
3372
3880
  if (!nearestDatum) return null;
3373
3881
  const tooltipPoints = Object.entries(tooltipData?.datumByKey || {}).map(([key, { datum }]) => ({
@@ -3378,7 +3886,10 @@ const renderDefaultTooltip = (params) => {
3378
3886
  className: line_chart_module_default["line-chart__tooltip"],
3379
3887
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3380
3888
  className: line_chart_module_default["line-chart__tooltip-date"],
3381
- children: nearestDatum.date?.toLocaleDateString()
3889
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipDate, {
3890
+ date: nearestDatum.date,
3891
+ displayResolution: bucketInfo?.displayResolution ?? "day"
3892
+ })
3382
3893
  }), tooltipPoints.map((point) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
3383
3894
  direction: "row",
3384
3895
  align: "center",
@@ -3422,14 +3933,14 @@ const LineChartScalesRef = ({ chartRef, width, height, margin }) => {
3422
3933
  ]);
3423
3934
  return null;
3424
3935
  };
3425
- const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, withStartGlyphs = false, withEndGlyphs = false, animation, options = {}, onPointerDown = void 0, onPointerUp = void 0, onPointerMove = void 0, onPointerOut = void 0, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
3936
+ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, withStartGlyphs = false, withEndGlyphs = false, animation, options = {}, onPointerDown = void 0, onPointerUp = void 0, onPointerMove = void 0, onPointerOut = void 0, onDatumActivate = void 0, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
3426
3937
  const legendInteractive = legend.interactive ?? false;
3427
3938
  const legendCollapseGroups = legend.collapseGroups ?? false;
3428
3939
  const legendShape = legend.shape ?? "line";
3429
3940
  const legendPosition = legend.position ?? "bottom";
3430
- const providerTheme = useGlobalChartsTheme();
3941
+ const formatting = useChartFormatting();
3431
3942
  const theme = useXYChartTheme(data);
3432
- const resolvedBackgroundColor = theme.backgroundColor ?? providerTheme.backgroundColor;
3943
+ const resolvedBackgroundColor = theme.backgroundColor;
3433
3944
  const chartId = useChartId(providedChartId);
3434
3945
  const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
3435
3946
  const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
@@ -3484,26 +3995,36 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3484
3995
  }
3485
3996
  return min < max ? [min, max] : void 0;
3486
3997
  }, [rescaleYOnVisibilityChange, dataSorted]);
3998
+ const activateSelectedPoint = (0, react.useCallback)((index) => {
3999
+ const series = dataSorted[0];
4000
+ const datum = series?.data[index];
4001
+ if (series && datum) onDatumActivate?.({
4002
+ datum,
4003
+ index,
4004
+ key: series.label
4005
+ });
4006
+ }, [dataSorted, onDatumActivate]);
3487
4007
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
3488
4008
  selectedIndex,
3489
4009
  setSelectedIndex,
3490
4010
  isNavigating,
3491
4011
  setIsNavigating,
3492
4012
  chartRef,
3493
- totalPoints: dataSorted[0]?.data.length || 0
4013
+ totalPoints: dataSorted[0]?.data.length || 0,
4014
+ onActivate: activateSelectedPoint
3494
4015
  });
3495
4016
  const chartOptions = (0, react.useMemo)(() => {
3496
- const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
3497
- const formatter = tickFormat || getFormatter(dataSorted, tickResolution);
3498
4017
  return {
3499
4018
  axis: {
3500
- x: {
3501
- orientation: "bottom",
3502
- numTicks: guessOptimalNumTicks(dataSorted, width, formatter),
3503
- tickFormat: formatter,
3504
- display: true,
3505
- ...xAxisOptions
3506
- },
4019
+ x: buildTimeAxisOptions({
4020
+ dataSorted,
4021
+ width,
4022
+ axisOptions: options?.axis?.x,
4023
+ scaleDomain: options?.xScale?.domain,
4024
+ zoomDomain: zoom.domain,
4025
+ formatting,
4026
+ isSeriesRendered: (series) => isSeriesVisible(series.label)
4027
+ }),
3507
4028
  y: {
3508
4029
  orientation: "left",
3509
4030
  numTicks: 4,
@@ -3530,7 +4051,14 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3530
4051
  dataSorted,
3531
4052
  width,
3532
4053
  zoom.domain,
3533
- stableYDomain
4054
+ stableYDomain,
4055
+ formatting,
4056
+ isSeriesVisible
4057
+ ]);
4058
+ const bucketInfo = (0, react.useMemo)(() => getBucketInfo(dataSorted.filter((series) => isSeriesVisible(series.label)), options?.axis?.x?.tickResolution), [
4059
+ dataSorted,
4060
+ isSeriesVisible,
4061
+ options?.axis?.x?.tickResolution
3534
4062
  ]);
3535
4063
  const tooltipRenderGlyph = (0, react.useMemo)(() => {
3536
4064
  return (props) => {
@@ -3554,42 +4082,48 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3554
4082
  const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme);
3555
4083
  const error = validateData$4(dataSorted);
3556
4084
  const isDataValid = !error;
4085
+ const legendItems = useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({
4086
+ withGlyph: withLegendGlyph,
4087
+ glyphSize: Math.max(0, toNumber(glyphStyle?.radius) ?? 4),
4088
+ collapseGroups: legendCollapseGroups,
4089
+ renderGlyph
4090
+ }), [
4091
+ withLegendGlyph,
4092
+ glyphStyle?.radius,
4093
+ legendCollapseGroups,
4094
+ renderGlyph
4095
+ ]), legendShape);
4096
+ const chartMetadata = (0, react.useMemo)(() => ({
4097
+ withGradientFill,
4098
+ smoothing,
4099
+ curveType,
4100
+ withStartGlyphs,
4101
+ withEndGlyphs,
4102
+ withLegendGlyph
4103
+ }), [
4104
+ withGradientFill,
4105
+ smoothing,
4106
+ curveType,
4107
+ withStartGlyphs,
4108
+ withEndGlyphs,
4109
+ withLegendGlyph
4110
+ ]);
3557
4111
  useChartRegistration({
3558
4112
  chartId,
3559
- legendItems: useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({
3560
- withGlyph: withLegendGlyph,
3561
- glyphSize: Math.max(0, toNumber(glyphStyle?.radius) ?? 4),
3562
- collapseGroups: legendCollapseGroups,
3563
- renderGlyph
3564
- }), [
3565
- withLegendGlyph,
3566
- glyphStyle?.radius,
3567
- legendCollapseGroups,
3568
- renderGlyph
3569
- ]), legendShape),
4113
+ legendItems,
3570
4114
  chartType: "line",
3571
4115
  isDataValid,
3572
- metadata: (0, react.useMemo)(() => ({
3573
- withGradientFill,
3574
- smoothing,
3575
- curveType,
3576
- withStartGlyphs,
3577
- withEndGlyphs,
3578
- withLegendGlyph
3579
- }), [
3580
- withGradientFill,
3581
- smoothing,
3582
- curveType,
3583
- withStartGlyphs,
3584
- withEndGlyphs,
3585
- withLegendGlyph
3586
- ])
4116
+ metadata: chartMetadata
3587
4117
  });
3588
4118
  const prefersReducedMotion = usePrefersReducedMotion();
3589
4119
  const accessors = {
3590
4120
  xAccessor: (d) => d?.date,
3591
4121
  yAccessor: (d) => d?.value
3592
4122
  };
4123
+ const tooltipRenderer = (0, react.useMemo)(() => (params) => renderTooltip({
4124
+ ...params,
4125
+ bucketInfo
4126
+ }), [renderTooltip, bucketInfo]);
3593
4127
  if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3594
4128
  className: (0, clsx.default)("line-chart", line_chart_module_default["line-chart"]),
3595
4129
  children: error
@@ -3638,7 +4172,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3638
4172
  onBlur: onChartBlur,
3639
4173
  children: chartHeight > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3640
4174
  ref: chartRef,
3641
- style: { position: "relative" },
4175
+ className: xy_plot_module_default["xy-plot"],
3642
4176
  children: [zoomable && zoom.domain && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_xychart.XYChart, {
3643
4177
  theme,
3644
4178
  width,
@@ -3730,7 +4264,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3730
4264
  snapTooltipToDatumX: true,
3731
4265
  snapTooltipToDatumY: true,
3732
4266
  showSeriesGlyphs: true,
3733
- renderTooltip,
4267
+ renderTooltip: tooltipRenderer,
3734
4268
  renderGlyph: tooltipRenderGlyph,
3735
4269
  glyphStyle,
3736
4270
  showVerticalCrosshair: withTooltipCrosshairs?.showVertical,
@@ -3868,12 +4402,11 @@ const HoverGlyphs = ({ visibleSeries, stacked, stackOffset, getElementStyles, st
3868
4402
  };
3869
4403
  //#endregion
3870
4404
  //#region src/charts/area-chart/area-chart.tsx
3871
- const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange, rescaleYOnLegendToggle, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
4405
+ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
3872
4406
  const legendInteractive = legend.interactive ?? false;
3873
4407
  const legendShape = legend.shape ?? "rect";
3874
4408
  const legendPosition = legend.position ?? "bottom";
3875
- const rescaleYOnVisibility = rescaleYOnVisibilityChange ?? rescaleYOnLegendToggle ?? true;
3876
- const providerTheme = useGlobalChartsTheme();
4409
+ const formatting = useChartFormatting();
3877
4410
  const theme = useXYChartTheme(data);
3878
4411
  const chartId = useChartId(providedChartId);
3879
4412
  const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
@@ -3923,7 +4456,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3923
4456
  totalPoints: dataSorted[0]?.data.length || 0
3924
4457
  });
3925
4458
  const fixedYDomain = (0, react.useMemo)(() => {
3926
- if (rescaleYOnVisibility || !dataSorted.length || !dataSorted[0].data.length || stacked && stackOffset !== "none") return;
4459
+ if (rescaleYOnVisibilityChange || !dataSorted.length || !dataSorted[0].data.length || stacked && stackOffset !== "none") return;
3927
4460
  if (stacked) {
3928
4461
  const numPoints = Math.max(...dataSorted.map((s) => s.data.length));
3929
4462
  let posMax = 0;
@@ -3957,20 +4490,19 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3957
4490
  dataSorted,
3958
4491
  stacked,
3959
4492
  stackOffset,
3960
- rescaleYOnVisibility
4493
+ rescaleYOnVisibilityChange
3961
4494
  ]);
3962
4495
  const chartOptions = (0, react.useMemo)(() => {
3963
- const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
3964
- const formatter = tickFormat || getFormatter(dataSorted, tickResolution);
3965
4496
  return {
3966
4497
  axis: {
3967
- x: {
3968
- orientation: "bottom",
3969
- numTicks: guessOptimalNumTicks(dataSorted, width, formatter),
3970
- tickFormat: formatter,
3971
- display: true,
3972
- ...xAxisOptions
3973
- },
4498
+ x: buildTimeAxisOptions({
4499
+ dataSorted,
4500
+ width,
4501
+ axisOptions: options?.axis?.x,
4502
+ scaleDomain: options?.xScale?.domain,
4503
+ zoomDomain: zoom.domain,
4504
+ formatting
4505
+ }),
3974
4506
  y: {
3975
4507
  orientation: "left",
3976
4508
  numTicks: 4,
@@ -3998,31 +4530,34 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
3998
4530
  width,
3999
4531
  stacked,
4000
4532
  fixedYDomain,
4001
- zoom.domain
4533
+ zoom.domain,
4534
+ formatting
4002
4535
  ]);
4003
4536
  const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme);
4004
4537
  const error = validateData$3(dataSorted);
4005
4538
  const isDataValid = !error;
4539
+ const legendItems = useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({
4540
+ withGlyph: false,
4541
+ glyphSize: 0,
4542
+ collapseGroups: legend.collapseGroups ?? false
4543
+ }), [legend.collapseGroups]), legendShape);
4544
+ const chartMetadata = (0, react.useMemo)(() => ({
4545
+ stacked,
4546
+ stackOffset,
4547
+ smoothing,
4548
+ curveType
4549
+ }), [
4550
+ stacked,
4551
+ stackOffset,
4552
+ smoothing,
4553
+ curveType
4554
+ ]);
4006
4555
  useChartRegistration({
4007
4556
  chartId,
4008
- legendItems: useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({
4009
- withGlyph: false,
4010
- glyphSize: 0,
4011
- collapseGroups: legend.collapseGroups ?? false
4012
- }), [legend.collapseGroups]), legendShape),
4557
+ legendItems,
4013
4558
  chartType: "area",
4014
4559
  isDataValid,
4015
- metadata: (0, react.useMemo)(() => ({
4016
- stacked,
4017
- stackOffset,
4018
- smoothing,
4019
- curveType
4020
- }), [
4021
- stacked,
4022
- stackOffset,
4023
- smoothing,
4024
- curveType
4025
- ])
4560
+ metadata: chartMetadata
4026
4561
  });
4027
4562
  const prefersReducedMotion = usePrefersReducedMotion();
4028
4563
  const animationEnabled = !!animation && !prefersReducedMotion;
@@ -4032,9 +4567,17 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
4032
4567
  };
4033
4568
  const zeroYAccessor = (0, react.useCallback)(() => 0, []);
4034
4569
  const visibleLabels = (0, react.useMemo)(() => new Set(seriesWithVisibility.filter((s) => s.isVisible).map((s) => s.series.label)), [seriesWithVisibility]);
4570
+ const bucketInfo = (0, react.useMemo)(() => getBucketInfo(dataSorted.filter((series) => visibleLabels.has(series.label)), options?.axis?.x?.tickResolution), [
4571
+ dataSorted,
4572
+ visibleLabels,
4573
+ options?.axis?.x?.tickResolution
4574
+ ]);
4035
4575
  const filteredRenderTooltip = (0, react.useCallback)((params) => {
4036
4576
  const datumByKey = params?.tooltipData?.datumByKey;
4037
- if (!datumByKey) return renderTooltip(params);
4577
+ if (!datumByKey) return renderTooltip({
4578
+ ...params,
4579
+ bucketInfo
4580
+ });
4038
4581
  const filtered = Object.fromEntries(Object.entries(datumByKey).filter(([key]) => visibleLabels.has(key)));
4039
4582
  if (Object.keys(filtered).length === 0) return null;
4040
4583
  const nearestDatum = params?.tooltipData?.nearestDatum;
@@ -4044,13 +4587,18 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
4044
4587
  };
4045
4588
  return renderTooltip({
4046
4589
  ...params,
4590
+ bucketInfo,
4047
4591
  tooltipData: {
4048
4592
  ...params.tooltipData,
4049
4593
  datumByKey: filtered,
4050
4594
  nearestDatum: nextNearest
4051
4595
  }
4052
4596
  });
4053
- }, [renderTooltip, visibleLabels]);
4597
+ }, [
4598
+ renderTooltip,
4599
+ visibleLabels,
4600
+ bucketInfo
4601
+ ]);
4054
4602
  const resolvedFillOpacity = fillOpacity ?? (stacked ? .85 : .4);
4055
4603
  const resolvedWithStroke = withStroke ?? !stacked;
4056
4604
  if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -4125,7 +4673,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
4125
4673
  onBlur: onChartBlur,
4126
4674
  children: chartHeight > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4127
4675
  ref: chartRef,
4128
- style: { position: "relative" },
4676
+ className: xy_plot_module_default["xy-plot"],
4129
4677
  children: [zoomable && zoom.domain && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_xychart.XYChart, {
4130
4678
  theme,
4131
4679
  width,
@@ -4181,7 +4729,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
4181
4729
  stacked,
4182
4730
  stackOffset,
4183
4731
  getElementStyles,
4184
- strokeColor: theme.backgroundColor ?? providerTheme.backgroundColor
4732
+ strokeColor: theme.backgroundColor
4185
4733
  })] }),
4186
4734
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AreaChartScalesRef, {
4187
4735
  chartRef: internalChartRef,
@@ -4349,8 +4897,7 @@ const TOOLTIP_FORMAT_BY_RESOLUTION = {
4349
4897
  year: "numeric",
4350
4898
  month: "long",
4351
4899
  day: "numeric",
4352
- hour: "numeric",
4353
- hour12: true
4900
+ hour: "numeric"
4354
4901
  },
4355
4902
  day: {
4356
4903
  year: "numeric",
@@ -4369,16 +4916,16 @@ const TOOLTIP_FORMAT_BY_RESOLUTION = {
4369
4916
  *
4370
4917
  * @param data - Date-based series, already parsed and sorted by `useChartDataTransform`.
4371
4918
  * @param tickResolution - Caller-declared bucket resolution, when known.
4919
+ * @param formatting - Host locale and time zone.
4372
4920
  * @return Tooltip label formatter.
4373
4921
  */
4374
- const getTooltipFormatter = (data, tickResolution) => {
4375
- if (tickResolution === "week") return (timestamp) => (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("Week of %s", "jetpack-charts"), new Date(timestamp).toLocaleDateString(void 0, {
4376
- year: "numeric",
4377
- month: "long",
4378
- day: "numeric"
4379
- }));
4922
+ const getTooltipFormatter = (data, tickResolution, formatting) => {
4923
+ if (tickResolution === "week") {
4924
+ const formatDay = createDateFormatter(TOOLTIP_FORMAT_BY_RESOLUTION.day, formatting);
4925
+ return (timestamp) => (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("Week of %s", "jetpack-charts"), formatDay(timestamp));
4926
+ }
4380
4927
  const format = TOOLTIP_FORMAT_BY_RESOLUTION[getBucketResolution(data, tickResolution)] ?? TOOLTIP_FORMAT_BY_RESOLUTION.day;
4381
- return (timestamp) => new Date(timestamp).toLocaleString(void 0, format);
4928
+ return createDateFormatter(format, formatting);
4382
4929
  };
4383
4930
  const identity = (label) => label;
4384
4931
  const byBucket = (format) => (bucket) => typeof bucket === "string" ? bucket : format(Number(bucket));
@@ -4429,6 +4976,7 @@ const getGroupPadding = (scale) => {
4429
4976
  */
4430
4977
  function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = ALL_RENDERED) {
4431
4978
  const stableOptions = useDeepMemo(options);
4979
+ const formatting = useChartFormatting();
4432
4980
  const axisConfig = (0, react.useMemo)(() => {
4433
4981
  const { labelOverflow: xLabelOverflow, tickResolution: xTickResolution, tickFormat: xTickFormat, ...xAxisOptions } = stableOptions.axis?.x || {};
4434
4982
  const { labelOverflow: yLabelOverflow, tickResolution: yTickResolution, tickFormat: yTickFormat, ...yAxisOptions } = stableOptions.axis?.y || {};
@@ -4455,9 +5003,9 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
4455
5003
  zero: false
4456
5004
  };
4457
5005
  const hasLabels = Boolean(data?.[0]?.data?.[0]?.label);
4458
- const timeTickFormatter = hasLabels ? null : getFormatter(data, tickResolution);
5006
+ const timeTickFormatter = hasLabels ? null : getFormatter(data, tickResolution, formatting);
4459
5007
  const labelFormatter = timeTickFormatter ? byBucket(timeTickFormatter) : identity;
4460
- const tooltipDatumFormatter = hasLabels ? labelFormatter : byBucket(getTooltipFormatter(data, tickResolution));
5008
+ const tooltipDatumFormatter = hasLabels ? labelFormatter : byBucket(getTooltipFormatter(data, tickResolution, formatting));
4461
5009
  const valueFormatter = _automattic_number_formatters.formatNumberCompact;
4462
5010
  const bandDomain = timeTickFormatter ? getBandDomain(data, isSeriesRendered) : null;
4463
5011
  const valueAccessor = (d) => {
@@ -4493,7 +5041,8 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
4493
5041
  }, [
4494
5042
  data,
4495
5043
  tickResolution,
4496
- isSeriesRendered
5044
+ isSeriesRendered,
5045
+ formatting
4497
5046
  ]);
4498
5047
  return (0, react.useMemo)(() => {
4499
5048
  const { xTickFormat: defaultXTickFormat, yTickFormat: defaultYTickFormat, tooltipLabelFormatter: defaultTooltipLabelFormatter, xAccessor, yAccessor, gridVisibility, xScale: baseXScale, yScale: baseYScale } = defaultOptions[horizontal ? "horizontal" : "vertical"];
@@ -4617,11 +5166,6 @@ function computeComparisonRect(params) {
4617
5166
  };
4618
5167
  }
4619
5168
  /**
4620
- * Fraction of each per-series step left as a gap between bars within a single tick.
4621
- * Larger = more space between adjacent series; the shadow spans `1 - COMPARISON_INNER_GAP` of the step.
4622
- */
4623
- const COMPARISON_INNER_GAP = .1;
4624
- /**
4625
5169
  * Upper clamp on the computed group padding, so bars can never collapse to zero width
4626
5170
  * even at very large `widthFactor` values.
4627
5171
  */
@@ -4709,7 +5253,7 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ (0, react_jsx_runtime
4709
5253
  className: bar_chart_module_default["bar-chart__tooltip-row"],
4710
5254
  children: (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("%1$s: %2$s", "jetpack-charts"), label, value)
4711
5255
  });
4712
- 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" }) => {
5256
+ 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 }) => {
4713
5257
  const legendInteractive = legend.interactive ?? false;
4714
5258
  const legendCollapseGroups = legend.collapseGroups ?? false;
4715
5259
  const horizontal = orientation === "horizontal";
@@ -4736,14 +5280,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4736
5280
  const [selectedIndex, setSelectedIndex] = (0, react.useState)(void 0);
4737
5281
  const [isNavigating, setIsNavigating] = (0, react.useState)(false);
4738
5282
  const primarySeriesForNav = dataWithVisibleZeros.filter((s) => s.options?.type !== "comparison");
4739
- const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
4740
- selectedIndex,
4741
- setSelectedIndex,
4742
- isNavigating,
4743
- setIsNavigating,
4744
- chartRef,
4745
- totalPoints: Math.max(0, ...primarySeriesForNav.map((s) => s.data?.length || 0)) * primarySeriesForNav.length
4746
- });
5283
+ const totalPoints = Math.max(0, ...primarySeriesForNav.map((s) => s.data?.length || 0)) * primarySeriesForNav.length;
4747
5284
  const seriesWithVisibility = (0, react.useMemo)(() => dataWithVisibleZeros.map((series, index) => ({
4748
5285
  series,
4749
5286
  index,
@@ -4755,6 +5292,27 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4755
5292
  const primaryEntries = (0, react.useMemo)(() => seriesWithVisibility.filter(({ isVisible, series }) => isVisible && series.options?.type !== "comparison"), [seriesWithVisibility]);
4756
5293
  const primaryKeys = (0, react.useMemo)(() => primaryEntries.map(({ series }) => series.label), [primaryEntries]);
4757
5294
  const primarySeries = (0, react.useMemo)(() => primaryEntries.map(({ series }) => series), [primaryEntries]);
5295
+ const activateSelectedBar = (0, react.useCallback)((index) => {
5296
+ const primaryCount = primaryEntries.length;
5297
+ if (!primaryCount) return;
5298
+ const dataPointIndex = Math.floor(index / primaryCount);
5299
+ const series = primaryEntries[index % primaryCount]?.series;
5300
+ const datum = series?.data[dataPointIndex];
5301
+ if (series && datum) onDatumActivate?.({
5302
+ datum,
5303
+ index: dataPointIndex,
5304
+ key: series.label
5305
+ });
5306
+ }, [primaryEntries, onDatumActivate]);
5307
+ const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
5308
+ selectedIndex,
5309
+ setSelectedIndex,
5310
+ isNavigating,
5311
+ setIsNavigating,
5312
+ chartRef,
5313
+ totalPoints,
5314
+ onActivate: activateSelectedBar
5315
+ });
4758
5316
  const comparisonEntries = (0, react.useMemo)(() => {
4759
5317
  const primaryByGroup = new Map(primaryEntries.map(({ series, index }) => [series.group, {
4760
5318
  label: series.label,
@@ -4791,7 +5349,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4791
5349
  const groupPadding = (0, react.useMemo)(() => {
4792
5350
  const basePadding = chartOptions.barGroup.padding;
4793
5351
  if (!comparisonWidthFactor || comparisonWidthFactor <= 1) return basePadding;
4794
- const p = 1 - (1 - COMPARISON_INNER_GAP) / comparisonWidthFactor;
5352
+ const p = 1 - .9 / comparisonWidthFactor;
4795
5353
  return Math.min(Math.max(p, basePadding), MAX_GROUP_PADDING);
4796
5354
  }, [chartOptions.barGroup.padding, comparisonWidthFactor]);
4797
5355
  const { xScale, yScale } = (0, react.useMemo)(() => {
@@ -4929,15 +5487,17 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4929
5487
  chartId
4930
5488
  ]);
4931
5489
  const error = validateData$2(dataSorted);
5490
+ const isDataValid = !error;
5491
+ const chartMetadata = (0, react.useMemo)(() => ({
5492
+ orientation,
5493
+ withPatterns
5494
+ }), [orientation, withPatterns]);
4932
5495
  useChartRegistration({
4933
5496
  chartId,
4934
5497
  legendItems,
4935
5498
  chartType: "bar",
4936
- isDataValid: !error,
4937
- metadata: (0, react.useMemo)(() => ({
4938
- orientation,
4939
- withPatterns
4940
- }), [orientation, withPatterns])
5499
+ isDataValid,
5500
+ metadata: chartMetadata
4941
5501
  });
4942
5502
  const prefersReducedMotion = usePrefersReducedMotion();
4943
5503
  if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -4991,6 +5551,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
4991
5551
  onBlur: onChartBlur,
4992
5552
  children: chartHeight > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4993
5553
  ref: chartRef,
5554
+ className: xy_plot_module_default["xy-plot"],
4994
5555
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_xychart.XYChart, {
4995
5556
  theme,
4996
5557
  width,
@@ -5002,6 +5563,8 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
5002
5563
  xScale,
5003
5564
  yScale,
5004
5565
  horizontal,
5566
+ onPointerDown,
5567
+ onPointerUp,
5005
5568
  pointerEventsDataKey: "nearest",
5006
5569
  children: [
5007
5570
  !allSeriesHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Grid, {
@@ -5156,7 +5719,26 @@ const BarListChartInternal = ({ data, width, height, options = {}, margin = {
5156
5719
  right: 20,
5157
5720
  bottom: 0,
5158
5721
  top: 0
5159
- }, ...rest }) => {
5722
+ }, className, ...rest }) => {
5723
+ const { getElementStyles } = useGlobalChartsContext();
5724
+ const tintedData = (0, react.useMemo)(() => {
5725
+ if (data.length > 1) return data;
5726
+ return data.map((series, index) => {
5727
+ if (series.options?.stroke) return series;
5728
+ const { color } = getElementStyles({
5729
+ data: series,
5730
+ index
5731
+ });
5732
+ if (!isValidHexColor(color)) return series;
5733
+ return {
5734
+ ...series,
5735
+ options: {
5736
+ ...series.options,
5737
+ stroke: lightenHexColor(color, .6)
5738
+ }
5739
+ };
5740
+ });
5741
+ }, [data, getElementStyles]);
5160
5742
  const chartOptions = (0, react.useMemo)(() => {
5161
5743
  const isMultiSeries = data.length > 1;
5162
5744
  const defaultYScale = {
@@ -5189,7 +5771,8 @@ const BarListChartInternal = ({ data, width, height, options = {}, margin = {
5189
5771
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BarChart, {
5190
5772
  orientation: "horizontal",
5191
5773
  gridVisibility: "none",
5192
- data,
5774
+ className: (0, clsx.default)("bar-list-chart", className),
5775
+ data: tintedData,
5193
5776
  width,
5194
5777
  height,
5195
5778
  margin,
@@ -5225,6 +5808,9 @@ const BarListChartResponsive = withResponsive(BarListChart);
5225
5808
  var conversion_funnel_chart_module_default = {
5226
5809
  "bar-container": "a8ccharts-mGEVca-bar-container",
5227
5810
  "change-indicator": "a8ccharts-mGEVca-change-indicator",
5811
+ "change-indicator--negative": "a8ccharts-mGEVca-change-indicator--negative",
5812
+ "change-indicator--positive": "a8ccharts-mGEVca-change-indicator--positive",
5813
+ "conversion-funnel-chart": "a8ccharts-mGEVca-conversion-funnel-chart",
5228
5814
  "conversion-funnel-chart--loading": "a8ccharts-mGEVca-conversion-funnel-chart--loading",
5229
5815
  "empty-state": "a8ccharts-mGEVca-empty-state",
5230
5816
  "funnel-bar": "a8ccharts-mGEVca-funnel-bar",
@@ -5305,23 +5891,14 @@ const useFunnelSelection = (hideTooltip) => {
5305
5891
  */
5306
5892
  const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loading = false, animation, className, chartId: providedChartId, height, style, renderStepLabel, renderStepRate, renderMainMetric, renderTooltip }) => {
5307
5893
  const chartId = useChartId(providedChartId);
5308
- const { conversionFunnelChart: conversionFunnelChartSettings } = useGlobalChartsTheme();
5309
5894
  const { getElementStyles, isColorPaletteResolved } = useGlobalChartsContext();
5310
5895
  const chartRef = (0, react.useRef)(null);
5311
5896
  const selectedBarRef = (0, react.useRef)(null);
5312
5897
  const [scopeNode, setScopeNode] = (0, react.useState)(null);
5313
5898
  const { tooltipData, tooltipLeft, tooltipTop, tooltipOpen, showTooltip, hideTooltip } = (0, _visx_tooltip.useTooltip)();
5314
5899
  const { handleBarClick, handleBarKeyDown, clearSelection, getStepState } = useFunnelSelection(hideTooltip);
5315
- const { containerRef: portalContainerRef, TooltipInPortal, containerBounds } = (0, _visx_tooltip.useTooltipInPortal)({
5316
- detectBounds: true,
5317
- scroll: true
5318
- });
5319
- const portalContainerRefRef = (0, react.useRef)(portalContainerRef);
5320
- (0, react.useEffect)(() => {
5321
- portalContainerRefRef.current = portalContainerRef;
5322
- }, [portalContainerRef]);
5900
+ const standaloneScopeClass = useStandaloneScopeClass();
5323
5901
  const setChartRef = (0, react.useCallback)((node) => {
5324
- portalContainerRefRef.current(node);
5325
5902
  chartRef.current = node;
5326
5903
  setScopeNode(node);
5327
5904
  }, []);
@@ -5338,30 +5915,22 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5338
5915
  });
5339
5916
  }, [showTooltip]);
5340
5917
  const getMouseTooltipCoords = (0, react.useCallback)((event) => {
5341
- if (containerBounds.width === 0 || containerBounds.height === 0) return null;
5918
+ const bounds = chartRef.current?.getBoundingClientRect();
5919
+ if (!bounds) return null;
5342
5920
  return {
5343
- x: event.clientX - containerBounds.left,
5344
- y: event.clientY - containerBounds.top
5921
+ x: event.clientX - bounds.left,
5922
+ y: event.clientY - bounds.top
5345
5923
  };
5346
- }, [
5347
- containerBounds.width,
5348
- containerBounds.height,
5349
- containerBounds.left,
5350
- containerBounds.top
5351
- ]);
5924
+ }, []);
5352
5925
  const getKeyboardTooltipCoords = (0, react.useCallback)((event) => {
5353
- if (containerBounds.width === 0 || containerBounds.height === 0) return null;
5926
+ const bounds = chartRef.current?.getBoundingClientRect();
5927
+ if (!bounds) return null;
5354
5928
  const rect = event.currentTarget.getBoundingClientRect();
5355
5929
  return {
5356
- x: rect.left + rect.width / 2 - containerBounds.left,
5357
- y: rect.top - containerBounds.top
5930
+ x: rect.left + rect.width / 2 - bounds.left,
5931
+ y: rect.top - bounds.top
5358
5932
  };
5359
- }, [
5360
- containerBounds.width,
5361
- containerBounds.height,
5362
- containerBounds.left,
5363
- containerBounds.top
5364
- ]);
5933
+ }, []);
5365
5934
  const handleStepInteraction = (0, react.useCallback)((step, event, interactionType) => {
5366
5935
  selectedBarRef.current = event.currentTarget;
5367
5936
  const { isClicked } = getStepState(step.id);
@@ -5422,19 +5991,14 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5422
5991
  };
5423
5992
  }, [clearSelectionAndRef]);
5424
5993
  const resolvedHeight = height ?? style?.height ?? "100%";
5425
- const { primaryColor, backgroundColor, positiveChangeColor, negativeChangeColor } = conversionFunnelChartSettings;
5426
- const { color: barColor } = getElementStyles ? getElementStyles({
5427
- index: 0,
5428
- overrideColor: primaryColor
5429
- }) : { color: primaryColor || "#000000" };
5430
- const changeColor = changeIndicator?.startsWith("+") ? positiveChangeColor : negativeChangeColor;
5431
- const barBackgroundColor = backgroundColor || hexToRgba(barColor, .08) || "rgba(0, 0, 0, 0.08)";
5994
+ const { color: barColor } = getElementStyles ? getElementStyles({ index: 0 }) : { color: "#000000" };
5995
+ const isPositiveChange = changeIndicator?.startsWith("+");
5996
+ const changeColor = isPositiveChange ? CATALOG_POINTERS.trendUp : CATALOG_POINTERS.trendDown;
5432
5997
  const renderDefaultMainMetric = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5433
5998
  className: conversion_funnel_chart_module_default["main-rate"],
5434
5999
  children: formatPercentage(mainRate)
5435
6000
  }), changeIndicator && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5436
- className: conversion_funnel_chart_module_default["change-indicator"],
5437
- style: { color: changeColor },
6001
+ className: (0, clsx.default)(conversion_funnel_chart_module_default["change-indicator"], conversion_funnel_chart_module_default[isPositiveChange ? "change-indicator--positive" : "change-indicator--negative"]),
5438
6002
  children: changeIndicator
5439
6003
  })] });
5440
6004
  const renderDefaultTooltip = (step) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
@@ -5450,20 +6014,21 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5450
6014
  })]
5451
6015
  });
5452
6016
  const isDataValid = Boolean(steps && steps.length > 0);
6017
+ const chartMetadata = (0, react.useMemo)(() => ({
6018
+ mainRate,
6019
+ changeIndicator,
6020
+ stepsCount: steps?.length || 0
6021
+ }), [
6022
+ mainRate,
6023
+ changeIndicator,
6024
+ steps?.length
6025
+ ]);
5453
6026
  useChartRegistration({
5454
6027
  chartId,
5455
6028
  legendItems: [],
5456
6029
  chartType: "conversion-funnel",
5457
6030
  isDataValid,
5458
- metadata: (0, react.useMemo)(() => ({
5459
- mainRate,
5460
- changeIndicator,
5461
- stepsCount: steps?.length || 0
5462
- }), [
5463
- mainRate,
5464
- changeIndicator,
5465
- steps?.length
5466
- ])
6031
+ metadata: chartMetadata
5467
6032
  });
5468
6033
  const prefersReducedMotion = usePrefersReducedMotion();
5469
6034
  if (!isDataValid) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
@@ -5471,7 +6036,7 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5471
6036
  align: "center",
5472
6037
  justify: "center",
5473
6038
  ref: setScopeNode,
5474
- className: (0, clsx.default)(conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
6039
+ className: (0, clsx.default)("conversion-funnel-chart", conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
5475
6040
  style: {
5476
6041
  ...style,
5477
6042
  height: resolvedHeight
@@ -5482,16 +6047,16 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5482
6047
  })
5483
6048
  });
5484
6049
  const maxRate = Math.max(...steps.map((step) => step.rate));
5485
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
6050
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
5486
6051
  direction: "column",
5487
6052
  gap: "xl",
5488
6053
  ref: setChartRef,
5489
- className: (0, clsx.default)(conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
6054
+ className: (0, clsx.default)("conversion-funnel-chart", conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
5490
6055
  style: {
5491
6056
  ...style,
5492
6057
  height: resolvedHeight
5493
6058
  },
5494
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartScopeContext.Provider, {
6059
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartScopeContext.Provider, {
5495
6060
  value: scopeNode,
5496
6061
  children: [renderMainMetric ? renderMainMetric({
5497
6062
  mainRate,
@@ -5543,7 +6108,6 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5543
6108
  role: "button",
5544
6109
  tabIndex: isBlurred ? -1 : 0,
5545
6110
  "aria-label": step.label,
5546
- style: { backgroundColor: barBackgroundColor },
5547
6111
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5548
6112
  className: (0, clsx.default)(conversion_funnel_chart_module_default["funnel-bar"], { [conversion_funnel_chart_module_default["funnel-bar--animated"]]: animation && !loading && !prefersReducedMotion }),
5549
6113
  style: {
@@ -5555,23 +6119,23 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
5555
6119
  }, step.id);
5556
6120
  })
5557
6121
  })]
5558
- })
5559
- }), tooltipOpen && tooltipData && (() => {
5560
- const tooltipContent = renderTooltip ? renderTooltip({
5561
- step: tooltipData,
5562
- index: steps.findIndex((s) => s.id === tooltipData.id),
5563
- top: tooltipTop,
5564
- left: tooltipLeft,
5565
- className: conversion_funnel_chart_module_default["tooltip-wrapper"]
5566
- }) : renderDefaultTooltip(tooltipData);
5567
- if (!tooltipContent) return null;
5568
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipInPortal, {
5569
- top: tooltipTop,
5570
- left: tooltipLeft,
5571
- className: (0, clsx.default)("a8c-charts-scope", conversion_funnel_chart_module_default["tooltip-wrapper"]),
5572
- children: tooltipContent
5573
- }, Math.random());
5574
- })()] });
6122
+ }), tooltipOpen && tooltipData && (() => {
6123
+ const tooltipContent = renderTooltip ? renderTooltip({
6124
+ step: tooltipData,
6125
+ index: steps.findIndex((s) => s.id === tooltipData.id),
6126
+ top: tooltipTop,
6127
+ left: tooltipLeft,
6128
+ className: conversion_funnel_chart_module_default["tooltip-wrapper"]
6129
+ }) : renderDefaultTooltip(tooltipData);
6130
+ if (!tooltipContent) return null;
6131
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundedTooltip, {
6132
+ top: tooltipTop,
6133
+ left: tooltipLeft,
6134
+ className: (0, clsx.default)(standaloneScopeClass, conversion_funnel_chart_module_default["tooltip-wrapper"]),
6135
+ children: tooltipContent
6136
+ });
6137
+ })()]
6138
+ });
5575
6139
  };
5576
6140
  /**
5577
6141
  * ConversionFunnelChart component with provider wrapper
@@ -5717,7 +6281,7 @@ function normalizeGeoChartError(eventArgs) {
5717
6281
  * @return A React component displaying an interactive map with data visualization
5718
6282
  */
5719
6283
  const GeoChartInternal = ({ className, data, width, height, region = "world", resolution = "countries", onError, renderPlaceholder }) => {
5720
- const { getElementStyles, theme: { geoChart: { featureFillColor }, backgroundColor } } = useGlobalChartsContext();
6284
+ const { getElementStyles } = useGlobalChartsContext();
5721
6285
  const scopeElement = useChartScopeElement();
5722
6286
  const containerRef = (0, react.useRef)(null);
5723
6287
  const reportedErrorIdsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
@@ -5751,8 +6315,8 @@ const GeoChartInternal = ({ className, data, width, height, region = "world", re
5751
6315
  });
5752
6316
  const fullColorHex = getElementStyles({ index: 0 }).color;
5753
6317
  const lightColorHex = lightenHexColor(fullColorHex, .8);
5754
- const backgroundColorHex = normalizeColorToHex(backgroundColor, scopeElement, resolveCssVariable) || DEFAULT_BACKGROUND_COLOR;
5755
- const defaultFillColorHex = normalizeColorToHex(featureFillColor, scopeElement, resolveCssVariable) || DEFAULT_FEATURE_FILL_COLOR;
6318
+ const backgroundColorHex = normalizeColorToHex(CATALOG_POINTERS.background, scopeElement, resolveCssVariable) || DEFAULT_BACKGROUND_COLOR;
6319
+ const defaultFillColorHex = normalizeColorToHex(CATALOG_POINTERS.surfaceSecondary, scopeElement, resolveCssVariable) || DEFAULT_FEATURE_FILL_COLOR;
5756
6320
  const sanitizedData = (0, react.useMemo)(() => {
5757
6321
  if (data.length === 0) return {
5758
6322
  data,
@@ -5813,8 +6377,7 @@ const GeoChartInternal = ({ className, data, width, height, region = "world", re
5813
6377
  className: (0, clsx.default)("geo-chart", geo_chart_module_default.container, className),
5814
6378
  style: {
5815
6379
  width,
5816
- height,
5817
- backgroundColor
6380
+ height
5818
6381
  },
5819
6382
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_google_charts.Chart, {
5820
6383
  chartType: "GeoChart",
@@ -5864,6 +6427,7 @@ var heatmap_chart_module_default = {
5864
6427
  "heatmap-chart__grid": "a8ccharts-O3YMOW-heatmap-chart__grid",
5865
6428
  "heatmap-chart__grid--compact": "a8ccharts-O3YMOW-heatmap-chart__grid--compact",
5866
6429
  "heatmap-chart__grid--height-capped": "a8ccharts-O3YMOW-heatmap-chart__grid--height-capped",
6430
+ "heatmap-chart__legend-label": "a8ccharts-O3YMOW-heatmap-chart__legend-label",
5867
6431
  "heatmap-chart__legend-swatch": "a8ccharts-O3YMOW-heatmap-chart__legend-swatch",
5868
6432
  "heatmap-chart__row": "a8ccharts-O3YMOW-heatmap-chart__row",
5869
6433
  "heatmap-chart__row-label": "a8ccharts-O3YMOW-heatmap-chart__row-label",
@@ -6006,7 +6570,7 @@ const HeatmapContext = (0, react.createContext)(null);
6006
6570
  //#region src/charts/heatmap-chart/private/heatmap-legend.tsx
6007
6571
  const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6008
6572
  const context = (0, react.useContext)(HeatmapContext);
6009
- const { legend, backgroundColor } = useGlobalChartsTheme();
6573
+ const { legend } = useGlobalChartsTheme();
6010
6574
  if (!context) return null;
6011
6575
  const { primaryColorHex } = context;
6012
6576
  const labelStyle = legend.labelStyles;
@@ -6017,6 +6581,7 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6017
6581
  children: [
6018
6582
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Text, {
6019
6583
  variant: "body-sm",
6584
+ className: heatmap_chart_module_default["heatmap-chart__legend-label"],
6020
6585
  style: labelStyle,
6021
6586
  children: lessLabel ?? (0, _wordpress_i18n.__)("Less", "jetpack-charts")
6022
6587
  }),
@@ -6030,7 +6595,6 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6030
6595
  className: heatmap_chart_module_default["heatmap-chart__legend-swatch"],
6031
6596
  style: {
6032
6597
  "--a8c-charts-color-heatmap-primary": primaryColorHex,
6033
- "--a8c-charts-color-heatmap-background": backgroundColor,
6034
6598
  "--a8c-charts-heatmap-cell-intensity": intensity
6035
6599
  }
6036
6600
  }, index);
@@ -6038,6 +6602,7 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6038
6602
  }),
6039
6603
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Text, {
6040
6604
  variant: "body-sm",
6605
+ className: heatmap_chart_module_default["heatmap-chart__legend-label"],
6041
6606
  style: labelStyle,
6042
6607
  children: moreLabel ?? (0, _wordpress_i18n.__)("More", "jetpack-charts")
6043
6608
  })
@@ -6055,19 +6620,16 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6055
6620
  const { nonLegendChildren } = useChartChildren(children, "HeatmapChart");
6056
6621
  const [selectedIndex, setSelectedIndex] = (0, react.useState)();
6057
6622
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, showTooltip, hideTooltip } = (0, _visx_tooltip.useTooltip)();
6058
- const { containerRef, containerBounds, TooltipInPortal } = (0, _visx_tooltip.useTooltipInPortal)({
6059
- detectBounds: true,
6060
- scroll: true
6061
- });
6062
- const containerBoundsRef = (0, react.useRef)(containerBounds);
6063
- containerBoundsRef.current = containerBounds;
6623
+ const standaloneScopeClass = useStandaloneScopeClass();
6624
+ const containerRef = (0, react.useRef)(null);
6625
+ const getTooltipOrigin = (0, react.useCallback)(() => containerRef.current?.closest(`[data-chart-id="heatmap-chart-${chartId}"]`)?.getBoundingClientRect() ?? null, [chartId]);
6064
6626
  const { color: primaryColorHex } = getElementStyles({
6065
6627
  index: 0,
6066
- overrideColor: primaryColor || heatmapChartSettings.primaryColor
6628
+ overrideColor: primaryColor
6067
6629
  });
6068
- const chartBackgroundHex = normalizeColorToHex(theme.backgroundColor, scopeElement, resolveCssVariable);
6630
+ const chartBackgroundHex = normalizeColorToHex(CATALOG_POINTERS.background, scopeElement, resolveCssVariable);
6069
6631
  const primaryHex = normalizeColorToHex(primaryColorHex);
6070
- const cellHasLightText = (intensity) => isValidHexColor(primaryHex) && isValidHexColor(chartBackgroundHex) && prefersLightText(mixHexColors(primaryHex, chartBackgroundHex, 1 - (CELL_MIX_FLOOR + (1 - CELL_MIX_FLOOR) * intensity)));
6632
+ const cellHasLightText = (intensity) => isValidHexColor(primaryHex) && isValidHexColor(chartBackgroundHex) && prefersLightText(mixHexColors(primaryHex, chartBackgroundHex, 1 - (CELL_MIX_FLOOR + .85 * intensity)));
6071
6633
  const extent = (0, react.useMemo)(() => getValueExtent(data), [data]);
6072
6634
  const heatmapContext = (0, react.useMemo)(() => ({
6073
6635
  extent,
@@ -6141,19 +6703,21 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6141
6703
  ]);
6142
6704
  const handleCellMouseMove = (0, react.useCallback)((event) => {
6143
6705
  if (!withTooltips) return;
6706
+ const origin = getTooltipOrigin();
6707
+ if (!origin) return;
6144
6708
  const target = event.currentTarget;
6145
6709
  const columnIndex = Number(target.dataset.column);
6146
6710
  const rowIndex = Number(target.dataset.row);
6147
- const bounds = containerBoundsRef.current;
6148
6711
  showTooltip({
6149
- tooltipLeft: event.clientX - bounds.left,
6150
- tooltipTop: event.clientY - bounds.top,
6712
+ tooltipLeft: event.clientX - origin.left,
6713
+ tooltipTop: event.clientY - origin.top,
6151
6714
  tooltipData: buildTooltipData(columnIndex, rowIndex)
6152
6715
  });
6153
6716
  }, [
6154
6717
  withTooltips,
6155
6718
  showTooltip,
6156
- buildTooltipData
6719
+ buildTooltipData,
6720
+ getTooltipOrigin
6157
6721
  ]);
6158
6722
  const handleCellMouseLeave = (0, react.useCallback)(() => {
6159
6723
  if (withTooltips && selectedIndex === void 0) hideTooltip();
@@ -6164,13 +6728,14 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6164
6728
  ]);
6165
6729
  (0, react.useEffect)(() => {
6166
6730
  if (!withTooltips || selectedIndex === void 0) return;
6731
+ const origin = getTooltipOrigin();
6732
+ if (!origin) return;
6167
6733
  const col = Math.floor(selectedIndex / rows);
6168
6734
  const row = selectedIndex % rows;
6169
6735
  const rect = (typeof document !== "undefined" ? document.getElementById(`${chartId}-cell-${col}-${row}`) : null)?.getBoundingClientRect();
6170
- const bounds = containerBoundsRef.current;
6171
6736
  showTooltip({
6172
- tooltipLeft: rect ? rect.left + rect.width / 2 - bounds.left : 0,
6173
- tooltipTop: rect ? rect.top + rect.height / 2 - bounds.top : 0,
6737
+ tooltipLeft: rect ? rect.left + rect.width / 2 - origin.left : 0,
6738
+ tooltipTop: rect ? rect.top + rect.height / 2 - origin.top : 0,
6174
6739
  tooltipData: buildTooltipData(col, row)
6175
6740
  });
6176
6741
  }, [
@@ -6179,7 +6744,8 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6179
6744
  rows,
6180
6745
  chartId,
6181
6746
  buildTooltipData,
6182
- showTooltip
6747
+ showTooltip,
6748
+ getTooltipOrigin
6183
6749
  ]);
6184
6750
  const defaultRenderTooltip = (0, react.useCallback)((info) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: info.cellLabel || `${info.columnLabel ?? ""} ${info.rowLabel ?? ""}`.trim() }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: info.value === null ? (0, _wordpress_i18n.__)("No data", "jetpack-charts") : (0, _automattic_number_formatters.formatNumber)(info.value) })] }), []);
6185
6751
  if (!columns || !rows) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Center, {
@@ -6197,7 +6763,6 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6197
6763
  const rowTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
6198
6764
  const gridStyle = {
6199
6765
  "--a8c-charts-color-heatmap-primary": primaryColorHex,
6200
- "--a8c-charts-color-heatmap-background": theme.backgroundColor,
6201
6766
  gridTemplateColumns: `auto repeat(${columns}, ${columnTrack})`,
6202
6767
  gridTemplateRows: `auto repeat(${rows}, ${rowTrack})`
6203
6768
  };
@@ -6295,11 +6860,11 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6295
6860
  })]
6296
6861
  }, `row-${rowIndex}`);
6297
6862
  })]
6298
- }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipInPortal, {
6863
+ }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundedTooltip, {
6299
6864
  top: tooltipTop,
6300
6865
  left: tooltipLeft,
6301
6866
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6302
- className: "a8c-charts-scope",
6867
+ className: standaloneScopeClass,
6303
6868
  role: "tooltip",
6304
6869
  tabIndex: -1,
6305
6870
  children: (renderTooltip ?? defaultRenderTooltip)(tooltipData)
@@ -6323,7 +6888,7 @@ const HeatmapChartResponsiveInner = (props) => /* @__PURE__ */ (0, react_jsx_run
6323
6888
  HeatmapChartResponsiveInner.displayName = "HeatmapChart";
6324
6889
  const HeatmapChartResponsive = attachSubComponents(withResponsive(HeatmapChartResponsiveInner), { Legend: HeatmapLegend });
6325
6890
  //#endregion
6326
- //#region ../../../node_modules/.pnpm/is-plain-object@5.0.0/node_modules/is-plain-object/dist/is-plain-object.mjs
6891
+ //#region ../../../node_modules/.pnpm/is-plain-object@5.1.0/node_modules/is-plain-object/dist/is-plain-object.mjs
6327
6892
  /*!
6328
6893
  * is-plain-object <https://github.com/jonschlinkert/is-plain-object>
6329
6894
  *
@@ -6686,7 +7251,7 @@ var require_dist = /* @__PURE__ */ __commonJSMin(((exports) => {
6686
7251
  tslib_1.__exportStar(require_dist$1(), exports);
6687
7252
  }));
6688
7253
  //#endregion
6689
- //#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
7254
+ //#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
6690
7255
  function useUpdateEffect(effect, deps) {
6691
7256
  const mountedRef = (0, react.useRef)(false);
6692
7257
  (0, react.useEffect)(() => {
@@ -6718,10 +7283,11 @@ var StyleSheet = /*#__PURE__*/ function() {
6718
7283
  var _this = this;
6719
7284
  this._insertTag = function(tag) {
6720
7285
  var before;
6721
- if (_this.tags.length === 0) if (_this.insertionPoint) before = _this.insertionPoint.nextSibling;
6722
- else if (_this.prepend) before = _this.container.firstChild;
6723
- else before = _this.before;
6724
- else before = _this.tags[_this.tags.length - 1].nextSibling;
7286
+ if (_this.tags.length === 0) {
7287
+ if (_this.insertionPoint) before = _this.insertionPoint.nextSibling;
7288
+ else if (_this.prepend) before = _this.container.firstChild;
7289
+ else before = _this.before;
7290
+ } else before = _this.tags[_this.tags.length - 1].nextSibling;
6725
7291
  _this.container.insertBefore(tag, before);
6726
7292
  _this.tags.push(tag);
6727
7293
  };
@@ -7040,9 +7606,7 @@ function delimiter(type) {
7040
7606
  case 40:
7041
7607
  if (type === 41) delimiter(type);
7042
7608
  break;
7043
- case 92:
7044
- next();
7045
- break;
7609
+ case 92: next();
7046
7610
  }
7047
7611
  return position;
7048
7612
  }
@@ -8065,14 +8629,12 @@ function handleInterpolation(mergedProps, registered, interpolation) {
8065
8629
  return serializedStyles.styles + ";";
8066
8630
  }
8067
8631
  return createStringFromObject(mergedProps, registered, interpolation);
8068
- case "function":
8069
- if (mergedProps !== void 0) {
8070
- var previousCursor = cursor;
8071
- var result = interpolation(mergedProps);
8072
- cursor = previousCursor;
8073
- return handleInterpolation(mergedProps, registered, result);
8074
- }
8075
- break;
8632
+ case "function": if (mergedProps !== void 0) {
8633
+ var previousCursor = cursor;
8634
+ var result = interpolation(mergedProps);
8635
+ cursor = previousCursor;
8636
+ return handleInterpolation(mergedProps, registered, result);
8637
+ }
8076
8638
  }
8077
8639
  var asString = interpolation;
8078
8640
  if (registered == null) return asString;
@@ -8327,7 +8889,7 @@ _createEmotion.css;
8327
8889
  _createEmotion.sheet;
8328
8890
  _createEmotion.cache;
8329
8891
  //#endregion
8330
- //#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
8892
+ //#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
8331
8893
  var isSerializedStyles = (o) => typeof o !== "undefined" && o !== null && ["name", "styles"].every((p) => typeof o[p] !== "undefined");
8332
8894
  var useCx = () => {
8333
8895
  const cache = __unsafe_useEmotionCache();
@@ -8434,7 +8996,7 @@ function memize(fn, options) {
8434
8996
  return memoized;
8435
8997
  }
8436
8998
  //#endregion
8437
- //#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
8999
+ //#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
8438
9000
  var white = "#fff";
8439
9001
  var GRAY = {
8440
9002
  900: "#1e1e1e",
@@ -8513,7 +9075,7 @@ var COLORS = Object.freeze({
8513
9075
  ui: UI
8514
9076
  });
8515
9077
  //#endregion
8516
- //#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
9078
+ //#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
8517
9079
  var CONTROL_HEIGHT = "36px";
8518
9080
  var CONTROL_PROPS = {
8519
9081
  controlPaddingX: 12,
@@ -8528,9 +9090,6 @@ var CONTROL_PROPS = {
8528
9090
  };
8529
9091
  var config_values_default = Object.assign({}, CONTROL_PROPS, {
8530
9092
  colorDivider: "rgba(0, 0, 0, 0.1)",
8531
- colorScrollbarThumb: "rgba(0, 0, 0, 0.2)",
8532
- colorScrollbarThumbHover: "rgba(0, 0, 0, 0.5)",
8533
- colorScrollbarTrack: "rgba(0, 0, 0, 0.04)",
8534
9093
  elevationIntensity: 1,
8535
9094
  radiusXSmall: "1px",
8536
9095
  radiusSmall: "2px",
@@ -8541,7 +9100,6 @@ var config_values_default = Object.assign({}, CONTROL_PROPS, {
8541
9100
  borderWidth: "1px",
8542
9101
  borderWidthFocus: "1.5px",
8543
9102
  borderWidthTab: "4px",
8544
- spinnerSize: 16,
8545
9103
  fontSize: "13px",
8546
9104
  fontSizeH1: "calc(2.44 * 13px)",
8547
9105
  fontSizeH2: "calc(1.95 * 13px)",
@@ -8618,10 +9176,10 @@ var require_es6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8618
9176
  };
8619
9177
  }));
8620
9178
  //#endregion
8621
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.52.0/node_modules/@wordpress/warning/build-module/utils.mjs
9179
+ //#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/utils.mjs
8622
9180
  var logged = /* @__PURE__ */ new Set();
8623
9181
  //#endregion
8624
- //#region ../../../node_modules/.pnpm/@wordpress+warning@3.52.0/node_modules/@wordpress/warning/build-module/index.mjs
9182
+ //#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/index.mjs
8625
9183
  function isDev() {
8626
9184
  return globalThis.SCRIPT_DEBUG === true;
8627
9185
  }
@@ -8635,7 +9193,7 @@ function warning(message) {
8635
9193
  logged.add(message);
8636
9194
  }
8637
9195
  //#endregion
8638
- //#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
9196
+ //#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
8639
9197
  var import_es6 = /* @__PURE__ */ __toESM(require_es6(), 1);
8640
9198
  var ComponentsContext = (0, react.createContext)(
8641
9199
  /** @type {Record<string, any>} */
@@ -8662,19 +9220,19 @@ var BaseContextSystemProvider = ({ children, value }) => {
8662
9220
  };
8663
9221
  (0, react.memo)(BaseContextSystemProvider);
8664
9222
  //#endregion
8665
- //#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
9223
+ //#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
8666
9224
  var COMPONENT_NAMESPACE = "data-wp-component";
8667
9225
  var CONNECTED_NAMESPACE = "data-wp-c16t";
8668
9226
  var CONNECT_STATIC_NAMESPACE = "__contextSystemKey__";
8669
9227
  //#endregion
8670
- //#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
9228
+ //#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
8671
9229
  var import_dist = require_dist();
8672
9230
  function getStyledClassName(namespace) {
8673
9231
  return `components-${(0, import_dist.paramCase)(namespace)}`;
8674
9232
  }
8675
9233
  var getStyledClassNameFromKey = memize(getStyledClassName);
8676
9234
  //#endregion
8677
- //#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
9235
+ //#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
8678
9236
  function contextConnect(Component, namespace) {
8679
9237
  return _contextConnect(Component, namespace, { forwardsRef: true });
8680
9238
  }
@@ -8691,7 +9249,7 @@ function _contextConnect(Component, namespace, options) {
8691
9249
  });
8692
9250
  }
8693
9251
  //#endregion
8694
- //#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
9252
+ //#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
8695
9253
  function getNamespace(componentName) {
8696
9254
  return { [COMPONENT_NAMESPACE]: componentName };
8697
9255
  }
@@ -8699,7 +9257,7 @@ function getConnectedNamespace() {
8699
9257
  return { [CONNECTED_NAMESPACE]: true };
8700
9258
  }
8701
9259
  //#endregion
8702
- //#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
9260
+ //#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
8703
9261
  function useContextSystem(props, namespace) {
8704
9262
  const contextSystemProps = useComponentsContext();
8705
9263
  if (typeof namespace === "undefined") globalThis.SCRIPT_DEBUG === true && warning("useContextSystem: Please provide a namespace");
@@ -8719,7 +9277,7 @@ function useContextSystem(props, namespace) {
8719
9277
  return finalComponentProps;
8720
9278
  }
8721
9279
  //#endregion
8722
- //#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
9280
+ //#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
8723
9281
  var visuallyHidden = {
8724
9282
  border: 0,
8725
9283
  clip: "rect(1px, 1px, 1px, 1px)",
@@ -8735,7 +9293,7 @@ var visuallyHidden = {
8735
9293
  wordBreak: "normal"
8736
9294
  };
8737
9295
  //#endregion
8738
- //#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
9296
+ //#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
8739
9297
  var customAttributeRegExp = /^(data|aria|x)-/i;
8740
9298
  var eventHandlerRegExp = /^on[A-Z]/;
8741
9299
  var svgElementNames = new Set(`animate animateMotion animateTransform circle clipPath defs desc ellipse
@@ -8817,14 +9375,15 @@ function filterIntrinsicElementProps(props, element) {
8817
9375
  }
8818
9376
  function UnforwardedPolymorphicElement({ as, ...props }, ref) {
8819
9377
  const Element = as || "div";
9378
+ const forwardedProps = typeof Element === "string" ? filterIntrinsicElementProps(props, Element) : props;
8820
9379
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Element, {
8821
9380
  ref,
8822
- ...typeof Element === "string" ? filterIntrinsicElementProps(props, Element) : props
9381
+ ...forwardedProps
8823
9382
  });
8824
9383
  }
8825
9384
  var PolymorphicElement = (0, react.forwardRef)(UnforwardedPolymorphicElement);
8826
9385
  //#endregion
8827
- //#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
9386
+ //#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
8828
9387
  function UnforwardedView({ css, ...restProps }, ref) {
8829
9388
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PolymorphicElement, {
8830
9389
  ref,
@@ -8833,7 +9392,7 @@ function UnforwardedView({ css, ...restProps }, ref) {
8833
9392
  }
8834
9393
  var component_default$2 = Object.assign((0, react.forwardRef)(UnforwardedView), { selector: ".components-view" });
8835
9394
  //#endregion
8836
- //#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
9395
+ //#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
8837
9396
  function UnconnectedVisuallyHidden(props, forwardedRef) {
8838
9397
  const { style: styleProp, ...contextProps } = useContextSystem(props, "VisuallyHidden");
8839
9398
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
@@ -8848,7 +9407,7 @@ function UnconnectedVisuallyHidden(props, forwardedRef) {
8848
9407
  }
8849
9408
  var component_default$1 = contextConnect(UnconnectedVisuallyHidden, "VisuallyHidden");
8850
9409
  //#endregion
8851
- //#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
9410
+ //#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
8852
9411
  var breakpoints = [
8853
9412
  "40em",
8854
9413
  "52em",
@@ -8882,7 +9441,7 @@ function useResponsiveValue(values, options = {}) {
8882
9441
  return array[index >= array.length ? array.length - 1 : index];
8883
9442
  }
8884
9443
  //#endregion
8885
- //#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
9444
+ //#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
8886
9445
  var ALIGNMENTS = {
8887
9446
  bottom: {
8888
9447
  alignItems: "flex-end",
@@ -8930,7 +9489,7 @@ function getAlignmentProps(alignment) {
8930
9489
  return alignment ? ALIGNMENTS[alignment] : {};
8931
9490
  }
8932
9491
  //#endregion
8933
- //#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
9492
+ //#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
8934
9493
  function useGrid(props) {
8935
9494
  const { align, alignment, className, columnGap, columns = 2, gap = 3, isInline = false, justify, rowGap, rows, templateColumns, templateRows, ...otherProps } = useContextSystem(props, "Grid");
8936
9495
  const column = useResponsiveValue(Array.isArray(columns) ? columns : [columns]);
@@ -8951,7 +9510,7 @@ function useGrid(props) {
8951
9510
  justifyContent: justify,
8952
9511
  verticalAlign: isInline ? "middle" : void 0,
8953
9512
  ...alignmentProps
8954
- }, process.env.NODE_ENV === "production" ? "" : ";label:gridClasses;", process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImhvb2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBdURzQiIsImZpbGUiOiJob29rLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBFeHRlcm5hbCBkZXBlbmRlbmNpZXNcbiAqL1xuaW1wb3J0IHsgY3NzIH0gZnJvbSAnQGVtb3Rpb24vcmVhY3QnO1xuXG4vKipcbiAqIFdvcmRQcmVzcyBkZXBlbmRlbmNpZXNcbiAqL1xuaW1wb3J0IHsgdXNlTWVtbyB9IGZyb20gJ0B3b3JkcHJlc3MvZWxlbWVudCc7XG5cbi8qKlxuICogSW50ZXJuYWwgZGVwZW5kZW5jaWVzXG4gKi9cbmltcG9ydCB0eXBlIHsgV29yZFByZXNzQ29tcG9uZW50UHJvcHMgfSBmcm9tICcuLi9jb250ZXh0JztcbmltcG9ydCB7IHVzZUNvbnRleHRTeXN0ZW0gfSBmcm9tICcuLi9jb250ZXh0JztcbmltcG9ydCB7IGdldEFsaWdubWVudFByb3BzIH0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQgeyB1c2VSZXNwb25zaXZlVmFsdWUgfSBmcm9tICcuLi91dGlscy91c2UtcmVzcG9uc2l2ZS12YWx1ZSc7XG5pbXBvcnQgQ09ORklHIGZyb20gJy4uL3V0aWxzL2NvbmZpZy12YWx1ZXMnO1xuaW1wb3J0IHsgdXNlQ3ggfSBmcm9tICcuLi91dGlscy9ob29rcy91c2UtY3gnO1xuaW1wb3J0IHR5cGUgeyBHcmlkUHJvcHMgfSBmcm9tICcuL3R5cGVzJztcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdXNlR3JpZChcblx0cHJvcHM6IFdvcmRQcmVzc0NvbXBvbmVudFByb3BzPCBHcmlkUHJvcHMsICdkaXYnID5cbikge1xuXHRjb25zdCB7XG5cdFx0YWxpZ24sXG5cdFx0YWxpZ25tZW50LFxuXHRcdGNsYXNzTmFtZSxcblx0XHRjb2x1bW5HYXAsXG5cdFx0Y29sdW1ucyA9IDIsXG5cdFx0Z2FwID0gMyxcblx0XHRpc0lubGluZSA9IGZhbHNlLFxuXHRcdGp1c3RpZnksXG5cdFx0cm93R2FwLFxuXHRcdHJvd3MsXG5cdFx0dGVtcGxhdGVDb2x1bW5zLFxuXHRcdHRlbXBsYXRlUm93cyxcblx0XHQuLi5vdGhlclByb3BzXG5cdH0gPSB1c2VDb250ZXh0U3lzdGVtKCBwcm9wcywgJ0dyaWQnICk7XG5cblx0Y29uc3QgY29sdW1uc0FzQXJyYXkgPSBBcnJheS5pc0FycmF5KCBjb2x1bW5zICkgPyBjb2x1bW5zIDogWyBjb2x1bW5zIF07XG5cdGNvbnN0IGNvbHVtbiA9IHVzZVJlc3BvbnNpdmVWYWx1ZSggY29sdW1uc0FzQXJyYXkgKTtcblx0Y29uc3Qgcm93c0FzQXJyYXkgPSBBcnJheS5pc0FycmF5KCByb3dzICkgPyByb3dzIDogWyByb3dzIF07XG5cdGNvbnN0IHJvdyA9IHVzZVJlc3BvbnNpdmVWYWx1ZSggcm93c0FzQXJyYXkgKTtcblxuXHRjb25zdCBncmlkVGVtcGxhdGVDb2x1bW5zID1cblx0XHR0ZW1wbGF0ZUNvbHVtbnMgfHwgKCAhISBjb2x1bW5zICYmIGByZXBlYXQoICR7IGNvbHVtbiB9LCAxZnIgKWAgKTtcblx0Y29uc3QgZ3JpZFRlbXBsYXRlUm93cyA9XG5cdFx0dGVtcGxhdGVSb3dzIHx8ICggISEgcm93cyAmJiBgcmVwZWF0KCAkeyByb3cgfSwgMWZyIClgICk7XG5cblx0Y29uc3QgY3ggPSB1c2VDeCgpO1xuXG5cdGNvbnN0IGNsYXNzZXMgPSB1c2VNZW1vKCAoKSA9PiB7XG5cdFx0Y29uc3QgYWxpZ25tZW50UHJvcHMgPSBnZXRBbGlnbm1lbnRQcm9wcyggYWxpZ25tZW50ICk7XG5cblx0XHRjb25zdCBncmlkQ2xhc3NlcyA9IGNzcygge1xuXHRcdFx0YWxpZ25JdGVtczogYWxpZ24sXG5cdFx0XHRkaXNwbGF5OiBpc0lubGluZSA/ICdpbmxpbmUtZ3JpZCcgOiAnZ3JpZCcsXG5cdFx0XHRnYXA6IGBjYWxjKCAkeyBDT05GSUcuZ3JpZEJhc2UgfSAqICR7IGdhcCB9IClgLFxuXHRcdFx0Z3JpZFRlbXBsYXRlQ29sdW1uczogZ3JpZFRlbXBsYXRlQ29sdW1ucyB8fCB1bmRlZmluZWQsXG5cdFx0XHRncmlkVGVtcGxhdGVSb3dzOiBncmlkVGVtcGxhdGVSb3dzIHx8IHVuZGVmaW5lZCxcblx0XHRcdGdyaWRSb3dHYXA6IHJvd0dhcCxcblx0XHRcdGdyaWRDb2x1bW5HYXA6IGNvbHVtbkdhcCxcblx0XHRcdGp1c3RpZnlDb250ZW50OiBqdXN0aWZ5LFxuXHRcdFx0dmVydGljYWxBbGlnbjogaXNJbmxpbmUgPyAnbWlkZGxlJyA6IHVuZGVmaW5lZCxcblx0XHRcdC4uLmFsaWdubWVudFByb3BzLFxuXHRcdH0gKTtcblxuXHRcdHJldHVybiBjeCggZ3JpZENsYXNzZXMsIGNsYXNzTmFtZSApO1xuXHR9LCBbXG5cdFx0YWxpZ24sXG5cdFx0YWxpZ25tZW50LFxuXHRcdGNsYXNzTmFtZSxcblx0XHRjb2x1bW5HYXAsXG5cdFx0Y3gsXG5cdFx0Z2FwLFxuXHRcdGdyaWRUZW1wbGF0ZUNvbHVtbnMsXG5cdFx0Z3JpZFRlbXBsYXRlUm93cyxcblx0XHRpc0lubGluZSxcblx0XHRqdXN0aWZ5LFxuXHRcdHJvd0dhcCxcblx0XSApO1xuXG5cdHJldHVybiB7IC4uLm90aGVyUHJvcHMsIGNsYXNzTmFtZTogY2xhc3NlcyB9O1xufVxuIl19 */");
9513
+ }, process.env.NODE_ENV === "production" ? "" : ";label:gridClasses;", process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImhvb2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBNENzQiIsImZpbGUiOiJob29rLnRzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3NzIH0gZnJvbSAnQGVtb3Rpb24vcmVhY3QnO1xuaW1wb3J0IHsgdXNlTWVtbyB9IGZyb20gJ0B3b3JkcHJlc3MvZWxlbWVudCc7XG5pbXBvcnQgdHlwZSB7IFdvcmRQcmVzc0NvbXBvbmVudFByb3BzIH0gZnJvbSAnLi4vY29udGV4dCc7XG5pbXBvcnQgeyB1c2VDb250ZXh0U3lzdGVtIH0gZnJvbSAnLi4vY29udGV4dCc7XG5pbXBvcnQgeyBnZXRBbGlnbm1lbnRQcm9wcyB9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHsgdXNlUmVzcG9uc2l2ZVZhbHVlIH0gZnJvbSAnLi4vdXRpbHMvdXNlLXJlc3BvbnNpdmUtdmFsdWUnO1xuaW1wb3J0IENPTkZJRyBmcm9tICcuLi91dGlscy9jb25maWctdmFsdWVzJztcbmltcG9ydCB7IHVzZUN4IH0gZnJvbSAnLi4vdXRpbHMvaG9va3MvdXNlLWN4JztcbmltcG9ydCB0eXBlIHsgR3JpZFByb3BzIH0gZnJvbSAnLi90eXBlcyc7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHVzZUdyaWQoXG5cdHByb3BzOiBXb3JkUHJlc3NDb21wb25lbnRQcm9wczwgR3JpZFByb3BzLCAnZGl2JyA+XG4pIHtcblx0Y29uc3Qge1xuXHRcdGFsaWduLFxuXHRcdGFsaWdubWVudCxcblx0XHRjbGFzc05hbWUsXG5cdFx0Y29sdW1uR2FwLFxuXHRcdGNvbHVtbnMgPSAyLFxuXHRcdGdhcCA9IDMsXG5cdFx0aXNJbmxpbmUgPSBmYWxzZSxcblx0XHRqdXN0aWZ5LFxuXHRcdHJvd0dhcCxcblx0XHRyb3dzLFxuXHRcdHRlbXBsYXRlQ29sdW1ucyxcblx0XHR0ZW1wbGF0ZVJvd3MsXG5cdFx0Li4ub3RoZXJQcm9wc1xuXHR9ID0gdXNlQ29udGV4dFN5c3RlbSggcHJvcHMsICdHcmlkJyApO1xuXG5cdGNvbnN0IGNvbHVtbnNBc0FycmF5ID0gQXJyYXkuaXNBcnJheSggY29sdW1ucyApID8gY29sdW1ucyA6IFsgY29sdW1ucyBdO1xuXHRjb25zdCBjb2x1bW4gPSB1c2VSZXNwb25zaXZlVmFsdWUoIGNvbHVtbnNBc0FycmF5ICk7XG5cdGNvbnN0IHJvd3NBc0FycmF5ID0gQXJyYXkuaXNBcnJheSggcm93cyApID8gcm93cyA6IFsgcm93cyBdO1xuXHRjb25zdCByb3cgPSB1c2VSZXNwb25zaXZlVmFsdWUoIHJvd3NBc0FycmF5ICk7XG5cblx0Y29uc3QgZ3JpZFRlbXBsYXRlQ29sdW1ucyA9XG5cdFx0dGVtcGxhdGVDb2x1bW5zIHx8ICggISEgY29sdW1ucyAmJiBgcmVwZWF0KCAkeyBjb2x1bW4gfSwgMWZyIClgICk7XG5cdGNvbnN0IGdyaWRUZW1wbGF0ZVJvd3MgPVxuXHRcdHRlbXBsYXRlUm93cyB8fCAoICEhIHJvd3MgJiYgYHJlcGVhdCggJHsgcm93IH0sIDFmciApYCApO1xuXG5cdGNvbnN0IGN4ID0gdXNlQ3goKTtcblxuXHRjb25zdCBjbGFzc2VzID0gdXNlTWVtbyggKCkgPT4ge1xuXHRcdGNvbnN0IGFsaWdubWVudFByb3BzID0gZ2V0QWxpZ25tZW50UHJvcHMoIGFsaWdubWVudCApO1xuXG5cdFx0Y29uc3QgZ3JpZENsYXNzZXMgPSBjc3MoIHtcblx0XHRcdGFsaWduSXRlbXM6IGFsaWduLFxuXHRcdFx0ZGlzcGxheTogaXNJbmxpbmUgPyAnaW5saW5lLWdyaWQnIDogJ2dyaWQnLFxuXHRcdFx0Z2FwOiBgY2FsYyggJHsgQ09ORklHLmdyaWRCYXNlIH0gKiAkeyBnYXAgfSApYCxcblx0XHRcdGdyaWRUZW1wbGF0ZUNvbHVtbnM6IGdyaWRUZW1wbGF0ZUNvbHVtbnMgfHwgdW5kZWZpbmVkLFxuXHRcdFx0Z3JpZFRlbXBsYXRlUm93czogZ3JpZFRlbXBsYXRlUm93cyB8fCB1bmRlZmluZWQsXG5cdFx0XHRncmlkUm93R2FwOiByb3dHYXAsXG5cdFx0XHRncmlkQ29sdW1uR2FwOiBjb2x1bW5HYXAsXG5cdFx0XHRqdXN0aWZ5Q29udGVudDoganVzdGlmeSxcblx0XHRcdHZlcnRpY2FsQWxpZ246IGlzSW5saW5lID8gJ21pZGRsZScgOiB1bmRlZmluZWQsXG5cdFx0XHQuLi5hbGlnbm1lbnRQcm9wcyxcblx0XHR9ICk7XG5cblx0XHRyZXR1cm4gY3goIGdyaWRDbGFzc2VzLCBjbGFzc05hbWUgKTtcblx0fSwgW1xuXHRcdGFsaWduLFxuXHRcdGFsaWdubWVudCxcblx0XHRjbGFzc05hbWUsXG5cdFx0Y29sdW1uR2FwLFxuXHRcdGN4LFxuXHRcdGdhcCxcblx0XHRncmlkVGVtcGxhdGVDb2x1bW5zLFxuXHRcdGdyaWRUZW1wbGF0ZVJvd3MsXG5cdFx0aXNJbmxpbmUsXG5cdFx0anVzdGlmeSxcblx0XHRyb3dHYXAsXG5cdF0gKTtcblxuXHRyZXR1cm4geyAuLi5vdGhlclByb3BzLCBjbGFzc05hbWU6IGNsYXNzZXMgfTtcbn1cbiJdfQ== */");
8955
9514
  return cx(gridClasses, className);
8956
9515
  }, [
8957
9516
  align,
@@ -8972,10 +9531,11 @@ function useGrid(props) {
8972
9531
  };
8973
9532
  }
8974
9533
  //#endregion
8975
- //#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
9534
+ //#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
8976
9535
  function UnconnectedGrid(props, forwardedRef) {
9536
+ const gridProps = useGrid(props);
8977
9537
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
8978
- ...useGrid(props),
9538
+ ...gridProps,
8979
9539
  ref: forwardedRef
8980
9540
  });
8981
9541
  }
@@ -8994,8 +9554,8 @@ const SUBPIXEL_TOLERANCE = .5;
8994
9554
  * Counts how many leading rows fit inside the content container.
8995
9555
  *
8996
9556
  * Rows are read from the DOM rather than derived from constants: row height
8997
- * depends on the theme's row gap, label wrapping, and whatever a caller renders
8998
- * as a label, so any restated number would drift.
9557
+ * depends on `--a8c-charts-dimension-leaderboard-row-gap`, label wrapping, and
9558
+ * whatever a caller renders as a label, so any restated number would drift.
8999
9559
  *
9000
9560
  * Every row is a direct grid child marked with `data-row-index`; interactive rows
9001
9561
  * use a button wrapper and non-interactive rows use a div wrapper.
@@ -9080,14 +9640,13 @@ function useFittedRowCount(enabled, rowCount, data) {
9080
9640
  * @return Array of legend items for the leaderboard chart
9081
9641
  */
9082
9642
  function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withComparison = false, withOverlayLabel = false, legendLabels }) {
9083
- const { leaderboardChart: leaderboardChartSettings } = useGlobalChartsTheme();
9084
9643
  const { getElementStyles } = useGlobalChartsContext();
9085
9644
  return (0, react.useMemo)(() => {
9086
9645
  if (!data || data.length === 0) return [];
9087
9646
  const items = [];
9088
9647
  const { color: resolvedPrimaryColor } = getElementStyles({
9089
9648
  index: 0,
9090
- overrideColor: primaryColor || leaderboardChartSettings.primaryColor
9649
+ overrideColor: primaryColor
9091
9650
  });
9092
9651
  items.push({
9093
9652
  label: legendLabels?.primary || (0, _wordpress_i18n.__)("Current period", "jetpack-charts"),
@@ -9096,7 +9655,7 @@ function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withCom
9096
9655
  if (withComparison && !withOverlayLabel) {
9097
9656
  const { color: resolvedSecondaryColor } = getElementStyles({
9098
9657
  index: 1,
9099
- overrideColor: secondaryColor || leaderboardChartSettings.secondaryColor
9658
+ overrideColor: secondaryColor
9100
9659
  });
9101
9660
  items.push({
9102
9661
  label: legendLabels?.comparison || (0, _wordpress_i18n.__)("Previous period", "jetpack-charts"),
@@ -9110,7 +9669,6 @@ function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withCom
9110
9669
  secondaryColor,
9111
9670
  withComparison,
9112
9671
  legendLabels,
9113
- leaderboardChartSettings,
9114
9672
  getElementStyles,
9115
9673
  withOverlayLabel
9116
9674
  ]);
@@ -9124,6 +9682,9 @@ var leaderboard_chart_module_default = {
9124
9682
  "chevron": "a8ccharts-GovfoW-chevron",
9125
9683
  "deltaPlaceholder": "a8ccharts-GovfoW-deltaPlaceholder",
9126
9684
  "deltaValue": "a8ccharts-GovfoW-deltaValue",
9685
+ "deltaValue--down": "a8ccharts-GovfoW-deltaValue--down",
9686
+ "deltaValue--neutral": "a8ccharts-GovfoW-deltaValue--neutral",
9687
+ "deltaValue--up": "a8ccharts-GovfoW-deltaValue--up",
9127
9688
  "emptyState": "a8ccharts-GovfoW-emptyState",
9128
9689
  "fitEmptyState": "a8ccharts-GovfoW-fitEmptyState",
9129
9690
  "interactiveRow": "a8ccharts-GovfoW-interactiveRow",
@@ -9142,6 +9703,14 @@ var leaderboard_chart_module_default = {
9142
9703
  };
9143
9704
  //#endregion
9144
9705
  //#region src/charts/leaderboard-chart/leaderboard-chart.tsx
9706
+ const ROW_GAP = "var(--a8c-charts-dimension-leaderboard-row-gap, 12px)";
9707
+ const COLUMN_GAP = "var(--a8c-charts-dimension-leaderboard-column-gap, 4px)";
9708
+ /** Indexed by `Math.sign( delta ) + 1`, so [negative, neutral, positive]. */
9709
+ const DELTA_TREND_CLASS = [
9710
+ "deltaValue--down",
9711
+ "deltaValue--neutral",
9712
+ "deltaValue--up"
9713
+ ];
9145
9714
  /**
9146
9715
  * Default value formatter using formatMetricValue
9147
9716
  *
@@ -9244,15 +9813,15 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9244
9813
  ...legend.shapeStyles
9245
9814
  };
9246
9815
  const { legendChildren, nonLegendChildren } = useChartChildren(children, "LeaderboardChart");
9247
- const { labelSpacing, rowGap, columnGap, primaryColor: settingsPrimaryColor, secondaryColor: settingsSecondaryColor, deltaColors } = leaderboardChartSettings;
9816
+ const { labelSpacing } = leaderboardChartSettings;
9248
9817
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
9249
9818
  const { color: resolvedPrimaryColor } = getElementStyles({
9250
9819
  index: 0,
9251
- overrideColor: primaryColor || settingsPrimaryColor
9820
+ overrideColor: primaryColor
9252
9821
  });
9253
9822
  const { color: resolvedSecondaryColor } = getElementStyles({
9254
9823
  index: 1,
9255
- overrideColor: secondaryColor || settingsSecondaryColor
9824
+ overrideColor: secondaryColor
9256
9825
  });
9257
9826
  const legendItems = useLeaderboardLegendItems({
9258
9827
  data: data || [],
@@ -9287,15 +9856,17 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9287
9856
  withComparison,
9288
9857
  withOverlayLabel
9289
9858
  ]);
9859
+ const isDataValid = Boolean(data && data.length > 0);
9860
+ const chartMetadata = (0, react.useMemo)(() => ({
9861
+ withComparison,
9862
+ withOverlayLabel
9863
+ }), [withComparison, withOverlayLabel]);
9290
9864
  useChartRegistration({
9291
9865
  chartId,
9292
9866
  legendItems,
9293
9867
  chartType: "leaderboard",
9294
- isDataValid: Boolean(data && data.length > 0),
9295
- metadata: (0, react.useMemo)(() => ({
9296
- withComparison,
9297
- withOverlayLabel
9298
- }), [withComparison, withOverlayLabel])
9868
+ isDataValid,
9869
+ metadata: chartMetadata
9299
9870
  });
9300
9871
  const prefersReducedMotion = usePrefersReducedMotion();
9301
9872
  const { contentRef, fittedCount, isMeasurable } = useFittedRowCount(fitRows && !allSeriesHidden, data?.length ?? 0, data);
@@ -9306,7 +9877,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9306
9877
  legendPosition,
9307
9878
  legendElement: false,
9308
9879
  legendChildren,
9309
- className: (0, clsx.default)(leaderboard_chart_module_default.leaderboardChart, {
9880
+ className: (0, clsx.default)("leaderboard-chart", leaderboard_chart_module_default.leaderboardChart, {
9310
9881
  [leaderboard_chart_module_default["leaderboardChart--responsive"]]: !propWidth && !propHeight,
9311
9882
  [leaderboard_chart_module_default["leaderboardChart--loading"]]: loading
9312
9883
  }, className),
@@ -9341,7 +9912,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9341
9912
  legendPosition,
9342
9913
  legendElement,
9343
9914
  legendChildren,
9344
- className: (0, clsx.default)(leaderboard_chart_module_default.leaderboardChart, {
9915
+ className: (0, clsx.default)("leaderboard-chart", leaderboard_chart_module_default.leaderboardChart, {
9345
9916
  [leaderboard_chart_module_default["leaderboardChart--responsive"]]: !propWidth && !propHeight,
9346
9917
  [leaderboard_chart_module_default["leaderboardChart--loading"]]: loading
9347
9918
  }, className),
@@ -9363,8 +9934,8 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9363
9934
  children: getAllHiddenMessage(legendInteractive, "series")
9364
9935
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default, {
9365
9936
  templateColumns: "minmax(0, 1fr) auto",
9366
- rowGap,
9367
- columnGap,
9937
+ rowGap: ROW_GAP,
9938
+ columnGap: COLUMN_GAP,
9368
9939
  "data-leaderboard-grid": true,
9369
9940
  children: data.map((entry, rowIndex) => {
9370
9941
  const rowStyle = shouldFitRows && rowIndex >= fittedCount ? { visibility: "hidden" } : void 0;
@@ -9373,8 +9944,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9373
9944
  const hasDelta = hasDeltaValue(entry);
9374
9945
  const showComparisonValue = showComparisonColumn && hasDelta;
9375
9946
  const showComparisonPlaceholder = showComparisonColumn && !hasDelta;
9376
- const colorIndex = showComparisonValue ? Math.sign(entry.delta) + 1 : 1;
9377
- const deltaColor = deltaColors[colorIndex];
9947
+ const deltaTrendClass = leaderboard_chart_module_default[DELTA_TREND_CLASS[showComparisonValue ? Math.sign(entry.delta) + 1 : 1]];
9378
9948
  const rowCells = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
9379
9949
  direction: "column",
9380
9950
  gap: labelSpacing,
@@ -9395,13 +9965,11 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
9395
9965
  children: [
9396
9966
  isPrimaryVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Text, { children: valueFormatter(entry.currentValue) }),
9397
9967
  showComparisonValue && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Text, {
9398
- className: leaderboard_chart_module_default.deltaValue,
9399
- style: { color: deltaColor },
9968
+ className: (0, clsx.default)(leaderboard_chart_module_default.deltaValue, deltaTrendClass),
9400
9969
  children: deltaFormatter(entry.delta)
9401
9970
  }),
9402
9971
  showComparisonPlaceholder && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Text, {
9403
- className: (0, clsx.default)(leaderboard_chart_module_default.deltaValue, leaderboard_chart_module_default.deltaPlaceholder),
9404
- style: { color: deltaColor },
9972
+ className: (0, clsx.default)(leaderboard_chart_module_default.deltaValue, leaderboard_chart_module_default.deltaPlaceholder, deltaTrendClass),
9405
9973
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9406
9974
  "aria-hidden": "true",
9407
9975
  children: "—"
@@ -9485,7 +10053,9 @@ function RadialWipeAnimation({ id, radius, innerRadius = 0, durationMs = 1e3, wi
9485
10053
  //#endregion
9486
10054
  //#region src/charts/pie-chart/pie-chart.module.scss
9487
10055
  var pie_chart_module_default = {
9488
- "pie-chart": "a8ccharts-gnszbG-pie-chart",
10056
+ "pie-chart__label-plate": "a8ccharts-gnszbG-pie-chart__label-plate",
10057
+ "pie-chart__label-text": "a8ccharts-gnszbG-pie-chart__label-text",
10058
+ "pie-chart__plot": "a8ccharts-gnszbG-pie-chart__plot",
9489
10059
  "pie-chart--responsive": "a8ccharts-gnszbG-pie-chart--responsive"
9490
10060
  };
9491
10061
  //#endregion
@@ -9540,18 +10110,16 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9540
10110
  const providerTheme = useGlobalChartsTheme();
9541
10111
  const chartId = useChartId(providedChartId);
9542
10112
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = (0, _visx_tooltip.useTooltip)();
9543
- const { containerRef, TooltipInPortal, containerBounds } = (0, _visx_tooltip.useTooltipInPortal)({
9544
- detectBounds: true,
9545
- scroll: true,
9546
- debounce: 0
9547
- });
10113
+ const standaloneScopeClass = useStandaloneScopeClass();
10114
+ const containerRef = (0, react.useRef)(null);
9548
10115
  const onMouseLeave = (0, react.useCallback)(() => {
9549
10116
  if (!withTooltips) return;
9550
10117
  hideTooltip();
9551
10118
  }, [withTooltips, hideTooltip]);
9552
10119
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
10120
+ const dataWithPercentages = useDataWithPercentages(data);
9553
10121
  const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9554
- data: useDataWithPercentages(data),
10122
+ data: dataWithPercentages,
9555
10123
  chartId,
9556
10124
  isSeriesVisible
9557
10125
  });
@@ -9561,20 +10129,21 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9561
10129
  }), [legendValueDisplay]));
9562
10130
  const { isValid, message } = validateData$1(data);
9563
10131
  const { svgChildren, htmlChildren, legendChildren, otherChildren } = useChartChildren(children, "PieChart");
10132
+ const chartMetadata = (0, react.useMemo)(() => ({
10133
+ thickness,
10134
+ gapScale,
10135
+ cornerScale
10136
+ }), [
10137
+ thickness,
10138
+ gapScale,
10139
+ cornerScale
10140
+ ]);
9564
10141
  useChartRegistration({
9565
10142
  chartId,
9566
10143
  legendItems,
9567
10144
  chartType: "pie",
9568
10145
  isDataValid: isValid,
9569
- metadata: (0, react.useMemo)(() => ({
9570
- thickness,
9571
- gapScale,
9572
- cornerScale
9573
- }), [
9574
- thickness,
9575
- gapScale,
9576
- cornerScale
9577
- ])
10146
+ metadata: chartMetadata
9578
10147
  });
9579
10148
  const prefersReducedMotion = usePrefersReducedMotion();
9580
10149
  if (!isValid) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -9625,19 +10194,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9625
10194
  width: propWidth || void 0,
9626
10195
  height: propHeight || void 0
9627
10196
  },
9628
- trailingContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9629
- withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipInPortal, {
9630
- top: tooltipTop || 0,
9631
- left: tooltipLeft || 0,
9632
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9633
- className: "a8c-charts-scope",
9634
- role: "tooltip",
9635
- children: renderTooltip({ tooltipData })
9636
- })
9637
- }),
9638
- htmlChildren,
9639
- otherChildren
9640
- ] }),
10197
+ trailingContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [htmlChildren, otherChildren] }),
9641
10198
  children: ({ contentWidth, contentHeight }) => {
9642
10199
  const availableSize = Math.min(contentWidth > 0 ? contentWidth : 300, contentHeight > 0 ? contentHeight : 300);
9643
10200
  const actualSize = size ? Math.min(size, availableSize) : availableSize;
@@ -9650,9 +10207,10 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9650
10207
  const innerRadius = thickness === 0 ? 0 : outerRadius * (1 - thickness);
9651
10208
  const maxCornerRadius = (outerRadius - innerRadius) / 2;
9652
10209
  const cornerRadius = cornerScale ? Math.min(cornerScale * outerRadius, maxCornerRadius) : 0;
9653
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Center, {
10210
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Center, {
9654
10211
  ref: containerRef,
9655
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
10212
+ className: pie_chart_module_default["pie-chart__plot"],
10213
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
9656
10214
  viewBox: `0 0 ${width} ${height}`,
9657
10215
  preserveAspectRatio: "xMidYMid meet",
9658
10216
  width,
@@ -9684,11 +10242,12 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9684
10242
  const hasSpaceForLabel = arc.endAngle - arc.startAngle >= .25;
9685
10243
  const handleMouseMove = (event) => {
9686
10244
  if (!withTooltips) return;
9687
- if (containerBounds.width === 0 || containerBounds.height === 0) return;
10245
+ const bounds = containerRef.current?.getBoundingClientRect();
10246
+ if (!bounds) return;
9688
10247
  showTooltip({
9689
10248
  tooltipData: arc.data,
9690
- tooltipLeft: event.clientX - containerBounds.left + tooltipOffsetX,
9691
- tooltipTop: event.clientY - containerBounds.top + tooltipOffsetY
10249
+ tooltipLeft: event.clientX - bounds.left + tooltipOffsetX,
10250
+ tooltipTop: event.clientY - bounds.top + tooltipOffsetY
9692
10251
  });
9693
10252
  };
9694
10253
  const pathProps = {
@@ -9703,30 +10262,28 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9703
10262
  }
9704
10263
  const svgLabelSmall = providerTheme.svgLabelSmall;
9705
10264
  const fontSize = resolveFontSize(svgLabelSmall?.fontSize) ?? 12;
9706
- const estimatedTextWidth = (0, _visx_text.getStringWidth)(arc.data.label, {
10265
+ const backgroundWidth = (0, _visx_text.getStringWidth)(arc.data.label, {
9707
10266
  fontSize,
9708
10267
  fontFamily: svgLabelSmall?.fontFamily,
9709
10268
  fontWeight: svgLabelSmall?.fontWeight
9710
- });
9711
- const labelPadding = 6;
9712
- const backgroundWidth = estimatedTextWidth + labelPadding * 2;
9713
- const backgroundHeight = fontSize + labelPadding * 2;
10269
+ }) + 12;
10270
+ const backgroundHeight = fontSize + 12;
9714
10271
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
9715
10272
  ...groupProps,
9716
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { ...pathProps }), showLabels && hasSpaceForLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [providerTheme.labelBackgroundColor && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
10273
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { ...pathProps }), showLabels && hasSpaceForLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
10274
+ className: pie_chart_module_default["pie-chart__label-plate"],
9717
10275
  x: centroidX - backgroundWidth / 2,
9718
10276
  y: centroidY - backgroundHeight / 2,
9719
10277
  width: backgroundWidth,
9720
10278
  height: backgroundHeight,
9721
- fill: providerTheme.labelBackgroundColor,
9722
10279
  rx: 4,
9723
10280
  ry: 4,
9724
10281
  pointerEvents: "none"
9725
10282
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
10283
+ className: pie_chart_module_default["pie-chart__label-text"],
9726
10284
  x: centroidX,
9727
10285
  y: centroidY,
9728
10286
  dy: ".33em",
9729
- fill: providerTheme.labelTextColor || "#333",
9730
10287
  fontSize,
9731
10288
  textAnchor: "middle",
9732
10289
  pointerEvents: "none",
@@ -9737,7 +10294,15 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
9737
10294
  }
9738
10295
  }), !allSegmentsHidden && svgChildren]
9739
10296
  })]
9740
- })
10297
+ }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundedTooltip, {
10298
+ top: tooltipTop || 0,
10299
+ left: tooltipLeft || 0,
10300
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10301
+ className: standaloneScopeClass,
10302
+ role: "tooltip",
10303
+ children: renderTooltip({ tooltipData })
10304
+ })
10305
+ })]
9741
10306
  });
9742
10307
  }
9743
10308
  })
@@ -9764,6 +10329,7 @@ var pie_semi_circle_chart_module_default = {
9764
10329
  "label": "a8ccharts-YtTOxW-label",
9765
10330
  "note": "a8ccharts-YtTOxW-note",
9766
10331
  "pie-semi-circle-chart": "a8ccharts-YtTOxW-pie-semi-circle-chart",
10332
+ "pie-semi-circle-chart__plot": "a8ccharts-YtTOxW-pie-semi-circle-chart__plot",
9767
10333
  "pie-semi-circle-chart--responsive": "a8ccharts-YtTOxW-pie-semi-circle-chart--responsive"
9768
10334
  };
9769
10335
  //#endregion
@@ -9813,23 +10379,17 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9813
10379
  const legendPosition = legend.position ?? "bottom";
9814
10380
  const chartId = useChartId(providedChartId);
9815
10381
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = (0, _visx_tooltip.useTooltip)();
9816
- const { containerRef, TooltipInPortal, containerBounds } = (0, _visx_tooltip.useTooltipInPortal)({
9817
- detectBounds: true,
9818
- scroll: true,
9819
- debounce: 0
9820
- });
10382
+ const standaloneScopeClass = useStandaloneScopeClass();
10383
+ const containerRef = (0, react.useRef)(null);
9821
10384
  const handleMouseMove = (0, react.useCallback)((event, arc) => {
9822
- if (containerBounds.width === 0 || containerBounds.height === 0) return;
10385
+ const bounds = containerRef.current?.getBoundingClientRect();
10386
+ if (!bounds) return;
9823
10387
  showTooltip({
9824
10388
  tooltipData: arc.data,
9825
- tooltipLeft: event.clientX - containerBounds.left + tooltipOffsetX,
9826
- tooltipTop: event.clientY - containerBounds.top + tooltipOffsetY
10389
+ tooltipLeft: event.clientX - bounds.left + tooltipOffsetX,
10390
+ tooltipTop: event.clientY - bounds.top + tooltipOffsetY
9827
10391
  });
9828
10392
  }, [
9829
- containerBounds.width,
9830
- containerBounds.height,
9831
- containerBounds.left,
9832
- containerBounds.top,
9833
10393
  showTooltip,
9834
10394
  tooltipOffsetX,
9835
10395
  tooltipOffsetY
@@ -9842,8 +10402,9 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9842
10402
  }, [handleMouseMove]);
9843
10403
  const { isValid, message } = validateData(data);
9844
10404
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
10405
+ const dataWithPercentages = useDataWithPercentages(data);
9845
10406
  const { visibleData, allSegmentsHidden, legendData } = useLegendVisibilityData({
9846
- data: useDataWithPercentages(data),
10407
+ data: dataWithPercentages,
9847
10408
  chartId,
9848
10409
  isSeriesVisible
9849
10410
  });
@@ -9860,15 +10421,16 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9860
10421
  legendValueDisplay
9861
10422
  }), [legendValueDisplay]));
9862
10423
  const { svgChildren, htmlChildren, legendChildren, otherChildren } = useChartChildren(children, "PieSemiCircleChart");
10424
+ const chartMetadata = (0, react.useMemo)(() => ({
10425
+ thickness,
10426
+ clockwise
10427
+ }), [thickness, clockwise]);
9863
10428
  useChartRegistration({
9864
10429
  chartId,
9865
10430
  legendItems,
9866
10431
  chartType: "pie-semi-circle",
9867
10432
  isDataValid: isValid,
9868
- metadata: (0, react.useMemo)(() => ({
9869
- thickness,
9870
- clockwise
9871
- }), [thickness, clockwise])
10433
+ metadata: chartMetadata
9872
10434
  });
9873
10435
  const prefersReducedMotion = usePrefersReducedMotion();
9874
10436
  const effectiveWidth = propWidth || DEFAULT_WIDTH$1;
@@ -9876,7 +10438,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9876
10438
  const errorWidth = propHeight ? Math.min(propWidth || propHeight * 2, propHeight * 2) : effectiveWidth;
9877
10439
  const errorHeight = errorWidth / 2;
9878
10440
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9879
- className: pie_semi_circle_chart_module_default["pie-semi-circle-chart"],
10441
+ className: (0, clsx.default)("pie-semi-circle-chart", pie_semi_circle_chart_module_default["pie-semi-circle-chart"], className),
9880
10442
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
9881
10443
  width: errorWidth,
9882
10444
  height: errorHeight,
@@ -9923,19 +10485,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9923
10485
  width: propWidth || void 0,
9924
10486
  height: propHeight || void 0
9925
10487
  },
9926
- trailingContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9927
- withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipInPortal, {
9928
- top: tooltipTop || 0,
9929
- left: tooltipLeft || 0,
9930
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9931
- className: "a8c-charts-scope",
9932
- role: "tooltip",
9933
- children: renderTooltip({ tooltipData })
9934
- })
9935
- }),
9936
- htmlChildren,
9937
- otherChildren
9938
- ] }),
10488
+ trailingContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [htmlChildren, otherChildren] }),
9939
10489
  children: ({ contentWidth, contentHeight }) => {
9940
10490
  const availableWidth = contentWidth > 0 ? contentWidth : effectiveWidth;
9941
10491
  const availableHeight = contentHeight > 0 ? contentHeight : propHeight || effectiveWidth / 2;
@@ -9943,9 +10493,10 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
9943
10493
  const height = width / 2;
9944
10494
  const radius = height;
9945
10495
  const innerRadius = radius * (1 - thickness);
9946
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Center, {
10496
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Center, {
9947
10497
  ref: containerRef,
9948
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
10498
+ className: pie_semi_circle_chart_module_default["pie-semi-circle-chart__plot"],
10499
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
9949
10500
  width,
9950
10501
  height,
9951
10502
  viewBox: `0 0 ${width} ${height}`,
@@ -10003,7 +10554,15 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
10003
10554
  !allSegmentsHidden && svgChildren
10004
10555
  ] })
10005
10556
  })]
10006
- })
10557
+ }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundedTooltip, {
10558
+ top: tooltipTop || 0,
10559
+ left: tooltipLeft || 0,
10560
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10561
+ className: standaloneScopeClass,
10562
+ role: "tooltip",
10563
+ children: renderTooltip({ tooltipData })
10564
+ })
10565
+ })]
10007
10566
  });
10008
10567
  }
10009
10568
  })
@@ -10059,6 +10618,7 @@ const transformToSeriesData = (data, color, strokeWidth) => {
10059
10618
  };
10060
10619
  const SparklineComponent = (0, react.forwardRef)(({ data, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT, color, strokeWidth: strokeWidthProp, withGradientFill = true, gradient, className, chartId, margin: marginProp, animation }, ref) => {
10061
10620
  const theme = useGlobalChartsTheme();
10621
+ const chartsContext = (0, react.useContext)(GlobalChartsContext);
10062
10622
  const themeStrokeWidth = theme.sparkline?.strokeWidth ?? 1.5;
10063
10623
  const strokeWidth = strokeWidthProp ?? themeStrokeWidth;
10064
10624
  const seriesData = (0, react.useMemo)(() => {
@@ -10112,7 +10672,7 @@ const SparklineComponent = (0, react.forwardRef)(({ data, width = DEFAULT_WIDTH,
10112
10672
  if (data.length === 1) {
10113
10673
  const cx = width / 2;
10114
10674
  const cy = height / 2;
10115
- const resolvedColor = color || "#000000";
10675
+ const resolvedColor = color || chartsContext?.getElementStyles({ index: 0 }).color || "#000000";
10116
10676
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10117
10677
  ref,
10118
10678
  className: (0, clsx.default)("sparkline", sparkline_module_default.sparkline, sparkline_module_default["sparkline--single-point"], className),
@@ -10215,8 +10775,9 @@ const Icon = ({ direction }) => {
10215
10775
  */
10216
10776
  function TrendIndicator({ direction, value, className, style, showIcon = true }) {
10217
10777
  const ariaLabel = `${DIRECTION_LABELS[direction]}: ${value}`;
10778
+ const standaloneScopeClass = useStandaloneScopeClass();
10218
10779
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
10219
- className: (0, clsx.default)(useStandaloneScopeClass(), trend_indicator_module_default["trend-indicator"], trend_indicator_module_default[`trend-indicator--${direction}`], className),
10780
+ className: (0, clsx.default)(standaloneScopeClass, trend_indicator_module_default["trend-indicator"], trend_indicator_module_default[`trend-indicator--${direction}`], className),
10220
10781
  style,
10221
10782
  "aria-label": ariaLabel,
10222
10783
  children: [showIcon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { direction }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -10259,6 +10820,7 @@ exports.buildCalendarHeatmapData = buildCalendarHeatmapData;
10259
10820
  exports.defaultTheme = defaultTheme;
10260
10821
  exports.formatMetricValue = formatMetricValue;
10261
10822
  exports.formatPercentage = formatPercentage;
10823
+ exports.getBucketInfo = getBucketInfo;
10262
10824
  exports.getColorDistance = getColorDistance;
10263
10825
  exports.hexToRgba = hexToRgba;
10264
10826
  exports.isValidHexColor = isValidHexColor;
@@ -10268,10 +10830,10 @@ exports.mixHexColors = mixHexColors;
10268
10830
  exports.normalizeColorToHex = normalizeColorToHex;
10269
10831
  exports.parseAsLocalDate = parseAsLocalDate;
10270
10832
  exports.parseHslString = parseHslString;
10271
- exports.parseRgbString = parseRgbString;
10272
10833
  exports.prefersLightText = prefersLightText;
10273
10834
  exports.relativeLuminance = relativeLuminance;
10274
10835
  exports.resolveCssVariable = resolveCssVariable;
10836
+ exports.useChartFormatting = useChartFormatting;
10275
10837
  exports.useChartLegendItems = useChartLegendItems;
10276
10838
  exports.useChartRegistration = useChartRegistration;
10277
10839
  exports.useChartScopeElement = useChartScopeElement;