@automattic/charts 4.11.1 → 4.12.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 (54) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.cjs +856 -236
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +19 -13
  5. package/dist/index.d.cts +43 -4
  6. package/dist/index.d.ts +43 -4
  7. package/dist/index.js +855 -237
  8. package/dist/index.js.map +1 -1
  9. package/package.json +9 -9
  10. package/src/charts/bar-chart/bar-chart.tsx +134 -138
  11. package/src/charts/bar-chart/index.ts +3 -0
  12. package/src/charts/bar-chart/private/band-tooltip.tsx +41 -15
  13. package/src/charts/bar-chart/private/bar-layer.tsx +132 -0
  14. package/src/charts/bar-chart/private/comparison-bars-geometry.ts +22 -34
  15. package/src/charts/bar-chart/private/comparison-bars.tsx +9 -18
  16. package/src/charts/bar-chart/private/comparison-constants.ts +4 -10
  17. package/src/charts/bar-chart/private/grouped-bars.ts +68 -0
  18. package/src/charts/bar-chart/private/index.ts +7 -1
  19. package/src/charts/bar-chart/private/overlap-bars.ts +183 -0
  20. package/src/charts/bar-chart/private/selected-bar-ring.tsx +84 -0
  21. package/src/charts/bar-chart/private/slot-tooltip-members.tsx +29 -0
  22. package/src/charts/bar-chart/private/slotted-bar-series.tsx +84 -0
  23. package/src/charts/bar-chart/private/test/comparison-bars-geometry.test.ts +4 -11
  24. package/src/charts/bar-chart/private/test/comparison-bars.test.tsx +2 -3
  25. package/src/charts/bar-chart/private/test/grouped-bars.test.ts +87 -0
  26. package/src/charts/bar-chart/private/test/overlap-bars.test.ts +159 -0
  27. package/src/charts/bar-chart/private/use-bar-stops.ts +89 -0
  28. package/src/charts/bar-chart/private/use-overlap-slots.ts +194 -0
  29. package/src/charts/bar-chart/private/value-domain.ts +0 -10
  30. package/src/charts/bar-chart/test/bar-chart-overlap.test.tsx +347 -0
  31. package/src/charts/bar-chart/test/bar-chart.test.tsx +66 -19
  32. package/src/charts/bar-chart/test/use-bar-legend-items.test.tsx +81 -0
  33. package/src/charts/bar-chart/types.ts +3 -0
  34. package/src/charts/bar-chart/use-bar-colors.ts +46 -0
  35. package/src/charts/bar-chart/use-bar-legend-items.ts +87 -0
  36. package/src/charts/line-chart/line-chart.tsx +22 -17
  37. package/src/charts/line-chart/test/line-chart.test.tsx +165 -1
  38. package/src/charts/pie-chart/pie-chart.tsx +7 -2
  39. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +7 -2
  40. package/src/charts/private/pie-keyboard-navigation/index.ts +0 -1
  41. package/src/charts/private/{pie-keyboard-navigation/pie-keyboard-navigation.module.scss → selected-ring/selected-ring.module.scss} +11 -6
  42. package/src/charts/private/selected-ring/selected-ring.tsx +73 -0
  43. package/src/charts/private/{pie-keyboard-navigation/test/selected-segment-ring.test.ts → selected-ring/test/selected-ring.test.ts} +1 -1
  44. package/src/components/legend/hooks/test/use-chart-legend-items.test.tsx +20 -0
  45. package/src/components/legend/hooks/use-chart-legend-items.ts +27 -3
  46. package/src/components/tooltip/xy-chart-tooltip.tsx +3 -2
  47. package/src/index.ts +8 -2
  48. package/src/providers/chart-context/global-charts-provider.tsx +2 -0
  49. package/src/providers/chart-context/test/chart-context.test.tsx +0 -13
  50. package/src/providers/chart-context/themes.ts +0 -1
  51. package/src/providers/chart-context/types.ts +2 -0
  52. package/src/test-utils/selected-bar-ring.ts +14 -0
  53. package/src/types.ts +10 -2
  54. package/src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx +0 -47
package/dist/index.cjs CHANGED
@@ -1298,10 +1298,7 @@ const defaultTheme = {
1298
1298
  strokeDasharray: "4 4",
1299
1299
  strokeLinecap: "square"
1300
1300
  } } },
1301
- barChart: { barStyles: { comparison: {
1302
- widthFactor: 1.5,
1303
- opacity: .5
1304
- } } },
1301
+ barChart: { barStyles: { comparison: { widthFactor: 1.5 } } },
1305
1302
  sparkline: {
1306
1303
  margin: {
1307
1304
  top: 2,
@@ -1459,6 +1456,7 @@ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
1459
1456
  theme: providerTheme,
1460
1457
  formatting,
1461
1458
  getElementStyles,
1459
+ backgroundColor: colorCache.background,
1462
1460
  toggleSeriesVisibility,
1463
1461
  setSeriesVisibility,
1464
1462
  setChartHiddenSeries,
@@ -1475,6 +1473,7 @@ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
1475
1473
  providerTheme,
1476
1474
  formatting,
1477
1475
  getElementStyles,
1476
+ colorCache.background,
1478
1477
  toggleSeriesVisibility,
1479
1478
  setSeriesVisibility,
1480
1479
  setChartHiddenSeries,
@@ -2299,6 +2298,21 @@ const Legend = (0, react.forwardRef)(({ chartId, items, shape, ...props }, ref)
2299
2298
  //#endregion
2300
2299
  //#region src/components/legend/hooks/use-chart-legend-items.ts
2301
2300
  /**
2301
+ * Wraps `getElementStyles` so a swatch takes the color given for its series.
2302
+ * @param getElementStyles - Function to get element styles
2303
+ * @param seriesColors - Each series' swatch color, by index
2304
+ * @return Element styles with the given color
2305
+ */
2306
+ function withSeriesColors(getElementStyles, seriesColors) {
2307
+ return (params) => {
2308
+ const styles = getElementStyles(params);
2309
+ return {
2310
+ ...styles,
2311
+ color: seriesColors[params.index] ?? styles.color
2312
+ };
2313
+ };
2314
+ }
2315
+ /**
2302
2316
  * Formats the value for a data point based on its type and display preference
2303
2317
  * @param point - The data point to format
2304
2318
  * @param showValues - Whether to show values or return empty string
@@ -2510,21 +2524,22 @@ function processPointData(pointData, getElementStyles, showValues, legendValueDi
2510
2524
  * @return Array of legend items ready for display
2511
2525
  */
2512
2526
  function useChartLegendItems(data, options = {}, legendShape) {
2513
- const { showValues = false, legendValueDisplay = "percentage", withGlyph = false, glyphSize = 8, collapseGroups = false, comparisonItem = false, comparisonSeriesItems, renderGlyph } = options;
2514
- const { getElementStyles } = useGlobalChartsContext();
2527
+ const { showValues = false, legendValueDisplay = "percentage", withGlyph = false, glyphSize = 8, collapseGroups = false, comparisonItem = false, comparisonSeriesItems, seriesColors, renderGlyph } = options;
2528
+ const { getElementStyles: getSeriesStyles } = useGlobalChartsContext();
2515
2529
  return (0, react.useMemo)(() => {
2516
2530
  if (!data || !Array.isArray(data) || data.length === 0) return [];
2517
2531
  if ("data" in data[0]) {
2518
2532
  const seriesData = data;
2533
+ const getElementStyles = seriesColors ? withSeriesColors(getSeriesStyles, seriesColors) : getSeriesStyles;
2519
2534
  const items = processSeriesData(seriesData, getElementStyles, showValues, withGlyph, glyphSize, collapseGroups, renderGlyph, legendShape);
2520
2535
  if (comparisonSeriesItems) return [...items, ...buildPerSeriesComparisonItems(seriesData, items, comparisonSeriesItems, getElementStyles, legendShape)];
2521
2536
  const comparison = comparisonItem ? buildComparisonLegendItem(seriesData, items, typeof comparisonItem === "string" ? comparisonItem : (0, _wordpress_i18n.__)("Comparison period", "jetpack-charts"), getElementStyles, legendShape) : null;
2522
2537
  return comparison ? [...items, comparison] : items;
2523
2538
  }
2524
- return processPointData(data, getElementStyles, showValues, legendValueDisplay, withGlyph, glyphSize, renderGlyph, legendShape);
2539
+ return processPointData(data, getSeriesStyles, showValues, legendValueDisplay, withGlyph, glyphSize, renderGlyph, legendShape);
2525
2540
  }, [
2526
2541
  data,
2527
- getElementStyles,
2542
+ getSeriesStyles,
2528
2543
  showValues,
2529
2544
  legendValueDisplay,
2530
2545
  withGlyph,
@@ -2532,6 +2547,7 @@ function useChartLegendItems(data, options = {}, legendShape) {
2532
2547
  collapseGroups,
2533
2548
  comparisonItem,
2534
2549
  comparisonSeriesItems,
2550
+ seriesColors,
2535
2551
  renderGlyph,
2536
2552
  legendShape
2537
2553
  ]);
@@ -3057,17 +3073,19 @@ const XYChartTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocu
3057
3073
  return;
3058
3074
  }
3059
3075
  hasKeyboardSelection.current = true;
3060
- if (mode === "group") series.forEach((s, index) => {
3061
- if (selectedIndex < s.data.length) {
3062
- const datum = s.data[selectedIndex];
3063
- tooltipContext?.showTooltip({
3064
- datum,
3065
- key: s.label,
3066
- index
3067
- });
3068
- }
3069
- });
3070
- else if (mode === "individual") {
3076
+ if (mode === "group") {
3077
+ closeTooltipNow();
3078
+ series.forEach((s, index) => {
3079
+ if (selectedIndex < s.data.length) {
3080
+ const datum = s.data[selectedIndex];
3081
+ tooltipContext?.showTooltip({
3082
+ datum,
3083
+ key: s.label,
3084
+ index
3085
+ });
3086
+ }
3087
+ });
3088
+ } else if (mode === "individual") {
3071
3089
  if (selectedIndex < tooltipData.length) {
3072
3090
  const tooltipItem = tooltipData[selectedIndex];
3073
3091
  tooltipContext?.showTooltip({
@@ -4814,6 +4832,8 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
4814
4832
  isVisible: !hiddenSeries.has(series.label)
4815
4833
  }));
4816
4834
  }, [dataSorted, hiddenSeries]);
4835
+ const visibleSeries = (0, react.useMemo)(() => dataSorted.filter((series) => isSeriesVisible(series.label)), [dataSorted, isSeriesVisible]);
4836
+ const visibleSeriesKey = (0, react.useMemo)(() => JSON.stringify(visibleSeries.map((series) => series.label)), [visibleSeries]);
4817
4837
  const allSeriesHidden = (0, react.useMemo)(() => {
4818
4838
  return seriesWithVisibility.every(({ isVisible }) => !isVisible);
4819
4839
  }, [seriesWithVisibility]);
@@ -4844,21 +4864,21 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
4844
4864
  options?.yScale
4845
4865
  ]);
4846
4866
  const activateSelectedPoint = (0, react.useCallback)((index) => {
4847
- const series = dataSorted[0];
4848
- const datum = series?.data[index];
4849
- if (series && datum) onDatumActivate?.({
4850
- datum,
4867
+ const series = visibleSeries.find((candidate) => candidate.data[index]);
4868
+ if (series) onDatumActivate?.({
4869
+ datum: series.data[index],
4851
4870
  index,
4852
4871
  key: series.label
4853
4872
  });
4854
- }, [dataSorted, onDatumActivate]);
4873
+ }, [visibleSeries, onDatumActivate]);
4855
4874
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
4856
4875
  selectedIndex,
4857
4876
  setSelectedIndex,
4858
4877
  isNavigating,
4859
4878
  setIsNavigating,
4860
4879
  chartRef,
4861
- totalPoints: dataSorted[0]?.data.length || 0,
4880
+ totalPoints: Math.max(0, ...visibleSeries.map((series) => series.data.length)),
4881
+ visibleSeriesKey,
4862
4882
  onActivate: activateSelectedPoint,
4863
4883
  preventTooltipScroll: tooltipPlacement === "below-axis"
4864
4884
  });
