@acronis-platform/ui-react 5.0.0 → 5.1.1

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 (68) hide show
  1. package/dist/components/ui/area-chart/area-chart.js +191 -85
  2. package/dist/components/ui/area-chart/area-chart.js.map +1 -1
  3. package/dist/components/ui/bar-chart/bar-chart.js +288 -225
  4. package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
  5. package/dist/components/ui/card-widget-carousel/card-widget-carousel.js +85 -0
  6. package/dist/components/ui/card-widget-carousel/card-widget-carousel.js.map +1 -0
  7. package/dist/components/ui/card-widget-carousel/card-widget.js +82 -0
  8. package/dist/components/ui/card-widget-carousel/card-widget.js.map +1 -0
  9. package/dist/components/ui/category-bar/category-bar.js.map +1 -1
  10. package/dist/components/ui/chart/chart-format.js +25 -22
  11. package/dist/components/ui/chart/chart-format.js.map +1 -1
  12. package/dist/components/ui/chart/chart-palette.js +9 -8
  13. package/dist/components/ui/chart/chart-palette.js.map +1 -1
  14. package/dist/components/ui/chart/chart.js +118 -83
  15. package/dist/components/ui/chart/chart.js.map +1 -1
  16. package/dist/components/ui/chart-state/chart-state.js +15 -7
  17. package/dist/components/ui/chart-state/chart-state.js.map +1 -1
  18. package/dist/components/ui/line-chart/line-chart.js +194 -86
  19. package/dist/components/ui/line-chart/line-chart.js.map +1 -1
  20. package/dist/components/ui/scatter-chart/scatter-chart.js +5 -2
  21. package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
  22. package/dist/components/ui/separator/separator.js +52 -10
  23. package/dist/components/ui/separator/separator.js.map +1 -1
  24. package/dist/components/ui/sidebar-secondary/sidebar-secondary.js +144 -121
  25. package/dist/components/ui/sidebar-secondary/sidebar-secondary.js.map +1 -1
  26. package/dist/components/ui/tree-item/tree-item.js +67 -0
  27. package/dist/components/ui/tree-item/tree-item.js.map +1 -0
  28. package/dist/components/ui/treemap/treemap.js +73 -66
  29. package/dist/components/ui/treemap/treemap.js.map +1 -1
  30. package/dist/index.js +109 -107
  31. package/dist/llms/area-chart.md +2 -1
  32. package/dist/llms/bar-chart.md +132 -33
  33. package/dist/llms/card-widget-carousel.md +252 -0
  34. package/dist/llms/category-bar.md +2 -2
  35. package/dist/llms/chart-state.md +17 -4
  36. package/dist/llms/line-chart.md +2 -1
  37. package/dist/llms/metric.md +10 -5
  38. package/dist/llms/scatter-chart.md +2 -1
  39. package/dist/llms/separator.md +27 -8
  40. package/dist/llms/sidebar-secondary.md +235 -4
  41. package/dist/llms/tree-item.md +359 -0
  42. package/dist/llms/treemap.md +47 -36
  43. package/dist/llms.txt +4 -3
  44. package/dist/react.js +109 -107
  45. package/dist/src/components/ui/area-chart/area-chart.d.ts +10 -0
  46. package/dist/src/components/ui/bar-chart/bar-chart.d.ts +75 -10
  47. package/dist/src/components/ui/bar-chart/bar-chart.docs.d.ts +146 -0
  48. package/dist/src/components/ui/bar-chart/index.d.ts +1 -1
  49. package/dist/src/components/ui/card-widget-carousel/card-widget-carousel.d.ts +9 -0
  50. package/dist/src/components/ui/card-widget-carousel/card-widget.d.ts +43 -0
  51. package/dist/src/components/ui/card-widget-carousel/index.d.ts +2 -0
  52. package/dist/src/components/ui/chart/chart-format.d.ts +9 -1
  53. package/dist/src/components/ui/chart/chart-palette.d.ts +38 -10
  54. package/dist/src/components/ui/chart/index.d.ts +2 -2
  55. package/dist/src/components/ui/line-chart/line-chart.d.ts +11 -1
  56. package/dist/src/components/ui/separator/separator.d.ts +9 -1
  57. package/dist/src/components/ui/sidebar-secondary/sidebar-secondary.d.ts +23 -3
  58. package/dist/src/components/ui/tree-item/index.d.ts +1 -0
  59. package/dist/src/components/ui/tree-item/tree-item.d.ts +64 -0
  60. package/dist/src/components/ui/treemap/treemap.d.ts +16 -5
  61. package/dist/src/index.d.ts +2 -1
  62. package/dist/ui-react.css +1 -1
  63. package/package.json +3 -3
  64. package/dist/components/ui/meter/meter.js +0 -79
  65. package/dist/components/ui/meter/meter.js.map +0 -1
  66. package/dist/llms/meter.md +0 -177
  67. package/dist/src/components/ui/meter/index.d.ts +0 -1
  68. package/dist/src/components/ui/meter/meter.d.ts +0 -25
@@ -1,6 +1,6 @@
1
1
  # BarChart
2
2
 
3
- > A typed bar-chart composition over the shared `Chart` primitives. Takes `data` + `config` + a set of series `dataKeys` and renders a themed recharts `BarChart` inside a `ChartContainer`, with tooltip and legend chrome. Two variant axes: `orientation` (vertical / horizontal bars) and `layout` (grouped / stacked). Ported from the apps/demo `BarChartPlayground`.
3
+ > Two renderings behind one name, selected by `orientation`. `vertical` (the default) is a typed bar-chart composition over the shared `Chart` primitives: `data` + `config` + a set of series `dataKeys` become a themed recharts `BarChart` inside a `ChartContainer`, with tooltip and legend chrome, grouped or stacked by `layout`. `horizontal` is a labelled proportional bar list — one row per `items` entry carrying its label, value, share of `max`, and a track bar — with no axes, grid, or recharts at all.
4
4
 
5
5
  **Status:** draft · **Category:** data-display · **Since:** v0.57.0
