@automattic/charts 4.8.0 → 4.9.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 (56) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.cjs +624 -228
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +58 -8
  5. package/dist/index.d.cts +42 -8
  6. package/dist/index.d.ts +42 -8
  7. package/dist/index.js +628 -232
  8. package/dist/index.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/charts/area-chart/area-chart.tsx +5 -2
  11. package/src/charts/area-chart/test/area-chart.test.tsx +23 -14
  12. package/src/charts/area-chart/test/tooltip-resolution.test.tsx +2 -2
  13. package/src/charts/area-chart/types.ts +2 -0
  14. package/src/charts/bar-chart/bar-chart.tsx +5 -2
  15. package/src/charts/bar-chart/test/bar-chart.test.tsx +82 -63
  16. package/src/charts/bar-chart/types.ts +2 -0
  17. package/src/charts/bar-list-chart/test/bar-list-chart.test.tsx +3 -1
  18. package/src/charts/heatmap-chart/heatmap-chart.module.scss +10 -4
  19. package/src/charts/heatmap-chart/heatmap-chart.tsx +144 -37
  20. package/src/charts/heatmap-chart/private/column-groups.ts +79 -8
  21. package/src/charts/heatmap-chart/private/fit-compact-cells.ts +72 -0
  22. package/src/charts/heatmap-chart/private/heatmap-legend.tsx +14 -2
  23. package/src/charts/heatmap-chart/private/index.ts +4 -2
  24. package/src/charts/heatmap-chart/test/column-groups.test.ts +41 -4
  25. package/src/charts/heatmap-chart/test/fit-compact-cells.test.ts +59 -0
  26. package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +132 -1
  27. package/src/charts/heatmap-chart/types.ts +2 -0
  28. package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +46 -4
  29. package/src/charts/leaderboard-chart/leaderboard-chart.tsx +37 -1
  30. package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +53 -0
  31. package/src/charts/leaderboard-chart/types.ts +6 -1
  32. package/src/charts/line-chart/line-chart.tsx +5 -2
  33. package/src/charts/line-chart/private/line-chart-annotation-label-popover.tsx +19 -9
  34. package/src/charts/line-chart/test/default-tooltip-formatting.test.tsx +1 -1
  35. package/src/charts/line-chart/test/line-chart.test.tsx +45 -35
  36. package/src/charts/line-chart/test/tooltip-resolution.test.tsx +2 -2
  37. package/src/charts/line-chart/types.ts +5 -3
  38. package/src/charts/pie-chart/pie-chart.tsx +149 -139
  39. package/src/charts/pie-chart/test/pie-chart.test.tsx +171 -0
  40. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +122 -120
  41. package/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx +54 -0
  42. package/src/charts/private/chart-composition/use-chart-children.ts +11 -6
  43. package/src/charts/private/pie-keyboard-navigation/index.ts +4 -0
  44. package/src/charts/private/pie-keyboard-navigation/order-arcs-for-navigation.ts +10 -0
  45. package/src/charts/private/pie-keyboard-navigation/pie-keyboard-navigation.module.scss +16 -0
  46. package/src/charts/private/pie-keyboard-navigation/pie-selection-announcement.tsx +14 -0
  47. package/src/charts/private/pie-keyboard-navigation/pie-selection-output.tsx +82 -0
  48. package/src/charts/private/pie-keyboard-navigation/pie-tooltip.tsx +44 -0
  49. package/src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx +47 -0
  50. package/src/charts/private/pie-keyboard-navigation/test/order-arcs-for-navigation.test.ts +34 -0
  51. package/src/charts/private/pie-keyboard-navigation/test/selected-segment-ring.test.ts +14 -0
  52. package/src/charts/private/pie-keyboard-navigation/use-pie-keyboard-navigation.ts +139 -0
  53. package/src/charts/private/x-zoom/test/x-zoom.test.tsx +3 -2
  54. package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +20 -19
  55. package/src/components/tooltip/xy-chart-tooltip.tsx +3 -3
  56. package/src/types.ts +18 -2
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { AnimatedAreaSeries, AnimatedAreaStack, AreaSeries, Axis, BarGroup, BarS
4
4
  import { __, _x, sprintf } from "@wordpress/i18n";
5
5
  import clsx from "clsx";
6
6
  import * as React from "react";
7
- import { Children, Fragment, createContext, createContext as createContext$1, createElement, forwardRef, forwardRef as forwardRef$1, isValidElement, memo, useCallback, useCallback as useCallback$1, useContext, useContext as useContext$1, useEffect, useEffect as useEffect$1, useId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useRef, useRef as useRef$1, useState, useState as useState$1 } from "react";
7
+ import { Children, Fragment, createContext, createContext as createContext$1, createElement, forwardRef, forwardRef as forwardRef$1, isValidElement, memo, useCallback, useCallback as useCallback$1, useContext, useContext as useContext$1, useEffect, useEffect as useEffect$1, useId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useRef, useRef as useRef$1, useState, useState as useState$1, version } from "react";
8
8
  import { differenceInHours, differenceInYears, isValid, parse, parseISO } from "date-fns";
9
9
  import { tzOffset } from "@date-fns/tz";
10
10
  import { Text, getStringWidth } from "@visx/text";
@@ -15,9 +15,9 @@ import { useTooltip } from "@visx/tooltip";
15
15
  import { createScale, getTicks, scaleBand, scaleCanBeZeroed, scaleOrdinal, scaleTime } from "@visx/scale";
16
16
  import { Group } from "@visx/group";
17
17
  import { LegendItem, LegendLabel, LegendOrdinal, LegendShape } from "@visx/legend";
18
- import { IconButton, Stack, Text as Text$1 } from "@wordpress/ui";
18
+ import { IconButton, Stack, Text as Text$1, VisuallyHidden } from "@wordpress/ui";
19
19
  import { ThemeProvider } from "@wordpress/theme";
20
- import { createPortal } from "react-dom";
20
+ import { createPortal, flushSync } from "react-dom";
21
21
  import { LinearGradient } from "@visx/gradient";
22
22
  import { curveCatmullRom, curveLinear, curveMonotoneX } from "@visx/curve";
23
23
  import { useParentSize } from "@visx/responsive";
@@ -30,7 +30,7 @@ import { __assign } from "tslib";
30
30
  import "@babel/runtime/helpers/esm/extends";
31
31
  import "@babel/runtime/helpers/extends";
32
32
  import { Icon, chevronRight } from "@wordpress/icons";
33
- import { Pie } from "@visx/shape";
33
+ import { arc, pie } from "@visx/shape";
34
34
  //#region \0rolldown/runtime.js
35
35
  var __create = Object.create;
36
36
  var __defProp = Object.defineProperty;
@@ -3080,7 +3080,7 @@ const XYChartTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocu
3080
3080
  /** @deprecated Use `XYChartTooltip`. */
3081
3081
  const AccessibleTooltip = XYChartTooltip;
3082
3082
  const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints, onActivate, preventTooltipScroll = false, visibleSeriesKey }) => {
3083
- const getChartRoot = useCallback(() => chartRef.current?.closest("[role=\"grid\"]") ?? chartRef.current, [chartRef]);
3083
+ const getChartRoot = useCallback(() => chartRef.current?.closest("[role=\"application\"]") ?? chartRef.current, [chartRef]);
3084
3084
  const focusWithoutScrollIfNeeded = useCallback((element) => {
3085
3085
  if (preventTooltipScroll) element?.focus({ preventScroll: true });
3086
3086
  else element?.focus();
@@ -3295,11 +3295,13 @@ function useChartChildren(children, chartType) {
3295
3295
  }
3296
3296
  const displayName = child.type?.displayName;
3297
3297
  if (displayName === `${chartType}.SVG` || displayName === "Chart.SVG") {
3298
- if (child.props?.children) Children.forEach(child.props.children, (svgChild) => {
3298
+ const { children: svgChildren } = child.props ?? {};
3299
+ if (svgChildren) Children.forEach(svgChildren, (svgChild) => {
3299
3300
  svg.push(svgChild);
3300
3301
  });
3301
3302
  } else if (displayName === `${chartType}.HTML` || displayName === "Chart.HTML") {
3302
- if (child.props?.children) Children.forEach(child.props.children, (htmlChild) => {
3303
+ const { children: htmlChildren } = child.props ?? {};
3304
+ if (htmlChildren) Children.forEach(htmlChildren, (htmlChild) => {
3303
3305
  html.push(htmlChild);
3304
3306
  });
3305
3307
  } else if (child.type === Group) svg.push(child);
@@ -4121,6 +4123,7 @@ var line_chart_module_default = {
4121
4123
  "line-chart--animated": "a8ccharts-inuQka-line-chart--animated",
4122
4124
  "rise": "a8ccharts-inuQka-rise"
4123
4125
  };
4126
+ const isReact18 = version.startsWith("18.");
4124
4127
  const CloseIcon = () => /* @__PURE__ */ jsx("svg", {
4125
4128
  width: "16",
4126
4129
  height: "16",
@@ -4163,7 +4166,7 @@ const LineChartAnnotationLabelWithPopover = ({ title, subtitle, renderLabel, ren
4163
4166
  className: line_chart_module_default["line-chart__annotation-label"],
4164
4167
  children: [/* @__PURE__ */ jsx("button", {
4165
4168
  ref: buttonRef,
4166
- popovertarget: popoverId,
4169
+ ...isReact18 ? { popovertarget: popoverId } : { popoverTarget: popoverId },
4167
4170
  className: line_chart_module_default["line-chart__annotation-label-trigger-button"],
4168
4171
  style: {
4169
4172
  width: `44px`,
@@ -4191,8 +4194,13 @@ const LineChartAnnotationLabelWithPopover = ({ title, subtitle, renderLabel, ren
4191
4194
  subtitle
4192
4195
  })
4193
4196
  }), /* @__PURE__ */ jsx("button", {
4194
- popovertarget: popoverId,
4195
- popovertargetaction: "hide",
4197
+ ...isReact18 ? {
4198
+ popovertarget: popoverId,
4199
+ popovertargetaction: "hide"
4200
+ } : {
4201
+ popoverTarget: popoverId,
4202
+ popoverTargetAction: "hide"
4203
+ },
4196
4204
  className: line_chart_module_default["line-chart__annotation-label-popover-close-button"],
4197
4205
  "aria-label": __("Close", "jetpack-charts"),
4198
4206
  children: /* @__PURE__ */ jsx(CloseIcon, {})
@@ -4714,7 +4722,7 @@ const LineChartScalesRef = ({ chartRef, width, height, margin }) => {
4714
4722
  ]);
4715
4723
  return null;
4716
4724
  };
4717
- const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, tooltipPlacement, tooltipStyle, 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) => {
4725
+ const LineChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, tooltipPlacement, tooltipStyle, 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) => {
4718
4726
  const legendInteractive = legend.interactive ?? false;
4719
4727
  const legendCollapseGroups = legend.collapseGroups ?? false;
4720
4728
  const legendComparisonItem = legend.comparisonItem ?? false;
@@ -4982,8 +4990,8 @@ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4982
4990
  const chartHeight = contentHeight > 0 ? contentHeight : height;
4983
4991
  return /* @__PURE__ */ jsx("div", {
4984
4992
  ref: chartRef,
4985
- role: "grid",
4986
- "aria-label": __("Line chart", "jetpack-charts"),
4993
+ role: "application",
4994
+ "aria-label": ariaLabel ?? __("Line chart", "jetpack-charts"),
4987
4995
  tabIndex: 0,
4988
4996
  onKeyDown: onChartKeyDown,
4989
4997
  onFocus: onChartFocus,
@@ -4991,6 +4999,7 @@ const LineChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
4991
4999
  children: chartHeight > 0 && /* @__PURE__ */ jsxs("div", {
4992
5000
  className: xy_plot_module_default["xy-plot"],
4993
5001
  children: [zoomable && zoom.domain && /* @__PURE__ */ jsx(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ jsxs(XYChart, {
5002
+ accessibilityLabel: "",
4994
5003
  theme,
4995
5004
  width,
4996
5005
  height: chartHeight,
@@ -5231,7 +5240,7 @@ const HoverGlyphs = ({ visibleSeries, stacked, stackOffset, getElementStyles, st
5231
5240
  };
5232
5241
  //#endregion
5233
5242
  //#region src/charts/area-chart/area-chart.tsx
5234
- const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
5243
+ const AreaChartInternal = forwardRef(({ data, ariaLabel, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
5235
5244
  const legendInteractive = legend.interactive ?? false;
5236
5245
  const legendShape = legend.shape ?? "rect";
5237
5246
  const legendPosition = legend.position ?? "bottom";
@@ -5502,8 +5511,8 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
5502
5511
  const chartHeight = contentHeight > 0 ? contentHeight : height;
5503
5512
  return /* @__PURE__ */ jsx("div", {
5504
5513
  ref: chartRef,
5505
- role: "grid",
5506
- "aria-label": __("Area chart", "jetpack-charts"),
5514
+ role: "application",
5515
+ "aria-label": ariaLabel ?? __("Area chart", "jetpack-charts"),
5507
5516
  tabIndex: 0,
5508
5517
  onKeyDown: onChartKeyDown,
5509
5518
  onFocus: onChartFocus,
@@ -5511,6 +5520,7 @@ const AreaChartInternal = forwardRef(({ data, chartId: providedChartId, width, h
5511
5520
  children: chartHeight > 0 && /* @__PURE__ */ jsxs("div", {
5512
5521
  className: xy_plot_module_default["xy-plot"],
5513
5522
  children: [zoomable && zoom.domain && /* @__PURE__ */ jsx(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ jsxs(XYChart, {
5523
+ accessibilityLabel: "",
5514
5524
  theme,
5515
5525
  width,
5516
5526
  height: chartHeight,
@@ -6363,7 +6373,7 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ jsx("div", {
6363
6373
  className: bar_chart_module_default["bar-chart__tooltip-row"],
6364
6374
  children: sprintf(__("%1$s: %2$s", "jetpack-charts"), label, value)
6365
6375
  });
6366
- const BarChartInternal = ({ data, 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 }) => {
6376
+ 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 }) => {
6367
6377
  if (!withTooltips && (withBandHighlight || onBandHighlightChange)) warnOnce("bar-chart-band-highlight-without-tooltips", "BarChart: withBandHighlight and onBandHighlightChange require withTooltips.");
6368
6378
  const legendInteractive = legend.interactive ?? false;
6369
6379
  const legendCollapseGroups = legend.collapseGroups ?? false;
@@ -6671,9 +6681,9 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
6671
6681
  children: ({ contentHeight }) => {
6672
6682
  const chartHeight = contentHeight > 0 ? contentHeight : height;
6673
6683
  return /* @__PURE__ */ jsx("div", {
6674
- role: "grid",
6684
+ role: "application",
6675
6685
  ref: chartRef,
6676
- "aria-label": __("Bar chart", "jetpack-charts"),
6686
+ "aria-label": ariaLabel ?? __("Bar chart", "jetpack-charts"),
6677
6687
  tabIndex: 0,
6678
6688
  onKeyDown: onChartKeyDown,
6679
6689
  onFocus: onChartFocus,
@@ -6681,6 +6691,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
6681
6691
  children: chartHeight > 0 && /* @__PURE__ */ jsx("div", {
6682
6692
  className: xy_plot_module_default["xy-plot"],
6683
6693
  children: /* @__PURE__ */ jsxs(XYChart, {
6694
+ accessibilityLabel: "",
6684
6695
  theme,
6685
6696
  width,
6686
6697
  height: chartHeight,
@@ -7686,6 +7697,7 @@ var heatmap_chart_module_default = {
7686
7697
  "heatmap-chart__legend-label": "a8ccharts-O3YMOW-heatmap-chart__legend-label",
7687
7698
  "heatmap-chart__legend-scale--bar": "a8ccharts-O3YMOW-heatmap-chart__legend-scale--bar",
7688
7699
  "heatmap-chart__legend-swatch": "a8ccharts-O3YMOW-heatmap-chart__legend-swatch",
7700
+ "heatmap-chart__legend-swatch--empty": "a8ccharts-O3YMOW-heatmap-chart__legend-swatch--empty",
7689
7701
  "heatmap-chart__row": "a8ccharts-O3YMOW-heatmap-chart__row",
7690
7702
  "heatmap-chart__row-label": "a8ccharts-O3YMOW-heatmap-chart__row-label",
7691
7703
  "heatmap-chart--height-capped": "a8ccharts-O3YMOW-heatmap-chart--height-capped"
@@ -7783,8 +7795,9 @@ const HeatmapLegend = ({ steps = 5, variant = "swatches", lessLabel, moreLabel }
7783
7795
  const context = useContext(HeatmapContext);
7784
7796
  const { legend } = useGlobalChartsTheme();
7785
7797
  if (!context) return null;
7786
- const { fillVars } = context;
7798
+ const { extent, fillVars } = context;
7787
7799
  const labelStyle = legend.labelStyles;
7800
+ const showsEmptyCell = isEmptyValue(0, extent);
7788
7801
  return /* @__PURE__ */ jsxs(Stack, {
7789
7802
  direction: "row",
7790
7803
  gap: "xs",
@@ -7796,12 +7809,12 @@ const HeatmapLegend = ({ steps = 5, variant = "swatches", lessLabel, moreLabel }
7796
7809
  style: labelStyle,
7797
7810
  children: lessLabel ?? __("Less", "jetpack-charts")
7798
7811
  }),
7799
- /* @__PURE__ */ jsx(Stack, {
7812
+ /* @__PURE__ */ jsxs(Stack, {
7800
7813
  direction: "row",
7801
7814
  gap: variant === "bar" ? void 0 : "xs",
7802
7815
  "aria-hidden": "true",
7803
7816
  className: variant === "bar" ? heatmap_chart_module_default["heatmap-chart__legend-scale--bar"] : void 0,
7804
- children: Array.from({ length: steps }, (_, index) => {
7817
+ children: [showsEmptyCell && /* @__PURE__ */ jsx("span", { className: clsx(heatmap_chart_module_default["heatmap-chart__legend-swatch"], heatmap_chart_module_default["heatmap-chart__legend-swatch--empty"]) }), Array.from({ length: steps }, (_, index) => {
7805
7818
  const intensity = steps <= 1 ? 1 : index / (steps - 1);
7806
7819
  return /* @__PURE__ */ jsx("span", {
7807
7820
  className: heatmap_chart_module_default["heatmap-chart__legend-swatch"],
@@ -7810,7 +7823,7 @@ const HeatmapLegend = ({ steps = 5, variant = "swatches", lessLabel, moreLabel }
7810
7823
  "--a8c-charts-heatmap-cell-intensity": intensity
7811
7824
  }
7812
7825
  }, index);
7813
- })
7826
+ })]
7814
7827
  }),
7815
7828
  /* @__PURE__ */ jsx(Text$1, {
7816
7829
  variant: "body-sm",
@@ -7836,9 +7849,11 @@ const resolveColumnGroups = (groups, columnCount, firstLine) => {
7836
7849
  const ungrouped = () => ({
7837
7850
  columns: Array.from({ length: columnCount }, (_, index) => ({
7838
7851
  line: firstLine + index,
7852
+ band: 0,
7839
7853
  gapBefore: false
7840
7854
  })),
7841
- groups: []
7855
+ groups: [],
7856
+ bands: 1
7842
7857
  });
7843
7858
  if (!groups || groups.length === 0 || columnCount === 0) return ungrouped();
7844
7859
  const spans = groups.map((group) => group.span);
@@ -7854,11 +7869,13 @@ const resolveColumnGroups = (groups, columnCount, firstLine) => {
7854
7869
  if (groupIndex > 0) line += 1;
7855
7870
  laidOut.push({
7856
7871
  ...group,
7857
- line
7872
+ line,
7873
+ band: 0
7858
7874
  });
7859
7875
  for (let offset = 0; offset < group.span; offset++) {
7860
7876
  columns.push({
7861
7877
  line,
7878
+ band: 0,
7862
7879
  gapBefore: groupIndex > 0 && offset === 0,
7863
7880
  group: groupIndex
7864
7881
  });
@@ -7868,13 +7885,99 @@ const resolveColumnGroups = (groups, columnCount, firstLine) => {
7868
7885
  while (columns.length < columnCount) {
7869
7886
  columns.push({
7870
7887
  line,
7888
+ band: 0,
7871
7889
  gapBefore: false
7872
7890
  });
7873
7891
  line += 1;
7874
7892
  }
7875
7893
  return {
7876
7894
  columns,
7877
- groups: laidOut
7895
+ groups: laidOut,
7896
+ bands: 1
7897
+ };
7898
+ };
7899
+ /**
7900
+ * Wraps the groups onto bands of equal group count, each band starting back at
7901
+ * the first data line so the groups line up in slots down the bands.
7902
+ *
7903
+ * @param layout - The unwrapped layout from `resolveColumnGroups`.
7904
+ * @param bands - Bands to aim for; the groups split as evenly as whole bands allow.
7905
+ * @return The wrapped layout, or null when there is nothing to wrap: one band, one group, or a column outside every group.
7906
+ */
7907
+ const wrapColumnGroups = (layout, bands) => {
7908
+ const { columns, groups } = layout;
7909
+ if (bands < 2 || groups.length < 2 || columns.some((column) => column.group === void 0)) return null;
7910
+ const perBand = Math.ceil(groups.length / bands);
7911
+ const slotSpans = Array(perBand).fill(0);
7912
+ groups.forEach((group, index) => {
7913
+ const slot = index % perBand;
7914
+ slotSpans[slot] = Math.max(slotSpans[slot], group.span);
7915
+ });
7916
+ const slotLines = [];
7917
+ let line = columns[0].line;
7918
+ slotSpans.forEach((span, slot) => {
7919
+ if (slot > 0) line += 1;
7920
+ slotLines.push(line);
7921
+ line += span;
7922
+ });
7923
+ const wrappedGroups = groups.map((group, index) => ({
7924
+ ...group,
7925
+ line: slotLines[index % perBand],
7926
+ band: Math.floor(index / perBand)
7927
+ }));
7928
+ return {
7929
+ columns: columns.map((column) => {
7930
+ const group = column.group;
7931
+ const offset = column.line - groups[group].line;
7932
+ return {
7933
+ ...column,
7934
+ line: wrappedGroups[group].line + offset,
7935
+ band: wrappedGroups[group].band,
7936
+ gapBefore: offset === 0 && group % perBand > 0
7937
+ };
7938
+ }),
7939
+ groups: wrappedGroups,
7940
+ bands: Math.ceil(groups.length / perBand),
7941
+ slotSpans
7942
+ };
7943
+ };
7944
+ //#endregion
7945
+ //#region src/charts/heatmap-chart/private/fit-compact-cells.ts
7946
+ /**
7947
+ * Largest whole-pixel square cell, and the band count giving it, for a compact grid in a box.
7948
+ *
7949
+ * @param input - The box, the grid's layout, and its fixed gaps and label sizes.
7950
+ * @return The fit; one band at `minCellSize` when no band count reaches it.
7951
+ */
7952
+ const fitCompactCells = (input) => {
7953
+ const { width, height, layout, rows, cellGap, groupGap, minCellSize } = input;
7954
+ const columns = layout.columns.length;
7955
+ if (columns === 0 || rows === 0) return {
7956
+ bands: 1,
7957
+ cellSize: minCellSize
7958
+ };
7959
+ const maxBands = Math.max(1, layout.groups.length);
7960
+ const labelHeights = (input.columnLabelHeight ?? 0) + (input.groupLabelHeight ?? 0);
7961
+ const bandTracks = (input.columnLabelHeight === null ? 0 : 1) + rows + (input.groupLabelHeight === null ? 0 : 1);
7962
+ let best = null;
7963
+ for (let target = 1; target <= maxBands; target++) {
7964
+ const wrapped = target > 1 ? wrapColumnGroups(layout, target) : null;
7965
+ const { bands } = wrapped ?? layout;
7966
+ const cellColumns = wrapped ? wrapped.slotSpans.reduce((sum, span) => sum + span, 0) : columns;
7967
+ const gapTracks = wrapped ? wrapped.slotSpans.length - 1 : layout.columns.filter((column) => column.gapBefore).length;
7968
+ const columnTracks = 1 + cellColumns + gapTracks;
7969
+ const byWidth = (width - input.rowLabelWidth - gapTracks * groupGap - (columnTracks - 1) * cellGap) / cellColumns;
7970
+ const rowTracks = bands * bandTracks + (bands - 1);
7971
+ const byHeight = (height - (bands * labelHeights + (bands - 1) * groupGap + (rowTracks - 1) * cellGap)) / (bands * rows);
7972
+ const cellSize = Math.floor(Math.min(byWidth, byHeight));
7973
+ if (cellSize >= minCellSize && (!best || cellSize > best.cellSize)) best = {
7974
+ bands,
7975
+ cellSize
7976
+ };
7977
+ }
7978
+ return best ?? {
7979
+ bands: 1,
7980
+ cellSize: minCellSize
7878
7981
  };
7879
7982
  };
7880
7983
  //#endregion
@@ -8018,12 +8121,26 @@ const CELL_VALUE_MODIFIER = {
8018
8121
  };
8019
8122
  const NO_ROW_LABELS = [];
8020
8123
  const TOOLTIP_BOX_STYLE = { zIndex: 3 };
8124
+ const largest = (root, selector, size) => Math.max(0, ...Array.from(root.querySelectorAll(selector), (el) => el[size]));
8125
+ const gridBox = (chart, grid) => {
8126
+ let height = chart.clientHeight;
8127
+ const rowGap = parseFloat(getComputedStyle(chart).rowGap) || 0;
8128
+ Array.from(chart.children).forEach((child) => {
8129
+ const { display, position } = getComputedStyle(child);
8130
+ if (child === grid || display === "none" || position === "absolute" || position === "fixed") return;
8131
+ height -= child.offsetHeight + rowGap;
8132
+ });
8133
+ return {
8134
+ width: chart.clientWidth,
8135
+ height
8136
+ };
8137
+ };
8021
8138
  const cellName = (info) => info.cellLabel || [
8022
8139
  info.groupLabel,
8023
8140
  info.columnLabel,
8024
8141
  info.rowLabel
8025
8142
  ].filter(Boolean).join(" ");
8026
- const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, height = 0, className, compact = false, showValues, maxCellWidth, maxCellHeight, minCellWidth, minCellHeight, rowLabels = NO_ROW_LABELS, columnGroups, keyboardNavigation = "grid", ariaLabel, primaryColor, gap = "md", withTooltips = false, renderTooltip, tooltipStyle, children }) => {
8143
+ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, height = 0, className, compact = false, fitCells = false, showValues, maxCellWidth, maxCellHeight, minCellWidth, minCellHeight, rowLabels = NO_ROW_LABELS, columnGroups, keyboardNavigation = "grid", ariaLabel, primaryColor, gap = "md", withTooltips = false, renderTooltip, tooltipStyle, children }) => {
8027
8144
  const chartId = useChartId(providedChartId);
8028
8145
  const tooltipBoxStyle = tooltipStyle ? {
8029
8146
  ...TOOLTIP_BOX_STYLE,
@@ -8037,8 +8154,9 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
8037
8154
  const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, showTooltip, hideTooltip } = useTooltip();
8038
8155
  const standaloneScopeClass = useStandaloneScopeClass();
8039
8156
  const containerRef = useRef(null);
8157
+ const chartRef = useRef(null);
8040
8158
  const [labelRoles, setLabelRoles] = useState(null);
8041
- const getTooltipOrigin = useCallback(() => containerRef.current?.closest(`[data-chart-id="heatmap-chart-${chartId}"]`)?.getBoundingClientRect() ?? null, [chartId]);
8159
+ const getTooltipOrigin = useCallback(() => chartRef.current?.getBoundingClientRect() ?? null, []);
8042
8160
  const { color: primaryColorHex } = getElementStyles({
8043
8161
  index: 0,
8044
8162
  overrideColor: primaryColor
@@ -8080,6 +8198,49 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
8080
8198
  const groupLayout = useMemo(() => resolveColumnGroups(columnGroups, columns, 2), [columnGroups, columns]);
8081
8199
  const { compactCellGap, compactCellSize, groupGap } = heatmapChartSettings;
8082
8200
  const drawValues = showValues ?? !compact;
8201
+ const hasColumnLabels = data.some((column) => Boolean(column.label));
8202
+ const hasGroups = groupLayout.groups.length > 0;
8203
+ const [fit, setFit] = useState(null);
8204
+ const hasSummary = data.some((column) => column.summary);
8205
+ const fitting = compact && fitCells && !hasSummary;
8206
+ if (fitCells && !fitting) warnOnce("heatmap:fitCells", "fitCells applies only in compact mode without summary columns, so the cells keep their size.");
8207
+ useIsomorphicLayoutEffect(() => {
8208
+ const chart = chartRef.current;
8209
+ const grid = containerRef.current;
8210
+ if (!fitting || !chart || !grid) {
8211
+ setFit(null);
8212
+ return;
8213
+ }
8214
+ const measure = () => {
8215
+ const next = fitCompactCells({
8216
+ ...gridBox(chart, grid),
8217
+ layout: groupLayout,
8218
+ rows,
8219
+ cellGap: compactCellGap,
8220
+ groupGap,
8221
+ rowLabelWidth: largest(grid, `.${heatmap_chart_module_default["heatmap-chart__row-label"]}`, "offsetWidth"),
8222
+ columnLabelHeight: hasColumnLabels ? largest(grid, `.${heatmap_chart_module_default["heatmap-chart__col-label"]}`, "offsetHeight") : null,
8223
+ groupLabelHeight: hasGroups ? largest(grid, `.${heatmap_chart_module_default["heatmap-chart__group-label"]}`, "offsetHeight") : null,
8224
+ minCellSize: compactCellSize
8225
+ });
8226
+ setFit((previous) => (0, import_fast_deep_equal.default)(previous, next) ? previous : next);
8227
+ };
8228
+ measure();
8229
+ if (typeof ResizeObserver === "undefined") return;
8230
+ const observer = new ResizeObserver(() => flushSync(measure));
8231
+ observer.observe(chart);
8232
+ return () => observer.disconnect();
8233
+ }, [
8234
+ fitting,
8235
+ groupLayout,
8236
+ rows,
8237
+ hasColumnLabels,
8238
+ hasGroups,
8239
+ compactCellGap,
8240
+ compactCellSize,
8241
+ groupGap
8242
+ ]);
8243
+ const wrapped = useMemo(() => fit ? wrapColumnGroups(groupLayout, fit.bands) : null, [fit, groupLayout]);
8083
8244
  const buildTooltipData = useCallback((columnIndex, rowIndex) => {
8084
8245
  const cell = data[columnIndex]?.data[rowIndex];
8085
8246
  const group = groupLayout.columns[columnIndex]?.group;
@@ -8219,21 +8380,25 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
8219
8380
  });
8220
8381
  const columnTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellWidth ?? 0}px, ${maxCellWidth ? `${maxCellWidth}px` : "1fr"})`;
8221
8382
  const rowTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
8222
- const hasColumnLabels = data.some((column) => Boolean(column.label));
8223
- const hasGroups = groupLayout.groups.length > 0;
8224
- const columnLine = (columnIndex) => groupLayout.columns[columnIndex].line;
8225
- const firstDataRow = hasColumnLabels ? 2 : 1;
8383
+ const placed = wrapped ?? groupLayout;
8384
+ const columnLine = (columnIndex) => placed.columns[columnIndex].line;
8385
+ const bandOf = (columnIndex) => placed.columns[columnIndex].band;
8386
+ const { bands } = placed;
8387
+ const bandRowCount = (hasColumnLabels ? 1 : 0) + rows + (hasGroups ? 1 : 0) + 1;
8388
+ const labelRowOf = (band) => 1 + band * bandRowCount;
8389
+ const dataRowOf = (band, rowIndex) => labelRowOf(band) + (hasColumnLabels ? 1 : 0) + rowIndex;
8226
8390
  const dataTrack = (column) => column.summary ? "minmax(auto, max-content)" : columnTrack;
8227
8391
  const gapTrack = compact ? `minmax(${groupGap}px, 1fr)` : `${groupGap}px`;
8228
- const columnTracks = data.map((column, columnIndex) => groupLayout.columns[columnIndex].gapBefore ? `${gapTrack} ${dataTrack(column)}` : dataTrack(column)).join(" ");
8392
+ const columnTracks = wrapped ? wrapped.slotSpans.map((span, slot) => [...slot > 0 ? [gapTrack] : [], ...Array(span).fill(columnTrack)].join(" ")).join(" ") : data.map((column, columnIndex) => groupLayout.columns[columnIndex].gapBefore ? `${gapTrack} ${dataTrack(column)}` : dataTrack(column)).join(" ");
8393
+ const bandTracks = `${hasColumnLabels ? "auto " : ""}repeat(${rows}, ${rowTrack})${hasGroups ? " auto" : ""}`;
8229
8394
  const gridStyle = {
8230
8395
  ...fillVars,
8231
8396
  gridTemplateColumns: `auto ${columnTracks}`,
8232
- gridTemplateRows: `${hasColumnLabels ? "auto " : ""}repeat(${rows}, ${rowTrack})${hasGroups ? " auto" : ""}`
8397
+ gridTemplateRows: Array(bands).fill(bandTracks).join(` ${groupGap}px `)
8233
8398
  };
8234
8399
  if (compact) {
8235
8400
  gridStyle["--a8c-charts-dimension-heatmap-cell-gap"] = `${compactCellGap}px`;
8236
- gridStyle["--a8c-charts-dimension-heatmap-cell-size"] = `${compactCellSize}px`;
8401
+ gridStyle["--a8c-charts-dimension-heatmap-cell-size"] = `${fit?.cellSize ?? compactCellSize}px`;
8237
8402
  }
8238
8403
  const summaryGaps = (columnIndex) => {
8239
8404
  if (!data[columnIndex]?.summary) return {};
@@ -8253,6 +8418,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
8253
8418
  legendChildren: [],
8254
8419
  trailingContent: nonLegendChildren,
8255
8420
  gap,
8421
+ rootRef: chartRef,
8256
8422
  className: clsx("heatmap-chart", heatmap_chart_module_default["heatmap-chart"], className, { [heatmap_chart_module_default["heatmap-chart--height-capped"]]: heightCapped }),
8257
8423
  style: {
8258
8424
  width: width || void 0,
@@ -8282,11 +8448,11 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
8282
8448
  className: heatmap_chart_module_default["heatmap-chart__row"],
8283
8449
  children: [/* @__PURE__ */ jsx("span", { style: {
8284
8450
  gridColumn: 1,
8285
- gridRow: 1
8451
+ gridRow: labelRowOf(0)
8286
8452
  } }), data.map((column, columnIndex) => /* @__PURE__ */ jsx("span", {
8287
8453
  style: {
8288
8454
  gridColumn: columnLine(columnIndex),
8289
- gridRow: 1
8455
+ gridRow: labelRowOf(bandOf(columnIndex))
8290
8456
  },
8291
8457
  className: clsx(heatmap_chart_module_default["heatmap-chart__col-label"], {
8292
8458
  [heatmap_chart_module_default["heatmap-chart__col-label--summary"]]: column.summary,
@@ -8297,24 +8463,23 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
8297
8463
  }),
8298
8464
  Array.from({ length: rows }).map((_row, rowIndex) => {
8299
8465
  const labelVisible = !compact || rowIndex % 2 === 0;
8300
- const gridRow = firstDataRow + rowIndex;
8301
8466
  return /* @__PURE__ */ jsxs("div", {
8302
8467
  role: "row",
8303
8468
  "aria-rowindex": rowIndex + 1,
8304
8469
  className: heatmap_chart_module_default["heatmap-chart__row"],
8305
- children: [/* @__PURE__ */ jsx("span", {
8470
+ children: [Array.from({ length: bands }).map((_band, band) => /* @__PURE__ */ jsx("span", {
8306
8471
  "aria-hidden": "true",
8307
8472
  className: heatmap_chart_module_default["heatmap-chart__row-label"],
8308
8473
  style: {
8309
8474
  gridColumn: 1,
8310
- gridRow
8475
+ gridRow: dataRowOf(band, rowIndex)
8311
8476
  },
8312
8477
  children: labelVisible ? rowLabels[rowIndex] ?? "" : ""
8313
- }), data.map((column, columnIndex) => {
8478
+ }, `row-label-${band}`)), data.map((column, columnIndex) => {
8314
8479
  const cell = column.data[rowIndex];
8315
8480
  const placement = {
8316
8481
  gridColumn: columnLine(columnIndex),
8317
- gridRow
8482
+ gridRow: dataRowOf(bandOf(columnIndex), rowIndex)
8318
8483
  };
8319
8484
  if (cell?.hidden) return /* @__PURE__ */ jsx("div", {
8320
8485
  "aria-hidden": "true",
@@ -8364,11 +8529,11 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
8364
8529
  role: "row",
8365
8530
  "aria-hidden": "true",
8366
8531
  className: heatmap_chart_module_default["heatmap-chart__row"],
8367
- children: groupLayout.groups.map((group, groupIndex) => /* @__PURE__ */ jsx("span", {
8532
+ children: placed.groups.map((group, groupIndex) => /* @__PURE__ */ jsx("span", {
8368
8533
  className: heatmap_chart_module_default["heatmap-chart__group-label"],
8369
8534
  style: {
8370
8535
  gridColumn: `${group.line} / span ${group.span}`,
8371
- gridRow: firstDataRow + rows
8536
+ gridRow: dataRowOf(group.band, rows)
8372
8537
  },
8373
8538
  children: group.label
8374
8539
  }, `group-${groupIndex}`))
@@ -11238,6 +11403,7 @@ var leaderboard_chart_module_default = {
11238
11403
  "interactiveRow": "a8ccharts-GovfoW-interactiveRow",
11239
11404
  "is-overlay": "a8ccharts-GovfoW-is-overlay",
11240
11405
  "label": "a8ccharts-GovfoW-label",
11406
+ "layeredRow": "a8ccharts-GovfoW-layeredRow",
11241
11407
  "leaderboardChart": "a8ccharts-GovfoW-leaderboardChart",
11242
11408
  "leaderboardChart__content": "a8ccharts-GovfoW-leaderboardChart__content",
11243
11409
  "leaderboardChart__content--fit": "a8ccharts-GovfoW-leaderboardChart__content--fit",
@@ -11246,6 +11412,7 @@ var leaderboard_chart_module_default = {
11246
11412
  "overlap": "a8ccharts-GovfoW-overlap",
11247
11413
  "overlayLabel": "a8ccharts-GovfoW-overlayLabel",
11248
11414
  "row": "a8ccharts-GovfoW-row",
11415
+ "rowButton": "a8ccharts-GovfoW-rowButton",
11249
11416
  "stretch": "a8ccharts-GovfoW-stretch",
11250
11417
  "valueContainer": "a8ccharts-GovfoW-valueContainer"
11251
11418
  };
@@ -11296,6 +11463,16 @@ const defaultDeltaFormatter = (value) => {
11296
11463
  const getBarWidth = (share) => `calc(${share}% - var(--a8c-charts-dimension-leaderboard-bar-hover-inset, 0px) * ${share} / 100)`;
11297
11464
  const hasPreviousValue = (entry) => entry.previousValue != null && entry.previousShare != null;
11298
11465
  const hasDeltaValue = (entry) => hasPreviousValue(entry) && entry.delta != null;
11466
+ /**
11467
+ * A layered row's cell that takes pointer events of its own (a value tooltip)
11468
+ * sits above the row button; pass its clicks on so they still drill down.
11469
+ *
11470
+ * @param event - The click on the row.
11471
+ */
11472
+ function forwardCellClickToRowButton(event) {
11473
+ if (event.target.closest("a, button")) return;
11474
+ event.currentTarget.querySelector(":scope > button")?.click();
11475
+ }
11299
11476
  const BarLabel = ({ label }) => /* @__PURE__ */ jsx(Fragment$1, { children: typeof label === "string" ? /* @__PURE__ */ jsx(Text$1, {
11300
11477
  className: leaderboard_chart_module_default.label,
11301
11478
  children: label
@@ -11528,6 +11705,26 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
11528
11705
  })
11529
11706
  ]
11530
11707
  })] });
11708
+ if (entry.onClick && entry.hasInteractiveLabel) return /* @__PURE__ */ jsxs("div", {
11709
+ "data-row-index": rowIndex,
11710
+ style: rowStyle,
11711
+ className: clsx(leaderboard_chart_module_default.row, leaderboard_chart_module_default.interactiveRow, leaderboard_chart_module_default.layeredRow),
11712
+ onClick: forwardCellClickToRowButton,
11713
+ children: [
11714
+ rowCells,
11715
+ /* @__PURE__ */ jsx("button", {
11716
+ type: "button",
11717
+ className: leaderboard_chart_module_default.rowButton,
11718
+ onClick: entry.onClick,
11719
+ "aria-label": entry.ariaLabel
11720
+ }),
11721
+ /* @__PURE__ */ jsx(Icon, {
11722
+ className: leaderboard_chart_module_default.chevron,
11723
+ icon: chevronRight,
11724
+ size: 24
11725
+ })
11726
+ ]
11727
+ }, entry.id);
11531
11728
  if (entry.onClick) return /* @__PURE__ */ jsxs("button", {
11532
11729
  type: "button",
11533
11730
  "data-row-index": rowIndex,
@@ -11561,6 +11758,210 @@ LeaderboardChartWithProvider.displayName = "LeaderboardChart";
11561
11758
  const LeaderboardChart = attachSubComponents(LeaderboardChartWithProvider, { Legend });
11562
11759
  const LeaderboardChartResponsive = attachSubComponents(withResponsive(LeaderboardChartWithProvider), { Legend });
11563
11760
  //#endregion
11761
+ //#region src/charts/private/pie-keyboard-navigation/order-arcs-for-navigation.ts
11762
+ /**
11763
+ * Orders arcs the way they appear: clockwise from 12 o'clock, which for a semi-circle is left to right.
11764
+ *
11765
+ * @param arcs - Arcs from visx's `pie` factory, in data order.
11766
+ * @return A copy of `arcs` sorted by angular midpoint.
11767
+ */
11768
+ const orderArcsForNavigation = (arcs) => [...arcs].sort((a, b) => a.startAngle + a.endAngle - (b.startAngle + b.endAngle));
11769
+ //#endregion
11770
+ //#region src/charts/private/pie-keyboard-navigation/pie-selection-announcement.tsx
11771
+ const PieSelectionAnnouncement = ({ data }) => /* @__PURE__ */ jsx(VisuallyHidden, {
11772
+ role: "status",
11773
+ "aria-atomic": "true",
11774
+ children: data && /* @__PURE__ */ jsx(LabelValueContent, { data })
11775
+ });
11776
+ //#endregion
11777
+ //#region src/charts/private/pie-keyboard-navigation/pie-tooltip.tsx
11778
+ const PieTooltip = ({ top, left, selectedIndex, tooltipRef, children }) => {
11779
+ const standaloneScopeClass = useStandaloneScopeClass();
11780
+ return /* @__PURE__ */ jsx(BoundedTooltip, {
11781
+ top,
11782
+ left,
11783
+ children: selectedIndex === void 0 ? /* @__PURE__ */ jsx("div", {
11784
+ className: standaloneScopeClass,
11785
+ role: "tooltip",
11786
+ children
11787
+ }) : /* @__PURE__ */ jsx("div", {
11788
+ ref: tooltipRef,
11789
+ tabIndex: -1,
11790
+ role: "tooltip",
11791
+ "aria-atomic": "true",
11792
+ className: standaloneScopeClass,
11793
+ children
11794
+ }, `chart-tooltip-${selectedIndex}`)
11795
+ });
11796
+ };
11797
+ //#endregion
11798
+ //#region src/charts/private/pie-keyboard-navigation/pie-selection-output.tsx
11799
+ /**
11800
+ * Reports the selected segment: a focused tooltip for a keyboard selection, a pointer tooltip on hover, or a screen reader announcement without tooltips.
11801
+ *
11802
+ * @param {PieSelectionOutputProps} props - Component props
11803
+ * @return {JSX.Element} The announcement region and the tooltip, if any
11804
+ */
11805
+ const PieSelectionOutput = ({ withTooltips, selectedIndex, selectedData, keyboardTooltipAnchor, pointerTooltip, chartRef, svgRef, tooltipRef, renderTooltip }) => {
11806
+ const [svgOffset, setSvgOffset] = useState({
11807
+ top: 0,
11808
+ left: 0
11809
+ });
11810
+ const showKeyboardTooltip = withTooltips && selectedData && keyboardTooltipAnchor;
11811
+ useLayoutEffect(() => {
11812
+ const chartBounds = chartRef.current?.getBoundingClientRect();
11813
+ const svgBounds = svgRef.current?.getBoundingClientRect();
11814
+ if (!showKeyboardTooltip || !chartBounds || !svgBounds) return;
11815
+ const top = svgBounds.top - chartBounds.top;
11816
+ const left = svgBounds.left - chartBounds.left;
11817
+ setSvgOffset((current) => current.top === top && current.left === left ? current : {
11818
+ top,
11819
+ left
11820
+ });
11821
+ });
11822
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
11823
+ /* @__PURE__ */ jsx(PieSelectionAnnouncement, { data: withTooltips ? void 0 : selectedData }),
11824
+ showKeyboardTooltip && /* @__PURE__ */ jsx(PieTooltip, {
11825
+ top: svgOffset.top + keyboardTooltipAnchor.top,
11826
+ left: svgOffset.left + keyboardTooltipAnchor.left,
11827
+ selectedIndex,
11828
+ tooltipRef,
11829
+ children: renderTooltip({ tooltipData: selectedData })
11830
+ }),
11831
+ withTooltips && !selectedData && pointerTooltip?.data && /* @__PURE__ */ jsx(PieTooltip, {
11832
+ top: pointerTooltip.top,
11833
+ left: pointerTooltip.left,
11834
+ children: renderTooltip({ tooltipData: pointerTooltip.data })
11835
+ })
11836
+ ] });
11837
+ };
11838
+ //#endregion
11839
+ //#region src/charts/private/pie-keyboard-navigation/pie-keyboard-navigation.module.scss
11840
+ var pie_keyboard_navigation_module_default = {
11841
+ "selected-ring__contrast": "a8ccharts-V2HDKq-selected-ring__contrast",
11842
+ "selected-ring__focus": "a8ccharts-V2HDKq-selected-ring__focus"
11843
+ };
11844
+ //#endregion
11845
+ //#region src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx
11846
+ /**
11847
+ * Encodes `value` for an SVG id, losslessly so distinct chart ids never share a clip path.
11848
+ *
11849
+ * @param value - Any string.
11850
+ * @return `value` with each character outside `[A-Za-z0-9-]` written as `_<hex code point>_`.
11851
+ */
11852
+ const toSvgId = (value) => Array.from(value, (character) => /[A-Za-z0-9-]/.test(character) ? character : `_${character.codePointAt(0)?.toString(16)}_`).join("");
11853
+ /**
11854
+ * Marks the selected segment with a two-tone ring drawn inside its edge, so the SVG edge cannot cut it off.
11855
+ *
11856
+ * @param {SelectedSegmentRingProps} props - Component props
11857
+ * @return {JSX.Element} The ring
11858
+ */
11859
+ const SelectedSegmentRing = ({ chartId, d }) => {
11860
+ const clipId = `pie-selected-ring-clip-${toSvgId(chartId)}`;
11861
+ const clipPath = `url(#${clipId})`;
11862
+ return /* @__PURE__ */ jsxs("g", {
11863
+ pointerEvents: "none",
11864
+ "aria-hidden": "true",
11865
+ children: [
11866
+ /* @__PURE__ */ jsx("clipPath", {
11867
+ id: clipId,
11868
+ children: /* @__PURE__ */ jsx("path", { d })
11869
+ }),
11870
+ /* @__PURE__ */ jsx("path", {
11871
+ d,
11872
+ clipPath,
11873
+ className: pie_keyboard_navigation_module_default["selected-ring__contrast"]
11874
+ }),
11875
+ /* @__PURE__ */ jsx("path", {
11876
+ d,
11877
+ clipPath,
11878
+ className: pie_keyboard_navigation_module_default["selected-ring__focus"]
11879
+ })
11880
+ ]
11881
+ });
11882
+ };
11883
+ //#endregion
11884
+ //#region src/charts/private/pie-keyboard-navigation/use-pie-keyboard-navigation.ts
11885
+ const usePieKeyboardNavigation = ({ navigationArcs, withTooltips, tooltipOffsetX, tooltipOffsetY }) => {
11886
+ const chartRef = useRef(null);
11887
+ const svgRef = useRef(null);
11888
+ const [selectedIndex, setSelectedIndex] = useState(void 0);
11889
+ const [isNavigating, setIsNavigating] = useState(false);
11890
+ const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip();
11891
+ const { tooltipRef: focusTooltip, onChartFocus, onChartBlur, onChartKeyDown, onChartPointerMove } = useKeyboardNavigation({
11892
+ selectedIndex,
11893
+ setSelectedIndex,
11894
+ isNavigating,
11895
+ setIsNavigating,
11896
+ chartRef,
11897
+ totalPoints: navigationArcs.length,
11898
+ visibleSeriesKey: JSON.stringify(navigationArcs.map((arcDatum) => arcDatum.data.label))
11899
+ });
11900
+ const selectedArc = selectedIndex === void 0 ? void 0 : navigationArcs[selectedIndex];
11901
+ const keyboardTooltip = useRef(null);
11902
+ const tooltipLostFocus = useRef(false);
11903
+ const tooltipRef = useCallback((element) => {
11904
+ if (element) {
11905
+ keyboardTooltip.current = element;
11906
+ focusTooltip(element);
11907
+ } else if (keyboardTooltip.current?.ownerDocument.activeElement === keyboardTooltip.current) tooltipLostFocus.current = true;
11908
+ }, [focusTooltip]);
11909
+ useLayoutEffect(() => {
11910
+ if (!tooltipLostFocus.current) return;
11911
+ tooltipLostFocus.current = false;
11912
+ const { activeElement, body } = chartRef.current?.ownerDocument ?? document;
11913
+ if (activeElement === null || activeElement === body) chartRef.current?.focus();
11914
+ });
11915
+ const getSegmentHandlers = (data, navigationIndex) => ({
11916
+ onMouseMove: (event) => {
11917
+ onChartPointerMove(navigationIndex);
11918
+ if (!withTooltips) return;
11919
+ const bounds = chartRef.current?.getBoundingClientRect();
11920
+ if (!bounds) return;
11921
+ showTooltip({
11922
+ tooltipData: data,
11923
+ tooltipLeft: event.clientX - bounds.left + tooltipOffsetX,
11924
+ tooltipTop: event.clientY - bounds.top + tooltipOffsetY
11925
+ });
11926
+ },
11927
+ onMouseLeave: withTooltips ? hideTooltip : void 0
11928
+ });
11929
+ return {
11930
+ chartRef,
11931
+ svgRef,
11932
+ selectedArc,
11933
+ getSegmentHandlers,
11934
+ getKeyboardTooltipAnchor: useCallback((originX, originY, [x, y]) => withTooltips ? {
11935
+ left: originX + x + tooltipOffsetX,
11936
+ top: originY + y + tooltipOffsetY
11937
+ } : void 0, [
11938
+ withTooltips,
11939
+ tooltipOffsetX,
11940
+ tooltipOffsetY
11941
+ ]),
11942
+ selectionOutputProps: {
11943
+ withTooltips,
11944
+ selectedIndex,
11945
+ selectedData: selectedArc?.data,
11946
+ chartRef,
11947
+ svgRef,
11948
+ tooltipRef,
11949
+ pointerTooltip: tooltipOpen ? {
11950
+ data: tooltipData,
11951
+ left: tooltipLeft || 0,
11952
+ top: tooltipTop || 0
11953
+ } : void 0
11954
+ },
11955
+ chartProps: {
11956
+ role: "application",
11957
+ tabIndex: 0,
11958
+ onKeyDown: onChartKeyDown,
11959
+ onFocus: onChartFocus,
11960
+ onBlur: onChartBlur
11961
+ }
11962
+ };
11963
+ };
11964
+ //#endregion
11564
11965
  //#region src/charts/private/radial-wipe-animation/radial-wipe-animation.tsx
11565
11966
  /**
11566
11967
  * Renders a SVG mask that creates a radial wipe animation effect.
@@ -11656,20 +12057,13 @@ const LABEL_TEXT_MODIFIER = {
11656
12057
  * @param {PieChartProps} props - Component props
11657
12058
  * @return {JSX.Element} The rendered chart component
11658
12059
  */
11659
- const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false, className, showLegend = false, legend = {}, width: propWidth, height: propHeight, size, animation, thickness = 1, padding = 0, gapScale = 0, cornerScale = 0, showLabels = true, legendValueDisplay = "percentage", children = null, tooltipOffsetX = 0, tooltipOffsetY = -15, renderTooltip = renderDefaultPieTooltip, gap = "md" }) => {
12060
+ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false, className, showLegend = false, legend = {}, width: propWidth, height: propHeight, size, animation, thickness = 1, padding = 0, gapScale = 0, cornerScale = 0, showLabels = true, legendValueDisplay = "percentage", children = null, tooltipOffsetX = 0, tooltipOffsetY = -15, renderTooltip = renderDefaultPieTooltip, ariaLabel, gap = "md" }) => {
11660
12061
  const legendInteractive = legend.interactive ?? false;
11661
12062
  const legendPosition = legend.position ?? "bottom";
11662
12063
  const providerTheme = useGlobalChartsTheme();
11663
12064
  const chartId = useChartId(providedChartId);
11664
- const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip();
11665
- const standaloneScopeClass = useStandaloneScopeClass();
11666
- const containerRef = useRef(null);
11667
12065
  const rootRef = useRef(null);
11668
12066
  const [labelRoles, setLabelRoles] = useState(null);
11669
- const onMouseLeave = useCallback(() => {
11670
- if (!withTooltips) return;
11671
- hideTooltip();
11672
- }, [withTooltips, hideTooltip]);
11673
12067
  const { getElementStyles, isSeriesVisible, isColorPaletteResolved } = useGlobalChartsContext();
11674
12068
  const { isValid, message } = validateData$1(data);
11675
12069
  useLayoutEffect(() => {
@@ -11713,13 +12107,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
11713
12107
  metadata: chartMetadata
11714
12108
  });
11715
12109
  const prefersReducedMotion = usePrefersReducedMotion();
11716
- if (!isValid) return /* @__PURE__ */ jsx("div", {
11717
- className: clsx("pie-chart", pie_chart_module_default["pie-chart"], className),
11718
- children: /* @__PURE__ */ jsx("div", {
11719
- className: pie_chart_module_default["error-message"],
11720
- children: message
11721
- })
11722
- });
12110
+ const wipeMask = animation && !prefersReducedMotion ? `url(#radial-wipe-${chartId})` : null;
11723
12111
  const padAngle = gapScale * (2 * Math.PI / data.length);
11724
12112
  const dataWithIndex = visibleData.map((d) => {
11725
12113
  const originalIndex = data.findIndex((item) => item.label === d.label);
@@ -11737,6 +12125,24 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
11737
12125
  }).color;
11738
12126
  }
11739
12127
  };
12128
+ const arcs = pie({
12129
+ value: accessors.value,
12130
+ padAngle
12131
+ })(dataWithIndex);
12132
+ const navigationArcs = orderArcsForNavigation(arcs);
12133
+ const { chartRef, svgRef, selectedArc, getSegmentHandlers, getKeyboardTooltipAnchor, selectionOutputProps, chartProps } = usePieKeyboardNavigation({
12134
+ navigationArcs,
12135
+ withTooltips,
12136
+ tooltipOffsetX,
12137
+ tooltipOffsetY
12138
+ });
12139
+ if (!isValid) return /* @__PURE__ */ jsx("div", {
12140
+ className: clsx("pie-chart", pie_chart_module_default["pie-chart"], className),
12141
+ children: /* @__PURE__ */ jsx("div", {
12142
+ className: pie_chart_module_default["error-message"],
12143
+ children: message
12144
+ })
12145
+ });
11740
12146
  const legendElement = showLegend && /* @__PURE__ */ jsx(Legend, {
11741
12147
  orientation: legend.orientation ?? "horizontal",
11742
12148
  position: legendPosition,
@@ -11775,102 +12181,96 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
11775
12181
  const innerRadius = thickness === 0 ? 0 : outerRadius * (1 - thickness);
11776
12182
  const maxCornerRadius = (outerRadius - innerRadius) / 2;
11777
12183
  const cornerRadius = cornerScale ? Math.min(cornerScale * outerRadius, maxCornerRadius) : 0;
12184
+ const path = arc({
12185
+ innerRadius,
12186
+ outerRadius,
12187
+ cornerRadius
12188
+ });
12189
+ const svgLabelSmall = providerTheme.svgLabelSmall;
12190
+ const fontSize = resolveFontSize(svgLabelSmall?.fontSize) ?? 12;
12191
+ const renderLabel = (arcDatum, index) => {
12192
+ if (arcDatum.endAngle - arcDatum.startAngle < .25) return null;
12193
+ const [centroidX, centroidY] = path.centroid(arcDatum);
12194
+ const backgroundWidth = getStringWidth$1(arcDatum.data.label, {
12195
+ fontSize,
12196
+ fontFamily: svgLabelSmall?.fontFamily,
12197
+ fontWeight: svgLabelSmall?.fontWeight
12198
+ }) + 12;
12199
+ const backgroundHeight = fontSize + 12;
12200
+ return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("rect", {
12201
+ className: pie_chart_module_default["pie-chart__label-plate"],
12202
+ x: centroidX - backgroundWidth / 2,
12203
+ y: centroidY - backgroundHeight / 2,
12204
+ width: backgroundWidth,
12205
+ height: backgroundHeight,
12206
+ rx: 4,
12207
+ ry: 4,
12208
+ pointerEvents: "none"
12209
+ }), /* @__PURE__ */ jsx("text", {
12210
+ className: clsx(pie_chart_module_default["pie-chart__label-text"], LABEL_TEXT_MODIFIER[pickLabelTextColor(accessors.fill(arcDatum.data), labelRoles, "label-inverse")]),
12211
+ x: centroidX,
12212
+ y: centroidY,
12213
+ dy: ".33em",
12214
+ fontSize,
12215
+ textAnchor: "middle",
12216
+ pointerEvents: "none",
12217
+ children: arcDatum.data.label
12218
+ })] }, `label-${index}`);
12219
+ };
11778
12220
  return /* @__PURE__ */ jsxs(Center, {
11779
- ref: containerRef,
12221
+ ref: chartRef,
11780
12222
  className: pie_chart_module_default["pie-chart__plot"],
12223
+ ...chartProps,
12224
+ "aria-label": ariaLabel ?? __("Pie chart", "jetpack-charts"),
11781
12225
  children: [/* @__PURE__ */ jsxs("svg", {
12226
+ ref: svgRef,
11782
12227
  viewBox: `0 0 ${width} ${height}`,
11783
12228
  preserveAspectRatio: "xMidYMid meet",
11784
12229
  width,
11785
12230
  height,
11786
- children: [/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(RadialWipeAnimation, {
11787
- id: `radial-wipe-${chartId}`,
11788
- radius: outerRadius,
11789
- innerRadius
11790
- }) }), /* @__PURE__ */ jsxs(Group, {
11791
- top: centerY,
11792
- left: centerX,
11793
- mask: animation && !prefersReducedMotion ? `url(#radial-wipe-${chartId})` : null,
11794
- children: [allSegmentsHidden ? /* @__PURE__ */ jsx(SvgEmptyState, {
11795
- x: 0,
11796
- y: 0,
11797
- width,
11798
- height,
11799
- children: getAllHiddenMessage(legendInteractive, "segments")
11800
- }) : /* @__PURE__ */ jsx(Pie, {
11801
- data: dataWithIndex,
11802
- pieValue: accessors.value,
11803
- outerRadius,
11804
- innerRadius,
11805
- padAngle,
11806
- cornerRadius,
11807
- children: (pie) => {
11808
- return pie.arcs.map((arc, index) => {
11809
- const [centroidX, centroidY] = pie.path.centroid(arc);
11810
- const hasSpaceForLabel = arc.endAngle - arc.startAngle >= .25;
11811
- const handleMouseMove = (event) => {
11812
- if (!withTooltips) return;
11813
- const bounds = containerRef.current?.getBoundingClientRect();
11814
- if (!bounds) return;
11815
- showTooltip({
11816
- tooltipData: arc.data,
11817
- tooltipLeft: event.clientX - bounds.left + tooltipOffsetX,
11818
- tooltipTop: event.clientY - bounds.top + tooltipOffsetY
11819
- });
11820
- };
11821
- const fill = accessors.fill(arc.data);
11822
- const pathProps = {
11823
- d: pie.path(arc) || "",
11824
- fill,
11825
- "data-testid": "pie-segment"
11826
- };
11827
- const groupProps = {};
11828
- if (withTooltips) {
11829
- groupProps.onMouseMove = handleMouseMove;
11830
- groupProps.onMouseLeave = onMouseLeave;
11831
- }
11832
- const svgLabelSmall = providerTheme.svgLabelSmall;
11833
- const fontSize = resolveFontSize(svgLabelSmall?.fontSize) ?? 12;
11834
- const backgroundWidth = getStringWidth$1(arc.data.label, {
11835
- fontSize,
11836
- fontFamily: svgLabelSmall?.fontFamily,
11837
- fontWeight: svgLabelSmall?.fontWeight
11838
- }) + 12;
11839
- const backgroundHeight = fontSize + 12;
11840
- return /* @__PURE__ */ jsxs("g", {
11841
- ...groupProps,
11842
- children: [/* @__PURE__ */ jsx("path", { ...pathProps }), showLabels && hasSpaceForLabel && /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("rect", {
11843
- className: pie_chart_module_default["pie-chart__label-plate"],
11844
- x: centroidX - backgroundWidth / 2,
11845
- y: centroidY - backgroundHeight / 2,
11846
- width: backgroundWidth,
11847
- height: backgroundHeight,
11848
- rx: 4,
11849
- ry: 4,
11850
- pointerEvents: "none"
11851
- }), /* @__PURE__ */ jsx("text", {
11852
- className: clsx(pie_chart_module_default["pie-chart__label-text"], LABEL_TEXT_MODIFIER[pickLabelTextColor(fill, labelRoles, "label-inverse")]),
11853
- x: centroidX,
11854
- y: centroidY,
11855
- dy: ".33em",
11856
- fontSize,
11857
- textAnchor: "middle",
11858
- pointerEvents: "none",
11859
- children: arc.data.label
11860
- })] })]
11861
- }, `arc-${index}`);
11862
- });
11863
- }
11864
- }), !allSegmentsHidden && svgChildren]
11865
- })]
11866
- }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ jsx(BoundedTooltip, {
11867
- top: tooltipTop || 0,
11868
- left: tooltipLeft || 0,
11869
- children: /* @__PURE__ */ jsx("div", {
11870
- className: standaloneScopeClass,
11871
- role: "tooltip",
11872
- children: renderTooltip({ tooltipData })
11873
- })
12231
+ children: [
12232
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(RadialWipeAnimation, {
12233
+ id: `radial-wipe-${chartId}`,
12234
+ radius: outerRadius,
12235
+ innerRadius
12236
+ }) }),
12237
+ /* @__PURE__ */ jsx(Group, {
12238
+ top: centerY,
12239
+ left: centerX,
12240
+ mask: wipeMask,
12241
+ children: allSegmentsHidden ? /* @__PURE__ */ jsx(SvgEmptyState, {
12242
+ x: 0,
12243
+ y: 0,
12244
+ width,
12245
+ height,
12246
+ children: getAllHiddenMessage(legendInteractive, "segments")
12247
+ }) : arcs.map((arcDatum, index) => /* @__PURE__ */ jsx("g", {
12248
+ ...getSegmentHandlers(arcDatum.data, navigationArcs.indexOf(arcDatum)),
12249
+ children: /* @__PURE__ */ jsx("path", {
12250
+ d: path(arcDatum) || "",
12251
+ fill: accessors.fill(arcDatum.data)
12252
+ })
12253
+ }, `arc-${index}`))
12254
+ }),
12255
+ selectedArc && /* @__PURE__ */ jsx(Group, {
12256
+ top: centerY,
12257
+ left: centerX,
12258
+ children: /* @__PURE__ */ jsx(SelectedSegmentRing, {
12259
+ chartId,
12260
+ d: path(selectedArc) || ""
12261
+ })
12262
+ }),
12263
+ !allSegmentsHidden && /* @__PURE__ */ jsxs(Group, {
12264
+ top: centerY,
12265
+ left: centerX,
12266
+ mask: wipeMask,
12267
+ children: [showLabels && arcs.map(renderLabel), svgChildren]
12268
+ })
12269
+ ]
12270
+ }), /* @__PURE__ */ jsx(PieSelectionOutput, {
12271
+ ...selectionOutputProps,
12272
+ keyboardTooltipAnchor: selectedArc && getKeyboardTooltipAnchor(centerX, centerY, path.centroid(selectedArc)),
12273
+ renderTooltip
11874
12274
  })]
11875
12275
  });
11876
12276
  }
@@ -11938,32 +12338,10 @@ const validateData = (data) => {
11938
12338
  message: ""
11939
12339
  };
11940
12340
  };
11941
- const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: propWidth, height: propHeight, thickness = .4, clockwise = true, withTooltips = false, showLegend = false, legend = {}, legendValueDisplay = "percentage", label, animation, note, className, children, tooltipOffsetX = 0, tooltipOffsetY = -15, renderTooltip = renderDefaultPieSemiCircleTooltip, gap = "md" }) => {
12341
+ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: propWidth, height: propHeight, thickness = .4, clockwise = true, withTooltips = false, showLegend = false, legend = {}, legendValueDisplay = "percentage", label, animation, note, className, children, tooltipOffsetX = 0, tooltipOffsetY = -15, renderTooltip = renderDefaultPieSemiCircleTooltip, ariaLabel, gap = "md" }) => {
11942
12342
  const legendInteractive = legend.interactive ?? false;
11943
12343
  const legendPosition = legend.position ?? "bottom";
11944
12344
  const chartId = useChartId(providedChartId);
11945
- const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip();
11946
- const standaloneScopeClass = useStandaloneScopeClass();
11947
- const containerRef = useRef(null);
11948
- const handleMouseMove = useCallback((event, arc) => {
11949
- const bounds = containerRef.current?.getBoundingClientRect();
11950
- if (!bounds) return;
11951
- showTooltip({
11952
- tooltipData: arc.data,
11953
- tooltipLeft: event.clientX - bounds.left + tooltipOffsetX,
11954
- tooltipTop: event.clientY - bounds.top + tooltipOffsetY
11955
- });
11956
- }, [
11957
- showTooltip,
11958
- tooltipOffsetX,
11959
- tooltipOffsetY
11960
- ]);
11961
- const handleMouseLeave = useCallback(() => {
11962
- hideTooltip();
11963
- }, [hideTooltip]);
11964
- const handleArcMouseMove = useCallback((arc) => (event) => {
11965
- handleMouseMove(event, arc);
11966
- }, [handleMouseMove]);
11967
12345
  const { isValid, message } = validateData(data);
11968
12346
  const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
11969
12347
  const dataWithPercentages = useDataWithPercentages(data);
@@ -11997,7 +12375,31 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
11997
12375
  metadata: chartMetadata
11998
12376
  });
11999
12377
  const prefersReducedMotion = usePrefersReducedMotion();
12378
+ const wipeMask = animation && !prefersReducedMotion ? `url(#radial-wipe-${chartId})` : null;
12000
12379
  const effectiveWidth = propWidth || DEFAULT_WIDTH$1;
12380
+ const dataWithIndex = visibleData.map((d) => {
12381
+ const originalIndex = data.findIndex((item) => item.label === d.label);
12382
+ return {
12383
+ ...d,
12384
+ index: originalIndex >= 0 ? originalIndex : 0
12385
+ };
12386
+ });
12387
+ const startAngle = clockwise ? -Math.PI / 2 : Math.PI / 2;
12388
+ const endAngle = clockwise ? Math.PI / 2 : -Math.PI / 2;
12389
+ const arcs = pie({
12390
+ value: accessors.value,
12391
+ sort: accessors.sort,
12392
+ padAngle: PAD_ANGLE,
12393
+ startAngle,
12394
+ endAngle
12395
+ })(dataWithIndex);
12396
+ const navigationArcs = orderArcsForNavigation(arcs);
12397
+ const { chartRef, svgRef, selectedArc, getSegmentHandlers, getKeyboardTooltipAnchor, selectionOutputProps, chartProps } = usePieKeyboardNavigation({
12398
+ navigationArcs,
12399
+ withTooltips,
12400
+ tooltipOffsetX,
12401
+ tooltipOffsetY
12402
+ });
12001
12403
  if (!isValid) {
12002
12404
  const errorWidth = propHeight ? Math.min(propWidth || propHeight * 2, propHeight * 2) : effectiveWidth;
12003
12405
  const errorHeight = errorWidth / 2;
@@ -12016,15 +12418,6 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
12016
12418
  })
12017
12419
  });
12018
12420
  }
12019
- const dataWithIndex = visibleData.map((d) => {
12020
- const originalIndex = data.findIndex((item) => item.label === d.label);
12021
- return {
12022
- ...d,
12023
- index: originalIndex >= 0 ? originalIndex : 0
12024
- };
12025
- });
12026
- const startAngle = clockwise ? -Math.PI / 2 : Math.PI / 2;
12027
- const endAngle = clockwise ? Math.PI / 2 : -Math.PI / 2;
12028
12421
  const legendElement = showLegend && /* @__PURE__ */ jsx(Legend, {
12029
12422
  orientation: legend.orientation ?? "horizontal",
12030
12423
  position: legendPosition,
@@ -12057,52 +12450,60 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
12057
12450
  const height = width / 2;
12058
12451
  const radius = height;
12059
12452
  const innerRadius = radius * (1 - thickness);
12453
+ const path = arc({
12454
+ innerRadius,
12455
+ outerRadius: radius,
12456
+ cornerRadius: 3
12457
+ });
12060
12458
  return /* @__PURE__ */ jsxs(Center, {
12061
- ref: containerRef,
12459
+ ref: chartRef,
12062
12460
  className: pie_semi_circle_chart_module_default["pie-semi-circle-chart__plot"],
12461
+ ...chartProps,
12462
+ "aria-label": ariaLabel ?? __("Semi-circle chart", "jetpack-charts"),
12063
12463
  children: [/* @__PURE__ */ jsxs("svg", {
12464
+ ref: svgRef,
12064
12465
  width,
12065
12466
  height,
12066
12467
  viewBox: `0 0 ${width} ${height}`,
12067
- children: [/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(RadialWipeAnimation, {
12068
- id: `radial-wipe-${chartId}`,
12069
- radius,
12070
- innerRadius,
12071
- startAngle: "-180deg",
12072
- wipePercentage: 50
12073
- }) }), /* @__PURE__ */ jsx(Group, {
12074
- top: height,
12075
- left: width / 2,
12076
- mask: animation && !prefersReducedMotion ? `url(#radial-wipe-${chartId})` : null,
12077
- children: allSegmentsHidden ? /* @__PURE__ */ jsx(SvgEmptyState, {
12078
- x: 0,
12079
- y: -radius / 2,
12080
- width,
12081
- height,
12082
- children: getAllHiddenMessage(legendInteractive, "segments")
12083
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
12084
- /* @__PURE__ */ jsx(Pie, {
12085
- data: dataWithIndex,
12086
- pieValue: accessors.value,
12087
- outerRadius: radius,
12088
- innerRadius,
12089
- cornerRadius: 3,
12090
- padAngle: PAD_ANGLE,
12091
- startAngle,
12092
- endAngle,
12093
- pieSort: accessors.sort,
12094
- children: (pie) => {
12095
- return pie.arcs.map((arc) => /* @__PURE__ */ jsx("g", {
12096
- onMouseMove: withTooltips ? handleArcMouseMove(arc) : void 0,
12097
- onMouseLeave: withTooltips ? handleMouseLeave : void 0,
12098
- children: /* @__PURE__ */ jsx("path", {
12099
- d: pie.path(arc) || "",
12100
- fill: accessors.fill(arc.data)
12101
- })
12102
- }, arc.data.label));
12103
- }
12104
- }),
12105
- /* @__PURE__ */ jsxs(Group, { children: [/* @__PURE__ */ jsx(Text, {
12468
+ children: [
12469
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(RadialWipeAnimation, {
12470
+ id: `radial-wipe-${chartId}`,
12471
+ radius,
12472
+ innerRadius,
12473
+ startAngle: "-180deg",
12474
+ wipePercentage: 50
12475
+ }) }),
12476
+ /* @__PURE__ */ jsx(Group, {
12477
+ top: height,
12478
+ left: width / 2,
12479
+ mask: wipeMask,
12480
+ children: allSegmentsHidden ? /* @__PURE__ */ jsx(SvgEmptyState, {
12481
+ x: 0,
12482
+ y: -radius / 2,
12483
+ width,
12484
+ height,
12485
+ children: getAllHiddenMessage(legendInteractive, "segments")
12486
+ }) : arcs.map((arcDatum) => /* @__PURE__ */ jsx("g", {
12487
+ ...getSegmentHandlers(arcDatum.data, navigationArcs.indexOf(arcDatum)),
12488
+ children: /* @__PURE__ */ jsx("path", {
12489
+ d: path(arcDatum) || "",
12490
+ fill: accessors.fill(arcDatum.data)
12491
+ })
12492
+ }, arcDatum.data.label))
12493
+ }),
12494
+ selectedArc && /* @__PURE__ */ jsx(Group, {
12495
+ top: height,
12496
+ left: width / 2,
12497
+ children: /* @__PURE__ */ jsx(SelectedSegmentRing, {
12498
+ chartId,
12499
+ d: path(selectedArc) || ""
12500
+ })
12501
+ }),
12502
+ !allSegmentsHidden && /* @__PURE__ */ jsxs(Group, {
12503
+ top: height,
12504
+ left: width / 2,
12505
+ mask: wipeMask,
12506
+ children: [/* @__PURE__ */ jsxs(Group, { children: [/* @__PURE__ */ jsx(Text, {
12106
12507
  textAnchor: "middle",
12107
12508
  verticalAnchor: "start",
12108
12509
  y: -40,
@@ -12114,18 +12515,13 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
12114
12515
  y: -20,
12115
12516
  className: pie_semi_circle_chart_module_default.note,
12116
12517
  children: note
12117
- })] }),
12118
- !allSegmentsHidden && svgChildren
12119
- ] })
12120
- })]
12121
- }), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ jsx(BoundedTooltip, {
12122
- top: tooltipTop || 0,
12123
- left: tooltipLeft || 0,
12124
- children: /* @__PURE__ */ jsx("div", {
12125
- className: standaloneScopeClass,
12126
- role: "tooltip",
12127
- children: renderTooltip({ tooltipData })
12128
- })
12518
+ })] }), svgChildren]
12519
+ })
12520
+ ]
12521
+ }), /* @__PURE__ */ jsx(PieSelectionOutput, {
12522
+ ...selectionOutputProps,
12523
+ keyboardTooltipAnchor: selectedArc && getKeyboardTooltipAnchor(width / 2, height, path.centroid(selectedArc)),
12524
+ renderTooltip
12129
12525
  })]
12130
12526
  });
12131
12527
  }