@@ -4877,7 +4897,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
4877
4897
  }
4878
4898
  return min <= max ? [min, max] : void 0;
4879
4899
  }, [dataSorted, isSeriesVisible]);
4880
- const hasWholeNumberValues = (0, react.useMemo)(() => hasOnlyWholeNumbers(dataSorted.filter((series) => isSeriesVisible(series.label))), [dataSorted, isSeriesVisible]);
4900
+ const hasWholeNumberValues = (0, react.useMemo)(() => hasOnlyWholeNumbers(visibleSeries), [visibleSeries]);
4881
4901
  const chartOptions = (0, react.useMemo)(() => {
4882
4902
  const fallbackYDomain = getFallbackYDomain(visibleReadingExtent, options?.yScale?.type === "log");
4883
4903
  return {
@@ -4923,11 +4943,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
4923
4943
  formatting,
4924
4944
  isSeriesVisible
4925
4945
  ]);
4926
- const bucketInfo = (0, react.useMemo)(() => getBucketInfo(dataSorted.filter((series) => isSeriesVisible(series.label)), options?.axis?.x?.tickResolution), [
4927
- dataSorted,
4928
- isSeriesVisible,
4929
- options?.axis?.x?.tickResolution
4930
- ]);
4946
+ const bucketInfo = (0, react.useMemo)(() => getBucketInfo(visibleSeries, options?.axis?.x?.tickResolution), [visibleSeries, options?.axis?.x?.tickResolution]);
4931
4947
  const tooltipRenderGlyph = (0, react.useMemo)(() => {
4932
4948
  return (props) => {
4933
4949
  const seriesIndex = dataSorted.findIndex((series) => series.label === props.key || series.data.includes(props.datum));
@@ -5157,7 +5173,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
5157
5173
  selectedIndex,
5158
5174
  tooltipRef,
5159
5175
  keyboardFocusedClassName: line_chart_module_default["line-chart__tooltip--keyboard-focused"],
5160
- series: dataSorted
5176
+ series: visibleSeries
5161
5177
  }),
5162
5178
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LineChartScalesRef, {
5163
5179
  chartRef: internalChartRef,
@@ -5817,13 +5833,6 @@ const getBarValue = (point) => {
5817
5833
  */
5818
5834
  const isBarRendered = (point) => Number.isFinite(getBarValue(point));
5819
5835
  /**
5820
- * How many bars visx renders for a list of points.
5821
- *
5822
- * @param points - Data points handed to visx.
5823
- * @return The number of points that get a bar.
5824
- */
5825
- const countRenderedBars = (points) => points.filter(isBarRendered).length;
5826
- /**
5827
5836
  * An explicit domain for the value scale, or null to let visx fit one to the data.
5828
5837
  *
5829
5838
  * Zero goes into the domain here rather than through the scale's `zero` flag: visx applies
@@ -6085,8 +6094,7 @@ const createGroupScale = (keys, bandwidth, padding) => (0, _visx_scale.scaleBand
6085
6094
  //#endregion
6086
6095
  //#region src/charts/bar-chart/private/comparison-bars-geometry.ts
6087
6096
  /**
6088
- * Mirrors visx's getScaleBaseline so comparison shadows and primary bars
6089
- * share the same baseline, including out-of-domain zero values.
6097
+ * Mirrors visx's getScaleBaseline so bars drawn outside visx share the baseline of its bars, including out-of-domain zero values.
6090
6098
  *
6091
6099
  * @param {ValueScale} scale - The continuous value scale.
6092
6100
  * @return {number} The baseline output position in pixels.
@@ -6100,36 +6108,33 @@ function getValueScaleBaseline(scale) {
6100
6108
  return Number.isFinite(maybeZero) ? Math.max(maybeZero, minOutput) : minOutput;
6101
6109
  }
6102
6110
  /**
6103
- * Compute the rect for a comparison "shadow" bar, centered on the paired
6104
- * primary bar slot and scaled by `widthFactor`.
6105
- *
6106
- * @param {object} params - Geometry inputs.
6107
- * @param {boolean} params.horizontal - True for a horizontal bar chart, false for vertical.
6108
- * @param {number} params.bandPosition - bandScale(category): start px of the category band.
6109
- * @param {number} params.slotOffset - groupScale(primaryKey): offset of the primary slot within the band.
6110
- * @param {number} params.slotThickness - groupScale.bandwidth(): primary bar thickness in px.
6111
- * @param {number} params.valuePosition - valueScale(value): output px for the bar's data value.
6112
- * @param {number} params.baseline - getValueScaleBaseline(valueScale): zero-line output px.
6113
- * @param {number} params.widthFactor - Shadow thickness multiplier, e.g. 1.5 for 150% width.
6114
- * @return {ComparisonRect} The {x, y, width, height} of the shadow rect.
6115
- */
6116
- function computeComparisonRect(params) {
6117
- const { horizontal, bandPosition, slotOffset, slotThickness, valuePosition, baseline, widthFactor } = params;
6118
- const slotStart = bandPosition + slotOffset;
6119
- const shadowThickness = slotThickness * widthFactor;
6120
- const shadowStart = slotStart + slotThickness / 2 - shadowThickness / 2;
6121
- const valueStart = Math.min(valuePosition, baseline);
6122
- const valueLength = Math.abs(baseline - valuePosition);
6111
+ * Compute the rect that spans `valuePositions` along the value axis, centered on a slot and scaled by `widthFactor` across it.
6112
+ *
6113
+ * @param {object} params - Geometry inputs.
6114
+ * @param {boolean} params.horizontal - True for a horizontal bar chart, false for vertical.
6115
+ * @param {number} params.bandPosition - bandScale(category): start px of the category band.
6116
+ * @param {number} params.slotOffset - groupScale(slotKey): offset of the slot within the band.
6117
+ * @param {number} params.slotThickness - groupScale.bandwidth(): slot thickness in px.
6118
+ * @param {number[]} params.valuePositions - Output px the rect must reach, such as the baseline and each bar's value.
6119
+ * @param {number} params.widthFactor - Thickness multiplier, e.g. 0.5 for half the slot.
6120
+ * @return {SlotRect} The {x, y, width, height} of the rect.
6121
+ */
6122
+ function computeSlotRect(params) {
6123
+ const { horizontal, bandPosition, slotOffset, slotThickness, valuePositions, widthFactor } = params;
6124
+ const thickness = slotThickness * widthFactor;
6125
+ const start = bandPosition + slotOffset + slotThickness / 2 - thickness / 2;
6126
+ const valueStart = Math.min(...valuePositions);
6127
+ const valueLength = Math.max(...valuePositions) - valueStart;
6123
6128
  if (horizontal) return {
6124
6129
  x: valueStart,
6125
- y: shadowStart,
6130
+ y: start,
6126
6131
  width: valueLength,
6127
- height: shadowThickness
6132
+ height: thickness
6128
6133
  };
6129
6134
  return {
6130
- x: shadowStart,
6135
+ x: start,
6131
6136
  y: valueStart,
6132
- width: shadowThickness,
6137
+ width: thickness,
6133
6138
  height: valueLength
6134
6139
  };
6135
6140
  }
@@ -6178,6 +6183,518 @@ const ClassifiedBarSeries = ({ barClassName, primaryKeys, groupPadding, ...props
6178
6183
  renderGlyph
6179
6184
  });
6180
6185
  };
6186
+ //#endregion
6187
+ //#region src/charts/bar-chart/private/slotted-bar-series.tsx
6188
+ const SlottedBarSeries = ({ barClassName, slotKeys, slotKey, thickness, groupPadding, ...props }) => {
6189
+ const { xScale, yScale, horizontal } = (0, react.useContext)(_visx_xychart.DataContext);
6190
+ const bandScale = horizontal ? yScale : xScale;
6191
+ const valueScale = horizontal ? xScale : yScale;
6192
+ const groupScale = (0, react.useMemo)(() => createGroupScale(slotKeys, bandScale?.bandwidth?.() ?? 0, groupPadding), [
6193
+ slotKeys,
6194
+ bandScale,
6195
+ groupPadding
6196
+ ]);
6197
+ const renderGlyph = (0, react.useCallback)(({ datum, x, y, color }) => {
6198
+ if (!bandScale?.bandwidth || !valueScale) return null;
6199
+ const baseline = getValueScaleBaseline(valueScale);
6200
+ const rect = computeSlotRect({
6201
+ horizontal: !!horizontal,
6202
+ bandPosition: Number(bandScale((horizontal ? props.yAccessor : props.xAccessor)(datum))),
6203
+ slotOffset: groupScale(slotKey) ?? 0,
6204
+ slotThickness: groupScale.bandwidth(),
6205
+ valuePositions: [baseline, horizontal ? x : y],
6206
+ widthFactor: thickness
6207
+ });
6208
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
6209
+ ...rect,
6210
+ className: (0, clsx.default)("visx-bar", barClassName?.(datum)),
6211
+ fill: color
6212
+ });
6213
+ }, [
6214
+ bandScale,
6215
+ valueScale,
6216
+ horizontal,
6217
+ groupScale,
6218
+ slotKey,
6219
+ thickness,
6220
+ props.xAccessor,
6221
+ props.yAccessor,
6222
+ barClassName
6223
+ ]);
6224
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.GlyphSeries, {
6225
+ ...props,
6226
+ enableEvents: false,
6227
+ renderGlyph
6228
+ });
6229
+ };
6230
+ //#endregion
6231
+ //#region src/charts/private/selected-ring/selected-ring.module.scss
6232
+ var selected_ring_module_default = {
6233
+ "selected-ring__contrast": "a8ccharts-gx1_AW-selected-ring__contrast",
6234
+ "selected-ring__edge": "a8ccharts-gx1_AW-selected-ring__edge",
6235
+ "selected-ring__far": "a8ccharts-gx1_AW-selected-ring__far",
6236
+ "selected-ring__focus": "a8ccharts-gx1_AW-selected-ring__focus"
6237
+ };
6238
+ //#endregion
6239
+ //#region src/charts/private/selected-ring/selected-ring.tsx
6240
+ const TONES = {
6241
+ inside: ["contrast", "focus"],
6242
+ outside: ["focus", "contrast"]
6243
+ };
6244
+ const EVERYWHERE = "M-1e6 -1e6H1e6V1e6H-1e6Z";
6245
+ /**
6246
+ * Encodes `value` for an SVG id, losslessly so distinct chart ids never share a clip path.
6247
+ *
6248
+ * @param value - Any string.
6249
+ * @return `value` with each character outside `[A-Za-z0-9-]` written as `_<hex code point>_`.
6250
+ */
6251
+ const toSvgId = (value) => Array.from(value, (character) => /[A-Za-z0-9-]/.test(character) ? character : `_${character.codePointAt(0)?.toString(16)}_`).join("");
6252
+ /**
6253
+ * Marks a selected shape with a focus-colored ring and a contrast ring, so it reads against any fill.
6254
+ *
6255
+ * @param {SelectedRingProps} props - Component props
6256
+ * @return {JSX.Element} The ring
6257
+ */
6258
+ const SelectedRing = ({ chartId, d, side, testId }) => {
6259
+ const clipId = `selected-ring-clip-${toSvgId(chartId)}`;
6260
+ const clipPath = `url(#${clipId})`;
6261
+ const [far, edge] = TONES[side];
6262
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
6263
+ pointerEvents: "none",
6264
+ "aria-hidden": "true",
6265
+ children: [
6266
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("clipPath", {
6267
+ id: clipId,
6268
+ children: side === "inside" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
6269
+ d: `${EVERYWHERE} ${d}`,
6270
+ clipRule: "evenodd"
6271
+ })
6272
+ }),
6273
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
6274
+ d,
6275
+ clipPath,
6276
+ className: (0, clsx.default)(selected_ring_module_default["selected-ring__far"], selected_ring_module_default[`selected-ring__${far}`])
6277
+ }),
6278
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
6279
+ d,
6280
+ clipPath,
6281
+ className: (0, clsx.default)(selected_ring_module_default["selected-ring__edge"], selected_ring_module_default[`selected-ring__${edge}`])
6282
+ })
6283
+ ]
6284
+ });
6285
+ };
6286
+ //#endregion
6287
+ //#region src/charts/bar-chart/private/selected-bar-ring.tsx
6288
+ /**
6289
+ * Marks the keyboard-selected slot with a two-tone ring outside its edge, so the ring reads against any bar color.
6290
+ *
6291
+ * @param props - Component props.
6292
+ * @param props.chartId - The chart's id, which keeps the clip path unique on a page with several charts.
6293
+ * @param props.selected - The selected slot.
6294
+ * @param props.slotKeys - The slots, in the order the band lays them out.
6295
+ * @param props.groupPadding - The padding between slots.
6296
+ * @param props.horizontal - Whether the bars run horizontally.
6297
+ * @param props.xAccessor - Reads a datum's x value.
6298
+ * @param props.yAccessor - Reads a datum's y value.
6299
+ * @return The ring, or nothing when the slot has no position.
6300
+ */
6301
+ const SelectedBarRing = ({ chartId, selected, slotKeys, groupPadding, horizontal, xAccessor, yAccessor }) => {
6302
+ const context = (0, react.useContext)(_visx_xychart.DataContext);
6303
+ const bandScale = horizontal ? context?.yScale : context?.xScale;
6304
+ const valueScale = horizontal ? context?.xScale : context?.yScale;
6305
+ const bandwidth = bandScale?.bandwidth?.() ?? 0;
6306
+ const [category] = selected.data;
6307
+ if (!bandScale || !valueScale || !bandwidth || !category) return null;
6308
+ const groupScale = createGroupScale(slotKeys, bandwidth, groupPadding);
6309
+ const slotOffset = groupScale(selected.slotKey);
6310
+ const bandPosition = Number(bandScale((horizontal ? yAccessor : xAccessor)(category)));
6311
+ if (slotOffset == null || !Number.isFinite(bandPosition)) return null;
6312
+ const valuePositions = selected.data.map((datum) => Number(valueScale(Number((horizontal ? xAccessor : yAccessor)(datum)))));
6313
+ const rect = computeSlotRect({
6314
+ horizontal,
6315
+ bandPosition,
6316
+ slotOffset,
6317
+ slotThickness: groupScale.bandwidth(),
6318
+ valuePositions: [getValueScaleBaseline(valueScale), ...valuePositions],
6319
+ widthFactor: selected.thickness
6320
+ });
6321
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedRing, {
6322
+ chartId,
6323
+ d: `M${rect.x} ${rect.y}h${rect.width}v${rect.height}h${-rect.width}Z`,
6324
+ side: "outside",
6325
+ testId: "bar-chart-selected-ring"
6326
+ });
6327
+ };
6328
+ //#endregion
6329
+ //#region src/charts/bar-chart/private/bar-layer.tsx
6330
+ /**
6331
+ * Draws the bars in front of any comparison shadows, slotted for an overlap layout and grouped by visx otherwise, then the selected bar's ring.
6332
+ *
6333
+ * @param props - Component props.
6334
+ * @param props.chartId - The chart's id, which keeps the ring's clip path unique on a page with several charts.
6335
+ * @param props.overlapSlots - The overlap layout, or undefined for a grouped one.
6336
+ * @param props.selectedBar - The bar or slot the keyboard has selected.
6337
+ * @param props.primaryEntries - The visible primary series, for a grouped layout.
6338
+ * @param props.primaryKeys - Their labels, in group order.
6339
+ * @param props.getBarBackground - Reads a series' bar fill.
6340
+ * @param props.barClassName - Adds classes to a bar.
6341
+ * @param props.groupPadding - The padding between the bars of a category.
6342
+ * @param props.horizontal - Whether the bars run horizontally.
6343
+ * @param props.xAccessor - Reads a datum's x value.
6344
+ * @param props.yAccessor - Reads a datum's y value.
6345
+ * @return The bars.
6346
+ */
6347
+ const BarLayer = ({ chartId, overlapSlots, selectedBar, primaryEntries, primaryKeys, getBarBackground, barClassName, groupPadding, horizontal, xAccessor, yAccessor }) => {
6348
+ let bars;
6349
+ if (overlapSlots) bars = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
6350
+ className: "visx-bar-group",
6351
+ children: overlapSlots.entries.map(({ series, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlottedBarSeries, {
6352
+ dataKey: series.label,
6353
+ data: series.data,
6354
+ xAccessor,
6355
+ yAccessor,
6356
+ colorAccessor: getBarBackground(index),
6357
+ barClassName,
6358
+ slotKeys: overlapSlots.slotKeys,
6359
+ slotKey: overlapSlots.slotOf(series.label),
6360
+ thickness: overlapSlots.thicknessOf(series.label),
6361
+ groupPadding
6362
+ }, series.label))
6363
+ });
6364
+ else if (barClassName) bars = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
6365
+ className: "visx-bar-group",
6366
+ children: primaryEntries.map(({ series, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClassifiedBarSeries, {
6367
+ dataKey: series.label,
6368
+ data: series.data,
6369
+ xAccessor,
6370
+ yAccessor,
6371
+ colorAccessor: getBarBackground(index),
6372
+ barClassName,
6373
+ primaryKeys,
6374
+ groupPadding
6375
+ }, series.label))
6376
+ });
6377
+ else bars = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarGroup, {
6378
+ padding: groupPadding,
6379
+ children: primaryEntries.map(({ series, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarSeries, {
6380
+ dataKey: series.label,
6381
+ data: series.data,
6382
+ xAccessor,
6383
+ yAccessor,
6384
+ colorAccessor: getBarBackground(index)
6385
+ }, series.label))
6386
+ });
6387
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [bars, selectedBar && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedBarRing, {
6388
+ chartId,
6389
+ selected: selectedBar,
6390
+ slotKeys: overlapSlots?.slotKeys ?? primaryKeys,
6391
+ groupPadding,
6392
+ horizontal,
6393
+ xAccessor,
6394
+ yAccessor
6395
+ })] });
6396
+ };
6397
+ //#endregion
6398
+ //#region src/charts/bar-chart/private/slot-tooltip-members.tsx
6399
+ /**
6400
+ * Fills the tooltip with every bar of the keyboard-selected category, and only those.
6401
+ *
6402
+ * The last member shown becomes the tooltip's nearest datum, so the caller lists the keyboard stop last.
6403
+ *
6404
+ * @param props - Component props.
6405
+ * @param props.members - The category's bars, the keyboard stop last.
6406
+ * @param props.index - The selected data point.
6407
+ * @return No visual content.
6408
+ */
6409
+ function SlotTooltipMembers({ members, index }) {
6410
+ const { showTooltip, updateTooltip } = (0, react.useContext)(_visx_xychart.TooltipContext) ?? {};
6411
+ (0, react.useEffect)(() => {
6412
+ updateTooltip?.((state) => ({
6413
+ ...state,
6414
+ tooltipData: void 0
6415
+ }));
6416
+ members.forEach(({ key, datum }) => showTooltip?.({
6417
+ key,
6418
+ datum,
6419
+ index
6420
+ }));
6421
+ }, [members, index]);
6422
+ return null;
6423
+ }
6424
+ //#endregion
6425
+ //#region src/charts/bar-chart/private/overlap-bars.ts
6426
+ const PRIMARY_SLOT = "primary";
6427
+ const COMPARISON_SLOT = "comparison";
6428
+ const OVERLAP_SLOTS = [PRIMARY_SLOT, COMPARISON_SLOT];
6429
+ const SEARCH_STEPS = 16;
6430
+ /**
6431
+ * Places each series of an overlap layout: primaries in one slot and comparisons in the other, with each `group` a layer, back to front in order of first appearance.
6432
+ *
6433
+ * @param series - The series to place.
6434
+ * @return Each series' placement, by index.
6435
+ */
6436
+ function getOverlapPlacements(series) {
6437
+ const layerByGroup = /* @__PURE__ */ new Map();
6438
+ series.forEach(({ group }) => {
6439
+ if (!layerByGroup.has(group)) layerByGroup.set(group, layerByGroup.size);
6440
+ });
6441
+ const layerCount = layerByGroup.size;
6442
+ return series.map((item) => {
6443
+ const layer = layerByGroup.get(item.group) ?? 0;
6444
+ return {
6445
+ slot: item.options?.type === "comparison" ? COMPARISON_SLOT : PRIMARY_SLOT,
6446
+ layer,
6447
+ thickness: layerCount > 1 ? 1 - layer * .5 / (layerCount - 1) : 1
6448
+ };
6449
+ });
6450
+ }
6451
+ /**
6452
+ * Blends `from` toward `to` and returns the blend nearest the point where `passes` changes, on its passing side.
6453
+ *
6454
+ * @param from - Where the blend starts.
6455
+ * @param to - Where the blend ends.
6456
+ * @param passes - A test that changes at most once along the blend.
6457
+ * @return A hex color.
6458
+ */
6459
+ function blendToEdge(from, to, passes) {
6460
+ const passesAtStart = passes(from);
6461
+ let low = 0;
6462
+ let high = 1;
6463
+ for (let step = 0; step < SEARCH_STEPS; step++) {
6464
+ const middle = (low + high) / 2;
6465
+ if (passes(mixHexColors(from, to, middle)) === passesAtStart) low = middle;
6466
+ else high = middle;
6467
+ }
6468
+ return mixHexColors(from, to, passesAtStart ? low : high);
6469
+ }
6470
+ const awayFrom = (background) => contrastRatio(background, "#000000") >= contrastRatio(background, "#ffffff") ? "#000000" : "#ffffff";
6471
+ const opposite = (end) => end === "#000000" ? "#ffffff" : "#000000";
6472
+ /**
6473
+ * The tone of a color nearest the background that keeps 3:1 against it.
6474
+ *
6475
+ * @param color - A hex color.
6476
+ * @param background - The hex chart background.
6477
+ * @return A hex color.
6478
+ */
6479
+ function tintWithContrast(color, background) {
6480
+ const passes = (tone) => contrastRatio(tone, background) >= 3;
6481
+ return blendToEdge(color, passes(color) ? background : awayFrom(background), passes);
6482
+ }
6483
+ /**
6484
+ * The tone of a color nearest the original that keeps 3:1 against the bar it is drawn over, on the far side of that bar from the background when that side can reach 3:1.
6485
+ *
6486
+ * @param color - A hex color.
6487
+ * @param under - The hex color of the bar behind it.
6488
+ * @param background - The hex chart background.
6489
+ * @return A hex color.
6490
+ */
6491
+ function shadeWithContrast(color, under, background) {
6492
+ const passes = (tone) => contrastRatio(tone, under) >= 3;
6493
+ const start = contrastRatio(color, background) > contrastRatio(under, background) ? color : under;
6494
+ if (passes(start)) return start;
6495
+ const away = awayFrom(background);
6496
+ return blendToEdge(start, passes(away) ? away : opposite(away), passes);
6497
+ }
6498
+ /**
6499
+ * The colors an overlap layout fills its series with: per slot, the back layer tinted to 3:1 against the background and each layer in front shaded to 3:1 against the one behind. A slot whose front-most series has its own `stroke` keeps its colors as given.
6500
+ *
6501
+ * @param series - Every series of the chart.
6502
+ * @param colors - Each series' resolved color.
6503
+ * @param slotColors - The palette color of each slot, primaries first.
6504
+ * @param background - The hex chart background.
6505
+ * @return Each series' fill, by index.
6506
+ */
6507
+ function getOverlapBarColors(series, colors, slotColors, background) {
6508
+ const placements = getOverlapPlacements(series);
6509
+ const barColors = [...colors];
6510
+ OVERLAP_SLOTS.forEach((slot, slotIndex) => {
6511
+ const members = placements.flatMap((placement, index) => placement.slot === slot ? [{
6512
+ index,
6513
+ layer: placement.layer
6514
+ }] : []);
6515
+ if (!members.length) return;
6516
+ const layers = [...new Set(members.map(({ layer }) => layer))].sort((a, b) => a - b);
6517
+ const back = members.find(({ layer }) => layer === layers[0]);
6518
+ const base = back && series[back.index].options?.stroke ? colors[back.index] : slotColors[slotIndex];
6519
+ if (members.some(({ index, layer }) => layer === layers[layers.length - 1] && !!series[index].options?.stroke) || !isValidHexColor(base) || !isValidHexColor(background)) return;
6520
+ const toneByLayer = /* @__PURE__ */ new Map();
6521
+ let under;
6522
+ layers.forEach((layer) => {
6523
+ const tone = under === void 0 ? tintWithContrast(base, background) : shadeWithContrast(base, under, background);
6524
+ toneByLayer.set(layer, tone);
6525
+ under = tone;
6526
+ });
6527
+ members.forEach(({ index, layer }) => {
6528
+ barColors[index] = toneByLayer.get(layer) ?? colors[index];
6529
+ });
6530
+ });
6531
+ return barColors;
6532
+ }
6533
+ //#endregion
6534
+ //#region src/charts/bar-chart/private/use-overlap-slots.ts
6535
+ /**
6536
+ * Whether two data points sit at the same category: the same label, or the same date.
6537
+ *
6538
+ * @param point - A data point.
6539
+ * @param category - The data point naming the category.
6540
+ * @return Whether `point` is at `category`.
6541
+ */
6542
+ function isSameCategory(point, category) {
6543
+ return category.label != null ? point.label === category.label : !!category.date && !!point.date && point.date.getTime() === category.date.getTime();
6544
+ }
6545
+ /**
6546
+ * Lays out the visible series of an overlap layout in slots and layers.
6547
+ *
6548
+ * @param enabled - Whether the chart draws an overlap layout.
6549
+ * @param seriesWithVisibility - Every series, with its index and whether it is visible.
6550
+ * @return The layout, or undefined when the chart is not an overlap layout.
6551
+ */
6552
+ function useOverlapSlots(enabled, seriesWithVisibility) {
6553
+ return (0, react.useMemo)(() => {
6554
+ if (!enabled) return;
6555
+ const visible = seriesWithVisibility.filter(({ isVisible }) => isVisible);
6556
+ const placements = getOverlapPlacements(visible.map(({ series }) => series));
6557
+ const placed = visible.map(({ series, index }, position) => ({
6558
+ entry: {
6559
+ series,
6560
+ index
6561
+ },
6562
+ placement: placements[position]
6563
+ })).sort((a, b) => a.placement.layer - b.placement.layer);
6564
+ const entries = placed.map(({ entry }) => entry);
6565
+ const placementByKey = new Map(placed.map(({ entry, placement }) => [entry.series.label, placement]));
6566
+ const slotOf = (key) => placementByKey.get(key)?.slot ?? "primary";
6567
+ const thicknessOf = (key) => placementByKey.get(key)?.thickness ?? 1;
6568
+ const hasPrimaries = seriesWithVisibility.some(({ series }) => series.options?.type !== "comparison");
6569
+ const showsComparisons = entries.some(({ series }) => slotOf(series.label) === COMPARISON_SLOT);
6570
+ const slotKeys = [...hasPrimaries ? [PRIMARY_SLOT] : [], ...showsComparisons ? [COMPARISON_SLOT] : []];
6571
+ return {
6572
+ entries,
6573
+ keys: entries.map(({ series }) => series.label),
6574
+ slotKeys,
6575
+ slotOf,
6576
+ thicknessOf,
6577
+ positions: getStopPositions(entries, slotKeys, slotOf)
6578
+ };
6579
+ }, [enabled, seriesWithVisibility]);
6580
+ }
6581
+ /**
6582
+ * The category a data point sits at, as a key two points share when `isSameCategory` holds.
6583
+ *
6584
+ * @param point - A data point.
6585
+ * @return The category key.
6586
+ */
6587
+ function categoryKey(point) {
6588
+ return point.label != null ? `label:${point.label}` : `date:${point.date?.getTime()}`;
6589
+ }
6590
+ /**
6591
+ * Lists a stop for every slot at every category that slot draws a bar at, category by category.
6592
+ *
6593
+ * @param entries - The visible series, back to front.
6594
+ * @param slotKeys - The slots, in band order.
6595
+ * @param slotOf - The slot a series sits in.
6596
+ * @return The positions, in keyboard order.
6597
+ */
6598
+ function getStopPositions(entries, slotKeys, slotOf) {
6599
+ const categories = /* @__PURE__ */ new Map();
6600
+ entries.forEach(({ series }) => {
6601
+ series.data.forEach((datum, dataPointIndex) => {
6602
+ const key = categoryKey(datum);
6603
+ const slots = categories.get(key) ?? /* @__PURE__ */ new Map();
6604
+ categories.set(key, slots);
6605
+ if (!isBarRendered(datum)) return;
6606
+ const slotKey = slotOf(series.label);
6607
+ slots.set(slotKey, {
6608
+ slotKey,
6609
+ key: series.label,
6610
+ datum,
6611
+ dataPointIndex
6612
+ });
6613
+ });
6614
+ });
6615
+ const ordered = [...categories.values()].filter((slots) => slots.size > 0);
6616
+ if (ordered.every((slots) => [...slots.values()].every(({ datum }) => datum.label == null && datum.date))) {
6617
+ const time = (slots) => [...slots.values()][0].datum.date?.getTime() ?? 0;
6618
+ ordered.sort((a, b) => time(a) - time(b));
6619
+ }
6620
+ return ordered.flatMap((slots) => slotKeys.flatMap((slotKey) => {
6621
+ const position = slots.get(slotKey);
6622
+ return position ? [position] : [];
6623
+ }));
6624
+ }
6625
+ /**
6626
+ * The slot a keyboard index selects, and every bar at its category.
6627
+ *
6628
+ * @param slots - The overlap layout.
6629
+ * @param selectedIndex - The keyboard index into `slots.positions`.
6630
+ * @return The selected slot, or undefined when nothing is selected or drawn there.
6631
+ */
6632
+ function getSelectedSlot(slots, selectedIndex) {
6633
+ const position = selectedIndex === void 0 ? void 0 : slots?.positions[selectedIndex];
6634
+ if (!slots || !position) return;
6635
+ const { slotKey, key: stopKey, datum: category, dataPointIndex } = position;
6636
+ const members = slots.entries.flatMap(({ series }) => {
6637
+ const datum = series.data.find((point) => isSameCategory(point, category));
6638
+ return datum ? [{
6639
+ key: series.label,
6640
+ datum
6641
+ }] : [];
6642
+ });
6643
+ const slotBars = members.filter(({ key, datum }) => isBarRendered(datum) && slots.slotOf(key) === slotKey);
6644
+ return {
6645
+ data: slotBars.map(({ datum }) => datum),
6646
+ slotKey,
6647
+ thickness: Math.max(...slotBars.map(({ key }) => slots.thicknessOf(key))),
6648
+ index: dataPointIndex,
6649
+ members: [...members.filter(({ key }) => key !== stopKey), ...members.filter(({ key }) => key === stopKey)]
6650
+ };
6651
+ }
6652
+ //#endregion
6653
+ //#region src/charts/bar-chart/private/use-bar-stops.ts
6654
+ /**
6655
+ * Lays out the keyboard stops: one per slot and category in an overlap layout, else one per primary bar, series-major within each category.
6656
+ *
6657
+ * @param overlapSlots - The overlap layout, or undefined for a grouped one.
6658
+ * @param primaryEntries - The visible primary series.
6659
+ * @return The stops.
6660
+ */
6661
+ function useBarStops(overlapSlots, primaryEntries) {
6662
+ const series = (0, react.useMemo)(() => overlapSlots ? [] : primaryEntries.map((entry) => entry.series), [overlapSlots, primaryEntries]);
6663
+ return {
6664
+ count: overlapSlots?.positions.length ?? Math.max(0, ...series.map((item) => item.data.length)) * series.length,
6665
+ series,
6666
+ key: (0, react.useMemo)(() => JSON.stringify(overlapSlots?.keys ?? series.map((item) => item.label)), [overlapSlots, series]),
6667
+ barAt: (0, react.useCallback)((stop) => {
6668
+ if (overlapSlots) {
6669
+ const position = overlapSlots.positions[stop];
6670
+ return position && {
6671
+ datum: position.datum,
6672
+ index: position.dataPointIndex,
6673
+ key: position.key
6674
+ };
6675
+ }
6676
+ if (!series.length) return;
6677
+ const index = Math.floor(stop / series.length);
6678
+ const item = series[stop % series.length];
6679
+ const datum = item?.data[index];
6680
+ return item && datum ? {
6681
+ datum,
6682
+ index,
6683
+ key: item.label
6684
+ } : void 0;
6685
+ }, [overlapSlots, series]),
6686
+ stopOf: (0, react.useCallback)((seriesKey, dataPointIndex) => {
6687
+ if (overlapSlots) {
6688
+ const hovered = overlapSlots.entries.find((entry) => entry.series.label === seriesKey)?.series.data[dataPointIndex];
6689
+ const slotKey = overlapSlots.slotOf(seriesKey);
6690
+ const stop = hovered ? overlapSlots.positions.findIndex((position) => position.slotKey === slotKey && isSameCategory(position.datum, hovered)) : -1;
6691
+ return stop >= 0 ? stop : void 0;
6692
+ }
6693
+ const seriesIndex = series.findIndex((item) => item.label === seriesKey);
6694
+ return seriesIndex >= 0 ? dataPointIndex * series.length + seriesIndex : void 0;
6695
+ }, [overlapSlots, series])
6696
+ };
6697
+ }
6181
6698
  /**
6182
6699
  * Upper clamp on the computed group padding, so bars can never collapse to zero width
6183
6700
  * even at very large `widthFactor` values.
@@ -6213,9 +6730,7 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
6213
6730
  data: series,
6214
6731
  index
6215
6732
  });
6216
- const opacity = barStyles?.opacity ?? .5;
6217
6733
  const widthFactor = barStyles?.widthFactor ?? 1.5;
6218
- const fill = resolveFill(entry);
6219
6734
  series.data.forEach((datum, i) => {
6220
6735
  const bandPosition = Number(bandScale(bandAccessor(datum)));
6221
6736
  const valuePosition = Number(valueScale(Number(valueAccessor(datum))));
@@ -6223,13 +6738,12 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
6223
6738
  if (process.env.NODE_ENV !== "production" && !Number.isFinite(bandPosition)) console.warn(`[Charts] ComparisonBars: datum key "${String(bandAccessor(datum))}" did not match any primary category. Shadow will not be rendered. Ensure comparison series data uses the same label/date keys as the primary series.`);
6224
6739
  return;
6225
6740
  }
6226
- const rect = computeComparisonRect({
6741
+ const rect = computeSlotRect({
6227
6742
  horizontal,
6228
6743
  bandPosition,
6229
6744
  slotOffset,
6230
6745
  slotThickness,
6231
- valuePosition,
6232
- baseline,
6746
+ valuePositions: [baseline, valuePosition],
6233
6747
  widthFactor
6234
6748
  });
6235
6749
  rects.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
@@ -6238,8 +6752,8 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
6238
6752
  y: rect.y,
6239
6753
  width: rect.width,
6240
6754
  height: rect.height,
6241
- fill,
6242
- opacity
6755
+ fill: resolveFill(entry, datum),
6756
+ opacity: barStyles?.opacity
6243
6757
  }, `${index}-${i}`));
6244
6758
  });
6245
6759
  });
@@ -6338,7 +6852,8 @@ function nearestBandIndex(data, accessor, scale, position) {
6338
6852
  /**
6339
6853
  * Correct visx's range-based band inversion, which omits outer padding.
6340
6854
  * @param root0 - Registered series selection.
6341
- * @param root0.keys - Visible series keys.
6855
+ * @param root0.keys - Visible series keys, back to front where bars overlap.
6856
+ * @param root0.slots - Where series share slots: the slots of a category, in the order the band lays them out, and the slot each series sits in.
6342
6857
  * @param root0.groupPadding - Padding between grouped bars.
6343
6858
  * @param root0.withTooltips - Whether pointer events update the tooltip.
6344
6859
  * @param root0.onPointerDown - Receives the corrected pointer-down datum.
@@ -6346,12 +6861,13 @@ function nearestBandIndex(data, accessor, scale, position) {
6346
6861
  * @param root0.onPointerMove - Receives the corrected datum under a moving pointer.
6347
6862
  * @return No visual content.
6348
6863
  */