6
6
 
@@ -18,10 +18,19 @@ import { BarChart } from '@acronis-platform/ui-react'
18
18
  data={data}
19
19
  dataKeys={['desktop', 'mobile']}
20
20
  xKey="month"
21
- orientation="vertical"
22
21
  layout="grouped"
23
22
  className="h-[320px] w-[560px]"
24
23
  />
24
+
25
+ <BarChart
26
+ orientation="horizontal"
27
+ palette={{ type: 'status' }}
28
+ items={[
29
+ { label: 'Critical', value: 6, tone: { status: 'danger' } },
30
+ { label: 'High', value: 9, tone: { status: 'warning' } },
31
+ ]}
32
+ max={29}
33
+ />
25
34
  ```
26
35
 
27
36
  A typed bar-chart built on the shared `Chart` primitives. Give it `data`, a
@@ -42,6 +51,8 @@ Series colors come from the `palette` prop — a dataviz palette resolving to th
42
51
 
43
52
  ## When not to use
44
53
 
54
+ - A ranked breakdown where each row needs its own label, value, and percentage
55
+ — that is `orientation="horizontal"`, not a chart.
45
56
  - A single metric or KPI — use a `Tag`, `Badge`, or plain text.
46
57
  - Trends over a continuous dimension — prefer a line/area chart.
47
58
  - Part-to-whole of a single total — consider a pie/donut chart.
@@ -49,10 +60,48 @@ Series colors come from the `palette` prop — a dataviz palette resolving to th
49
60
 
50
61
  ## Variants
51
62
 
52
- | Axis | Values | Effect |
53
- | ------------- | ------------------------- | ------------------------------------------------------ |
54
- | `orientation` | `vertical` · `horizontal` | Bars grow up (category on x) vs right (category on y). |
55
- | `layout` | `grouped` · `stacked` | Series side-by-side vs summed on a shared stack. |
63
+ | Axis | Values | Effect |
64
+ | ------------- | ------------------------- | --------------------------------------------------------- |
65
+ | `orientation` | `vertical` · `horizontal` | The recharts chart vs the labelled proportional bar list. |
66
+ | `layout` | `grouped` · `stacked` | Vertical only: series side-by-side vs summed on a stack. |
67
+
68
+ `orientation` is not a direction knob on one drawing — it picks which of two
69
+ renderings you get, and the two take different props. See **Horizontal** below.
70
+
71
+ ## Horizontal
72
+
73
+ `orientation="horizontal"` renders a labelled proportional bar list instead of a
74
+ chart: one row per `items` entry (`{ label, value, color }`), each showing its
75
+ label, its formatted value, its share of `max`, and a track filled to that
76
+ share. There are no axes, no grid, and no recharts — just Base UI's `Meter`
77
+ primitive per row, so each carries `role="meter"` and its numbers reach the
78
+ accessibility tree.
79
+
80
+ `max` defaults to the sum of every `items[].value`, which makes each row its
81
+ share of the whole. Pass an explicit `max` when the rows are shares of a total
82
+ they do not add up to. `valueFormatter` formats the number, `showTooltip`
83
+ toggles the per-row hover card, and `tooltip` replaces its content.
84
+
85
+ ```tsx
86
+ <BarChart
87
+ orientation="horizontal"
88
+ items={[
89
+ {
90
+ label: 'Critical',
91
+ value: 6,
92
+ color: 'var(--ui-background-status-strong-danger)',
93
+ },
94
+ {
95
+ label: 'High',
96
+ value: 9,
97
+ color: 'var(--ui-background-status-strong-warning)',
98
+ },
99
+ ]}
100
+ max={29}
101
+ />
102
+ ```
103
+
104
+ Everything below applies to the default (`vertical`) chart only.
56
105
 
57
106
  ## Highlighting a range
58
107
 
@@ -109,24 +158,28 @@ across brands until the real data-viz palette lands.
109
158
 
110
159
  | Prop | Type | Default | Required | Description |
111
160
  |------|------|---------|----------|-------------|
112
- | `orientation` | `'vertical' | 'horizontal'` | `'vertical'` | no | Direction the bars grow. `vertical` = bars rise (category on the x-axis); `horizontal` = bars extend rightward (category on the y-axis). |
113
- | `layout` | `'grouped' | 'stacked'` | `'grouped'` | no | How multiple series are arranged. `grouped` = side-by-side bars; `stacked` = segments summed on a shared `stackId`. |
114
- | `data` | `ReadonlyArray<Record<string, string | number>>` | — | yes | Row-per-category data — each row holds `xKey` plus one numeric field per series. |
115
- | `config` | `ChartConfig` | — | yes | Series take their colour from the container's `palette`; each entry maps a data key to a `label` and an optional `tone`. Resolved into the `--color-<key>` custom properties the bars fill from. |
116
- | `dataKeys` | `string[]` | — | yes | Series to plot — one bar per key. Each must exist in `config` and every data row. |
117
- | `xKey` | `string` | — | yes | The category dimension key shared across rows. |
118
- | `referenceLine` | `Line | Line[] where Line = { value?: number; average?: boolean | string; label?: string }` | — | no | One or more dashed reference/average lines on the value axis (Y for vertical bars, X for horizontal). Pass a single object or an array. Per line, a fixed `value` wins; otherwise the mean of one series (`average: '<key>'`) or of every plotted series (`average: true`). An optional `label` captions each. |
161
+ | `orientation` | `'vertical' | 'horizontal'` | `'vertical'` | no | Which of the two renderings to use. `vertical` (the default) draws the chart: bars rising from a category x-axis, with axes, grid, tooltip and legend. `horizontal` draws a labelled proportional bar list instead — one row per `items` entry, no axes or grid. The two modes take different props: `data`/`config`/`dataKeys`/`xKey` and every chart knob below apply only to `vertical`; `items`, `max`, `valueFormatter` and `tooltip` only to `horizontal`. |
162
+ | `items` | `Array<{ label: string; value: number; color?: string; tone?: { status: ChartStatusTone }; forecast?: number }>` | — | no | Required when `orientation="horizontal"`. One row per entry — each renders as a labelled proportional bar carrying its value and share of `max`. Provide `color` (any CSS color; prefer a `--ui-*` token) when no `palette` is used, or `tone` together with `palette` to resolve the color from the palette. `color` and `tone` may both be set; `color` takes precedence. Optional `forecast`: projected total value — renders a translucent bar (30% opacity) extending beyond the actual value; the actual bar renders solid on top. Ignored when `forecast <= value`. ARIA values reflect the actual value; `aria-valuetext` also mentions the forecast. Ignored when `orientation="vertical"`. |
163
+ | `max` | `number` | — | no | `orientation="horizontal"` only. Upper bound the item values are shares of. Defaults to the sum of every `items[].value`. |
164
+ | `valueFormatter` | `(value: number) => string` | — | no | `orientation="horizontal"` only. Formats the numeric value in each row's label. Defaults to `toLocaleString()`. |
165
+ | `tooltip` | `ReactNode` | — | no | `orientation="horizontal"` only. Tooltip content shared by every row, replacing the default (swatch + label + value of max + percentage). Ignored when `showTooltip` is false. |
166
+ | `palette` | `ChartPalette` | — | no | `orientation="horizontal"` only. Dataviz palette to resolve each item's color from. When provided, each `items` entry should carry a `tone` (and can omit `color`). Colors are resolved in the same order and with the same tone rules as the vertical chart — `status` tones pick a specific stop, slot-less items consume the next available stop. |
167
+ | `layout` | `'grouped' | 'stacked'` | `'grouped'` | no | Vertical only. How multiple series are arranged. `grouped` = side-by-side bars; `stacked` = segments summed on a shared `stackId`. |
168
+ | `data` | `ReadonlyArray<Record<string, string | number>>` | — | no | Required when `orientation="vertical"`. Row-per-category data — each row holds `xKey` plus one numeric field per series. |
169
+ | `config` | `ChartConfig` | — | no | Required when `orientation="vertical"`. Series take their colour from the container's `palette`; each entry maps a data key to a `label` and an optional `tone`. Resolved into the `--color-<key>` custom properties the bars fill from. |
170
+ | `dataKeys` | `string[]` | — | no | Required when `orientation="vertical"`. Series to plot — one bar per key. Each must exist in `config` and every data row. |
171
+ | `xKey` | `string` | — | no | Required when `orientation="vertical"`. The category dimension key shared across rows. |
172
+ | `referenceLine` | `Line | Line[] where Line = { value?: number; average?: boolean | string; label?: string }` | — | no | One or more dashed reference/average lines on the value (Y) axis. Pass a single object or an array. Per line, a fixed `value` wins; otherwise the mean of one series (`average: '<key>'`) or of every plotted series (`average: true`). An optional `label` captions each. Vertical only. |
119
173
  | `xAxisLabel` | `string` | — | no | Title rendered beneath the horizontal (X) axis. |
120
174
  | `yAxisLabel` | `string` | — | no | Title rendered beside the vertical (Y) axis (rotated). |
121
- | `xUnit` | `string` | — | no | Unit suffix appended to X-axis tick values (recharts `unit`); applies when the X axis is numeric (horizontal orientation). |
122
- | `yUnit` | `string` | — | no | Unit suffix appended to Y-axis tick values (recharts `unit`); applies when the Y axis is numeric (vertical orientation). |
175
+ | `yUnit` | `string` | — | no | Unit suffix appended to Y-axis tick values (recharts `unit`) — the Y axis is the numeric one. |
123
176
  | `referenceArea` | `Area | Area[] where Area = { from?: string | number; to?: string | number; label?: string; highlightTicks?: boolean; divider?: boolean }` | — | no | One or more shaded bands behind a range of categories — e.g. a forecast period. `from`/`to` are inclusive and take either the category's value or its 0-based row index; an omitted bound runs to that end of the data. The category ticks under a band pick up the accent styling unless `highlightTicks` is false, and `divider` adds a dashed rule on the band's leading edge for a forecast hand-off. |
124
177
  | `barSettings` | `Record<string, { from?: string | number; to?: string | number; fill?: string; opacity?: number; dashed?: boolean; shape?: BarShape; background?: boolean | string }>` | — | no | Per-series style override for a range of categories, keyed by `dataKeys` entry — e.g. a projection tail that reads translucent and dashed over its own track. `background` takes `true` for a full-height track or a data field name to cap it at that row's value (the headroom up to an upper bound, which stacks on the bar and so needs `layout="grouped"`). Bars outside the range, and series with no entry, render normally. |
125
- | `barRadius` | `number` | `4` | no | Corner radius on the growing end of each bar (0 for square corners). |
178
+ | `barRadius` | `number` | `8` | no | Corner radius on the growing end of each bar (0 for square corners). |
126
179
  | `barShape` | `'rounded' | 'pill' | 'gradient' | 'pattern'` | `'rounded'` | no | How every bar is painted: the growing end rounded by `barRadius`, a capsule, a fade of the series color, or diagonal hatching in it. Per-range overrides come from `barSettings`. |
127
- | `barSize` | `number` | — | no | Fixed bar thickness in px; unset, recharts sizes bars from the available width. |
180
+ | `barSize` | `number` | `8` | no | Fixed bar thickness in px; unset, recharts sizes bars from the available width. |
128
181
  | `maxBarSize` | `number` | — | no | Upper bound on the computed bar thickness, in px. |
129
- | `barGap` | `number | string` | — | no | Gap between the bars of one category, in px or a percentage string. |
182
+ | `barGap` | `number | string` | `4` | no | Gap between the bars of one category, in px or a percentage string. |
130
183
  | `barCategoryGap` | `number | string` | — | no | Gap between category groups, in px or a percentage string. |
131
184
  | `minPointSize` | `number` | — | no | Minimum rendered length for a non-zero bar, in px, so tiny values stay visible. |
132
185
  | `showBackground` | `boolean` | `false` | no | Draw a full-height track behind every bar. |
@@ -134,7 +187,7 @@ across brands until the real data-viz palette lands.
134
187
  | `showActiveBar` | `boolean` | `false` | no | Highlight the hovered bar. |
135
188
  | `activeBar` | `{ fill?: string; opacity?: number }` | — | no | Painting of the hovered bar when `showActiveBar` is on; defaults to the series color. |
136
189
  | `showGrid` | `boolean` | `true` | no | Render the CartesianGrid. |
137
- | `showTooltip` | `boolean` | `true` | no | Render the hover tooltip. |
190
+ | `showTooltip` | `boolean` | `true` | no | Render the hover tooltip — recharts' for `vertical`, the per-row tooltip for `horizontal`. |
138
191
  | `showLegend` | `boolean` | `true` | no | Render the legend. |
139
192
  | `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. Ignored when showTooltip is false. |
