@wallarm-org/design-system 0.31.1 → 0.32.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 (35) hide show
  1. package/dist/components/SimpleCharts/BarList/BarListItem.js +1 -5
  2. package/dist/components/SimpleCharts/PieChart/LegendDot.d.ts +5 -0
  3. package/dist/components/SimpleCharts/PieChart/LegendDot.js +18 -0
  4. package/dist/components/SimpleCharts/PieChart/PieChart.d.ts +35 -0
  5. package/dist/components/SimpleCharts/PieChart/PieChart.figma.d.ts +1 -0
  6. package/dist/components/SimpleCharts/PieChart/PieChart.figma.js +120 -0
  7. package/dist/components/SimpleCharts/PieChart/PieChart.js +117 -0
  8. package/dist/components/SimpleCharts/PieChart/PieChartCenter.d.ts +13 -0
  9. package/dist/components/SimpleCharts/PieChart/PieChartCenter.js +39 -0
  10. package/dist/components/SimpleCharts/PieChart/PieChartContext.d.ts +60 -0
  11. package/dist/components/SimpleCharts/PieChart/PieChartContext.js +11 -0
  12. package/dist/components/SimpleCharts/PieChart/PieChartDonut.d.ts +15 -0
  13. package/dist/components/SimpleCharts/PieChart/PieChartDonut.js +112 -0
  14. package/dist/components/SimpleCharts/PieChart/PieChartLegend.d.ts +5 -0
  15. package/dist/components/SimpleCharts/PieChart/PieChartLegend.js +16 -0
  16. package/dist/components/SimpleCharts/PieChart/PieChartLegendItem.d.ts +26 -0
  17. package/dist/components/SimpleCharts/PieChart/PieChartLegendItem.js +116 -0
  18. package/dist/components/SimpleCharts/PieChart/PieChartLegendPercent.d.ts +15 -0
  19. package/dist/components/SimpleCharts/PieChart/PieChartLegendPercent.js +34 -0
  20. package/dist/components/SimpleCharts/PieChart/PieChartLegendValue.d.ts +5 -0
  21. package/dist/components/SimpleCharts/PieChart/PieChartLegendValue.js +16 -0
  22. package/dist/components/SimpleCharts/PieChart/PieChartSkeleton.d.ts +8 -0
  23. package/dist/components/SimpleCharts/PieChart/PieChartSkeleton.js +49 -0
  24. package/dist/components/SimpleCharts/PieChart/classes.d.ts +22 -0
  25. package/dist/components/SimpleCharts/PieChart/classes.js +62 -0
  26. package/dist/components/SimpleCharts/PieChart/constants.d.ts +9 -0
  27. package/dist/components/SimpleCharts/PieChart/constants.js +22 -0
  28. package/dist/components/SimpleCharts/PieChart/index.d.ts +10 -0
  29. package/dist/components/SimpleCharts/PieChart/index.js +10 -0
  30. package/dist/components/SimpleCharts/index.d.ts +2 -0
  31. package/dist/components/SimpleCharts/index.js +2 -1
  32. package/dist/components/SimpleCharts/lib/clamp01.d.ts +1 -0
  33. package/dist/components/SimpleCharts/lib/clamp01.js +6 -0
  34. package/dist/metadata/components.json +3099 -2
  35. package/package.json +2 -1
@@ -2,13 +2,9 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useContext, useMemo } from "react";
3
3
  import { cn } from "../../../utils/cn.js";
4
4
  import { useTestId } from "../../../utils/testId.js";
5
+ import { clamp01 } from "../lib/clamp01.js";
5
6
  import { BarListContext, BarListItemContext } from "./BarListContext.js";
6
7
  import { barListItemVariants } from "./classes.js";