6349
- function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointerUp, onPointerMove }) {
6864
+ function BandTooltip({ keys, slots, groupPadding, withTooltips, onPointerDown, onPointerUp, onPointerMove }) {
6350
6865
  const { xScale, yScale, dataRegistry, horizontal } = (0, react.useContext)(_visx_xychart.DataContext);
6351
- const { showTooltip } = (0, react.useContext)(_visx_xychart.TooltipContext);
6866
+ const { showTooltip, hideTooltip, updateTooltip } = (0, react.useContext)(_visx_xychart.TooltipContext);
6352
6867
  const scale = horizontal ? yScale : xScale;
6353
6868
  const bandwidth = scale?.bandwidth?.() ?? 0;
6354
- const groupScale = (0, react.useMemo)(() => createGroupScale(keys, bandwidth, groupPadding), [
6869
+ const groupScale = (0, react.useMemo)(() => createGroupScale(slots?.keys ?? keys, bandwidth, groupPadding), [
6870
+ slots,
6355
6871
  keys,
6356
6872
  bandwidth,
6357
6873
  groupPadding
@@ -6359,18 +6875,21 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
6359
6875
  const getSelections = (0, react.useCallback)((params) => {
6360
6876
  const point = params?.svgPoint;
6361
6877
  if (!point || !scale?.bandwidth) return [];
6878
+ const position = horizontal ? point.y : point.x;
6362
6879
  const selections = [];
6880
+ const bands = [];
6363
6881
  for (const key of keys) {
6364
6882
  const entry = dataRegistry?.get(key);
6365
6883
  if (!entry) continue;
6366
6884
  const accessor = horizontal ? entry.yAccessor : entry.xAccessor;
6367
- const index = nearestBandIndex(entry.data, accessor, scale, horizontal ? point.y : point.x);
6885
+ const index = nearestBandIndex(entry.data, accessor, scale, position);
6368
6886
  if (index < 0) continue;
6369
6887
  const datum = entry.data[index];
6370
- const start = Number(scale(accessor(datum))) + Number(groupScale(key));
6888
+ const band = Number(scale(accessor(datum)));
6889
+ const start = band + Number(groupScale(slots ? slots.slotOf(key) : key));
6371
6890
  const end = start + groupScale.step();
6372
- const position = horizontal ? point.y : point.x;
6373
6891
  const distance = position >= start && position <= end ? 0 : Math.abs(position - (start + end) / 2);
6892
+ bands.push(band);
6374
6893
  selections.push({
6375
6894
  event: params.event,
6376
6895
  key,
@@ -6381,12 +6900,16 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
6381
6900
  distanceY: horizontal ? distance : 0
6382
6901
  });
6383
6902
  }
6384
- return selections;
6903
+ if (!slots) return selections;
6904
+ const bandDistance = (band) => Math.abs(band + scale.bandwidth() / 2 - position);
6905
+ const hoveredBand = bands.reduce((best, band) => best === void 0 || bandDistance(band) < bandDistance(best) ? band : best, void 0);
6906
+ return selections.filter((_, index) => bands[index] === hoveredBand);
6385
6907
  }, [
6386
6908
  scale,
6387
6909
  dataRegistry,
6388
6910
  horizontal,
6389
6911
  keys,
6912
+ slots,
6390
6913
  groupScale
6391
6914
  ]);
6392
6915
  const handlePointer = (0, react.useCallback)((params) => {
@@ -6396,24 +6919,162 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
6396
6919
  return !best || distance(selection) <= distance(best) ? selection : best;
6397
6920
  }, void 0);
6398
6921
  if (params?.event.type === "pointermove" && nearest) onPointerMove?.(nearest);
6399
- if (withTooltips && params?.event.type !== "pointerup") selections.forEach(showTooltip);
6922
+ if (withTooltips && params?.event.type !== "pointerup") {
6923
+ if (slots) updateTooltip((state) => ({
6924
+ ...state,
6925
+ tooltipData: void 0
6926
+ }));
6927
+ selections.forEach(showTooltip);
6928
+ }
6400
6929
  const callback = params?.event.type === "pointerdown" ? onPointerDown : onPointerUp;
6401
6930
  if (params?.event.type === "pointermove" || !callback) return;
6402
6931
  if (nearest) callback(nearest);
6403
6932
  }, [
6404
6933
  getSelections,
6405
6934
  withTooltips,
6935
+ slots,
6406
6936
  showTooltip,
6937
+ updateTooltip,
6407
6938
  onPointerDown,
6408
6939
  onPointerUp,
6409
6940
  onPointerMove
6410
6941
  ]);
6411
6942
  (0, _visx_xychart.useEventEmitter)("pointermove", withTooltips ? handlePointer : void 0);
6943
+ (0, _visx_xychart.useEventEmitter)("pointerout", withTooltips && slots ? hideTooltip : void 0);
6412
6944
  (0, _visx_xychart.useEventEmitter)("pointerdown", handlePointer);
6413
6945
  (0, _visx_xychart.useEventEmitter)("pointerup", onPointerUp ? handlePointer : void 0);
6414
6946
  return null;
6415
6947
  }
6416
6948
  //#endregion
6949
+ //#region src/charts/bar-chart/private/grouped-bars.ts
6950
+ /**
6951
+ * Pairs each comparison series with the primary of its `group`, or with the only primary.
6952
+ *
6953
+ * @param series - The chart's series.
6954
+ * @return The index of each paired comparison's primary, keyed by the comparison's index.
6955
+ */
6956
+ function getComparisonPrimaries(series) {
6957
+ const primaries = series.flatMap((item, index) => item.options?.type === "comparison" ? [] : [{
6958
+ group: item.group,
6959
+ index
6960
+ }]);
6961
+ const primaryByGroup = new Map(primaries.map(({ group, index }) => [group, index]));
6962
+ const onlyPrimary = primaries.length === 1 ? primaries[0].index : void 0;
6963
+ const pairs = /* @__PURE__ */ new Map();
6964
+ series.forEach((item, index) => {
6965
+ const primary = item.options?.type === "comparison" ? primaryByGroup.get(item.group) ?? onlyPrimary : void 0;
6966
+ if (primary !== void 0) pairs.set(index, primary);
6967
+ });
6968
+ return pairs;
6969
+ }
6970
+ /**
6971
+ * The colors a grouped bar chart fills its series with. A comparison takes the lightest tint of its primary's color that keeps 3:1 against the background, and the primary in front of it the shade that keeps 3:1 against that tint. A primary with its own `stroke` keeps it, and a comparison with its own, different `stroke` keeps the pair's colors as given.
6972
+ *
6973
+ * @param series - The chart's series.
6974
+ * @param colors - Each series' resolved color.
6975
+ * @param background - The hex chart background.
6976
+ * @return Each series' fill, by index.
6977
+ */
6978
+ function getGroupedBarColors(series, colors, background) {
6979
+ const barColors = [...colors];
6980
+ getComparisonPrimaries(series).forEach((primary, comparison) => {
6981
+ const base = colors[primary];
6982
+ if (!!series[comparison].options?.stroke && colors[comparison].toLowerCase() !== base.toLowerCase() || !isValidHexColor(base) || !isValidHexColor(background)) return;
6983
+ const tint = tintWithContrast(base, background);
6984
+ barColors[comparison] = tint;
6985
+ if (!series[primary].options?.stroke) barColors[primary] = shadeWithContrast(base, tint, background);
6986
+ });
6987
+ return barColors;
6988
+ }
6989
+ //#endregion
6990
+ //#region src/charts/bar-chart/use-bar-colors.ts
6991
+ /**
6992
+ * Reads the colors `BarChart` fills series with, so a custom tooltip or legend can match the bars.
6993
+ *
6994
+ * @return A function from series and a layout to each series' bar color, by index.
6995
+ */
6996
+ function useBarColors() {
6997
+ const { getElementStyles, backgroundColor = BACKGROUND_FALLBACK, theme } = useGlobalChartsContext();
6998
+ const comparisonOpacity = theme.barChart?.barStyles?.comparison?.opacity;
6999
+ return (0, react.useCallback)((series, { layout = "grouped" } = {}) => {
7000
+ const colors = series.map((data, index) => getElementStyles({
7001
+ data,
7002
+ index
7003
+ }).color);
7004
+ if (layout !== "overlap") return comparisonOpacity === void 0 ? getGroupedBarColors(series, colors, backgroundColor) : colors;
7005
+ return getOverlapBarColors(series, colors, [getElementStyles({ index: 0 }).color, getElementStyles({ index: 1 }).color], backgroundColor);
7006
+ }, [
7007
+ getElementStyles,
7008
+ backgroundColor,
7009
+ comparisonOpacity
7010
+ ]);
7011
+ }
7012
+ //#endregion
7013
+ //#region src/charts/bar-chart/use-bar-legend-items.ts
7014
+ const isComparison = (data, label) => data.some((series) => series.label === label && series.options?.type === "comparison");
7015
+ /**
7016
+ * Applies what a bar layout changes about a legend: overlap lists every primary before every comparison, as it draws their slots, with every swatch opaque like its bar; a collapsed grouped item takes its comparison's tint, the wider bar that frames the group.
7017
+ *
7018
+ * @param data - The series the items were built from.
7019
+ * @param items - The legend items.
7020
+ * @param layout - The layout the bars are drawn in.
7021
+ * @param seriesColors - Each series' bar color, by index.
7022
+ * @return The legend items.
7023
+ */
7024
+ function forBarLayout(data, items, layout, seriesColors) {
7025
+ if (layout === "overlap") {
7026
+ const opaque = items.map((item) => item.shapeStyle ? {
7027
+ ...item,
7028
+ shapeStyle: {
7029
+ ...item.shapeStyle,
7030
+ opacity: void 0
7031
+ }
7032
+ } : item);
7033
+ const seriesItems = opaque.filter((item) => item.seriesLabels);
7034
+ const firstLabel = (item) => item.seriesLabels?.[0];
7035
+ return [
7036
+ ...seriesItems.filter((item) => !isComparison(data, firstLabel(item))),
7037
+ ...seriesItems.filter((item) => isComparison(data, firstLabel(item))),
7038
+ ...opaque.filter((item) => !item.seriesLabels)
7039
+ ];
7040
+ }
7041
+ return items.map((item) => {
7042
+ if ((item.seriesLabels?.length ?? 0) < 2) return item;
7043
+ const comparisonIndex = data.findIndex((series) => series.options?.type === "comparison" && item.seriesLabels?.includes(series.label));
7044
+ return comparisonIndex === -1 ? item : {
7045
+ ...item,
7046
+ color: seriesColors[comparisonIndex]
7047
+ };
7048
+ });
7049
+ }
7050
+ /**
7051
+ * Builds legend items that match `BarChart`'s bars in a layout, for a custom legend or a chart's own.
7052
+ *
7053
+ * @param data - The series the chart draws.
7054
+ * @param options - The legend options, and the layout the bars are drawn in.
7055
+ * @param legendShape - The shape of the legend swatches.
7056
+ * @return The legend items.
7057
+ */
7058
+ function useBarLegendItems(data, options = {}, legendShape) {
7059
+ const { layout } = options;
7060
+ const getBarColors = useBarColors();
7061
+ const seriesColors = (0, react.useMemo)(() => layout ? getBarColors(data, { layout }) : void 0, [
7062
+ data,
7063
+ layout,
7064
+ getBarColors
7065
+ ]);
7066
+ const items = useChartLegendItems(data, (0, react.useMemo)(() => ({
7067
+ ...options,
7068
+ seriesColors
7069
+ }), [options, seriesColors]), legendShape);
7070
+ return (0, react.useMemo)(() => layout && seriesColors ? forBarLayout(data, items, layout, seriesColors) : items, [
7071
+ data,
7072
+ items,
7073
+ layout,
7074
+ seriesColors
7075
+ ]);
7076
+ }
7077
+ //#endregion
6417
7078
  //#region src/charts/bar-chart/bar-chart.tsx
6418
7079
  const validateData$2 = (data) => {
6419
7080
  if (!data?.length) return (0, _wordpress_i18n.__)("No data available", "jetpack-charts");
@@ -6425,13 +7086,14 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ (0, react_jsx_runtime
6425
7086
  className: bar_chart_module_default["bar-chart__tooltip-row"],
6426
7087
  children: (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("%1$s: %2$s", "jetpack-charts"), label, value)
6427
7088
  });
6428
- const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, tooltipPlacement, tooltipAnchorTop, tooltipStyle, barClassName, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, withBandHighlight = false, onBandHighlightChange, defaultHiddenSeries, animation, children, gap = "md", onPointerDown, onPointerUp, onDatumActivate }) => {
7089
+ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, tooltipPlacement, tooltipAnchorTop, tooltipStyle, barClassName, options = {}, orientation = "vertical", layout = "grouped", withPatterns = false, showZeroValues = false, withBandHighlight = false, onBandHighlightChange, defaultHiddenSeries, animation, children, gap = "md", onPointerDown, onPointerUp, onDatumActivate }) => {
6429
7090
  if (!withTooltips && (withBandHighlight || onBandHighlightChange)) warnOnce("bar-chart-band-highlight-without-tooltips", "BarChart: withBandHighlight and onBandHighlightChange require withTooltips.");
6430
7091
  const legendInteractive = legend.interactive ?? false;
6431
7092
  const legendCollapseGroups = legend.collapseGroups ?? false;
6432
7093
  const legendComparisonItem = legend.comparisonItem ?? false;
6433
7094
  const legendComparisonSeriesItems = legend.comparisonSeriesItems;
6434
7095
  const horizontal = orientation === "horizontal";
7096
+ const isOverlap = layout === "overlap";
6435
7097
  const chartId = useChartId(providedChartId);
6436
7098
  const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
6437
7099
  const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
@@ -6441,16 +7103,25 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6441
7103
  enabled: showZeroValues,
6442
7104
  valueAxisLength: horizontal ? width : height
6443
7105
  });