140
193
  | `showXAxis` | `boolean` | `true` | no | Render the X axis (its ticks and title). Set false to hide it. |
@@ -143,9 +196,9 @@ across brands until the real data-viz palette lands.
143
196
  | `yTickFormatter` | `(value: number | string, index?: number) => string` | — | no | Format each Y-axis tick value (see xTickFormatter). |
144
197
  | `xAxisAngle` | `number` | — | no | Rotate the X-axis tick labels by this many degrees (a negative angle tilts them up toward the right); the tick anchor + axis height adjust. |
145
198
  | `xAxisInterval` | `number | 'preserveStart' | 'preserveEnd' | 'preserveStartEnd' | 'equidistantPreserveStart'` | — | no | X-axis tick density — a recharts placement strategy, or a fixed number of ticks to skip between two rendered ones (recharts interval: 0 shows every tick, 1 every other one, 2 every third). |
146
- | `yAxisTickCount` | `number` | — | no | Desired number of ticks on the value axis (recharts tickCount; a hint, not exact). Applies to whichever axis holds the values — Y for most charts, X for BarChart with orientation="horizontal". |
147
- | `yAxisDomain` | `'auto' | 'dataMin-dataMax' | 'zero'` | — | no | Value-axis domain preset — auto (fit the data at both ends; the axis need not include 0), zero (anchor the axis at 0, which is also recharts' behaviour when the prop is omitted), or dataMin-dataMax (tight to the data, no padding). Applies to whichever axis holds the values — Y for most charts, X for BarChart with orientation="horizontal". |
148
- | `gridDashed` | `boolean` | — | no | Draw grid lines dashed instead of solid. |
199
+ | `yAxisTickCount` | `number` | — | no | Desired number of ticks on the value (Y) axis (recharts tickCount; a hint, not exact). |
200
+ | `yAxisDomain` | `'auto' | 'dataMin-dataMax' | 'zero'` | — | no | Value-axis domain preset — auto (fit the data at both ends; the axis need not include 0), zero (anchor the axis at 0, which is also recharts' behaviour when the prop is omitted), or dataMin-dataMax (tight to the data, no padding). Applies to the value (Y) axis. |
201
+ | `gridDashed` | `boolean` | `true` | no | Draw grid lines dashed instead of solid. |
149
202
  | `gridHorizontal` | `boolean` | — | no | Show horizontal grid lines (defaults to the chart's own default when unset). |
150
203
  | `gridVertical` | `boolean` | — | no | Show vertical grid lines (defaults to the chart's own default when unset). |
151
204
  | `animate` | `boolean` | `false` | no | Enable entrance animation. Off by default so output/baselines are unchanged. |
@@ -153,7 +206,7 @@ across brands until the real data-viz palette lands.
153
206
  | `animationBegin` | `number` | — | no | Delay before the animation starts, in ms. |
154
207
  | `animationEasing` | `'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'` | — | no | Easing curve. |
155
208
  | `showLabels` | `boolean` | `false` | no | Render a value label on each bar segment. |
156
- | `labelPosition` | `'top' | 'bottom' | 'left' | 'right' | 'center' | 'insideTop' | 'insideBottom' | 'insideLeft' | 'insideRight' | 'insideStart' | 'insideEnd'` | — | no | Where the value labels sit. Defaults to the bar's growing end — top for vertical bars, right for horizontal — or center when layout is stacked. |
209
+ | `labelPosition` | `'top' | 'bottom' | 'left' | 'right' | 'center' | 'insideTop' | 'insideBottom' | 'insideLeft' | 'insideRight' | 'insideStart' | 'insideEnd'` | — | no | Where the value labels sit. Defaults to the bar's growing end (top), or center when layout is stacked. |
157
210
  | `labelFormatter` | `(value) => string` | — | no | Format each label value (reuses the tick formatters). |
158
211
  | `showBrush` | `boolean` | `false` | no | Render a range brush beneath the chart — drag a handle (or the selected window) to zoom the series into a slice of the data. |
159
212
  | `brushHeight` | `number` | `28` | no | Height of the brush strip in px. A non-positive value falls back to the default rather than erasing the brush. |
@@ -176,18 +229,45 @@ Scenario: Render bars from data and config
176
229
 
177
230
  ```gherkin
178
231
  Scenario: Vertical orientation (default)
179
- Given orientation is "vertical"
180
- Then bars grow upward
232
+ Given orientation is omitted or "vertical"
233
+ Then the recharts chart renders
234
+ And bars grow upward
181
235
  And the category axis (xKey) is the x-axis and the value axis is the y-axis
182
236
  And the grid draws horizontal lines
183
237
  ```
184
238
 
185
239
  ```gherkin
186
240
  Scenario: Horizontal orientation
187
- Given orientation is "horizontal"
188
- Then bars extend rightward (recharts layout="vertical")
189
- And the category axis (xKey) is the y-axis and the value axis is the x-axis
190
- And the grid draws vertical lines
241
+ Given orientation is "horizontal" and items is a list of { label, value, color }
242
+ Then no chart renders — one labelled bar row renders per item
243
+ And each row shows its label, its formatted value, and its share of max
244
+ And each row exposes role="meter" with aria-valuenow and aria-valuemax
245
+ And the track fills to value/max in the item's own color
246
+ ```
247
+
248
+ ```gherkin
249
+ Scenario: Horizontal orientation without an explicit max
250
+ Given orientation is "horizontal" and max is omitted
251
+ Then max falls back to the sum of every items[].value
252
+ And each row's percentage is its share of that total
253
+ ```
254
+
255
+ ```gherkin
256
+ Scenario: Horizontal item with forecast
257
+ Given orientation is "horizontal" and an item has forecast greater than value
258
+ Then a translucent bar (30% opacity) extends from the actual value to the forecast
259
+ And the actual bar renders solid on top
260
+ And aria-valuetext includes the forecast value
261
+ And aria-valuenow reflects the actual value only
262
+ But when forecast is omitted or less than or equal to value, no forecast bar renders
263
+ ```
264
+
265
+ ```gherkin
266
+ Scenario: Horizontal palette-driven colors
267
+ Given orientation is "horizontal" and a palette is set
268
+ Then each item's color is resolved through the palette machinery
269
+ And an item with a tone resolves to the palette's tone-specific token
270
+ And an explicit item.color still overrides the resolved palette color
191
271
  ```
192
272
 
193
273
  ```gherkin
@@ -223,9 +303,8 @@ Scenario: Legend
223
303
  ```gherkin
224
304
  Scenario: Fixed reference line
225
305
  Given referenceLine is { value: 250, label: "Target" }
226
- Then a dashed line draws across the value axis at 250
306
+ Then a horizontal dashed line draws across the value (y) axis at 250
227
307
  And it is captioned "Target"
228
- And for horizontal orientation the line is vertical (on the x-axis), else horizontal (on the y-axis)
229
308
  ```
230
309
 
231
310
  ```gherkin
@@ -302,7 +381,7 @@ Scenario: Address a range by index or by value
302
381
  Scenario: Mark the hand-off into a forecast
303
382
  Given a referenceArea entry sets divider: true
304
383
  Then a dashed rule draws on the band's leading edge
305
- And it follows the category axis — the band's left edge for vertical bars, its top edge for horizontal ones
384
+ And it follows the category (x) axis — the band's left edge
306
385
  ```
307
386
 
308
387
  ```gherkin
@@ -354,6 +433,9 @@ Scenario: Active bar on hover
354
433
 
355
434
  ## Accessibility
356
435
 
436
+ Everything below the "Horizontal" heading applies to
437
+ `orientation="horizontal"`; the rest describes the default chart.
438
+
357
439
  - recharts' `accessibilityLayer` is **on by default** (recharts v3), giving the
358
440
  chart keyboard focus and an accessible description of the plotted points.
359
441
  - The **range brush** (`showBrush`) is the only interactive control these charts
@@ -400,7 +482,24 @@ Scenario: Active bar on hover
400
482
  - Watch recharts issue [#4809](https://github.com/recharts/recharts/issues/4809)
401
483
  on the a11y layer for heavily-customized charts.
402
484
 
485
+ ## Horizontal
486
+
487
+ - Each row is Base UI's `Meter.Root`, so it carries `role="meter"` with
488
+ `aria-valuenow` / `aria-valuemax` and an `aria-valuetext` reading the
489
+ formatted value, the max, and the percentage — the numbers are in the
490
+ accessibility tree, not only in the picture.
491
+ - The row's accessible name comes from its `Meter.Label`, which renders the
492
+ caller's `label`. There is no fallback string to localize.
493
+ - A row takes focus (`tabindex="0"`) **only** when `showTooltip` is on, so the
494
+ tooltip is reachable without a pointer. A meter is not a control, so it is
495
+ deliberately out of the tab order when there is nothing to reveal.
496
+ - The percentage and the value are rendered as text next to every bar, so the
497
+ track's fill is never the only channel carrying the number. `color` is
498
+ caller-supplied — it still needs 3:1 against the track, and it must not be the
499
+ only thing telling two rows apart.
500
+
403
501
  ## Contrast
404
502
 
405
503
  Chart chrome meets contrast in both themes via the semantic tokens. Bar fills
406
- come from `config` and are the caller's responsibility.
504
+ come from `config` (vertical) or `items[].color` (horizontal) and are the
505
+ caller's responsibility.
@@ -0,0 +1,252 @@
1
+ # CardWidgetCarousel
2
+
3
+ > A horizontal scroll carousel that holds a row of action-widget cards and exposes floating Previous/Next chevron buttons to navigate the view. The carousel shell manages scroll; the individual cards are passed as `children`. Ships with a companion `CardWidget` component (same barrel) — a status-colored card with icon box, optional skeleton, and footer slot.
4
+
5
+ **Status:** ready · **Category:** data-display · **Since:** v5.1.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { CardWidgetCarousel, CardWidget } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <CardWidgetCarousel nextLabel="Next" prevLabel="Previous">
17
+ <CardWidget
18
+ header="Phishing attacks"
19
+ status="danger"
20
+ icon={<WarningIcon size={16} />}
21
+ title="Phishing attack detected"
22
+ metric="$15K"
23
+ caption="Projected new MRR"
24
+ footer={<Button variant="ghost">Review</Button>}
25
+ />
26
+ <CardWidget header="Loading…" status="info" skeleton />
27
+ </CardWidgetCarousel>
28
+ ```
29
+
30
+ A horizontal scroll carousel that holds a row of AI action-widget cards and
31
+ provides floating Previous/Next chevron buttons to navigate the view.
32
+
33
+ ## When to use
34
+
35
+ - Displaying three or more AI-generated action cards in a constrained horizontal
36
+ space (e.g. a dashboard widget row) where all cards cannot be visible at once.
37
+ - Any context where a horizontal scroll rail with progressive disclosure (one
38
+ click = one card step) is the intended interaction.
39
+
40
+ ## When not to use
41
+
42
+ - Full-page media galleries or image sliders — use a dedicated media carousel.
43
+ - Fewer than two cards that all fit in the available width — a plain flex row
44
+ without navigation buttons is simpler.
45
+
46
+ ## Parts
47
+
48
+ | Part | Description |
49
+ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
50
+ | `track` | Horizontally scrollable flex row. Pass cards as direct children. |
51
+ | `prev-button` | Circular 48 × 48 px `<button>` overlaid at the leading edge. Retreats the track by one card (288 px) + one gap (16 px). Disabled and invisible (`aria-disabled`, `opacity-0`) at the start. |
52
+ | `next-button` | Circular 48 × 48 px `<button>` overlaid at the trailing edge. Advances the track by one card (288 px) + one gap (16 px). Disabled and invisible at the end. |
53
+
54
+ ## Usage
55
+
56
+ ```tsx
57
+ import { CardWidgetCarousel, CardWidget } from '@acronis-platform/ui-react';
58
+
59
+ <CardWidgetCarousel nextLabel="Next" prevLabel="Previous">
60
+ <CardWidget
61
+ header="Phishing attacks"
62
+ status="danger"
63
+ icon={<WarningIcon size={16} />}
64
+ title="Phishing attack detected"
65
+ metric="$15K"
66
+ caption="Projected new MRR"
67
+ footer={<Button variant="ghost">Review</Button>}
68
+ />
69
+ <CardWidget header="Loading…" status="info" skeleton />
70
+ </CardWidgetCarousel>;
71
+ ```
72
+
73
+ ## Companion: CardWidget
74
+
75
+ `CardWidget` is the companion card exported from the same barrel. It composes
76
+ `Card` + `CardHeader` + `CardContent` + `CardFooter`, fixing its width at
77
+ 288 px, and adds:
78
+
79
+ - **Status-colored icon box** — driven by the `status` prop (`'danger'`,
80
+ `'warning'`, `'info'`), using `--ui-background-status-*` and
81
+ `--ui-glyph-on-status-*` tokens.
82
+ - **Skeleton loading** — pass `skeleton` to show animated placeholder lines
83
+ instead of body content. Bars inherit the status color.
84
+ - **Flexible footer** — pass any `ReactNode` via `footer` (action buttons,
85
+ CTAs). Omit to hide the footer entirely.
86
+
87
+ ### CardWidget props
88
+
89
+ | Prop | Type | Default | Description |
90
+ | -------------- | --------------------------------- | ----------- | --------------------------------------------------- |
91
+ | `status` | `'danger' \| 'warning' \| 'info'` | `'info'` | Semantic status — drives the icon-box colors. |
92
+ | `icon` | `ReactNode` | — | 16 px icon inside the status-colored box. |
93
+ | `header` | `string` | — | Card header title. |
94
+ | `title` | `string` | — | Insight title (14 px, semibold). |
95
+ | `description` | `string` | — | Supporting text (12 px). |
96
+ | `metric` | `ReactNode` | — | Primary value (24 px, semibold). |
97
+ | `caption` | `string` | — | Small caption beside the metric (12 px). |
98
+ | `skeleton` | `boolean` | `false` | Animated placeholder lines instead of body content. |
99
+ | `loadingLabel` | `string` | `'Loading'` | `aria-label` for the skeleton loading indicator. |
100
+ | `footer` | `ReactNode` | — | Action buttons in the card footer. |
101
+
102
+ ## Child card sizing
103
+
104
+ Each child card should be **288 px wide** (`w-72`) and `shrink-0` so the
105
+ scroll step (304 px) aligns to exactly one card width. `CardWidget` enforces
106
+ this by default.
107
+
108
+ ## RTL
109
+
110
+ Both chevron icons flip (`rtl:rotate-180`) and the scroll direction reverses
111
+ automatically when an ancestor has `dir="rtl"`.
112
+
113
+ ## Localization
114
+
115
+ The `nextLabel` (default `'Next'`) and `prevLabel` (default `'Previous'`) props
116
+ are `aria-label` values on the navigation buttons. Override them when deploying
117
+ in a non-English context.
118
+
119
+ ## Props
120
+
121
+ | Prop | Type | Default | Required | Description |
122
+ |------|------|---------|----------|-------------|
123
+ | `nextLabel` | `string` | `'Next'` | no | Accessible label for the Next chevron button (`aria-label`). Localize when deploying in a non-English context. |
124
+ | `prevLabel` | `string` | `'Previous'` | no | Accessible label for the Previous chevron button (`aria-label`). Localize when deploying in a non-English context. |
125
+
126
+ ## Content slots
127
+
128
+ | Slot | Description |
129
+ |------|-------------|
130
+ | `children` | The individual action cards — typically `CardWidget` instances (the companion component exported from the same barrel) or any card-shaped `ReactNode`. Each card should have a fixed width of 288 px so the carousel's scroll step (304 px = 288 + 16 gap) aligns correctly. `CardWidget` provides a status-colored icon box, optional skeleton loading state, and a flexible footer slot; its props are `status` (`'danger' | 'warning' | 'info'`), `icon`, `header`, `title`, `description`, `metric`, `caption`, `skeleton`, `loadingLabel`, and `footer`. |
131
+
132
+ ## Behavior
133
+
134
+ ### Scroll — Next button
135
+
136
+ **Given** a `CardWidgetCarousel` whose track overflows (more cards than visible area)
137
+ **When** the user activates the Next button (pointer click or Enter/Space via keyboard focus)
138
+ **Then** the scroll track advances by one card width + gap (304 px) towards the trailing edge
139
+ with a smooth scroll animation.
140
+
141
+ **Given** the carousel is in an RTL context (`dir="rtl"` on an ancestor)
142
+ **When** the user activates the Next button
143
+ **Then** the track scrolls 304 px towards the logical start (i.e. visually to the left in RTL)
144
+ so that the next card slides into view from the start edge.
145
+
146
+ ### Scroll — Previous button
147
+
148
+ **Given** a `CardWidgetCarousel` whose track has been scrolled past the start
149
+ **When** the user activates the Previous button (pointer click or Enter/Space via keyboard focus)
150
+ **Then** the scroll track moves back by one card width + gap (304 px) towards the leading edge
151
+ with a smooth scroll animation.
152
+
153
+ **Given** the carousel is in an RTL context (`dir="rtl"` on an ancestor)
154
+ **When** the user activates the Previous button
155
+ **Then** the track scrolls 304 px towards the logical end (i.e. visually to the right in RTL)
156
+ so that the previous card slides into view from the end edge.
157
+
158
+ ### Button visibility
159
+
160
+ Both navigation buttons are always present in the DOM to preserve keyboard
161
+ focus. At scroll boundaries they are `aria-disabled="true"` and visually
162
+ hidden (`opacity-0`, `pointer-events-none`).
163
+
164
+ **Given** the track is at the start (scroll position = 0)
165
+ **When** the carousel renders
166
+ **Then** the Next button is enabled; the Previous button is disabled and invisible.
167
+
168
+ **Given** the track is scrolled partway through
169
+ **When** the carousel renders
170
+ **Then** both the Previous and Next buttons are enabled and visible.
171
+
172
+ **Given** the track is scrolled to the end (scroll position + client width >= scroll width)
173
+ **When** the carousel renders
174
+ **Then** the Previous button is enabled; the Next button is disabled and invisible.
175
+
176
+ ### Dynamic children
177
+
178
+ **Given** a `CardWidgetCarousel` whose children change at runtime (e.g. async data load)
179
+ **When** children are added or removed from the track
180
+ **Then** the button disabled/visible state updates automatically via a
181
+ `MutationObserver` watching the track's `childList`.
182
+
183
+ ### Overflow behavior
184
+
185
+ **Given** a `CardWidgetCarousel` with fewer or equal cards than the visible area can show
186
+ **When** a navigation button is clicked
187
+ **Then** `scrollBy` is still called; the browser clamps the position at the scroll boundary
188
+ with no visual jank.
189
+
190
+ ### Pointer interaction behind the overlay
191
+
192
+ **Given** a card is partially obscured by a 180 px overlay panel (start or end)
193
+ **When** the user clicks on the visible portion of the card
194
+ **Then** the click reaches the card (the overlay panel has `pointer-events: none`;
195
+ only the navigation buttons have `pointer-events: auto`).
196
+
197
+ ## Accessibility
198
+
199
+ ## ARIA
200
+
201
+ - The Previous `<button>` carries an `aria-label` sourced from the `prevLabel`
202
+ prop (default `'Previous'`). Localize this string for non-English deployments.
203
+ - The Next `<button>` carries an `aria-label` sourced from the `nextLabel` prop
204
+ (default `'Next'`). Localize this string for non-English deployments.
205
+ - At scroll boundaries a button is marked `aria-disabled="true"` (not removed
206
+ from the DOM), so assistive technology still discovers it and announces its
207
+ disabled state.
208
+ - The scroll track (`div`) has no ARIA role. Screen readers traverse the child
209
+ cards directly since they are standard HTML elements.
210
+
211
+ ## Keyboard
212
+
213
+ | Key | Target | Effect |
214
+ | ------------- | --------------------------- | ------------------------------------------------------------- |
215
+ | Tab | Previous / Next button | Moves keyboard focus onto either button (both always in DOM). |
216
+ | Enter / Space | Navigation button (focused) | Advances or retreats the scroll track by one step. |
217
+ | Enter / Space | Disabled button (focused) | No-op — the click handler is guarded. |
218
+
219
+ Both buttons are native `<button>` elements, so Enter and Space activation follow
220
+ the UA's default behavior without custom `keydown` handling. Disabled buttons use
221
+ `aria-disabled` (not `disabled`) to remain in the tab order and preserve focus.
222
+
223
+ ## Focus management
224
+
225
+ - Both navigation buttons are always present in the DOM. When a button becomes
226
+ disabled at a scroll boundary, focus is not lost — the button stays focusable
227
+ via `aria-disabled` (rather than being unmounted).
228
+ - Both navigation buttons have a visible focus ring using `focus-visible:ring-2`
229
+ bound to `--ui-focus-primary`. The ring is suppressed for pointer interactions
230
+ (`:focus-visible` only).
231
+
232
+ ## Screen reader
233
+
234
+ - Screen readers can navigate to and activate each navigation button by label.
235
+ A disabled button is announced as "Previous, button, disabled" (or "Next").
236
+ - The scrollable track (`overflow-x: auto`, hidden scrollbar) does not expose
237
+ a scroll role; assistive technology reads through the children sequentially.
238
+ For very long lists of cards, consider complementing the carousel with a
239
+ "See all" link so AT users can reach all items without repeated button presses.
240
+
241
+ ## Contrast
242
+
243
+ - Both navigation buttons use `--ui-background-surface-primary` (white / dark
244
+ surface). The chevron icon inherits the default text color from the token
245
+ pipeline, which meets WCAG AA contrast against that surface.
246
+
247
+ ## CardWidget — skeleton loading
248
+
249
+ - When `skeleton` is true, the loading placeholder has `role="status"` with an
250
+ `aria-label` sourced from the `loadingLabel` prop (default `'Loading'`), so
251
+ assistive technology announces the loading state. Localize `loadingLabel` for
252
+ non-English deployments. The animated pulse bars inside are decorative.
@@ -44,8 +44,8 @@ Segment colors come from the `palette` prop — a dataviz palette resolving to t
44
44
 
45
45
  ## When not to use
46
46
 
47
- - Ranked single values that each need their own labelled row — use `Meter`
48
- stacked into a bar list.
47
+ - Ranked single values that each need their own labelled row — use
48
+ `BarChart` with `orientation="horizontal"`.
49
49
  - Precise value comparison across many categories — a bar chart reads better.
50
50
  - A flow across two or more levels — use `SankeyChart`.
51
51
  - More than ~6 segments, or very uneven values — thin slivers become unreadable;
@@ -72,8 +72,10 @@ function Sales({ status, data, config, refetch }) {
72
72
  ```
73
73
 
74
74
  The three states share one compact layout: a leading glyph (spinner / inbox /
75
- warning) over a centered label. Pass `message` to override a state's default
76
- label, and `action` to add a retry affordance to the error state.
75
+ warning) over a centered label. Pass `description` to override a state's default
76
+ label, and `action` to add a retry affordance to the error state. Long unbroken
77
+ error descriptions (CTI paths, API URLs) wrap at word boundaries and scroll
78
+ vertically when they exceed the slot height.
77
79
 
78
80
  ## Props
79
81
 
@@ -112,8 +114,19 @@ label, and `action` to add a retry affordance to the error state.
112
114
  ### Layout
113
115
 
114
116
  - **Given** any state, **when** placed in a sized slot (the same box a chart
115
- would occupy), **then** the block fills it (`size-full`) and centers its
116
- content, with a `min-h-32` floor so it stays legible in an unsized parent.
117
+ would occupy), **then** the block fills it (`size-full`) with a `min-h-32`
118
+ floor so it stays legible in an unsized parent.
119
+ - **Given** `state="loading"` or `state="empty"`, **when** rendered, **then**
120
+ the content is vertically centered in the slot (`justify-center`).
121
+ - **Given** `state="error"`, **when** the description is short enough to fit,
122
+ **then** the content is vertically centered via auto margins (`my-auto`).
123
+ - **Given** `state="error"` with a long unbroken `description` (e.g. CTI error
124
+ paths, API URLs), **when** the text overflows the slot height, **then** the
125
+ auto margins collapse and the icon stays anchored at the top, with vertical
126
+ scroll (`overflow-y: auto`) so the full diagnostic text is accessible. Long
127
+ unbroken strings wrap at word boundaries (`overflow-wrap: break-word`),
128
+ breaking mid-word only when a single token cannot fit on one line.
129
+ `overflow-x: hidden` prevents any horizontal scrollbar.
117
130
 
118
131
  ### Per-type empty states
119
132
 
@@ -108,12 +108,13 @@ across brands until the real data-viz palette lands.
108
108
  | `yAxisLabel` | `string` | — | no | Title rendered beside the vertical (Y) axis (rotated). |
109
109
  | `yUnit` | `string` | — | no | Unit suffix appended to Y-axis tick values (recharts `unit`); the X axis is categorical. |
110
110
  | `strokeWidth` | `number` | `2` | no | Stroke width of each line. |
111
- | `showDots` | `boolean` | `true` | no | Render a dot at each data point. |
111
+ | `showDots` | `boolean` | `false` | no | Render a dot at each data point. |
112
112
  | `dotSize` | `number` | `3` | no | Radius of each point's dot, in px. Its hover dot is 2px larger. |
113
113
  | `showActiveDot` | `boolean` | — | no | Enlarge the hovered point's dot. Defaults to following `showDots`, so set it explicitly for a hover-only line (dots off, active dot on) or a static-only line (dots on, active dot off). |
114
114
  | `lineSettings` | `Record<string, { color?: string; strokeWidth?: number; dashed?: boolean; curveType?: CurveType; showDots?: boolean; dotSize?: number; showLabel?: boolean; labelPosition?: LabelPosition }>` | — | no | Per-series style override keyed by `dataKeys` entry — anything unset falls back to the chart-wide prop, so an entry states only what differs. A series listed in `comparisonKeys` keeps its dashed, dimmed, dot-less treatment: `color` / `strokeWidth` / `curveType` still apply to it, but `showDots` / `dotSize` do not. |
115
115
  | `referenceLine` | `Line | Line[] where Line = { value?: number; average?: boolean | string; label?: string; labelPosition?: 'top' | 'insideTopLeft' | 'insideTopRight' | 'insideBottomLeft' | 'insideBottomRight' }` | — | no | One or more dashed reference/average lines on the value axis. Pass a single object or an array. Per line, a fixed `value` wins; otherwise the mean of one series (`average: '<key>'`) or of every plotted series (`average: true`, which includes any `comparisonKeys` overlay). An optional `label` captions each, at the rule's top right unless `labelPosition` moves it away from a series it would sit on. The same config shape as `BarChart` / `AreaChart`. |
116
116
  | `connectNulls` | `boolean` | `false` | no | Bridge `null` gaps in the data instead of breaking the line. |
117
+ | `projectionStart` | `string | number` | — | no | The category value (matching an `xKey` entry) at which the projection zone starts. Ticks from this point onward render in the disabled text color. The actual series are clipped to the left of the boundary; a dashed, dot-less projection continuation renders on the right. Comparison series (`comparisonKeys`) are excluded from the projection split and render unclipped across the full width. |
117
118
  | `showGrid` | `boolean` | `true` | no | Render the CartesianGrid. |
118
119
  | `showTooltip` | `boolean` | `true` | no | Render the hover tooltip. |
119
120
  | `showLegend` | `boolean` | `true` | no | Render the legend. |
@@ -57,7 +57,7 @@ GROSS MARGIN [Last 30 days] ← label + caption
57
57
  ## Composition
58
58
 
59
59
  Metric is a Card. Put the trend in the `trend` slot (a `TrendIndicator`), a
60
- timeframe in `caption` (a `Tag`), and drop a chart / `Meter` breakdown /
60
+ timeframe in `caption` (a `Tag`), and drop a chart / labelled-bar breakdown /
61
61
  `Separator` / insight line into `children`:
62
62
 
63
63
  ```tsx
@@ -76,11 +76,16 @@ timeframe in `caption` (a `Tag`), and drop a chart / `Meter` breakdown /
76
76
  />
77
77
  }
78
78
  >
79
- <Meter
80
- label="Healthy"
81
- value={46}
79
+ <BarChart
80
+ orientation="horizontal"
81
+ items={[
82
+ {
83
+ label: 'Healthy',
84
+ value: 46,
85
+ color: 'var(--ui-background-status-strong-success)',
86
+ },
87
+ ]}
82
88
  max={54}
83
- color="var(--ui-background-status-strong-success)"
84
89
  />
85
90
  <Separator className="my-3" />
86
91
  <p>+3 customers predicted at-risk within 30 days.</p>