7
- const clamp01 = (n)=>{
8
- if (n < 0) return 0;
9
- if (n > 1) return 1;
10
- return n;
11
- };
12
8
  const BarListItem = ({ value, selected = false, className, children, ref, onClick, onKeyDown, ...props })=>{
13
9
  const testId = useTestId('item');
14
10
  const listCtx = useContext(BarListContext);
@@ -0,0 +1,5 @@
1
+ import type { FC, HTMLAttributes, Ref } from 'react';
2
+ export interface LegendDotProps extends HTMLAttributes<HTMLSpanElement> {
3
+ ref?: Ref<HTMLSpanElement>;
4
+ }
5
+ export declare const LegendDot: FC<LegendDotProps>;
@@ -0,0 +1,18 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../../utils/cn.js";
3
+ import { useTestId } from "../../../utils/testId.js";
4
+ import { legendDotClasses } from "./classes.js";
5
+ const LegendDot = ({ className, children, ref, ...props })=>{
6
+ const testId = useTestId('legend-dot');
7
+ return /*#__PURE__*/ jsx("span", {
8
+ ...props,
9
+ ref: ref,
10
+ "data-slot": "legend-dot",
11
+ "data-testid": testId,
12
+ "aria-hidden": "true",
13
+ className: cn(legendDotClasses, className),
14
+ children: children ?? '·'
15
+ });
16
+ };
17
+ LegendDot.displayName = 'LegendDot';
18
+ export { LegendDot };
@@ -0,0 +1,35 @@
1
+ import { type FC, type HTMLAttributes, type Ref } from 'react';
2
+ import { type TestableProps } from '../../../utils/testId';
3
+ import { type PieChartDatum } from './PieChartContext';
4
+ export interface PieChartProps extends HTMLAttributes<HTMLDivElement>, TestableProps {
5
+ ref?: Ref<HTMLDivElement>;
6
+ /**
7
+ * Each datum's `name` is the join key used by `PieChartLegendItem` to sync hover/active state.
8
+ * Non-finite or negative `value`s are coerced to 0 so recharts can draw them.
9
+ */
10
+ data: PieChartDatum[];
11
+ /**
12
+ * Override for the value used to compute percentages and the centre total.
13
+ * Defaults to `sum(data.value)`. Pass an explicit value when the centre label
14
+ * should reflect a different denominator — e.g. the unfiltered total while
15
+ * the chart shows a filtered view.
16
+ */
17
+ total?: number;
18
+ /**
19
+ * Controlled active slice. When provided, the component does not manage hover state
20
+ * internally — the parent must update it via `onActiveNameChange`.
21
+ */
22
+ activeName?: string | null;
23
+ onActiveNameChange?: (name: string | null) => void;
24
+ /**
25
+ * Multi-selection set. When non-empty, donut slices and legend rows whose `name`
26
+ * is not in the set fade so emphasis lands on the chosen group. Hover
27
+ * (`activeName`) wins — while the user points at a specific slice, only that one
28
+ * stays bright. Names not present in `data` are ignored.
29
+ *
30
+ * Pass a stable reference (`useMemo`/state) — an inline array literal recreates
31
+ * on every parent render and invalidates the internal Set memo.
32
+ */
33
+ selectedNames?: string[];
34
+ }
35
+ export declare const PieChart: FC<PieChartProps>;
@@ -0,0 +1,120 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import code_connect from "@figma/code-connect";
3
+ import { Settings } from "../../../icons/index.js";
4
+ import { Badge } from "../../Badge/index.js";
5
+ import { Button } from "../../Button/index.js";
6
+ import { Chart } from "../Chart/Chart.js";
7
+ import { ChartActions } from "../Chart/ChartActions.js";
8
+ import { ChartHeader } from "../Chart/ChartHeader.js";
9
+ import { ChartTitle } from "../Chart/ChartTitle.js";
10
+ import { LegendDot } from "./LegendDot.js";
11
+ import { PieChart } from "./PieChart.js";
12
+ import { PieChartCenter, PieChartCenterLabel, PieChartCenterValue } from "./PieChartCenter.js";
13
+ import { PieChartDonut } from "./PieChartDonut.js";
14
+ import { PieChartLegend } from "./PieChartLegend.js";
15
+ import { PieChartLegendItem } from "./PieChartLegendItem.js";
16
+ import { PieChartLegendPercent } from "./PieChartLegendPercent.js";
17
+ import { PieChartLegendValue } from "./PieChartLegendValue.js";
18
+ const figmaNodeUrl = 'https://www.figma.com/design/VKb5gW46uSGw0rqrhZsbXT/WADS-Components?node-id=7490-122167&m=dev';
19
+ const sampleData = [
20
+ {
21
+ name: '4XX',
22
+ value: 35,
23
+ color: 'amber',
24
+ badgeColor: 'amber'
25
+ },
26
+ {
27
+ name: '2XX',
28
+ value: 30,
29
+ color: 'green',
30
+ badgeColor: 'green'
31
+ },
32
+ {
33
+ name: '5XX',
34
+ value: 15,
35
+ color: 'red',
36
+ badgeColor: 'red'
37
+ },
38
+ {
39
+ name: '3XX',
40
+ value: 12,
41
+ color: 'blue',
42
+ badgeColor: 'blue'
43
+ },
44
+ {
45
+ name: '1XX',
46
+ value: 8,
47
+ color: 'slate',
48
+ badgeColor: 'slate'
49
+ }
50
+ ];
51
+ const sampleTotal = sampleData.reduce((sum, d)=>sum + d.value, 0);
52
+ code_connect.connect(PieChart, figmaNodeUrl, {
53
+ props: {
54
+ title: code_connect.string('Title'),
55
+ state: code_connect["enum"]('State', {
56
+ Default: 'default',
57
+ Hovered: 'hovered',
58
+ Filtered: 'filtered'
59
+ })
60
+ },
61
+ example: ({ title, state })=>/*#__PURE__*/ jsxs(Chart, {
62
+ children: [
63
+ /*#__PURE__*/ jsxs(ChartHeader, {
64
+ children: [
65
+ /*#__PURE__*/ jsx(ChartTitle, {
66
+ children: title
67
+ }),
68
+ /*#__PURE__*/ jsx(ChartActions, {
69
+ children: /*#__PURE__*/ jsx(Button, {
70
+ variant: "ghost",
71
+ color: "neutral",
72
+ size: "small",
73
+ "aria-label": "Settings",
74
+ children: /*#__PURE__*/ jsx(Settings, {})
75
+ })
76
+ })
77
+ ]
78
+ }),
79
+ /*#__PURE__*/ jsxs(PieChart, {
80
+ data: sampleData,
81
+ total: sampleTotal,
82
+ children: [
83
+ /*#__PURE__*/ jsx(PieChartDonut, {
84
+ children: /*#__PURE__*/ jsxs(PieChartCenter, {
85
+ children: [
86
+ /*#__PURE__*/ jsx(PieChartCenterValue, {
87
+ children: sampleTotal
88
+ }),
89
+ /*#__PURE__*/ jsx(PieChartCenterLabel, {
90
+ children: "requests"
91
+ })
92
+ ]
93
+ })
94
+ }),
95
+ /*#__PURE__*/ jsx(PieChartLegend, {
96
+ children: sampleData.map((d)=>/*#__PURE__*/ jsxs(PieChartLegendItem, {
97
+ name: d.name,
98
+ selected: 'filtered' === state && d.name === sampleData[0]?.name,
99
+ children: [
100
+ /*#__PURE__*/ jsx(Badge, {
101
+ color: d.badgeColor,
102
+ type: "secondary",
103
+ textVariant: "code",
104
+ children: d.name
105
+ }),
106
+ /*#__PURE__*/ jsxs(PieChartLegendValue, {
107
+ children: [
108
+ d.value,
109
+ /*#__PURE__*/ jsx(LegendDot, {}),
110
+ /*#__PURE__*/ jsx(PieChartLegendPercent, {})
111
+ ]
112
+ })
113
+ ]
114
+ }, d.name))
115
+ })
116
+ ]
117
+ })
118
+ ]
119
+ })
120
+ });
@@ -0,0 +1,117 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo } from "react";
3
+ import { useControlled } from "../../../hooks/index.js";
4
+ import { cn } from "../../../utils/cn.js";
5
+ import { TestIdProvider } from "../../../utils/testId.js";
6
+ import { pieChartRootClasses } from "./classes.js";
7
+ import { EMPTY_SELECTION, PieChartActiveContext, PieChartDataContext, PieChartSelectionContext } from "./PieChartContext.js";
8
+ const sanitizeValue = (n)=>{
9
+ if ('number' != typeof n || !Number.isFinite(n) || n < 0) return 0;
10
+ return n;
11
+ };
12
+ const PieChart = ({ data, total, activeName: controlledActiveName, onActiveNameChange, selectedNames, className, children, ref, 'data-testid': testId, ...props })=>{
13
+ const sanitizedData = useMemo(()=>data.map((d)=>({
14
+ ...d,
15
+ value: sanitizeValue(d.value)
16
+ })), [
17
+ data
18
+ ]);
19
+ const { byName, computedTotal, hasDuplicateNames } = useMemo(()=>{
20
+ const map = new Map();
21
+ let sum = 0;
22
+ let duplicates = false;
23
+ for (const d of sanitizedData){
24
+ if (map.has(d.name)) duplicates = true;
25
+ map.set(d.name, d);
26
+ sum += d.value;
27
+ }
28
+ return {
29
+ byName: map,
30
+ computedTotal: sum,
31
+ hasDuplicateNames: duplicates
32
+ };
33
+ }, [
34
+ sanitizedData
35
+ ]);
36
+ useEffect(()=>{
37
+ if (hasDuplicateNames && 'production' !== process.env.NODE_ENV) console.warn("[PieChart] `data` contains duplicate `name` values. Names are used as the join key for percent lookup, hover sync, and React reconciliation — duplicates will produce incorrect percentages, ambiguous hover, and unstable rendering. Provide unique names.");
38
+ }, [
39
+ hasDuplicateNames
40
+ ]);
41
+ const resolvedTotal = 'number' == typeof total && Number.isFinite(total) ? total : computedTotal;
42
+ const isValidTotal = resolvedTotal > 0;
43
+ const [activeNameValue, setInternalActiveName] = useControlled({
44
+ controlled: controlledActiveName,
45
+ default: null
46
+ });
47
+ const rawActiveName = activeNameValue ?? null;
48
+ const activeName = null !== rawActiveName && byName.has(rawActiveName) ? rawActiveName : null;
49
+ useEffect(()=>{
50
+ if (null != controlledActiveName && !byName.has(controlledActiveName)) onActiveNameChange?.(null);
51
+ }, [
52
+ controlledActiveName,
53
+ byName,
54
+ onActiveNameChange
55
+ ]);
56
+ const setActive = useCallback((name)=>{
57
+ setInternalActiveName(name);
58
+ onActiveNameChange?.(name);
59
+ }, [
60
+ setInternalActiveName,
61
+ onActiveNameChange
62
+ ]);
63
+ const selectedSet = useMemo(()=>{
64
+ if (!selectedNames?.length) return EMPTY_SELECTION;
65
+ const set = new Set();
66
+ for (const name of selectedNames)if (byName.has(name)) set.add(name);
67
+ return set.size > 0 ? set : EMPTY_SELECTION;
68
+ }, [
69
+ selectedNames,
70
+ byName
71
+ ]);
72
+ const dataValue = useMemo(()=>({
73
+ data: sanitizedData,
74
+ byName,
75
+ total: resolvedTotal,
76
+ isValidTotal,
77
+ setActive
78
+ }), [
79
+ sanitizedData,
80
+ byName,
81
+ resolvedTotal,
82
+ isValidTotal,
83
+ setActive
84
+ ]);
85
+ const activeValue = useMemo(()=>({
86
+ activeName
87
+ }), [
88
+ activeName
89
+ ]);
90
+ const selectionValue = useMemo(()=>({
91
+ selectedSet
92
+ }), [
93
+ selectedSet
94
+ ]);
95
+ return /*#__PURE__*/ jsx(PieChartDataContext.Provider, {
96
+ value: dataValue,
97
+ children: /*#__PURE__*/ jsx(PieChartActiveContext.Provider, {
98
+ value: activeValue,
99
+ children: /*#__PURE__*/ jsx(PieChartSelectionContext.Provider, {
100
+ value: selectionValue,
101
+ children: /*#__PURE__*/ jsx(TestIdProvider, {
102
+ value: testId,
103
+ children: /*#__PURE__*/ jsx("div", {
104
+ ...props,
105
+ ref: ref,
106
+ "data-slot": "pie-chart",
107
+ "data-testid": testId,
108
+ className: cn(pieChartRootClasses, className),
109
+ children: children
110
+ })
111
+ })
112
+ })
113
+ })
114
+ });
115
+ };
116
+ PieChart.displayName = 'PieChart';
117
+ export { PieChart };
@@ -0,0 +1,13 @@
1
+ import type { FC, HTMLAttributes, Ref } from 'react';
2
+ export interface PieChartCenterProps extends HTMLAttributes<HTMLDivElement> {
3
+ ref?: Ref<HTMLDivElement>;
4
+ }
5
+ export declare const PieChartCenter: FC<PieChartCenterProps>;
6
+ export interface PieChartCenterValueProps extends HTMLAttributes<HTMLSpanElement> {
7
+ ref?: Ref<HTMLSpanElement>;
8
+ }
9
+ export declare const PieChartCenterValue: FC<PieChartCenterValueProps>;
10
+ export interface PieChartCenterLabelProps extends HTMLAttributes<HTMLSpanElement> {
11
+ ref?: Ref<HTMLSpanElement>;
12
+ }
13
+ export declare const PieChartCenterLabel: FC<PieChartCenterLabelProps>;
@@ -0,0 +1,39 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../../utils/cn.js";
3
+ import { useTestId } from "../../../utils/testId.js";
4
+ import { pieChartCenterClasses, pieChartCenterLabelClasses, pieChartCenterValueClasses } from "./classes.js";
5
+ const PieChartCenter = ({ className, ref, ...props })=>{
6
+ const testId = useTestId('center');
7
+ return /*#__PURE__*/ jsx("div", {
8
+ ...props,
9
+ ref: ref,
10
+ "data-slot": "pie-chart-center",
11
+ "data-testid": testId,
12
+ "aria-hidden": "true",
13
+ className: cn(pieChartCenterClasses, className)
14
+ });
15
+ };
16
+ PieChartCenter.displayName = 'PieChartCenter';
17
+ const PieChartCenterValue = ({ className, ref, ...props })=>{
18
+ const testId = useTestId('center-value');
19
+ return /*#__PURE__*/ jsx("span", {
20
+ ...props,
21
+ ref: ref,
22
+ "data-slot": "pie-chart-center-value",
23
+ "data-testid": testId,
24
+ className: cn(pieChartCenterValueClasses, className)
25
+ });
26
+ };
27
+ PieChartCenterValue.displayName = 'PieChartCenterValue';
28
+ const PieChartCenterLabel = ({ className, ref, ...props })=>{
29
+ const testId = useTestId('center-label');
30
+ return /*#__PURE__*/ jsx("span", {
31
+ ...props,
32
+ ref: ref,
33
+ "data-slot": "pie-chart-center-label",
34
+ "data-testid": testId,
35
+ className: cn(pieChartCenterLabelClasses, className)
36
+ });
37
+ };
38
+ PieChartCenterLabel.displayName = 'PieChartCenterLabel';
39
+ export { PieChartCenter, PieChartCenterLabel, PieChartCenterValue };
@@ -0,0 +1,60 @@
1
+ import type { ChartColor } from '../types';
2
+ export interface PieChartDatum {
3
+ /**
4
+ * Stable identity string — **never rendered by the component**. Used as the join key
5
+ * for percent lookup, bidirectional hover sync between slice and legend row, recharts'
6
+ * sector reconciliation, and the `data-name` E2E hook on slice + row DOM.
7
+ *
8
+ * Must be unique within `data` — duplicates trigger a dev-only warning and produce
9
+ * incorrect percentages, ambiguous hover, and unstable rendering. The visible label
10
+ * comes from caller JSX inside `<PieChartLegendItem>` (e.g. `<Badge>{row.name}</Badge>`),
11
+ * so this string can be a slug, ID, or any opaque key when display ≠ identity.
12
+ */
13
+ name: string;
14
+ /**
15
+ * Slice magnitude. Non-finite or negative values are coerced to `0` before reaching
16
+ * recharts (which rejects negatives). Percent = `value / total`.
17
+ */
18
+ value: number;
19
+ /**
20
+ * Built-in palette token. Resolves to a slice-fill CSS variable via `PIE_SLICE_FILL`
21
+ * in `constants.ts` — most colours map to `--color-{color}-500`, with documented
22
+ * exceptions for `'brand'` and `'slate'`. Ignored when `className` is set.
23
+ */
24
+ color?: ChartColor;
25
+ /**
26
+ * Tailwind `fill-*` utility applied directly to the slice `<path>`. Wins over `color`.
27
+ * `bg-*` does NOT work — slices are SVG paths, not divs.
28
+ */
29
+ className?: string;
30
+ }
31
+ /** Static chart shape — recomputes only when data/total changes. */
32
+ export interface PieChartDataContextValue {
33
+ data: PieChartDatum[];
34
+ byName: Map<string, PieChartDatum>;
35
+ total: number;
36
+ isValidTotal: boolean;
37
+ setActive: (name: string | null) => void;
38
+ }
39
+ /** Volatile hover state — recomputes on every hover, kept separate so static
40
+ * consumers (donut layout, legend rows that don't care about active) skip
41
+ * re-rendering when only the hover target changes. */
42
+ export interface PieChartActiveContextValue {
43
+ activeName: string | null;
44
+ }
45
+ /** Multi-selection set pushed by the root. Empty when no selection is active. */
46
+ export interface PieChartSelectionContextValue {
47
+ selectedSet: Set<string>;
48
+ }
49
+ export interface PieChartItemContextValue {
50
+ ratio: number;
51
+ selected: boolean;
52
+ interactive: boolean;
53
+ name: string;
54
+ active: boolean;
55
+ }
56
+ export declare const EMPTY_SELECTION: Set<string>;
57
+ export declare const PieChartDataContext: import("react").Context<PieChartDataContextValue | null>;
58
+ export declare const PieChartActiveContext: import("react").Context<PieChartActiveContextValue>;
59
+ export declare const PieChartSelectionContext: import("react").Context<PieChartSelectionContextValue>;
60
+ export declare const PieChartItemContext: import("react").Context<PieChartItemContextValue | null>;
@@ -0,0 +1,11 @@
1
+ import { createContext } from "react";
2
+ const EMPTY_SELECTION = new Set();
3
+ const PieChartDataContext = createContext(null);
4
+ const PieChartActiveContext = createContext({
5
+ activeName: null
6
+ });
7
+ const PieChartSelectionContext = createContext({
8
+ selectedSet: EMPTY_SELECTION
9
+ });
10
+ const PieChartItemContext = createContext(null);
11
+ export { EMPTY_SELECTION, PieChartActiveContext, PieChartDataContext, PieChartItemContext, PieChartSelectionContext };
@@ -0,0 +1,15 @@
1
+ import { type FC, type HTMLAttributes, type Ref } from 'react';
2
+ export interface PieChartDonutProps extends HTMLAttributes<HTMLDivElement> {
3
+ ref?: Ref<HTMLDivElement>;
4
+ /**
5
+ * Opacity applied to slices that are not currently hovered. Defaults to `0.3`.
6
+ * Set to `1` to disable the hover-dim effect.
7
+ */
8
+ inactiveOpacity?: number;
9
+ /**
10
+ * Disable the recharts mount/transition animation. Defaults to `false`.
11
+ * Set `true` in test/screenshot environments where deterministic frames matter.
12
+ */
13
+ disableAnimation?: boolean;
14
+ }
15
+ export declare const PieChartDonut: FC<PieChartDonutProps>;
@@ -0,0 +1,112 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useContext } from "react";
3
+ import { Pie, PieChart, Sector } from "recharts";
4
+ import { cn } from "../../../utils/cn.js";
5
+ import { useTestId } from "../../../utils/testId.js";
6
+ import { pieChartDonutClasses } from "./classes.js";
7
+ import { PIE_DONUT_ANIMATION_BEGIN, PIE_DONUT_ANIMATION_DURATION, PIE_DONUT_CORNER_RADIUS, PIE_DONUT_INNER_RADIUS, PIE_DONUT_OUTER_RADIUS, PIE_DONUT_PADDING_ANGLE, PIE_DONUT_SIZE, PIE_SLICE_FILL } from "./constants.js";
8
+ import { PieChartActiveContext, PieChartDataContext, PieChartSelectionContext } from "./PieChartContext.js";
9
+ const PLACEHOLDER_DATA = [
10
+ {
11
+ name: '',
12
+ value: 1
13
+ }
14
+ ];
15
+ const PieChartDonut = ({ inactiveOpacity = 0.3, disableAnimation = false, className, children, ref, ...props })=>{
16
+ const testId = useTestId('donut');
17
+ const dataCtx = useContext(PieChartDataContext);
18
+ const { activeName } = useContext(PieChartActiveContext);
19
+ const { selectedSet } = useContext(PieChartSelectionContext);
20
+ const handleEnter = useCallback((_payload, index)=>{
21
+ if (!dataCtx?.isValidTotal) return;
22
+ const datum = dataCtx.data[index];
23
+ if (datum) dataCtx.setActive(datum.name);
24
+ }, [
25
+ dataCtx
26
+ ]);
27
+ const handleLeave = useCallback(()=>{
28
+ dataCtx?.setActive(null);
29
+ }, [
30
+ dataCtx
31
+ ]);
32
+ const isValidTotal = !!dataCtx?.isValidTotal;
33
+ const isMultiSlice = isValidTotal && dataCtx.data.length > 1;
34
+ const renderSlice = useCallback((sectorProps)=>{
35
+ const { isActive: _isActive, index: _index, payload, ...sectorRest } = sectorProps;
36
+ if (!isValidTotal) return /*#__PURE__*/ jsx(Sector, {
37
+ ...sectorRest,
38
+ fill: "var(--color-border-primary-light)",
39
+ opacity: 1,
40
+ stroke: "none"
41
+ });
42
+ const datum = payload;
43
+ const name = datum?.name ?? '';
44
+ const fill = datum?.className ? void 0 : PIE_SLICE_FILL[datum?.color ?? 'slate'];
45
+ const isActive = activeName === name;
46
+ const isSelected = selectedSet.has(name);
47
+ let opacity = 1;
48
+ if (null !== activeName) opacity = isActive ? 1 : inactiveOpacity;
49
+ else if (selectedSet.size > 0) opacity = isSelected ? 1 : inactiveOpacity;
50
+ return /*#__PURE__*/ jsx(Sector, {
51
+ ...sectorRest,
52
+ fill: fill,
53
+ opacity: opacity,
54
+ stroke: "none",
55
+ className: cn('outline-none transition-opacity duration-150 ease-out', datum?.className),
56
+ "data-slot": "pie-chart-slice",
57
+ "data-name": name,
58
+ "data-active": isActive ? 'true' : void 0
59
+ });
60
+ }, [
61
+ isValidTotal,
62
+ activeName,
63
+ inactiveOpacity,
64
+ selectedSet
65
+ ]);
66
+ const pieData = isValidTotal ? dataCtx.data : PLACEHOLDER_DATA;
67
+ return /*#__PURE__*/ jsxs("div", {
68
+ ...props,
69
+ ref: ref,
70
+ "data-slot": "pie-chart-donut",
71
+ "data-testid": testId,
72
+ "aria-hidden": "true",
73
+ className: cn(pieChartDonutClasses, className),
74
+ children: [
75
+ /*#__PURE__*/ jsx(PieChart, {
76
+ width: PIE_DONUT_SIZE,
77
+ height: PIE_DONUT_SIZE,
78
+ margin: {
79
+ top: 0,
80
+ right: 0,
81
+ bottom: 0,
82
+ left: 0
83
+ },
84
+ accessibilityLayer: false,
85
+ children: /*#__PURE__*/ jsx(Pie, {
86
+ data: pieData,
87
+ dataKey: "value",
88
+ nameKey: "name",
89
+ cx: "50%",
90
+ cy: "50%",
91
+ innerRadius: PIE_DONUT_INNER_RADIUS,
92
+ outerRadius: PIE_DONUT_OUTER_RADIUS,
93
+ startAngle: 90,
94
+ endAngle: -270,
95
+ cornerRadius: isMultiSlice ? PIE_DONUT_CORNER_RADIUS : 0,
96
+ paddingAngle: isMultiSlice ? PIE_DONUT_PADDING_ANGLE : 0,
97
+ stroke: "none",
98
+ isAnimationActive: disableAnimation ? false : 'auto',
99
+ animationBegin: PIE_DONUT_ANIMATION_BEGIN,
100
+ animationDuration: PIE_DONUT_ANIMATION_DURATION,
101
+ animationEasing: "ease-out",
102
+ onMouseEnter: handleEnter,
103
+ onMouseLeave: handleLeave,
104
+ shape: renderSlice
105
+ })
106
+ }),
107
+ children
108
+ ]
109
+ });
110
+ };
111
+ PieChartDonut.displayName = 'PieChartDonut';
112
+ export { PieChartDonut };
@@ -0,0 +1,5 @@
1
+ import type { FC, HTMLAttributes, Ref } from 'react';
2
+ export interface PieChartLegendProps extends HTMLAttributes<HTMLDivElement> {
3
+ ref?: Ref<HTMLDivElement>;
4
+ }
5
+ export declare const PieChartLegend: FC<PieChartLegendProps>;
@@ -0,0 +1,16 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../../utils/cn.js";
3
+ import { useTestId } from "../../../utils/testId.js";
4
+ import { pieChartLegendClasses } from "./classes.js";
5
+ const PieChartLegend = ({ className, ref, ...props })=>{
6
+ const testId = useTestId('legend');
7
+ return /*#__PURE__*/ jsx("div", {
8
+ ...props,
9
+ ref: ref,
10
+ "data-slot": "pie-chart-legend",
11
+ "data-testid": testId,
12
+ className: cn(pieChartLegendClasses, className)
13
+ });
14
+ };
15
+ PieChartLegend.displayName = 'PieChartLegend';
16
+ export { PieChartLegend };
@@ -0,0 +1,26 @@
1
+ import { type FC, type HTMLAttributes, type Ref } from 'react';
2
+ export interface PieChartLegendItemProps extends HTMLAttributes<HTMLDivElement> {
3
+ ref?: Ref<HTMLDivElement>;
4
+ /**
5
+ * Identifier matching a `PieChartDatum.name` from the root `data` prop.
6
+ * Drives both the percent calculation and the bidirectional hover sync with the donut slice.
7
+ */
8
+ name: string;
9
+ /**
10
+ * Optional override for the value used in the percent calculation. Defaults to looking
11
+ * the value up from the root `data` array by `name`. Pass an explicit value when the
12
+ * legend renders entries that are not part of the donut (e.g. a synthetic "Other" row).
13
+ */
14
+ value?: number;
15
+ /**
16
+ * Visually marks the row as the selected/filtered datum — applies `bg-states-primary-active`
17
+ * and `aria-current`. When omitted, the row falls back to membership in the root's
18
+ * `selectedNames` set (if provided). When passed explicitly (`true` or `false`) it
19
+ * fully overrides the context — useful when the legend renders rows that aren't part
20
+ * of the chart's multi-selection (e.g. a single filtered view). The resolved value
21
+ * also feeds the dim calculation: a row resolved to `selected=false` while *some*
22
+ * peer is selected will fade like any other non-selected row.
23
+ */
24
+ selected?: boolean;
25
+ }
26
+ export declare const PieChartLegendItem: FC<PieChartLegendItemProps>;