6444
- const legendItems = useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({
7106
+ const legendItems = useBarLegendItems(dataSorted, (0, react.useMemo)(() => ({
6445
7107
  collapseGroups: legendCollapseGroups,
6446
7108
  comparisonItem: legendComparisonItem,
6447
- comparisonSeriesItems: legendComparisonSeriesItems
7109
+ comparisonSeriesItems: legendComparisonSeriesItems,
7110
+ layout
6448
7111
  }), [
6449
7112
  legendCollapseGroups,
6450
7113
  legendComparisonItem,
6451
- legendComparisonSeriesItems
7114
+ legendComparisonSeriesItems,
7115
+ layout
6452
7116
  ]));
6453
- const { getElementStyles } = useGlobalChartsContext();
7117
+ const { getElementStyles, theme: providerTheme } = useGlobalChartsContext();
7118
+ const getBarColors = useBarColors();
7119
+ const barColors = (0, react.useMemo)(() => getBarColors(dataSorted, { layout }), [
7120
+ layout,
7121
+ getBarColors,
7122
+ dataSorted
7123
+ ]);
7124
+ if (providerTheme.barChart?.barStyles?.comparison?.opacity !== void 0) warnOnce("bar-chart-comparison-opacity", "BarChart: theme.barChart.barStyles.comparison.opacity is deprecated. Comparison bars are opaque; leave it unset.");
6454
7125
  const isSeriesRendered = (0, react.useCallback)((series) => isSeriesVisible(series.label), [isSeriesVisible]);
6455
7126
  const hasWholeNumberValues = (0, react.useMemo)(() => hasOnlyWholeNumbers(dataSorted.filter(isSeriesRendered)), [dataSorted, isSeriesRendered]);
6456
7127
  const chartOptions = useBarChartOptions(dataWithVisibleZeros, horizontal, options, isSeriesRendered);
@@ -6479,37 +7150,30 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6479
7150
  return seriesWithVisibility.every(({ isVisible }) => !isVisible);
6480
7151
  }, [seriesWithVisibility]);
