@acronis-platform/ui-react 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ui/accordion-container/accordion-container.js +14 -14
- package/dist/components/ui/accordion-container/accordion-container.js.map +1 -1
- package/dist/components/ui/app-shell-chat/app-shell-chat.js.map +1 -1
- package/dist/components/ui/area-chart/area-chart.js +141 -139
- package/dist/components/ui/area-chart/area-chart.js.map +1 -1
- package/dist/components/ui/bar-chart/bar-chart.js +295 -293
- package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
- package/dist/components/ui/category-bar/category-bar.js +109 -93
- package/dist/components/ui/category-bar/category-bar.js.map +1 -1
- package/dist/components/ui/chart/chart-palette.js +113 -0
- package/dist/components/ui/chart/chart-palette.js.map +1 -0
- package/dist/components/ui/chart/chart.js +176 -129
- package/dist/components/ui/chart/chart.js.map +1 -1
- package/dist/components/ui/chart-state/chart-state-illustrations.js +310 -0
- package/dist/components/ui/chart-state/chart-state-illustrations.js.map +1 -0
- package/dist/components/ui/chart-state/chart-state.js +48 -34
- package/dist/components/ui/chart-state/chart-state.js.map +1 -1
- package/dist/components/ui/chart-widget/chart-widget.js +87 -0
- package/dist/components/ui/chart-widget/chart-widget.js.map +1 -0
- package/dist/components/ui/composed-chart/composed-chart.js +117 -115
- package/dist/components/ui/composed-chart/composed-chart.js.map +1 -1
- package/dist/components/ui/confidence-cone/confidence-cone.js +166 -158
- package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -1
- package/dist/components/ui/funnel-chart/funnel-chart.js +162 -161
- package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -1
- package/dist/components/ui/histogram/histogram.js +94 -92
- package/dist/components/ui/histogram/histogram.js.map +1 -1
- package/dist/components/ui/input/input.js.map +1 -1
- package/dist/components/ui/label/label.js +2 -9
- package/dist/components/ui/label/label.js.map +1 -1
- package/dist/components/ui/line-chart/line-chart.js +143 -141
- package/dist/components/ui/line-chart/line-chart.js.map +1 -1
- package/dist/components/ui/metric/metric.js +74 -185
- package/dist/components/ui/metric/metric.js.map +1 -1
- package/dist/components/ui/page-content/page-content.js.map +1 -1
- package/dist/components/ui/pie-chart/pie-chart.js +201 -195
- package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
- package/dist/components/ui/radar-chart/radar-chart.js +133 -131
- package/dist/components/ui/radar-chart/radar-chart.js.map +1 -1
- package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +297 -265
- package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -1
- package/dist/components/ui/sankey-chart/sankey-chart.js +123 -101
- package/dist/components/ui/sankey-chart/sankey-chart.js.map +1 -1
- package/dist/components/ui/scatter-chart/scatter-chart.js +76 -74
- package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
- package/dist/components/ui/section/section.js +157 -31
- package/dist/components/ui/section/section.js.map +1 -1
- package/dist/components/ui/stepper/stepper.js +24 -24
- package/dist/components/ui/stepper/stepper.js.map +1 -1
- package/dist/components/ui/stepper-item/stepper-item.js +43 -24
- package/dist/components/ui/stepper-item/stepper-item.js.map +1 -1
- package/dist/components/ui/treemap/treemap.js +133 -124
- package/dist/components/ui/treemap/treemap.js.map +1 -1
- package/dist/components/ui/trend-indicator/trend-indicator.js +34 -69
- package/dist/components/ui/trend-indicator/trend-indicator.js.map +1 -1
- package/dist/components/ui/wizard/wizard.js +59 -0
- package/dist/components/ui/wizard/wizard.js.map +1 -0
- package/dist/hooks/use-wizard.js +75 -0
- package/dist/hooks/use-wizard.js.map +1 -0
- package/dist/index.js +502 -497
- package/dist/index.js.map +1 -1
- package/dist/llms/accordion-container.md +181 -0
- package/dist/llms/accordion.md +124 -0
- package/dist/llms/alert.md +225 -0
- package/dist/llms/app-shell-chat.md +415 -0
- package/dist/llms/area-chart.md +355 -0
- package/dist/llms/avatar.md +208 -0
- package/dist/llms/bar-chart.md +406 -0
- package/dist/llms/breadcrumb.md +326 -0
- package/dist/llms/button-group.md +298 -0
- package/dist/llms/button-icon-input.md +142 -0
- package/dist/llms/button-icon-menu.md +209 -0
- package/dist/llms/button-icon.md +128 -0
- package/dist/llms/button-menu.md +172 -0
- package/dist/llms/button.md +185 -0
- package/dist/llms/calendar-panel.md +733 -0
- package/dist/llms/calendar.md +188 -0
- package/dist/llms/card-filter.md +214 -0
- package/dist/llms/card-section.md +293 -0
- package/dist/llms/card.md +447 -0
- package/dist/llms/carousel-dialog.md +156 -0
- package/dist/llms/category-bar.md +193 -0
- package/dist/llms/chart-state.md +190 -0
- package/dist/llms/chart-widget.md +209 -0
- package/dist/llms/chart.md +182 -0
- package/dist/llms/checkbox.md +257 -0
- package/dist/llms/chip.md +195 -0
- package/dist/llms/combobox.md +164 -0
- package/dist/llms/composed-chart.md +508 -0
- package/dist/llms/confidence-cone.md +277 -0
- package/dist/llms/data-table.md +474 -0
- package/dist/llms/date-range-picker.md +272 -0
- package/dist/llms/description-list.md +131 -0
- package/dist/llms/dialog-footer-carousel.md +147 -0
- package/dist/llms/dialog-footer-default.md +142 -0
- package/dist/llms/dialog-root.md +246 -0
- package/dist/llms/dialog-welcome.md +223 -0
- package/dist/llms/dialog.md +403 -0
- package/dist/llms/dropdown-menu.md +193 -0
- package/dist/llms/empty-overlay.md +146 -0
- package/dist/llms/empty.md +156 -0
- package/dist/llms/field.md +185 -0
- package/dist/llms/filter-cards.md +157 -0
- package/dist/llms/filter-chips.md +323 -0
- package/dist/llms/filter-search.md +512 -0
- package/dist/llms/form.md +154 -0
- package/dist/llms/funnel-chart.md +333 -0
- package/dist/llms/grid.md +80 -0
- package/dist/llms/histogram.md +190 -0
- package/dist/llms/input-date-picker.md +203 -0
- package/dist/llms/input-otp.md +173 -0
- package/dist/llms/input-password.md +197 -0
- package/dist/llms/input-search.md +170 -0
- package/dist/llms/input-select.md +278 -0
- package/dist/llms/input-text-area.md +271 -0
- package/dist/llms/input-text.md +192 -0
- package/dist/llms/line-chart.md +369 -0
- package/dist/llms/link.md +209 -0
- package/dist/llms/loading.md +130 -0
- package/dist/llms/meter.md +177 -0
- package/dist/llms/metric.md +204 -0
- package/dist/llms/number-field.md +122 -0
- package/dist/llms/page-content.md +69 -0
- package/dist/llms/page-header.md +182 -0
- package/dist/llms/pagination.md +133 -0
- package/dist/llms/pie-chart.md +307 -0
- package/dist/llms/popover.md +252 -0
- package/dist/llms/progress-circle.md +126 -0
- package/dist/llms/progress.md +111 -0
- package/dist/llms/radar-chart.md +316 -0
- package/dist/llms/radial-bar-chart.md +319 -0
- package/dist/llms/radio.md +255 -0
- package/dist/llms/resizable.md +195 -0
- package/dist/llms/sankey-chart.md +208 -0
- package/dist/llms/scatter-chart.md +239 -0
- package/dist/llms/scroll-area.md +183 -0
- package/dist/llms/section.md +401 -0
- package/dist/llms/select.md +210 -0
- package/dist/llms/separator.md +87 -0
- package/dist/llms/sheet.md +202 -0
- package/dist/llms/sidebar-primary.md +360 -0
- package/dist/llms/sidebar-secondary.md +417 -0
- package/dist/llms/skeleton.md +81 -0
- package/dist/llms/slider.md +97 -0
- package/dist/llms/spinner.md +100 -0
- package/dist/llms/stack.md +80 -0
- package/dist/llms/stepper-item.md +309 -0
- package/dist/llms/stepper.md +251 -0
- package/dist/llms/switch.md +140 -0
- package/dist/llms/table.md +436 -0
- package/dist/llms/tabs.md +197 -0
- package/dist/llms/tag.md +139 -0
- package/dist/llms/timeline.md +361 -0
- package/dist/llms/timer.md +259 -0
- package/dist/llms/toast.md +241 -0
- package/dist/llms/toggle-group.md +121 -0
- package/dist/llms/toolbar.md +453 -0
- package/dist/llms/tooltip.md +164 -0
- package/dist/llms/treemap.md +254 -0
- package/dist/llms/trend-indicator.md +132 -0
- package/dist/llms/widget-placeholder.md +169 -0
- package/dist/llms/wizard.md +412 -0
- package/dist/llms.txt +142 -0
- package/dist/react.js +502 -497
- package/dist/react.js.map +1 -1
- package/dist/src/components/ui/area-chart/area-chart.d.ts +9 -4
- package/dist/src/components/ui/avatar/avatar.d.ts +1 -1
- package/dist/src/components/ui/bar-chart/bar-chart.d.ts +9 -4
- package/dist/src/components/ui/category-bar/category-bar.d.ts +10 -4
- package/dist/src/components/ui/chart/__stories__/palette-control.d.ts +48 -0
- package/dist/src/components/ui/chart/__tests__/chart-palette.test.d.ts +1 -0
- package/dist/src/components/ui/chart/chart-palette.d.ts +128 -0
- package/dist/src/components/ui/chart/chart.d.ts +66 -15
- package/dist/src/components/ui/chart/index.d.ts +4 -2
- package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +6 -0
- package/dist/src/components/ui/chart-state/chart-state.d.ts +13 -2
- package/dist/src/components/ui/chart-state/index.d.ts +1 -0
- package/dist/src/components/ui/chart-widget/chart-widget.d.ts +64 -0
- package/dist/src/components/ui/chart-widget/index.d.ts +1 -0
- package/dist/src/components/ui/composed-chart/composed-chart.d.ts +12 -7
- package/dist/src/components/ui/confidence-cone/confidence-cone.d.ts +10 -5
- package/dist/src/components/ui/empty-overlay/empty-overlay.d.ts +1 -1
- package/dist/src/components/ui/funnel-chart/funnel-chart.d.ts +11 -5
- package/dist/src/components/ui/histogram/histogram.d.ts +10 -4
- package/dist/src/components/ui/label/index.d.ts +1 -1
- package/dist/src/components/ui/label/label.d.ts +1 -5
- package/dist/src/components/ui/line-chart/line-chart.d.ts +9 -4
- package/dist/src/components/ui/metric/metric.d.ts +13 -14
- package/dist/src/components/ui/pie-chart/pie-chart.d.ts +17 -8
- package/dist/src/components/ui/radar-chart/radar-chart.d.ts +10 -5
- package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +17 -6
- package/dist/src/components/ui/sankey-chart/sankey-chart.d.ts +10 -5
- package/dist/src/components/ui/scatter-chart/scatter-chart.d.ts +10 -5
- package/dist/src/components/ui/section/index.d.ts +1 -1
- package/dist/src/components/ui/section/section.d.ts +75 -6
- package/dist/src/components/ui/stepper-item/stepper-item.d.ts +6 -5
- package/dist/src/components/ui/tag/tag.d.ts +1 -1
- package/dist/src/components/ui/treemap/treemap.d.ts +11 -5
- package/dist/src/components/ui/trend-indicator/trend-indicator.d.ts +3 -7
- package/dist/src/components/ui/wizard/index.d.ts +1 -0
- package/dist/src/components/ui/wizard/wizard.d.ts +6 -0
- package/dist/src/hooks/index.d.ts +1 -0
- package/dist/src/hooks/use-wizard.d.ts +80 -0
- package/dist/src/index.d.ts +2 -3
- package/dist/ui-react.css +1 -1
- package/package.json +9 -3
- package/dist/components/ui/app-shell/app-shell.js +0 -75
- package/dist/components/ui/app-shell/app-shell.js.map +0 -1
- package/dist/components/ui/auth-layout/auth-layout.js +0 -56
- package/dist/components/ui/auth-layout/auth-layout.js.map +0 -1
- package/dist/components/ui/collapsible/collapsible.js +0 -22
- package/dist/components/ui/collapsible/collapsible.js.map +0 -1
- package/dist/src/components/ui/app-shell/app-shell.d.ts +0 -8
- package/dist/src/components/ui/app-shell/index.d.ts +0 -1
- package/dist/src/components/ui/auth-layout/auth-layout.d.ts +0 -6
- package/dist/src/components/ui/auth-layout/index.d.ts +0 -1
- package/dist/src/components/ui/collapsible/collapsible.d.ts +0 -5
- package/dist/src/components/ui/collapsible/index.d.ts +0 -1
- package/dist/src/components/ui/label/label.docs.d.ts +0 -10
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# PieChart
|
|
2
|
+
|
|
3
|
+
> A typed pie/donut-chart composition over the shared `Chart` primitives. Takes `data` + `config` + a `dataKey` (slice value) and `nameKey` (slice label) and renders a themed recharts `PieChart` inside a `ChartContainer`, with tooltip and legend chrome. One variant axis: `shape` (filled pie / hollow-centre donut). Ported from the apps/demo `PieChartPlayground`.
|
|
4
|
+
|
|
5
|
+
**Status:** draft · **Category:** data-display · **Since:** v0.58.0
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { PieChart } from '@acronis-platform/ui-react'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick example
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<PieChart
|
|
17
|
+
config={config}
|
|
18
|
+
data={data}
|
|
19
|
+
dataKey="value"
|
|
20
|
+
nameKey="browser"
|
|
21
|
+
shape="donut"
|
|
22
|
+
className="h-[360px] w-[360px]"
|
|
23
|
+
/>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
A typed pie/donut-chart built on the shared `Chart` primitives. Give it `data`, a
|
|
27
|
+
per-slice `config`, the value field (`dataKey`), and the label field (`nameKey`);
|
|
28
|
+
it renders a themed recharts `PieChart` — tooltip and legend included — so you
|
|
29
|
+
don't hand-compose recharts children.
|
|
30
|
+
|
|
31
|
+
Slice colors come from the `palette` prop — a dataviz palette resolving to the
|
|
32
|
+
`--ui-dataviz-*` tokens, `categorical` by default — not from `config`.
|
|
33
|
+
|
|
34
|
+
> **Design-pending v1.** Ported from the apps/demo `PieChartPlayground`. The
|
|
35
|
+
> chrome is reconciled with Figma later; Code Connect is deferred.
|
|
36
|
+
|
|
37
|
+
## When to use
|
|
38
|
+
|
|
39
|
+
- Showing part-to-whole proportions across a handful of categories.
|
|
40
|
+
- A compact, at-a-glance distribution (device share, channel split).
|
|
41
|
+
- Use `shape="donut"` when you want a lighter footprint or a centre for a total.
|
|
42
|
+
|
|
43
|
+
## When not to use
|
|
44
|
+
|
|
45
|
+
- More than ~6 categories, or many similar-sized slices — use a bar chart, which
|
|
46
|
+
compares magnitudes far more precisely.
|
|
47
|
+
- Trends over time — use a line or area chart.
|
|
48
|
+
- Comparing exact values — angles are hard to read; prefer bars or a table.
|
|
49
|
+
|
|
50
|
+
## Variants
|
|
51
|
+
|
|
52
|
+
| Axis | Values | Effect |
|
|
53
|
+
| ------- | --------------- | ----------------------------------------- |
|
|
54
|
+
| `shape` | `pie` · `donut` | Filled arc vs a hollow-centre donut ring. |
|
|
55
|
+
|
|
56
|
+
## Example
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { PieChart } from '@acronis-platform/ui-react';
|
|
60
|
+
import type { ChartConfig } from '@acronis-platform/ui-react';
|
|
61
|
+
|
|
62
|
+
const data = [
|
|
63
|
+
{ browser: 'Chrome', value: 275 },
|
|
64
|
+
{ browser: 'Safari', value: 200 },
|
|
65
|
+
{ browser: 'Firefox', value: 187 },
|
|
66
|
+
{ browser: 'Edge', value: 173 },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const config = {
|
|
70
|
+
Chrome: { label: 'Chrome', color: 'var(--ui-background-brand-secondary)' },
|
|
71
|
+
Safari: {
|
|
72
|
+
label: 'Safari',
|
|
73
|
+
color: 'var(--ui-background-status-strong-danger)',
|
|
74
|
+
},
|
|
75
|
+
Firefox: {
|
|
76
|
+
label: 'Firefox',
|
|
77
|
+
color: 'var(--ui-background-status-strong-success)',
|
|
78
|
+
},
|
|
79
|
+
Edge: { label: 'Edge', color: 'var(--ui-background-status-strong-warning)' },
|
|
80
|
+
} satisfies ChartConfig;
|
|
81
|
+
|
|
82
|
+
<PieChart
|
|
83
|
+
config={config}
|
|
84
|
+
data={data}
|
|
85
|
+
dataKey="value"
|
|
86
|
+
nameKey="browser"
|
|
87
|
+
shape="donut"
|
|
88
|
+
className="h-[360px] w-[360px]"
|
|
89
|
+
/>;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Slice colors reference existing semantic `--ui-*` tokens, keyed by each slice's
|
|
93
|
+
`nameKey` value. `--ui-background-status-strong-*` is chromatic in every brand;
|
|
94
|
+
`--ui-background-brand-secondary` is brand-dependent (blue in `default`, neutral
|
|
95
|
+
in some white-label brands), so it is not color-stable across brands until the
|
|
96
|
+
real data-viz palette lands.
|
|
97
|
+
|
|
98
|
+
## Props
|
|
99
|
+
|
|
100
|
+
| Prop | Type | Default | Required | Description |
|
|
101
|
+
|------|------|---------|----------|-------------|
|
|
102
|
+
| `shape` | `'pie' | 'donut'` | `'donut'` | no | Arc shape. `donut` leaves a hollow centre (`innerRadius`); `pie` fills to the centre. |
|
|
103
|
+
| `data` | `ReadonlyArray<Record<string, string | number>>` | — | yes | Row-per-slice data — each row holds a `nameKey` label and a `dataKey` numeric value. |
|
|
104
|
+
| `config` | `ChartConfig` | — | yes | Slices take their colour from the container's `palette`; each entry, keyed by the slice's `nameKey` value, maps that slice to a `label` and an optional `tone`. Resolved into the `--color-<name>` custom properties the slices fill from. |
|
|
105
|
+
| `dataKey` | `string` | — | yes | The numeric field that sizes each slice. |
|
|
106
|
+
| `nameKey` | `string` | — | yes | The label field that names each slice (drives legend, tooltip, and the `--color-<name>` lookup). Values should be unique per chart — rows sharing a name share one config/color entry. |
|
|
107
|
+
| `innerRadius` | `number` | `48` | no | Inner radius of the arc when `shape="donut"` (ignored for `shape="pie"`). |
|
|
108
|
+
| `centerLabel` | `{ value?: string | number; label?: string | number }` | — | no | Custom content for the donut hole — a headline `value` and/or a `label` caption, stacked and centered. Rendered only for `shape="donut"`. |
|
|
109
|
+
| `outerRadius` | `number` | `60` | no | Outer radius of the arc. |
|
|
110
|
+
| `paddingAngle` | `number` | `2` | no | Gap between slices, in degrees. |
|
|
111
|
+
| `cornerRadius` | `number` | — | no | Corner radius of each slice. |
|
|
112
|
+
| `startAngle` | `number` | `90` | no | Angle the sweep starts at, in degrees (90 is 12 o'clock). Defaults to the top of the clock face. Pair with endAngle for a semicircle (180 → 0) or an arc. A partial sweep still centres on the full plot area, so a half turn fills only half its box — give it a short, wide one. |
|
|
113
|
+
| `endAngle` | `number` | `-270` | no | Angle the sweep ends at, in degrees. Defaults to a full clockwise circle from the startAngle (startAngle − 360). |
|
|
114
|
+
| `minAngle` | `number` | — | no | Smallest angle a non-zero slice may occupy, in degrees — keeps a tiny slice visible (and hoverable) at the cost of being drawn out of proportion. |
|
|
115
|
+
| `sliceSettings` | `Record<string, { color?: string; hideLabel?: boolean; labelFormat?: PieChartLabelFormat }>` | — | no | Per-slice overrides keyed by the slice's nameKey value: its color, and whether/how its data label reads. A name with no entry keeps the chart-level behavior. |
|
|
116
|
+
| `showTooltip` | `boolean` | `true` | no | Render the hover tooltip. |
|
|
117
|
+
| `showLegend` | `boolean` | `true` | no | Render the legend. |
|
|
118
|
+
| `legendValueFormatter` | `(value: string | number) => string` | — | no | Format each slice's value in the list legend. Omit to stringify the raw value. |
|
|
119
|
+
| `margin` | `{ top?: number; right?: number; bottom?: number; left?: number }` | — | no | Plot-area margin, in px. Omit to use the renderer's default (5 on every side). |
|
|
120
|
+
| `tooltipContent` | `ReactElement | ((props) => ReactNode)` | — | no | Replace the default tooltip. Pass a configured ChartTooltipContent (from this library) — e.g. with a formatter / labelFormatter — to customize the tooltip content without composing recharts. Takes precedence over tooltipFormat. Ignored when showTooltip is false. |
|
|
121
|
+
| `tooltipFormat` | `'value' | 'value-percent'` | `'value'` | no | How the default tooltip reads a slice: its value alone, or the value followed by its share of the total (formatted like a data label's share, so the two always agree). Pass tooltipContent to format it differently. |
|
|
122
|
+
| `animate` | `boolean` | `false` | no | Enable entrance animation. Off by default so output/baselines are unchanged. |
|
|
123
|
+
| `animationDuration` | `number` | — | no | Animation duration in ms. |
|
|
124
|
+
| `animationBegin` | `number` | — | no | Delay before the animation starts, in ms. |
|
|
125
|
+
| `animationEasing` | `'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'` | — | no | Easing curve. |
|
|
126
|
+
| `showLabels` | `boolean` | `false` | no | Render a value label on each slice. |
|
|
127
|
+
| `labelPosition` | `'outside' | 'center' | 'centerTop' | 'centerBottom' | 'insideStart' | 'insideEnd' | 'end'` | `'outside'` | no | Where the value labels sit. Defaults to outside the arc, on the chart surface — the only placement guaranteed to have contrast against any palette's slice colors. Inside placements switch the label to the on-fill token. Ignored when labelLine is on — a leader line only ever ends outside the arc. |
|
|
128
|
+
| `labelFormat` | `'value' | 'name-value' | 'name-percent' | 'percent'` | `'value'` | no | What each data label reads — the value alone, or the slice name and/or its share of the total (to one decimal, with a "." decimal separator and a bare "%", not locale-aware). Overridable per slice via sliceSettings. |
|
|
129
|
+
| `labelLine` | `boolean` | `false` | no | Draw a leader line from each slice to its data label, in the slice's own color. Moves the labels outside the arc; a slice whose label is hidden by sliceSettings gets no line either. |
|
|
130
|
+
| `labelFormatter` | `(value) => string` | — | no | Format each label value (reuses the tick formatters). Formats the slice's value, so it reaches the value and name-value formats only — a share is always rendered as NN.N% and never passes through it. |
|
|
131
|
+
|
|
132
|
+
## Behavior
|
|
133
|
+
|
|
134
|
+
`PieChart` is a typed [recharts](https://recharts.org) composition over the
|
|
135
|
+
shared `Chart` primitives. It takes `data`, a per-slice `config`, the value field
|
|
136
|
+
(`dataKey`), and the label field (`nameKey`), and renders a themed recharts
|
|
137
|
+
`PieChart` inside a `ChartContainer`.
|
|
138
|
+
|
|
139
|
+
```gherkin
|
|
140
|
+
Scenario: Render slices from data and config
|
|
141
|
+
Given data rows and a config mapping each slice name to a label and color
|
|
142
|
+
And dataKey "value" and nameKey "browser"
|
|
143
|
+
Then one arc slice renders per row, sized by its dataKey value
|
|
144
|
+
And each slice fills from its injected --color-<name> custom property
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```gherkin
|
|
148
|
+
Scenario: Pie shape (default)
|
|
149
|
+
Given shape is "pie"
|
|
150
|
+
Then the arc fills to the centre (innerRadius 0)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```gherkin
|
|
154
|
+
Scenario: Donut shape
|
|
155
|
+
Given shape is "donut"
|
|
156
|
+
Then the arc has a hollow centre at innerRadius
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```gherkin
|
|
160
|
+
Scenario: Padding between slices
|
|
161
|
+
Given paddingAngle is greater than 0
|
|
162
|
+
Then a gap of that many degrees separates adjacent slices
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```gherkin
|
|
166
|
+
Scenario: Partial sweep
|
|
167
|
+
Given a startAngle of 180 and an endAngle of 0
|
|
168
|
+
Then the slices are laid out over that half turn instead of a full circle
|
|
169
|
+
And their relative sizes are unchanged
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```gherkin
|
|
173
|
+
Scenario: Rounded slices
|
|
174
|
+
Given cornerRadius is greater than 0
|
|
175
|
+
Then each slice's corners are rounded by that radius
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```gherkin
|
|
179
|
+
Scenario: Minimum slice angle
|
|
180
|
+
Given minAngle is greater than 0
|
|
181
|
+
Then every non-zero slice occupies at least that angle
|
|
182
|
+
And a slice smaller than it is drawn out of proportion so it stays hoverable
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```gherkin
|
|
186
|
+
Scenario: Data-label format
|
|
187
|
+
Given showLabels is true
|
|
188
|
+
And labelFormat is one of value, name-value, name-percent or percent
|
|
189
|
+
Then each slice's label reads its value, its name, and/or its share of the
|
|
190
|
+
sum of every slice value, to one decimal
|
|
191
|
+
And a labelFormatter formats the slice's value, so it reaches the value and
|
|
192
|
+
name-value formats only — a share is always rendered as NN.N%
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```gherkin
|
|
196
|
+
Scenario: Percent with nothing to divide by
|
|
197
|
+
Given a percent-bearing labelFormat
|
|
198
|
+
And the slice values sum to zero, or this slice's value is non-numeric
|
|
199
|
+
Then name-percent falls back to the slice name and percent renders no label
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```gherkin
|
|
203
|
+
Scenario: Slice with no value at all
|
|
204
|
+
Given a data row that carries no dataKey field
|
|
205
|
+
Then the name-bearing formats fall back to the bare slice name
|
|
206
|
+
And the value-only formats render no label, rather than an empty one
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```gherkin
|
|
210
|
+
Scenario: Leader lines
|
|
211
|
+
Given showLabels and labelLine are both true
|
|
212
|
+
Then a line runs from each slice to its label, in that slice's colour
|
|
213
|
+
And the labels sit outside the arc regardless of labelPosition
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
```gherkin
|
|
217
|
+
Scenario: Per-slice overrides
|
|
218
|
+
Given a sliceSettings entry keyed by a slice's nameKey value
|
|
219
|
+
Then that slice takes the entry's colour and labelFormat in place of the
|
|
220
|
+
chart-level ones
|
|
221
|
+
And an entry with hideLabel drops that slice's label — and its leader line —
|
|
222
|
+
while every other slice keeps its own
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```gherkin
|
|
226
|
+
Scenario: Tooltip on hover
|
|
227
|
+
Given showTooltip is true
|
|
228
|
+
When the user hovers a slice
|
|
229
|
+
Then a card shows that slice's name and value
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
```gherkin
|
|
233
|
+
Scenario: Tooltip value format
|
|
234
|
+
Given tooltipFormat is "value-percent"
|
|
235
|
+
When the user hovers a slice
|
|
236
|
+
Then the card shows that slice's value followed by its share of the total
|
|
237
|
+
And a tooltipContent of the caller's own takes precedence over the preset
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
```gherkin
|
|
241
|
+
Scenario: Legend
|
|
242
|
+
Given showLegend is true
|
|
243
|
+
Then a swatch + label renders for each slice (from nameKey / config)
|
|
244
|
+
And it sits on the edge given by legendPosition (bottom by default)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```gherkin
|
|
248
|
+
Scenario: Donut center label
|
|
249
|
+
Given shape is "donut" and a centerLabel { value, label }
|
|
250
|
+
Then the value renders large and the label smaller beneath it, centered in the hole
|
|
251
|
+
And the block stays centered on the donut whether or not a legend is shown,
|
|
252
|
+
and on whichever edge legendPosition puts it
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```gherkin
|
|
256
|
+
Scenario: Center label ignored for a pie
|
|
257
|
+
Given shape is "pie" and a centerLabel
|
|
258
|
+
Then no center content renders (a filled pie has no hole)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
```gherkin
|
|
262
|
+
Scenario: Empty data
|
|
263
|
+
Given data is an empty array
|
|
264
|
+
Then the chart renders with no slices and does not throw
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```gherkin
|
|
268
|
+
Scenario: Entrance animation is opt-in
|
|
269
|
+
Given animate is unset
|
|
270
|
+
Then the series render at their final geometry with no entrance animation
|
|
271
|
+
And the committed visual baselines are unaffected
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
```gherkin
|
|
275
|
+
Scenario: Reduced motion
|
|
276
|
+
Given animate is true
|
|
277
|
+
And a user with prefers-reduced-motion
|
|
278
|
+
Then the entrance animation does not play and the series render at their final geometry
|
|
279
|
+
And the same applies when rendering on the server
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Accessibility
|
|
283
|
+
|
|
284
|
+
- recharts' `accessibilityLayer` is **on by default** (recharts v3), giving the
|
|
285
|
+
chart keyboard focus and an accessible description of the plotted slices.
|
|
286
|
+
- A pie chart is inherently visual. **Pair it with a text alternative** — a
|
|
287
|
+
caption, a summary sentence, or an adjacent data table carrying the same
|
|
288
|
+
numbers — and give the chart an accessible name (`aria-label` /
|
|
289
|
+
`aria-labelledby` referencing a visible heading) when it is meaningful on its
|
|
290
|
+
own. The wrapper forwards native `div` attributes, so `aria-*` pass through.
|
|
291
|
+
- Do **not** rely on color alone to distinguish slices. Keep `showLegend` (or the
|
|
292
|
+
tooltip) visible so each color is paired with a text label. Pie charts read
|
|
293
|
+
poorly with many similar-sized slices — prefer a handful of slices and combine
|
|
294
|
+
the tail into an "Other" slice.
|
|
295
|
+
- The chrome (tooltip, legend) resolves to semantic `--ui-*` tokens that meet
|
|
296
|
+
contrast in light and dark. **Slice colors are caller-supplied** via `config` —
|
|
297
|
+
pick values that meet 3:1 against the surface and are distinguishable for
|
|
298
|
+
color-vision deficiencies (adjacent slices especially). The borrowed semantic
|
|
299
|
+
tokens are a design-pending stopgap until the `--ui-chart-*` palette lands.
|
|
300
|
+
- Watch recharts issue [#4809](https://github.com/recharts/recharts/issues/4809)
|
|
301
|
+
on the a11y layer for heavily-customized charts.
|
|
302
|
+
|
|
303
|
+
## Contrast
|
|
304
|
+
|
|
305
|
+
Chart chrome meets contrast in both themes via the semantic tokens. Slice fills
|
|
306
|
+
come from `config` and are the caller's responsibility — keep adjacent slices
|
|
307
|
+
distinguishable, not just against the surface but from each other.
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Popover
|
|
2
|
+
|
|
3
|
+
> A floating panel anchored to a trigger, shown on demand for secondary content or quick actions. Built on the Base UI Popover primitive (positioning, focus management, outside-press / Esc dismissal). Ported from the legacy shadcn-uikit `Popover`; container chrome themed by the `--ui-popover-*` tier, with optional `PopoverBody`/`PopoverFooter` parts for the body-rhythm and default action-row recipe shown in Figma.
|
|
4
|
+
|
|
5
|
+
**Status:** ready · **Category:** overlays · **Since:** v0.12.0
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Popover, PopoverTrigger, PopoverContent, PopoverBody, PopoverFooter } from '@acronis-platform/ui-react'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick example
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Popover>
|
|
17
|
+
<PopoverTrigger render={<Button variant="secondary">Open</Button>} />
|
|
18
|
+
<PopoverContent side="bottom" align="center">
|
|
19
|
+
<PopoverBody>
|
|
20
|
+
<h4>Dimensions</h4>
|
|
21
|
+
<p>Set the dimensions for the layer.</p>
|
|
22
|
+
</PopoverBody>
|
|
23
|
+
<PopoverFooter>
|
|
24
|
+
<Button variant="secondary">Cancel</Button>
|
|
25
|
+
<Button>Apply</Button>
|
|
26
|
+
</PopoverFooter>
|
|
27
|
+
</PopoverContent>
|
|
28
|
+
</Popover>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
A floating panel anchored to a trigger, shown on demand for secondary content or
|
|
32
|
+
quick actions. Built on the Base UI Popover primitive.
|
|
33
|
+
|
|
34
|
+
> Ported from the legacy `@acronis-platform/shadcn-uikit` `Popover`. Container
|
|
35
|
+
> chrome is themed by the `--ui-popover-*` tier; the optional `PopoverBody` /
|
|
36
|
+
> `PopoverFooter` parts (body rhythm, default action-row footer) are themed by
|
|
37
|
+
> `--ui-popover-body-*` and the shared `--ui-footer-*` tier. Enter/exit
|
|
38
|
+
> animations use `tw-animate-css`.
|
|
39
|
+
|
|
40
|
+
## When to use
|
|
41
|
+
|
|
42
|
+
- Showing secondary content or a small set of controls anchored to a trigger
|
|
43
|
+
(a filter form, a quick edit, extra detail) without leaving the page.
|
|
44
|
+
|
|
45
|
+
## When not to use
|
|
46
|
+
|
|
47
|
+
- For a blocking decision or a focused task — use a **Dialog** (modal).
|
|
48
|
+
- For a short, non-interactive hint on hover/focus — use a **Tooltip**.
|
|
49
|
+
- For a list of actions/commands — use a menu.
|
|
50
|
+
|
|
51
|
+
## Parts
|
|
52
|
+
|
|
53
|
+
| Part | Element | Purpose |
|
|
54
|
+
| ---------------- | ------------------ | ---------------------------------------------- |
|
|
55
|
+
| `Popover` | — (Root) | Owns the open state. |
|
|
56
|
+
| `PopoverTrigger` | `button` | Toggles and anchors the popover. |
|
|
57
|
+
| `PopoverContent` | `div[role=dialog]` | The portaled, positioned popup panel. |
|
|
58
|
+
| `PopoverPortal` | — | Lower-level portal (`PopoverContent` uses it). |
|
|
59
|
+
| `PopoverBody` | `div` | Optional vertical-rhythm wrapper for content. |
|
|
60
|
+
| `PopoverFooter` | `div` | Optional default action-row footer. |
|
|
61
|
+
|
|
62
|
+
## Example
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import {
|
|
66
|
+
Popover,
|
|
67
|
+
PopoverTrigger,
|
|
68
|
+
PopoverContent,
|
|
69
|
+
PopoverBody,
|
|
70
|
+
Button,
|
|
71
|
+
} from '@acronis-platform/ui-react';
|
|
72
|
+
|
|
73
|
+
<Popover>
|
|
74
|
+
<PopoverTrigger render={<Button variant="secondary">Open</Button>} />
|
|
75
|
+
<PopoverContent side="bottom" align="center">
|
|
76
|
+
<PopoverBody>
|
|
77
|
+
<h4 className="font-medium leading-none">Dimensions</h4>
|
|
78
|
+
<p className="text-sm text-muted-foreground">
|
|
79
|
+
Set the dimensions for the layer.
|
|
80
|
+
</p>
|
|
81
|
+
</PopoverBody>
|
|
82
|
+
</PopoverContent>
|
|
83
|
+
</Popover>;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
With the default action-row footer (the Figma "Popover" recipe — Cancel + Apply):
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { PopoverFooter } from '@acronis-platform/ui-react';
|
|
90
|
+
|
|
91
|
+
<PopoverContent>
|
|
92
|
+
<PopoverBody>Drop any content into this slot.</PopoverBody>
|
|
93
|
+
<PopoverFooter>
|
|
94
|
+
<Button variant="secondary">Cancel</Button>
|
|
95
|
+
<Button>Apply</Button>
|
|
96
|
+
</PopoverFooter>
|
|
97
|
+
</PopoverContent>;
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
For an isolated container (e.g. a shadow root), pass `portalContainer` so the
|
|
101
|
+
popup inherits that scope's styles. When portaled into a resolved
|
|
102
|
+
`portalContainer`, `PopoverContent` also defaults `positionMethod` to `'fixed'`
|
|
103
|
+
so the popup escapes a plain overflow-clipping ancestor (e.g. a Shadow DOM host
|
|
104
|
+
with `overflow: hidden`) instead of being clipped at the container's edge.
|
|
105
|
+
|
|
106
|
+
## Props
|
|
107
|
+
|
|
108
|
+
| Prop | Type | Default | Required | Description |
|
|
109
|
+
|------|------|---------|----------|-------------|
|
|
110
|
+
| `open` | `boolean` | — | no | Controlled open state. Pair with the change event. |
|
|
111
|
+
| `default-open` | `boolean` | `false` | no | Initial open state when uncontrolled. |
|
|
112
|
+
| `side` | `'top' | 'bottom' | 'left' | 'right' | 'inline-start' | 'inline-end'` | `'bottom'` | no | On the content — which side of the trigger to render on. |
|
|
113
|
+
| `align` | `'start' | 'center' | 'end'` | `'center'` | no | On the content — alignment along the chosen side. |
|
|
114
|
+
| `side-offset` | `number` | `4` | no | On the content — distance in px from the trigger. |
|
|
115
|
+
| `portal` | `boolean` | `true` | no | On the content — render inside a portal. Set to false for inline usage; requires a consumer-supplied ancestor Portal. When false, none of portal-container/collision-boundary/position-method's portal-dependent defaults apply — even if that ancestor Portal targets a constrained container, so pass position-method explicitly in that case. |
|
|
116
|
+
| `portal-container` | `Element | ShadowRoot | null | RefObject<Element | ShadowRoot | null>` | `nearest PortalContainerProvider, or document.body` | no | On the content — portal mount point. Pass a shadow-root mount for isolated-style previews, or a constrained MFE/Shadow DOM mount point. |
|
|
117
|
+
| `collision-boundary` | `Element | Element[] | Rect | 'clipping-ancestors'` | `the platform default (clipping ancestors)` | no | On the content — element/rect the popup is confined to when avoiding collisions. Left at the platform default — paired with position-method's default, that default already avoids clipping inside a constrained portal-container without needing an explicit override (see position-method). |
|
|
118
|
+
| `position-method` | `'absolute' | 'fixed'` | `'fixed' whenever the popup is portaled into a resolved portal-container; otherwise the platform default ('absolute')` | no | On the content — which CSS position the popup uses. An 'absolute'-positioned popup that IS portaled into a custom portal-container is still clipped by it when it constrains overflow (e.g. a Shadow DOM host with overflow: hidden) regardless of the collision boundary, since it shares that ancestor's containing block. 'fixed' escapes a plain overflow-clipping ancestor: collision detection drops an overflow ancestor that doesn't establish its own containing block from a fixed element's clipping chain, resolving to the real, scroll-independent viewport — not one whose chain also has a transform/filter/will-change/etc. that establishes its own containing block for fixed descendants. Has no effect when not portaled. |
|
|
119
|
+
| `keep-mounted` | `boolean` | `false` | no | On the content — keep the popup mounted in the DOM while closed. |
|
|
120
|
+
|
|
121
|
+
## Events
|
|
122
|
+
|
|
123
|
+
| Event | Payload | Description |
|
|
124
|
+
|-------|---------|-------------|
|
|
125
|
+
| `open-change` | `boolean` | Fired when the popover opens or closes (React: `onOpenChange`). Payload is the new open value. |
|
|
126
|
+
|
|
127
|
+
## Content slots
|
|
128
|
+
|
|
129
|
+
| Slot | Description |
|
|
130
|
+
|------|-------------|
|
|
131
|
+
| `default` | A `trigger` plus the popover `content` (the root popup), which holds arbitrary content — text, fields, actions. |
|
|
132
|
+
| `body` | Optional vertical-rhythm wrapper for the popup's main content (`PopoverBody`); a themed gap + vertical padding around whatever is passed in. |
|
|
133
|
+
| `footer` | Optional default action-row footer (`PopoverFooter`) holding one or more actions (e.g. Cancel / Apply buttons), end-aligned. |
|
|
134
|
+
|
|
135
|
+
## Behavior
|
|
136
|
+
|
|
137
|
+
Popover shows a floating panel anchored to a trigger. It owns the open state (or
|
|
138
|
+
the consumer controls it via `open`).
|
|
139
|
+
|
|
140
|
+
### Opening & closing
|
|
141
|
+
|
|
142
|
+
```gherkin
|
|
143
|
+
Scenario: Toggling from the trigger
|
|
144
|
+
Given a closed popover
|
|
145
|
+
When the user activates the trigger
|
|
146
|
+
Then the popup opens, positioned per side/align, and receives focus
|
|
147
|
+
And open-change(true) fires
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
```gherkin
|
|
151
|
+
Scenario: Dismissing
|
|
152
|
+
Given an open popover
|
|
153
|
+
When the user presses Escape, or presses outside the popup
|
|
154
|
+
Then the popover closes and open-change(false) fires
|
|
155
|
+
And focus returns to the trigger
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Controlled vs uncontrolled
|
|
159
|
+
|
|
160
|
+
```gherkin
|
|
161
|
+
Scenario: Uncontrolled with default-open
|
|
162
|
+
Given a Popover with default-open = true and no open prop
|
|
163
|
+
When it mounts
|
|
164
|
+
Then it renders open and the user can dismiss it without consumer code
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```gherkin
|
|
168
|
+
Scenario: Controlled
|
|
169
|
+
Given a Popover with a fixed open prop
|
|
170
|
+
When the user attempts to toggle it
|
|
171
|
+
Then internal state does NOT change on its own
|
|
172
|
+
And open-change fires so the consumer can update open
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Positioning
|
|
176
|
+
|
|
177
|
+
```gherkin
|
|
178
|
+
Scenario: Side and alignment
|
|
179
|
+
Given a PopoverContent with side="right" and align="start"
|
|
180
|
+
When it opens
|
|
181
|
+
Then the popup renders to the right of the trigger, aligned to its start edge
|
|
182
|
+
And it flips/shifts to stay within the viewport when space is tight
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Positioning inside a constrained portal container
|
|
186
|
+
|
|
187
|
+
```gherkin
|
|
188
|
+
Scenario: Escaping a constrained portal container's clipping
|
|
189
|
+
Given a PopoverContent portaled (default) into a resolved portal-container
|
|
190
|
+
that constrains overflow (e.g. a small or offset MFE/Shadow DOM mount)
|
|
191
|
+
When it opens
|
|
192
|
+
Then it uses 'fixed' positioning by default, with collision-boundary left at
|
|
193
|
+
the platform default ('clipping-ancestors')
|
|
194
|
+
And it is not clipped at the portal container's own edge, since 'fixed'
|
|
195
|
+
already drops that overflow-clipping ancestor from its clipping chain
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```gherkin
|
|
199
|
+
Scenario: Overriding the computed defaults
|
|
200
|
+
Given the same constrained-container setup
|
|
201
|
+
When a consumer passes an explicit collision-boundary or position-method
|
|
202
|
+
Then that value overrides the computed default
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
```gherkin
|
|
206
|
+
Scenario: Non-portaled content is unaffected
|
|
207
|
+
Given a PopoverContent rendered without a portal (inline usage), even when
|
|
208
|
+
a portal-container is resolved from context
|
|
209
|
+
When it opens
|
|
210
|
+
Then it keeps the platform's default positioning and collision boundary —
|
|
211
|
+
the computed defaults only apply when this component's own `portal` prop
|
|
212
|
+
is true. If a consumer supplies their own ancestor Portal targeting a
|
|
213
|
+
constrained container, `portal={false}` content can still end up inside
|
|
214
|
+
that container's containing block and clip; pass `position-method`
|
|
215
|
+
explicitly in that case.
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Accessibility
|
|
219
|
+
|
|
220
|
+
Popover leans on the Base UI Popover primitive for focus management, dismissal,
|
|
221
|
+
and ARIA wiring.
|
|
222
|
+
|
|
223
|
+
## Roles & semantics
|
|
224
|
+
|
|
225
|
+
- The popup has `role="dialog"`; the trigger is associated with it via
|
|
226
|
+
`aria-haspopup` / `aria-expanded` and `aria-controls` (managed by Base UI).
|
|
227
|
+
- **Name the popup** when it isn't self-evident: render a heading inside and
|
|
228
|
+
reference it (or pass `aria-label`/`aria-labelledby` on the content), so screen
|
|
229
|
+
readers announce what the popover is.
|
|
230
|
+
- Unlike a modal dialog, a popover is **non-modal** by default — focus is placed
|
|
231
|
+
inside on open but background content remains available.
|
|
232
|
+
|
|
233
|
+
## Keyboard
|
|
234
|
+
|
|
235
|
+
- The trigger opens the popover on **Enter / Space** (it's a button).
|
|
236
|
+
- On open, focus moves into the popup (first focusable, or the popup itself).
|
|
237
|
+
- **Escape** closes the popover and returns focus to the trigger.
|
|
238
|
+
- **Tab** moves through the popup's focusable content; focus is not trapped
|
|
239
|
+
(non-modal), so tabbing past the end returns to the page.
|
|
240
|
+
|
|
241
|
+
## Screen reader
|
|
242
|
+
|
|
243
|
+
- Opening announces the dialog role and its name (heading / label). Closing
|
|
244
|
+
returns the user to the trigger.
|
|
245
|
+
|
|
246
|
+
## Contrast
|
|
247
|
+
|
|
248
|
+
- The popup uses `--ui-text-on-surface-primary` text on `--ui-popover-container-color`
|
|
249
|
+
with a `--ui-popover-container-border-color` border — meeting WCAG AA in light
|
|
250
|
+
and dark. The default footer (`PopoverFooter`) uses `--ui-footer-default-color`
|
|
251
|
+
with a `--ui-footer-default-border-color` top border; its action buttons carry
|
|
252
|
+
their own contrast guarantees (see the Button spec).
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# ProgressCircle
|
|
2
|
+
|
|
3
|
+
> A circular (radial) progress indicator — an SVG ring whose arc fills with the value and whose color tracks the level (danger / critical / warning / success), with an optional numeric or icon center. Built from the Cyber-Compliance "Compliance %" design; the sibling of the linear Progress. Design reconciliation against the Figma component master is pending.
|
|
4
|
+
|
|
5
|
+
**Status:** draft · **Category:** feedback · **Since:** v0.43.0
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { ProgressCircle } from '@acronis-platform/ui-react'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick example
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<ProgressCircle value={75} max={100} size="sm" showValue />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A circular (radial) progress indicator — an SVG ring whose arc fills with the
|
|
20
|
+
value and whose color tracks the level.
|
|
21
|
+
|
|
22
|
+
> **Status: draft (design-pending v1).** Built from the Cyber-Compliance
|
|
23
|
+
> "Compliance %" design and the Vue kit's `AvProgressRadial`. Reuses the shared
|
|
24
|
+
> status colors — no `--ui-progress-circle-*` tier. Reconcile with
|
|
25
|
+
> `/figma-component ProgressCircle <url> --update` once the component master node
|
|
26
|
+
> is confirmed.
|
|
27
|
+
|
|
28
|
+
## When to use
|
|
29
|
+
|
|
30
|
+
- A compact, at-a-glance completion/score in a table cell, card, or widget
|
|
31
|
+
(e.g. compliance %, usage, health).
|
|
32
|
+
|
|
33
|
+
## When not to use
|
|
34
|
+
|
|
35
|
+
- A full-width determinate bar — use the linear `Progress`.
|
|
36
|
+
- An indeterminate "busy" state — use `Loading`.
|
|
37
|
+
|
|
38
|
+
## Example
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { ProgressCircle } from '@acronis-platform/ui-react';
|
|
42
|
+
|
|
43
|
+
<ProgressCircle value={75} size="md" showValue />
|
|
44
|
+
<ProgressCircle value={25} size="sm" /> {/* arc auto-colors to danger */}
|
|
45
|
+
<ProgressCircle value={100} size="lg" showIcon /> {/* success check in center */}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
In a table cell, compose the ring with an external label:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<div className="flex items-center gap-2">
|
|
52
|
+
<ProgressCircle value={81} size="sm" />
|
|
53
|
+
<span className="text-sm">81%</span>
|
|
54
|
+
</div>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Props
|
|
58
|
+
|
|
59
|
+
| Prop | Type | Default | Required | Description |
|
|
60
|
+
|------|------|---------|----------|-------------|
|
|
61
|
+
| `value` | `number` | `0` | no | Current progress, clamped to [0, max]. |
|
|
62
|
+
| `max` | `number` | `100` | no | Value representing full completion. |
|
|
63
|
+
| `size` | `'tiny' | 'sm' | 'md' | 'lg'` | `'sm'` | no | Ring diameter + stroke width. |
|
|
64
|
+
| `status` | `'brand' | 'danger' | 'critical' | 'warning' | 'success'` | — | no | Arc color level. `brand` is a single neutral color; the rest are score levels. Omit to derive the level from value/max by thresholds (≈ <40% danger, <60% critical, <80% warning, else success — confirm with design). |
|
|
65
|
+
| `showValue` | `boolean` | `false` | no | Show the rounded percentage in the center. |
|
|
66
|
+
| `showIcon` | `boolean` | `false` | no | Show a status icon in the center (priority over showValue). |
|
|
67
|
+
|
|
68
|
+
## Content slots
|
|
69
|
+
|
|
70
|
+
| Slot | Description |
|
|
71
|
+
|------|-------------|
|
|
72
|
+
| `children` | Custom center content (priority over icon / value). |
|
|
73
|
+
|
|
74
|
+
## Behavior
|
|
75
|
+
|
|
76
|
+
A circular progress indicator. State is driven by `value`; there is no internal
|
|
77
|
+
state.
|
|
78
|
+
|
|
79
|
+
```gherkin
|
|
80
|
+
Scenario: Fill
|
|
81
|
+
Given a ProgressCircle with value={75}
|
|
82
|
+
Then the arc fills 75% of the ring
|
|
83
|
+
And aria-valuenow is 75
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```gherkin
|
|
87
|
+
Scenario: Status by level
|
|
88
|
+
Given no explicit status
|
|
89
|
+
Then the arc color is derived from value/max (danger → critical → warning → success)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```gherkin
|
|
93
|
+
Scenario: Status override
|
|
94
|
+
Given status="danger" with value={90}
|
|
95
|
+
Then the arc is the danger color regardless of value
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```gherkin
|
|
99
|
+
Scenario: Center content
|
|
100
|
+
Given showValue, the center shows the rounded percentage
|
|
101
|
+
Given showIcon, the center shows a status icon (priority over the percentage)
|
|
102
|
+
Given children, they render in the center (priority over icon / percentage)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```gherkin
|
|
106
|
+
Scenario: Custom range
|
|
107
|
+
Given value={5} and max={10}
|
|
108
|
+
Then the arc fills 50% and aria-valuemax is 10
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Accessibility
|
|
112
|
+
|
|
113
|
+
- The root is `role="progressbar"` (Base UI Progress.Root) with
|
|
114
|
+
`aria-valuenow`/`aria-valuemin`/`aria-valuemax`. The SVG is `aria-hidden`; the
|
|
115
|
+
value semantics come from the root.
|
|
116
|
+
- Give it an accessible name via `aria-label`/`aria-labelledby` (e.g. "Compliance")
|
|
117
|
+
so assistive tech announces what is progressing.
|
|
118
|
+
- Status is conveyed by the arc color **and** the value/icon — never color alone.
|
|
119
|
+
Keep the meaningful value visible (`showValue`) or labelled when color encodes
|
|
120
|
+
status.
|
|
121
|
+
|
|
122
|
+
## Contrast
|
|
123
|
+
|
|
124
|
+
The arc uses `--ui-text-on-status-*` over the `--ui-border-on-surface-border`
|
|
125
|
+
track, and the center label uses `--ui-text-on-surface-primary` — all meeting the
|
|
126
|
+
relevant contrast in light and dark.
|