6481
7152
  const primaryEntries = (0, react.useMemo)(() => seriesWithVisibility.filter(({ isVisible, series }) => isVisible && series.options?.type !== "comparison"), [seriesWithVisibility]);
6482
- const totalPoints = Math.max(0, ...primaryEntries.map((e) => e.series.data.length)) * primaryEntries.length;
7153
+ const overlapSlots = useOverlapSlots(isOverlap, seriesWithVisibility);
6483
7154
  const primaryKeys = (0, react.useMemo)(() => primaryEntries.map(({ series }) => series.label), [primaryEntries]);
6484
- const primarySeries = (0, react.useMemo)(() => primaryEntries.map(({ series }) => series), [primaryEntries]);
6485
- const activateSelectedBar = (0, react.useCallback)((index) => {
6486
- const primaryCount = primaryEntries.length;
6487
- if (!primaryCount) return;
6488
- const dataPointIndex = Math.floor(index / primaryCount);
6489
- const series = primaryEntries[index % primaryCount]?.series;
6490
- const datum = series?.data[dataPointIndex];
6491
- if (series && datum) onDatumActivate?.({
6492
- datum,
6493
- index: dataPointIndex,
6494
- key: series.label
6495
- });
6496
- }, [primaryEntries, onDatumActivate]);
6497
- const visibleSeriesKey = (0, react.useMemo)(() => JSON.stringify(primaryKeys), [primaryKeys]);
7155
+ const stops = useBarStops(overlapSlots, primaryEntries);
7156
+ const { barAt, stopOf } = stops;
7157
+ const activateSelectedBar = (0, react.useCallback)((stop) => {
7158
+ const bar = barAt(stop);
7159
+ if (bar) onDatumActivate?.(bar);
7160
+ }, [barAt, onDatumActivate]);
6498
7161
  const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown, onChartPointerMove } = useKeyboardNavigation({
6499
7162
  selectedIndex,
6500
7163
  setSelectedIndex,
6501
7164
  isNavigating,
6502
7165
  setIsNavigating,
6503
7166
  chartRef,
6504
- totalPoints,
7167
+ totalPoints: stops.count,
6505
7168
  onActivate: activateSelectedBar,
6506
- visibleSeriesKey
7169
+ visibleSeriesKey: stops.key
6507
7170
  });
6508
7171
  const handlePointerMove = (0, react.useCallback)(({ key, index }) => {
6509
- const seriesIndex = primaryKeys.indexOf(key);
6510
- if (seriesIndex >= 0) onChartPointerMove(index * primaryKeys.length + seriesIndex);
6511
- }, [primaryKeys, onChartPointerMove]);
7172
+ const stop = stopOf(key, index);
7173
+ if (stop !== void 0) onChartPointerMove(stop);
7174
+ }, [stopOf, onChartPointerMove]);
6512
7175
  const comparisonEntries = (0, react.useMemo)(() => {
7176
+ if (isOverlap) return [];
6513
7177
  const primaryByGroup = new Map(primaryEntries.map(({ series, index }) => [series.group, {
6514
7178
  label: series.label,
6515
7179
  index
@@ -6525,12 +7189,12 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6525
7189
  entries.push({
6526
7190
  series,
6527
7191
  index,
6528
- primaryKey: primary.label,
6529
- primaryIndex: primary.index
7192
+ primaryKey: primary.label
6530
7193
  });
6531
7194
  });
6532
7195
  return entries;
6533
7196
  }, [
7197
+ isOverlap,
6534
7198
  seriesWithVisibility,
6535
7199
  primaryEntries,
6536
7200
  primaryKeys
@@ -6570,23 +7234,32 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6570
7234
  chartOptions.yScale,
6571
7235
  horizontal
6572
7236
  ]);
6573
- const getBarBackground = (0, react.useCallback)((index) => (datum) => withPatterns ? `url(#${getPatternId(chartId, index)})` : datum.color ?? getElementStyles({
6574
- data: dataSorted[index],
6575
- index
6576
- }).color, [
7237
+ const getBarBackground = (0, react.useCallback)((index) => (datum) => withPatterns ? `url(#${getPatternId(chartId, index)})` : datum.color ?? barColors[index] ?? "", [
6577
7238
  withPatterns,
6578
- getElementStyles,
6579
- dataSorted,
7239
+ barColors,
6580
7240
  chartId
6581
7241
  ]);
6582
- const resolveComparisonFill = (0, react.useCallback)((entry) => withPatterns ? `url(#${getPatternId(chartId, entry.primaryIndex)})` : getElementStyles({
6583
- data: entry.series,
6584
- index: entry.index
6585
- }).color, [
7242
+ const resolveComparisonFill = (0, react.useCallback)((entry, datum) => withPatterns ? `url(#${getPatternId(chartId, entry.index)})` : datum.color ?? barColors[entry.index] ?? "", [
6586
7243
  withPatterns,
6587
7244
  chartId,
6588
- getElementStyles
7245
+ barColors
6589
7246
  ]);
7247
+ const renderOverlapTooltip = (0, react.useCallback)(({ tooltipData }) => {
7248
+ const nearestDatum = tooltipData?.nearestDatum?.datum;
7249
+ if (!nearestDatum) return null;
7250
+ const datumByKey = tooltipData?.datumByKey ?? {};
7251
+ const categoryLabel = chartOptions.tooltip.labelFormatter(nearestDatum.label || (nearestDatum.date ? nearestDatum.date.getTime() : 0), 0, []);
7252
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7253
+ className: bar_chart_module_default["bar-chart__tooltip"],
7254
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7255
+ className: bar_chart_module_default["bar-chart__tooltip-header"],
7256
+ children: categoryLabel
7257
+ }), dataSorted.flatMap(({ label }) => {
7258
+ const datum = datumByKey[label]?.datum;
7259
+ return datum ? [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderTooltipRow(label, formatReading(datum.value)) }, label)] : [];
7260
+ })]
7261
+ });
7262
+ }, [chartOptions.tooltip, dataSorted]);
6590
7263
  const renderDefaultTooltip = (0, react.useCallback)(({ tooltipData }) => {
6591
7264
  const nearestDatum = tooltipData?.nearestDatum?.datum;
6592
7265
  if (!nearestDatum) return null;
@@ -6661,26 +7334,24 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6661
7334
  }
6662
7335
  `;
6663
7336
  }, [chartId]);
6664
- const createKeyboardHighlightStyle = (0, react.useCallback)(() => {
6665
- if (selectedIndex === void 0) return "";
6666
- const primaryCount = primaryEntries.length;
6667
- if (!primaryCount) return "";
6668
- const dataPointIndex = Math.floor(selectedIndex / primaryCount);
6669
- const seriesIndex = selectedIndex % primaryCount;
6670
- const seriesData = primaryEntries[seriesIndex]?.series;
6671
- const datum = seriesData?.data[dataPointIndex];
6672
- if (!seriesData || !datum || !isBarRendered(datum)) return "";
6673
- const domBarIndex = primaryEntries.slice(0, seriesIndex).reduce((count, { series }) => count + countRenderedBars(series.data), 0) + countRenderedBars(seriesData.data.slice(0, dataPointIndex));
6674
- return `
6675
- .bar-chart[data-chart-id="bar-chart-${chartId}"] .visx-bar-group .visx-bar:nth-child(${domBarIndex + 1}) {
6676
- stroke: #005fcc;
6677
- stroke-width: 2px;
6678
- }
6679
- `;
7337
+ const overlapBandSlots = (0, react.useMemo)(() => overlapSlots && {
7338
+ keys: overlapSlots.slotKeys,
7339
+ slotOf: overlapSlots.slotOf
7340
+ }, [overlapSlots]);
7341
+ const selectedSlot = (0, react.useMemo)(() => getSelectedSlot(overlapSlots, selectedIndex), [overlapSlots, selectedIndex]);
7342
+ const selectedBar = (0, react.useMemo)(() => {
7343
+ if (overlapSlots) return selectedSlot;
7344
+ const bar = selectedIndex === void 0 ? void 0 : barAt(selectedIndex);
7345
+ return bar && isBarRendered(bar.datum) ? {
7346
+ data: [bar.datum],
7347
+ slotKey: bar.key,
7348
+ thickness: 1
7349
+ } : void 0;
6680
7350
  }, [
7351
+ overlapSlots,
7352
+ selectedSlot,
6681
7353
  selectedIndex,
6682
- primaryEntries,
6683
- chartId
7354
+ barAt
6684
7355
  ]);
6685
7356
  const error = validateData$2(dataSorted);
6686
7357
  const isDataValid = !error;
@@ -6701,7 +7372,6 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6701
7372
  children: error
6702
7373
  });
6703
7374
  const gridVisibility = gridVisibilityProp ?? chartOptions.gridVisibility;
6704
- const highlightedBarStyle = createKeyboardHighlightStyle();
6705
7375
  const legendPosition = legend.position ?? "bottom";
6706
7376
  const legendElement = showLegend && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Legend, {
6707
7377
  orientation: legend.orientation ?? "horizontal",
@@ -6781,14 +7451,7 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6781
7451
  tickValues: (horizontal ? void 0 : valueTicks) ?? chartOptions.axis.y.tickValues
6782
7452
  })] })
6783
7453
  }),
6784
- withPatterns && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: dataSorted.map((seriesData, index) => renderPattern(index, getElementStyles({
6785
- data: seriesData,
6786
- index
6787
- }).color)) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: dataSorted.map((seriesData, index) => createPatternBorderStyle(index, getElementStyles({
6788
- data: seriesData,
6789
- index
6790
- }).color)) })] }),
6791
- highlightedBarStyle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: highlightedBarStyle }),
7454
+ withPatterns && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: barColors.map((color, index) => renderPattern(index, color)) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: barColors.map((color, index) => createPatternBorderStyle(index, color)) })] }),
6792
7455
  allSeriesHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SvgEmptyState, {
6793
7456
  x: width / 2,
6794
7457
  y: chartHeight / 2,
@@ -6806,30 +7469,22 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6806
7469
  getElementStyles,
6807
7470
  resolveFill: resolveComparisonFill
6808
7471
  }),
6809
- barClassName ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
6810
- className: "visx-bar-group",
6811
- children: primaryEntries.map(({ series: seriesData, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClassifiedBarSeries, {
6812
- dataKey: seriesData.label,
6813
- data: seriesData.data,
6814
- xAccessor: chartOptions.accessors.xAccessor,
6815
- yAccessor: chartOptions.accessors.yAccessor,
6816
- colorAccessor: getBarBackground(index),
6817
- barClassName,
6818
- primaryKeys,
6819
- groupPadding
6820
- }, seriesData.label))
6821
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarGroup, {
6822
- padding: groupPadding,
6823
- children: primaryEntries.map(({ series: seriesData, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarSeries, {
6824
- dataKey: seriesData?.label,
6825
- data: seriesData.data,
6826
- yAccessor: chartOptions.accessors.yAccessor,
6827
- xAccessor: chartOptions.accessors.xAccessor,
6828
- colorAccessor: getBarBackground(index)
6829
- }, seriesData?.label))
7472
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BarLayer, {
7473
+ chartId,
7474
+ overlapSlots,
7475
+ selectedBar,
7476
+ primaryEntries,
7477
+ primaryKeys,
7478
+ getBarBackground,
7479
+ barClassName,
7480
+ groupPadding,
7481
+ horizontal,
7482
+ xAccessor: chartOptions.accessors.xAccessor,
7483
+ yAccessor: chartOptions.accessors.yAccessor
6830
7484
  }),
6831
7485
  (withTooltips || onPointerDown || onPointerUp) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BandTooltip, {
6832
- keys: primaryKeys,
7486
+ keys: overlapSlots?.keys ?? primaryKeys,
7487
+ slots: overlapBandSlots,
6833
7488
  groupPadding,
6834
7489
  withTooltips,
6835
7490
  onPointerDown,
@@ -6846,6 +7501,10 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6846
7501
  ...!horizontal && valueTicks ? { tickValues: valueTicks } : {}
6847
7502
  })] })
6848
7503
  }),
7504
+ withTooltips && selectedSlot && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlotTooltipMembers, {
7505
+ members: selectedSlot.members,
7506
+ index: selectedSlot.index
7507
+ }),
6849
7508
  withTooltips && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XYChartTooltip, {
6850
7509
  tooltipPlacement,
6851
7510
  tooltipAnchorTop,
@@ -6853,11 +7512,11 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
6853
7512
  detectBounds: true,
6854
7513
  snapTooltipToDatumX: true,
6855
7514
  snapTooltipToDatumY: true,
6856
- renderTooltip: renderTooltip || renderDefaultTooltip,
7515
+ renderTooltip: renderTooltip || (isOverlap ? renderOverlapTooltip : renderDefaultTooltip),
6857
7516
  selectedIndex,
6858
7517
  tooltipRef,
6859
7518
  keyboardFocusedClassName: bar_chart_module_default["bar-chart__tooltip--keyboard-focused"],
6860
- series: primarySeries,
7519
+ series: stops.series,
6861
7520
  mode: "individual"
6862
7521
  })
6863
7522
  ]
@@ -9517,7 +10176,7 @@ function warning(message) {
9517
10176
  logged.add(message);
9518
10177
  }
9519
10178
  //#endregion
9520
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
10179
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
9521
10180
  function useUpdateEffect(effect, deps) {
9522
10181
  const mountedRef = (0, react.useRef)(false);
9523
10182
  (0, react.useEffect)(() => {
@@ -10612,7 +11271,7 @@ function serializeStyles(args, registered, mergedProps) {
10612
11271
  }
10613
11272
  react["useInsertionEffect"] && react["useInsertionEffect"] || react.useLayoutEffect;
10614
11273
  //#endregion
10615
- //#region ../../../node_modules/.pnpm/@emotion+react@11.14.0_@types+react@18.3.31_react@18.3.1/node_modules/@emotion/react/dist/emotion-element-d59e098f.esm.js
11274
+ //#region ../../../node_modules/.pnpm/@emotion+react@11.14.0_@types+react@19.3.0_react@19.3.0/node_modules/@emotion/react/dist/emotion-element-d59e098f.esm.js
10616
11275
  var isBrowser = typeof document !== "undefined";
10617
11276
  var EmotionCacheContext = /* #__PURE__ */ react.createContext(typeof HTMLElement !== "undefined" ? /* #__PURE__ */ createCache({ key: "css" }) : null);
10618
11277
  EmotionCacheContext.Provider;
@@ -10721,7 +11380,7 @@ _createEmotion.css;
10721
11380
  _createEmotion.sheet;
10722
11381
  _createEmotion.cache;
10723
11382
  //#endregion
10724
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
11383
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
10725
11384
  var isSerializedStyles = (o) => typeof o !== "undefined" && o !== null && ["name", "styles"].every((p) => typeof o[p] !== "undefined");
10726
11385
  var useCx = () => {
10727
11386
  const cache = __unsafe_useEmotionCache();
@@ -10828,7 +11487,7 @@ function memize(fn, options) {
10828
11487
  return memoized;
10829
11488
  }
10830
11489
  //#endregion
10831
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
11490
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
10832
11491
  var import_es6 = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
10833
11492
  module.exports = function equal(a, b) {
10834
11493
  if (a === b) return true;
@@ -10896,19 +11555,19 @@ var BaseContextSystemProvider = ({ children, value }) => {
10896
11555
  };
10897
11556
  (0, react.memo)(BaseContextSystemProvider);
10898
11557
  //#endregion
10899
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/constants.mjs
11558
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/constants.mjs
10900
11559
  var COMPONENT_NAMESPACE = "data-wp-component";
10901
11560
  var CONNECTED_NAMESPACE = "data-wp-c16t";
10902
11561
  var CONNECT_STATIC_NAMESPACE = "__contextSystemKey__";
10903
11562
  //#endregion
10904
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
11563
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
10905
11564
  var import_dist = require_dist();
10906
11565
  function getStyledClassName(namespace) {
10907
11566
  return `components-${(0, import_dist.paramCase)(namespace)}`;
10908
11567
  }
10909
11568
  var getStyledClassNameFromKey = memize(getStyledClassName);
10910
11569
  //#endregion
10911
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/context-connect.mjs
11570
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/context-connect.mjs
10912
11571
  function contextConnect(Component, namespace) {
10913
11572
  return _contextConnect(Component, namespace, { forwardsRef: true });
10914
11573
  }
@@ -10925,7 +11584,7 @@ function _contextConnect(Component, namespace, options) {
10925
11584
  });
10926
11585
  }
10927
11586
  //#endregion
10928
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/utils.mjs
11587
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/utils.mjs
10929
11588
  function getNamespace(componentName) {
10930
11589
  return { [COMPONENT_NAMESPACE]: componentName };
10931
11590
  }
@@ -10933,7 +11592,7 @@ function getConnectedNamespace() {
10933
11592
  return { [CONNECTED_NAMESPACE]: true };
10934
11593
  }
10935
11594
  //#endregion
10936
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
11595
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
10937
11596
  function useContextSystem(props, namespace) {
10938
11597
  const contextSystemProps = useComponentsContext();
10939
11598
  if (typeof namespace === "undefined") globalThis.SCRIPT_DEBUG === true && warning("useContextSystem: Please provide a namespace");
@@ -10955,7 +11614,7 @@ function useContextSystem(props, namespace) {
10955
11614
  return finalComponentProps;
10956
11615
  }
10957
11616
  //#endregion
10958
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
11617
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
10959
11618
  var visuallyHidden = {
10960
11619
  border: 0,
10961
11620
  clip: "rect(1px, 1px, 1px, 1px)",
@@ -10971,7 +11630,7 @@ var visuallyHidden = {
10971
11630
  wordBreak: "normal"
10972
11631
  };
10973
11632
  //#endregion
10974
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
11633
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
10975
11634
  var customAttributeRegExp = /^(data|aria|x)-/i;
10976
11635
  var eventHandlerRegExp = /^on[A-Z]/;
10977
11636
  var svgElementNames = new Set(`animate animateMotion animateTransform circle clipPath defs desc ellipse
@@ -11061,7 +11720,7 @@ function UnforwardedPolymorphicElement({ as, ...props }, ref) {
11061
11720
  }
11062
11721
  var PolymorphicElement = (0, react.forwardRef)(UnforwardedPolymorphicElement);
11063
11722
  //#endregion
11064
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/view/component.mjs
11723
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/view/component.mjs
11065
11724
  function UnforwardedView({ css, ...restProps }, ref) {
11066
11725
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PolymorphicElement, {
11067
11726
  ref,
@@ -11070,7 +11729,7 @@ function UnforwardedView({ css, ...restProps }, ref) {
11070
11729
  }
11071
11730
  var component_default$2 = Object.assign((0, react.forwardRef)(UnforwardedView), { selector: ".components-view" });
11072
11731
  //#endregion
11073
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
11732
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
11074
11733
  function UnconnectedVisuallyHidden(props, forwardedRef) {
11075
11734
  const { style: styleProp, ...contextProps } = useContextSystem(props, "VisuallyHidden");
11076
11735
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
@@ -11085,7 +11744,7 @@ function UnconnectedVisuallyHidden(props, forwardedRef) {
11085
11744
  }
11086
11745
  var component_default$1 = contextConnect(UnconnectedVisuallyHidden, "VisuallyHidden");
11087
11746
  //#endregion
11088
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
11747
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
11089
11748
  var breakpoints = [
11090
11749
  "40em",
11091
11750
  "52em",
@@ -11124,7 +11783,7 @@ function useResponsiveValue(values, options = {}) {
11124
11783
  return array[index >= array.length ? array.length - 1 : index];
11125
11784
  }
11126
11785
  //#endregion
11127
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/grid/utils.mjs
11786
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/grid/utils.mjs
11128
11787
  var ALIGNMENTS = {
11129
11788
  bottom: {
11130
11789
  alignItems: "flex-end",
@@ -11172,7 +11831,7 @@ function getAlignmentProps(alignment) {
11172
11831
  return alignment ? ALIGNMENTS[alignment] ?? {} : {};
11173
11832
  }
11174
11833
  //#endregion
11175
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/grid/hook.mjs
11834
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/grid/hook.mjs
11176
11835
  var STYLE_HASH_ATTRIBUTE = "data-wp-hash";
11177
11836
  function getRuntime() {
11178
11837
  const globalScope = globalThis;
@@ -11317,7 +11976,7 @@ function useGrid(props) {
11317
11976
  };
11318
11977
  }
11319
11978
  //#endregion
11320
- //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@18.3.7_@types+react@18_5beefdda355715e908adfea668830e1f/node_modules/@wordpress/components/build-module/grid/component.mjs
11979
+ //#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/grid/component.mjs
11321
11980
  function UnconnectedGrid(props, forwardedRef) {
11322
11981
  const gridProps = useGrid(props);
11323
11982
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
@@ -11924,51 +12583,6 @@ const PieSelectionOutput = ({ withTooltips, selectedIndex, selectedData, keyboar
11924
12583
  ] });
11925
12584
  };
11926
12585
  //#endregion
11927
- //#region src/charts/private/pie-keyboard-navigation/pie-keyboard-navigation.module.scss
11928
- var pie_keyboard_navigation_module_default = {
11929
- "selected-ring__contrast": "a8ccharts-V2HDKq-selected-ring__contrast",
11930
- "selected-ring__focus": "a8ccharts-V2HDKq-selected-ring__focus"
11931
- };
11932
- //#endregion
11933
- //#region src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx
11934
- /**
11935
- * Encodes `value` for an SVG id, losslessly so distinct chart ids never share a clip path.
11936
- *
11937
- * @param value - Any string.
11938
- * @return `value` with each character outside `[A-Za-z0-9-]` written as `_<hex code point>_`.
11939
- */
11940
- const toSvgId = (value) => Array.from(value, (character) => /[A-Za-z0-9-]/.test(character) ? character : `_${character.codePointAt(0)?.toString(16)}_`).join("");
11941
- /**
11942
- * Marks the selected segment with a two-tone ring drawn inside its edge, so the SVG edge cannot cut it off.
11943
- *
11944
- * @param {SelectedSegmentRingProps} props - Component props
11945
- * @return {JSX.Element} The ring
11946
- */
11947
- const SelectedSegmentRing = ({ chartId, d }) => {
11948
- const clipId = `pie-selected-ring-clip-${toSvgId(chartId)}`;
11949
- const clipPath = `url(#${clipId})`;
11950
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
11951
- pointerEvents: "none",
11952
- "aria-hidden": "true",
11953
- children: [
11954
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("clipPath", {
11955
- id: clipId,
11956
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d })
11957
- }),
11958
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
11959
- d,
11960
- clipPath,
11961
- className: pie_keyboard_navigation_module_default["selected-ring__contrast"]
11962
- }),
11963
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
11964
- d,
11965
- clipPath,
11966
- className: pie_keyboard_navigation_module_default["selected-ring__focus"]
11967
- })
11968
- ]
11969
- });
11970
- };
11971
- //#endregion
11972
12586
  //#region src/charts/private/pie-keyboard-navigation/use-pie-keyboard-navigation.ts
11973
12587
  const usePieKeyboardNavigation = ({ navigationArcs, withTooltips, tooltipOffsetX, tooltipOffsetY }) => {
11974
12588
  const chartRef = (0, react.useRef)(null);
@@ -12344,9 +12958,11 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
12344
12958
  selectedArc && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_group.Group, {
12345
12959
  top: centerY,
12346
12960
  left: centerX,
12347
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedSegmentRing, {
12961
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedRing, {
12348
12962
  chartId,
12349
- d: path(selectedArc) || ""
12963
+ d: path(selectedArc) || "",
12964
+ side: "inside",
12965
+ testId: "pie-selected-ring"
12350
12966
  })
12351
12967
  }),
12352
12968
  !allSegmentsHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_group.Group, {
@@ -12583,9 +13199,11 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
12583
13199
  selectedArc && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_group.Group, {
12584
13200
  top: height,
12585
13201
  left: width / 2,
12586
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedSegmentRing, {
13202
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedRing, {
12587
13203
  chartId,
12588
- d: path(selectedArc) || ""
13204
+ d: path(selectedArc) || "",
13205
+ side: "inside",
13206
+ testId: "pie-selected-ring"
12589
13207
  })
12590
13208
  }),
12591
13209
  !allSegmentsHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_group.Group, {
@@ -12885,6 +13503,8 @@ exports.parseHslString = parseHslString;
12885
13503
  exports.prefersLightText = prefersLightText;
12886
13504
  exports.relativeLuminance = relativeLuminance;
12887
13505
  exports.resolveCssVariable = resolveCssVariable;
13506
+ exports.useBarColors = useBarColors;
13507
+ exports.useBarLegendItems = useBarLegendItems;
12888
13508
  exports.useCalendarHeatmapData = useCalendarHeatmapData;
12889
13509
  exports.useChartFormatting = useChartFormatting;
12890
13510
  exports.useChartLegendItems = useChartLegendItems;