@acronis-platform/ui-react 2.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.
Files changed (243) hide show
  1. package/dist/components/ui/accordion-container/accordion-container.js +144 -0
  2. package/dist/components/ui/accordion-container/accordion-container.js.map +1 -0
  3. package/dist/components/ui/app-shell-chat/app-shell-chat.js.map +1 -1
  4. package/dist/components/ui/area-chart/area-chart.js +141 -139
  5. package/dist/components/ui/area-chart/area-chart.js.map +1 -1
  6. package/dist/components/ui/avatar/avatar.js +33 -22
  7. package/dist/components/ui/avatar/avatar.js.map +1 -1
  8. package/dist/components/ui/bar-chart/bar-chart.js +295 -293
  9. package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
  10. package/dist/components/ui/card/card.js +150 -74
  11. package/dist/components/ui/card/card.js.map +1 -1
  12. package/dist/components/ui/card-section/card-section.js +111 -0
  13. package/dist/components/ui/card-section/card-section.js.map +1 -0
  14. package/dist/components/ui/category-bar/category-bar.js +109 -93
  15. package/dist/components/ui/category-bar/category-bar.js.map +1 -1
  16. package/dist/components/ui/chart/chart-palette.js +113 -0
  17. package/dist/components/ui/chart/chart-palette.js.map +1 -0
  18. package/dist/components/ui/chart/chart.js +176 -129
  19. package/dist/components/ui/chart/chart.js.map +1 -1
  20. package/dist/components/ui/chart-state/chart-state-illustrations.js +310 -0
  21. package/dist/components/ui/chart-state/chart-state-illustrations.js.map +1 -0
  22. package/dist/components/ui/chart-state/chart-state.js +48 -34
  23. package/dist/components/ui/chart-state/chart-state.js.map +1 -1
  24. package/dist/components/ui/chart-widget/chart-widget.js +87 -0
  25. package/dist/components/ui/chart-widget/chart-widget.js.map +1 -0
  26. package/dist/components/ui/composed-chart/composed-chart.js +117 -115
  27. package/dist/components/ui/composed-chart/composed-chart.js.map +1 -1
  28. package/dist/components/ui/confidence-cone/confidence-cone.js +166 -158
  29. package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -1
  30. package/dist/components/ui/data-table/data-table.js +4 -2
  31. package/dist/components/ui/data-table/data-table.js.map +1 -1
  32. package/dist/components/ui/empty-overlay/empty-overlay.js +49 -0
  33. package/dist/components/ui/empty-overlay/empty-overlay.js.map +1 -0
  34. package/dist/components/ui/funnel-chart/funnel-chart.js +162 -161
  35. package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -1
  36. package/dist/components/ui/histogram/histogram.js +94 -92
  37. package/dist/components/ui/histogram/histogram.js.map +1 -1
  38. package/dist/components/ui/input/input.js.map +1 -1
  39. package/dist/components/ui/label/label.js +2 -9
  40. package/dist/components/ui/label/label.js.map +1 -1
  41. package/dist/components/ui/line-chart/line-chart.js +143 -141
  42. package/dist/components/ui/line-chart/line-chart.js.map +1 -1
  43. package/dist/components/ui/metric/metric.js +74 -185
  44. package/dist/components/ui/metric/metric.js.map +1 -1
  45. package/dist/components/ui/page-content/page-content.js.map +1 -1
  46. package/dist/components/ui/pie-chart/pie-chart.js +201 -195
  47. package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
  48. package/dist/components/ui/radar-chart/radar-chart.js +133 -131
  49. package/dist/components/ui/radar-chart/radar-chart.js.map +1 -1
  50. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +297 -265
  51. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -1
  52. package/dist/components/ui/sankey-chart/sankey-chart.js +123 -101
  53. package/dist/components/ui/sankey-chart/sankey-chart.js.map +1 -1
  54. package/dist/components/ui/scatter-chart/scatter-chart.js +76 -74
  55. package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
  56. package/dist/components/ui/section/section.js +157 -31
  57. package/dist/components/ui/section/section.js.map +1 -1
  58. package/dist/components/ui/stepper/stepper.js +24 -24
  59. package/dist/components/ui/stepper/stepper.js.map +1 -1
  60. package/dist/components/ui/stepper-item/stepper-item.js +43 -24
  61. package/dist/components/ui/stepper-item/stepper-item.js.map +1 -1
  62. package/dist/components/ui/table/table.js +27 -27
  63. package/dist/components/ui/table/table.js.map +1 -1
  64. package/dist/components/ui/treemap/treemap.js +133 -124
  65. package/dist/components/ui/treemap/treemap.js.map +1 -1
  66. package/dist/components/ui/trend-indicator/trend-indicator.js +34 -69
  67. package/dist/components/ui/trend-indicator/trend-indicator.js.map +1 -1
  68. package/dist/components/ui/wizard/wizard.js +59 -0
  69. package/dist/components/ui/wizard/wizard.js.map +1 -0
  70. package/dist/hooks/use-wizard.js +75 -0
  71. package/dist/hooks/use-wizard.js.map +1 -0
  72. package/dist/index.js +540 -525
  73. package/dist/index.js.map +1 -1
  74. package/dist/llms/accordion-container.md +181 -0
  75. package/dist/llms/accordion.md +124 -0
  76. package/dist/llms/alert.md +225 -0
  77. package/dist/llms/app-shell-chat.md +415 -0
  78. package/dist/llms/area-chart.md +355 -0
  79. package/dist/llms/avatar.md +208 -0
  80. package/dist/llms/bar-chart.md +406 -0
  81. package/dist/llms/breadcrumb.md +326 -0
  82. package/dist/llms/button-group.md +298 -0
  83. package/dist/llms/button-icon-input.md +142 -0
  84. package/dist/llms/button-icon-menu.md +209 -0
  85. package/dist/llms/button-icon.md +128 -0
  86. package/dist/llms/button-menu.md +172 -0
  87. package/dist/llms/button.md +185 -0
  88. package/dist/llms/calendar-panel.md +733 -0
  89. package/dist/llms/calendar.md +188 -0
  90. package/dist/llms/card-filter.md +214 -0
  91. package/dist/llms/card-section.md +293 -0
  92. package/dist/llms/card.md +447 -0
  93. package/dist/llms/carousel-dialog.md +156 -0
  94. package/dist/llms/category-bar.md +193 -0
  95. package/dist/llms/chart-state.md +190 -0
  96. package/dist/llms/chart-widget.md +209 -0
  97. package/dist/llms/chart.md +182 -0
  98. package/dist/llms/checkbox.md +257 -0
  99. package/dist/llms/chip.md +195 -0
  100. package/dist/llms/combobox.md +164 -0
  101. package/dist/llms/composed-chart.md +508 -0
  102. package/dist/llms/confidence-cone.md +277 -0
  103. package/dist/llms/data-table.md +474 -0
  104. package/dist/llms/date-range-picker.md +272 -0
  105. package/dist/llms/description-list.md +131 -0
  106. package/dist/llms/dialog-footer-carousel.md +147 -0
  107. package/dist/llms/dialog-footer-default.md +142 -0
  108. package/dist/llms/dialog-root.md +246 -0
  109. package/dist/llms/dialog-welcome.md +223 -0
  110. package/dist/llms/dialog.md +403 -0
  111. package/dist/llms/dropdown-menu.md +193 -0
  112. package/dist/llms/empty-overlay.md +146 -0
  113. package/dist/llms/empty.md +156 -0
  114. package/dist/llms/field.md +185 -0
  115. package/dist/llms/filter-cards.md +157 -0
  116. package/dist/llms/filter-chips.md +323 -0
  117. package/dist/llms/filter-search.md +512 -0
  118. package/dist/llms/form.md +154 -0
  119. package/dist/llms/funnel-chart.md +333 -0
  120. package/dist/llms/grid.md +80 -0
  121. package/dist/llms/histogram.md +190 -0
  122. package/dist/llms/input-date-picker.md +203 -0
  123. package/dist/llms/input-otp.md +173 -0
  124. package/dist/llms/input-password.md +197 -0
  125. package/dist/llms/input-search.md +170 -0
  126. package/dist/llms/input-select.md +278 -0
  127. package/dist/llms/input-text-area.md +271 -0
  128. package/dist/llms/input-text.md +192 -0
  129. package/dist/llms/line-chart.md +369 -0
  130. package/dist/llms/link.md +209 -0
  131. package/dist/llms/loading.md +130 -0
  132. package/dist/llms/meter.md +177 -0
  133. package/dist/llms/metric.md +204 -0
  134. package/dist/llms/number-field.md +122 -0
  135. package/dist/llms/page-content.md +69 -0
  136. package/dist/llms/page-header.md +182 -0
  137. package/dist/llms/pagination.md +133 -0
  138. package/dist/llms/pie-chart.md +307 -0
  139. package/dist/llms/popover.md +252 -0
  140. package/dist/llms/progress-circle.md +126 -0
  141. package/dist/llms/progress.md +111 -0
  142. package/dist/llms/radar-chart.md +316 -0
  143. package/dist/llms/radial-bar-chart.md +319 -0
  144. package/dist/llms/radio.md +255 -0
  145. package/dist/llms/resizable.md +195 -0
  146. package/dist/llms/sankey-chart.md +208 -0
  147. package/dist/llms/scatter-chart.md +239 -0
  148. package/dist/llms/scroll-area.md +183 -0
  149. package/dist/llms/section.md +401 -0
  150. package/dist/llms/select.md +210 -0
  151. package/dist/llms/separator.md +87 -0
  152. package/dist/llms/sheet.md +202 -0
  153. package/dist/llms/sidebar-primary.md +360 -0
  154. package/dist/llms/sidebar-secondary.md +417 -0
  155. package/dist/llms/skeleton.md +81 -0
  156. package/dist/llms/slider.md +97 -0
  157. package/dist/llms/spinner.md +100 -0
  158. package/dist/llms/stack.md +80 -0
  159. package/dist/llms/stepper-item.md +309 -0
  160. package/dist/llms/stepper.md +251 -0
  161. package/dist/llms/switch.md +140 -0
  162. package/dist/llms/table.md +436 -0
  163. package/dist/llms/tabs.md +197 -0
  164. package/dist/llms/tag.md +139 -0
  165. package/dist/llms/timeline.md +361 -0
  166. package/dist/llms/timer.md +259 -0
  167. package/dist/llms/toast.md +241 -0
  168. package/dist/llms/toggle-group.md +121 -0
  169. package/dist/llms/toolbar.md +453 -0
  170. package/dist/llms/tooltip.md +164 -0
  171. package/dist/llms/treemap.md +254 -0
  172. package/dist/llms/trend-indicator.md +132 -0
  173. package/dist/llms/widget-placeholder.md +169 -0
  174. package/dist/llms/wizard.md +412 -0
  175. package/dist/llms.txt +142 -0
  176. package/dist/react.js +540 -525
  177. package/dist/react.js.map +1 -1
  178. package/dist/src/components/ui/accordion-container/accordion-container.d.ts +44 -0
  179. package/dist/src/components/ui/accordion-container/index.d.ts +1 -0
  180. package/dist/src/components/ui/area-chart/area-chart.d.ts +9 -4
  181. package/dist/src/components/ui/avatar/avatar.d.ts +27 -8
  182. package/dist/src/components/ui/bar-chart/bar-chart.d.ts +9 -4
  183. package/dist/src/components/ui/button/button.d.ts +1 -1
  184. package/dist/src/components/ui/card/card.d.ts +69 -8
  185. package/dist/src/components/ui/card/index.d.ts +1 -1
  186. package/dist/src/components/ui/card-section/card-section.d.ts +50 -0
  187. package/dist/src/components/ui/card-section/index.d.ts +1 -0
  188. package/dist/src/components/ui/category-bar/category-bar.d.ts +10 -4
  189. package/dist/src/components/ui/chart/__stories__/palette-control.d.ts +48 -0
  190. package/dist/src/components/ui/chart/__tests__/chart-palette.test.d.ts +1 -0
  191. package/dist/src/components/ui/chart/chart-palette.d.ts +128 -0
  192. package/dist/src/components/ui/chart/chart.d.ts +66 -15
  193. package/dist/src/components/ui/chart/index.d.ts +4 -2
  194. package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +6 -0
  195. package/dist/src/components/ui/chart-state/chart-state.d.ts +13 -2
  196. package/dist/src/components/ui/chart-state/index.d.ts +1 -0
  197. package/dist/src/components/ui/chart-widget/chart-widget.d.ts +64 -0
  198. package/dist/src/components/ui/chart-widget/index.d.ts +1 -0
  199. package/dist/src/components/ui/composed-chart/composed-chart.d.ts +12 -7
  200. package/dist/src/components/ui/confidence-cone/confidence-cone.d.ts +10 -5
  201. package/dist/src/components/ui/dialog/dialog.d.ts +1 -1
  202. package/dist/src/components/ui/empty-overlay/empty-overlay.d.ts +20 -0
  203. package/dist/src/components/ui/empty-overlay/index.d.ts +2 -0
  204. package/dist/src/components/ui/funnel-chart/funnel-chart.d.ts +11 -5
  205. package/dist/src/components/ui/grid/grid.d.ts +1 -1
  206. package/dist/src/components/ui/histogram/histogram.d.ts +10 -4
  207. package/dist/src/components/ui/label/index.d.ts +1 -1
  208. package/dist/src/components/ui/label/label.d.ts +1 -5
  209. package/dist/src/components/ui/line-chart/line-chart.d.ts +9 -4
  210. package/dist/src/components/ui/metric/metric.d.ts +13 -14
  211. package/dist/src/components/ui/pie-chart/pie-chart.d.ts +17 -8
  212. package/dist/src/components/ui/radar-chart/radar-chart.d.ts +10 -5
  213. package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +17 -6
  214. package/dist/src/components/ui/sankey-chart/sankey-chart.d.ts +10 -5
  215. package/dist/src/components/ui/scatter-chart/scatter-chart.d.ts +10 -5
  216. package/dist/src/components/ui/section/index.d.ts +1 -1
  217. package/dist/src/components/ui/section/section.d.ts +75 -6
  218. package/dist/src/components/ui/sheet/sheet.d.ts +1 -1
  219. package/dist/src/components/ui/stack/stack.d.ts +1 -1
  220. package/dist/src/components/ui/stepper-item/stepper-item.d.ts +6 -5
  221. package/dist/src/components/ui/tag/tag.d.ts +1 -1
  222. package/dist/src/components/ui/treemap/treemap.d.ts +11 -5
  223. package/dist/src/components/ui/trend-indicator/trend-indicator.d.ts +3 -7
  224. package/dist/src/components/ui/wizard/index.d.ts +1 -0
  225. package/dist/src/components/ui/wizard/wizard.d.ts +6 -0
  226. package/dist/src/hooks/index.d.ts +1 -0
  227. package/dist/src/hooks/use-wizard.d.ts +80 -0
  228. package/dist/src/index.d.ts +5 -3
  229. package/dist/ui-react.css +1 -1
  230. package/package.json +12 -6
  231. package/dist/components/ui/app-shell/app-shell.js +0 -75
  232. package/dist/components/ui/app-shell/app-shell.js.map +0 -1
  233. package/dist/components/ui/auth-layout/auth-layout.js +0 -56
  234. package/dist/components/ui/auth-layout/auth-layout.js.map +0 -1
  235. package/dist/components/ui/collapsible/collapsible.js +0 -22
  236. package/dist/components/ui/collapsible/collapsible.js.map +0 -1
  237. package/dist/src/components/ui/app-shell/app-shell.d.ts +0 -8
  238. package/dist/src/components/ui/app-shell/index.d.ts +0 -1
  239. package/dist/src/components/ui/auth-layout/auth-layout.d.ts +0 -6
  240. package/dist/src/components/ui/auth-layout/index.d.ts +0 -1
  241. package/dist/src/components/ui/collapsible/collapsible.d.ts +0 -5
  242. package/dist/src/components/ui/collapsible/index.d.ts +0 -1
  243. package/dist/src/components/ui/label/label.docs.d.ts +0 -10
@@ -0,0 +1,193 @@
1
+ # CategoryBar
2
+
3
+ > A single horizontal bar split into proportional colored segments — one part-to-whole across a handful of categories in one row (onboarding stages, certification status, a pass/fail split). Each segment's width is its value over the sum of all values; segment colors are caller-supplied via `config`. A plain flex composition (not a recharts chart) with one CVA axis (`size`), an optional count/% legend, and per-segment hover tooltips. Design-pending v1 — no chart token tier exists yet.
4
+
5
+ **Status:** draft · **Category:** data-display · **Since:** v0.60.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { CategoryBar } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <CategoryBar
17
+ config={config}
18
+ data={[
19
+ { key: 'passed', value: 68 },
20
+ { key: 'warnings', value: 22 },
21
+ { key: 'failed', value: 10 },
22
+ ]}
23
+ showLegend
24
+ />
25
+ ```
26
+
27
+ A single horizontal bar split into proportional colored segments — a
28
+ part-to-whole across a handful of categories, in one row. Give it a `data` array
29
+ (`key` + `value`) and a per-segment `config`; each segment's width is its value
30
+ over the total.
31
+
32
+ Segment colors come from the `palette` prop — a dataviz palette resolving to the
33
+ `--ui-dataviz-*` tokens, `categorical` by default — not from `config`.
34
+
35
+ > **Design-pending v1.** A plain flex composition (no recharts, no demo
36
+ > playground). Chrome is reconciled with Figma later; Code Connect is deferred.
37
+
38
+ ## When to use
39
+
40
+ - A single-level part-to-whole where the **relative size** of each category
41
+ matters and you want it in one compact row (onboarding stages, certification
42
+ status, a pass/warn/fail split).
43
+ - As a header/summary widget above a table or in a dashboard card.
44
+
45
+ ## When not to use
46
+
47
+ - Ranked single values that each need their own labelled row — use `Meter`
48
+ stacked into a bar list.
49
+ - Precise value comparison across many categories — a bar chart reads better.
50
+ - A flow across two or more levels — use `SankeyChart`.
51
+ - More than ~6 segments, or very uneven values — thin slivers become unreadable;
52
+ lean on the legend or a different chart.
53
+
54
+ ## Variants
55
+
56
+ One CVA axis: `size` (`sm` / `md` / `lg`) sets the track height. Everything else
57
+ is plain props: `showLegend`, `showTooltip`, `valueFormatter`, `defaultOpenIndex`.
58
+
59
+ ## Example
60
+
61
+ ```tsx
62
+ import { CategoryBar } from '@acronis-platform/ui-react';
63
+ import type { ChartConfig } from '@acronis-platform/ui-react';
64
+
65
+ const config = {
66
+ passed: {
67
+ label: 'Passed',
68
+ color: 'var(--ui-background-status-strong-success)',
69
+ },
70
+ warnings: {
71
+ label: 'Warnings',
72
+ color: 'var(--ui-background-status-strong-warning)',
73
+ },
74
+ failed: {
75
+ label: 'Failed',
76
+ color: 'var(--ui-background-status-strong-danger)',
77
+ },
78
+ } satisfies ChartConfig;
79
+
80
+ <CategoryBar
81
+ config={config}
82
+ data={[
83
+ { key: 'passed', value: 68 },
84
+ { key: 'warnings', value: 22 },
85
+ { key: 'failed', value: 10 },
86
+ ]}
87
+ showLegend
88
+ />;
89
+ ```
90
+
91
+ `showLegend` renders a color dot + label per segment plus each segment's value
92
+ and share of the total. Segment `key`s must be unique and CSS-safe;
93
+ `config[key].label` carries the human-readable text.
94
+
95
+ ## Props
96
+
97
+ | Prop | Type | Default | Required | Description |
98
+ |------|------|---------|----------|-------------|
99
+ | `data` | `ReadonlyArray<{ key: string; value: number }>` | — | yes | Ordered segments (left → right). Each `key` indexes `config` for the segment's label and color; each segment's width is its `value` over the sum of all values. |
100
+ | `config` | `ChartConfig` | — | yes | Segments take their colour from the container's `palette`; each entry, keyed by the segment's `key`, maps that segment to a `label` and an optional `tone`. Resolved into the `--color-<key>` custom properties the segments fill from. |
101
+ | `size` | `'sm' | 'md' | 'lg'` | `'md'` | no | Track height. |
102
+ | `showLegend` | `boolean` | `false` | no | Render a legend below the bar — a color dot + label per segment, its value, and its share of the total as a percentage. |
103
+ | `showTooltip` | `boolean` | `true` | no | Wrap each segment in a hover tooltip (dot + label + value + %). |
104
+ | `tooltipContent` | `(segment: { key; value; label; percent; color }) => ReactNode` | — | no | Custom tooltip content per segment — replaces the default (dot + label + `value · %`) inside the same card. Ignored when showTooltip is false. `label` is the resolved config label (any node, not just a string) and `color` is the segment's `var(--color-<key>)` reference, so it follows whichever palette colour the container resolved for that segment. |
105
+ | `valueFormatter` | `(value: number) => string` | `toLocaleString()` | no | Format the numeric value shown in the legend and tooltip. |
106
+ | `defaultOpenIndex` | `number` | — | no | Render the tooltip for this segment index initially open (for visual regression / testing; the tooltip is otherwise hover-only). |
107
+ | `aria-label` | `string` | — | no | Accessible summary of the whole bar. Defaults to a locale-neutral `label value` list built from the data; pass a fuller sentence if you have one. |
108
+
109
+ ## Behavior
110
+
111
+ `CategoryBar` renders a single bar split into proportional colored segments from
112
+ a `data` array and a per-segment `config`. It is a plain flex composition, not a
113
+ recharts chart.
114
+
115
+ ```gherkin
116
+ Scenario: Render segments proportional to value
117
+ Given data with segments (each a key + value) and a config mapping each key to a label and color
118
+ Then one segment renders per datum, its width its value over the sum of all values
119
+ And each segment is filled from its config color
120
+ ```
121
+
122
+ ```gherkin
123
+ Scenario: Size variant
124
+ Given size is sm, md (default), or lg
125
+ Then the track renders at the corresponding height
126
+ ```
127
+
128
+ ```gherkin
129
+ Scenario: Legend
130
+ Given showLegend is true
131
+ Then a legend renders below the bar with a color dot + label per segment
132
+ And each segment's value plus its share of the total as a percentage
133
+ And when showLegend is false no legend renders
134
+ ```
135
+
136
+ ```gherkin
137
+ Scenario: Tooltip on hover
138
+ Given showTooltip is true
139
+ When the user hovers a segment
140
+ Then a card shows a color dot, the segment's label, and its value + percentage
141
+ And defaultOpenIndex renders that segment's tooltip initially open
142
+ And tooltipContent replaces the default card body with a per-segment render
143
+ ```
144
+
145
+ ```gherkin
146
+ Scenario: Value formatting
147
+ Given a valueFormatter
148
+ Then the legend and tooltip show the formatted value
149
+ ```
150
+
151
+ ```gherkin
152
+ Scenario: Accessible summary
153
+ Given no aria-label
154
+ Then the bar exposes a role="img" with a generated "<label> <value>" summary
155
+ Given an aria-label
156
+ Then that string is used instead
157
+ ```
158
+
159
+ ```gherkin
160
+ Scenario: All-zero data
161
+ Given every segment value is 0
162
+ Then no segment takes width, each share reads 0%, and nothing divides by zero
163
+ ```
164
+
165
+ ## Accessibility
166
+
167
+ `CategoryBar` is a data visualization: a single bar of proportional segments.
168
+
169
+ ## Text alternative
170
+
171
+ - The track carries `role="img"` with an `aria-label`. By default the label is a
172
+ locale-neutral `"<label> <value>"` list built from the data (e.g. `"Registered
173
+ 42, Trained 32, Certified 26"`); pass a fuller `aria-label` sentence when you
174
+ have one. This is the screen-reader equivalent of the bar.
175
+ - **Do not rely on color alone.** The legend (`showLegend`) and the per-segment
176
+ tooltip both carry the text label, so the meaning survives without color
177
+ perception.
178
+
179
+ ## Tooltip
180
+
181
+ - The per-segment tooltip is a **hover/pointer** affordance layered on the
182
+ `role="img"` bar; the `aria-label` already conveys the same information to
183
+ assistive tech, so the tooltip is supplementary rather than the sole source.
184
+ - Because the segments sit inside a `role="img"`, their tooltip triggers are not
185
+ exposed as separate focusable controls — keeping a single, coherent image
186
+ semantic instead of a row of unlabeled controls.
187
+
188
+ ## Contrast
189
+
190
+ - Segment fills are caller-supplied tokens; pick colors that stay distinguishable
191
+ from each other and from the track background in both light and dark themes.
192
+ Adjacent segments should not read as one block — the legend disambiguates when
193
+ hues are close.
@@ -0,0 +1,190 @@
1
+ # ChartState
2
+
3
+ > A shared loading / empty / error placeholder for the chart types, rendered in place of the chart inside the same sized slot. A compact status block — a leading glyph over a centered label, with an optional retry action for the error state. The `empty` state draws the silhouette the design defines for each chart type (`variant`), plus an optional "Widget description" line; `loading` and `error` share one treatment. Distinct from `WidgetPlaceholder`, which is the composable skeleton for a widget that isn't a chart — see behavior.md.
4
+
5
+ **Status:** draft · **Category:** data-display · **Since:** v0.58.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { ChartState } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <ChartState state="loading" />
17
+ <ChartState state="empty" variant="bar" description="No results for the selected range" />
18
+ <ChartState
19
+ state="error"
20
+ action={<Button variant="ghost" onClick={refetch}>Try again</Button>}
21
+ />
22
+ ```
23
+
24
+ A shared loading / empty / error placeholder for the chart types. Render it in
25
+ place of a chart — inside the same sized slot — while data is loading, when
26
+ there is nothing to plot, or when the fetch failed.
27
+
28
+ > **Design-pending v1.** Ported from the Figma InputSelect dropdown states (the
29
+ > reference node for this task) and kept visually in step with the shipped
30
+ > `InputSelectStatus`. There is no `--ui-chart-*` token tier yet, so it themes
31
+ > from existing semantic tokens directly.
32
+
33
+ ## When to use
34
+
35
+ - A chart cannot render yet (data in flight), has no data to plot, or failed to
36
+ load — swap the chart for the matching `ChartState`.
37
+ - You want the same placeholder across every chart type, rather than each chart
38
+ hand-rolling its own.
39
+
40
+ ## When not to use
41
+
42
+ - A full-page empty state (illustration, title, description, primary action) —
43
+ use the larger `Empty` component instead.
44
+ - A bare loading indicator with no surrounding message — use `Spinner` directly.
45
+
46
+ ## Example
47
+
48
+ ```tsx
49
+ import { ChartState, ChartContainer, Button } from '@acronis-platform/ui-react';
50
+
51
+ function Sales({ status, data, config, refetch }) {
52
+ if (status === 'loading') return <ChartState state="loading" />;
53
+ if (status === 'error')
54
+ return (
55
+ <ChartState
56
+ state="error"
57
+ action={
58
+ <Button variant="ghost" onClick={refetch}>
59
+ Try again
60
+ </Button>
61
+ }
62
+ />
63
+ );
64
+ if (!data.length) return <ChartState state="empty" />;
65
+
66
+ return (
67
+ <ChartContainer config={config} className="h-[300px] w-[500px]">
68
+ {/* …the chart… */}
69
+ </ChartContainer>
70
+ );
71
+ }
72
+ ```
73
+
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.
77
+
78
+ ## Props
79
+
80
+ | Prop | Type | Default | Required | Description |
81
+ |------|------|---------|----------|-------------|
82
+ | `state` | `'loading' | 'empty' | 'error'` | — | yes | Which placeholder to show. Drives the leading artwork and the default label (loading → spinner + "Data is loading…"; empty → variant silhouette or no artwork + "No data found"; error → warning glyph + "Something went wrong"). |
83
+ | `variant` | `'area' | 'bar' | 'line' | 'donut' | 'radial' | 'funnel' | 'radar' | 'sankey' | 'scatter' | 'treemap' | 'table' | 'text'` | — | no | Which chart the slot holds. The `empty` state then draws that type's silhouette instead of the generic inbox glyph — the design defines one per chart type. `donut` and `radial` share a ring. Ignored by `loading` and `error`, which share one treatment. |
84
+ | `description` | `ReactNode` | — | no | Text shown below the illustration or glyph. Defaults to `"Data is loading…"` / `"No data found"` / `"Something went wrong"` based on `state`. Pass a value to override for any state. |
85
+ | `action` | `ReactNode` | — | no | Trailing action rendered for the `error` state only (e.g. a "Try again" button). |
86
+
87
+ ## Behavior
88
+
89
+ ### Rendering per state
90
+
91
+ - **Given** `state="loading"`, **when** rendered, **then** it shows the shared
92
+ `Spinner` (lg) above the label "Data is loading…" and announces the label via
93
+ its `role="status"` live region (no `aria-busy`, which could defer that
94
+ announcement).
95
+ - **Given** `state="empty"`, **when** rendered, **then** it shows the label
96
+ "No data found" (no artwork unless a `variant` is passed).
97
+ - **Given** `state="error"`, **when** rendered, **then** it shows the warning
98
+ glyph above the label "Something went wrong" and announces as an alert.
99
+
100
+ ### Label override
101
+
102
+ - **Given** a `description` prop, **when** rendered in any state, **then** the
103
+ supplied text replaces that state's default label.
104
+
105
+ ### Action
106
+
107
+ - **Given** an `action` node, **when** `state="error"`, **then** the action is
108
+ rendered after the label (e.g. a "Try again" button).
109
+ - **Given** an `action` node, **when** `state` is `loading` or `empty`, **then**
110
+ the action is **not** rendered.
111
+
112
+ ### Layout
113
+
114
+ - **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
+
118
+ ### Per-type empty states
119
+
120
+ `empty` is the one state the design draws per chart type: an area silhouette for
121
+ an area chart, a ring for a donut, a funnel for a funnel. An empty widget then
122
+ still says what it _would_ have shown, which a single generic glyph can't.
123
+
124
+ `variant` selects it. `donut` and `radial` share one ring — a radial-bar widget
125
+ with no data has nothing to tell it apart. Without a `variant` the empty state
126
+ shows no artwork — just the text.
127
+
128
+ The geometry is exported from the Figma instances rather than redrawn, and every
129
+ path is `currentColor`: the tone is set once on the container, so brand and theme
130
+ overrides reach the artwork. Two of the eleven silhouettes use a second, fainter
131
+ tone (the table and metric-list ones tint their header row darker than the body
132
+ rows); that hierarchy is kept as `fill-opacity` on the same colour rather than a
133
+ second token.
134
+
135
+ The caption is **one line, in one style**. Figma styles it `body/default` on
136
+ `text/onSurface/primary` — 14/24, centred, _not_ muted — which is what the root
137
+ already applies, so the description and the fallback status label read
138
+ identically. For a per-type empty state that line is the `description`: the mockups draw the silhouette over "Widget description" with no
139
+ status label, because the artwork already says "no data" and the sentence worth
140
+ the space is what the widget _would_ show. With no `description` it falls back to
141
+ the status label, so a caller that only sets `variant` still reads sensibly.
142
+
143
+ `loading` and `error` deliberately keep one shared treatment — the design only
144
+ redesigned `empty`.
145
+
146
+ ### Not `WidgetPlaceholder`
147
+
148
+ Two components, two jobs, and they are not interchangeable:
149
+
150
+ - **`ChartState`** is the placeholder _inside_ a chart's slot. It fills whatever
151
+ gives the slot its height, renders one of three states, and is what a chart
152
+ widget swaps in for its plot.
153
+ - **`WidgetPlaceholder`** is a composable skeleton with its own header, footer
154
+ and action parts, plus an `interactive` affordance — for a dashboard widget
155
+ that isn't a chart, or a whole tile that is still being set up.
156
+
157
+ `WidgetPlaceholder` predates the per-type empty states and is used by consuming
158
+ MFEs, so it stays.
159
+
160
+ ## Accessibility
161
+
162
+ - The root is a **live region** so a screen reader announces the state change
163
+ when a chart swaps its content in:
164
+ - `loading` / `empty` → `role="status"` with `aria-live="polite"`.
165
+ - `error` → `role="alert"` with `aria-live="assertive"`.
166
+ - On `loading` the shared `Spinner` is rendered `aria-hidden` so the root's own
167
+ live region (with the label) announces once, rather than the Spinner's
168
+ `role="status"` + sr-only "Loading…" nesting inside it and double-announcing.
169
+ - The root does **not** carry `aria-busy`. It is itself the live region, and some
170
+ screen readers defer announcing a busy region's content until `aria-busy`
171
+ clears; because this placeholder is unmounted (swapped for the chart) rather
172
+ than un-busied, a busy flag would risk swallowing the "Data is loading…"
173
+ announcement. Put `aria-busy` on the chart-slot **container** instead if a busy
174
+ signal is needed (same guidance as `Skeleton`).
175
+ - The live-region attributes (`role` / `aria-live`) are applied after the prop
176
+ spread, so the state's a11y contract is not clobbered by a consumer-passed
177
+ attribute.
178
+ - The label text is a real `<p>`, so the state message is always in the
179
+ accessible tree — the glyph is decorative and does not need its own label.
180
+ - **Do not rely on the glyph color alone** to convey the state — the label
181
+ carries the meaning. Icon colors (`--ui-glyph-on-status-info` /
182
+ `--ui-glyph-on-status-warning`) and the label (`--ui-text-on-surface-primary`)
183
+ resolve to semantic tokens that meet contrast in both themes.
184
+ - If the error state offers a retry, pass an actual interactive control as
185
+ `action` (a `Button`) so it is focusable and keyboard-operable.
186
+
187
+ ## Contrast
188
+
189
+ Label and glyph colors resolve to semantic `--ui-*` tokens that meet contrast on
190
+ the surface in both light and dark themes.
@@ -0,0 +1,209 @@
1
+ # ChartWidget
2
+
3
+ > The dashboard card a chart sits in: a `Card` with the standard widget header (title, optional filter chip and the ⋯ actions menu) and an optional metric readout above the plot. Adds the one thing a `Card` doesn't know about — the body's loading / empty / error placeholder — and re-implements nothing else: its `header` is `CardHeaderProps`, spread. It declares no size: the grid sizes the card and the widget passes that height down, so a `size-full` chart fills whatever the header leaves. The per-type chart components stay card-less, so a chart is still usable outside a widget.
4
+
5
+ **Status:** draft · **Category:** data-display · **Since:** v2.1.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { ChartWidget } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <ChartWidget
17
+ header={{
18
+ title: 'Active alerts by severity',
19
+ extras: <Tag variant="info">Last 6 months</Tag>,
20
+ actions: <ButtonIcon aria-label="Widget actions"><EllipsisIcon size={16} /></ButtonIcon>,
21
+ }}
22
+ className="h-[300px] w-[592px]"
23
+ >
24
+ <AreaChart config={config} data={data} dataKeys={['sessions']} xKey="month" className="size-full" />
25
+ </ChartWidget>
26
+
27
+ <ChartWidget header={{ title: 'Severity' }} state="empty" bodyClassName="h-[252px]" />
28
+ ```
29
+
30
+ The dashboard card a chart sits in.
31
+
32
+ Every chart in the Figma library is drawn inside the same card: a header with a
33
+ title, an optional filter chip and the ⋯ actions menu, then an optional metric
34
+ readout, then the plot. `ChartWidget` is that composition — and only that.
35
+
36
+ ## What it adds
37
+
38
+ One thing a `Card` legitimately doesn't know about: **what the body shows while
39
+ there is no plot.** `state="loading" | "empty" | "error"` renders `ChartState` in
40
+ place of the chart, and `error` also gives the Card its error border — one prop,
41
+ not two.
42
+
43
+ ## What it doesn't add
44
+
45
+ A header. `header` is typed `CardHeaderProps` and spread onto `CardHeader`, so
46
+ the whole header surface — including the parts this component never mentions
47
+ (`isDraggable`, `hasRename`, `isCollapsible`, …) — comes from `Card` and stays
48
+ in sync with it.
49
+
50
+ Nor a chart. The per-type chart components stay card-less, which keeps them
51
+ usable outside a widget.
52
+
53
+ ## Why it takes no `size`
54
+
55
+ The Figma sets carry `size` = `sm` / `md` / `lg`, but it only changes the frame
56
+ width (288 / 592 / 896). The height is the dashboard grid's, and the widget
57
+ passes it down: the header takes what it needs, and a `size-full` chart fills
58
+ the rest of the card. See `behavior.md`.
59
+
60
+ ## Files
61
+
62
+ | File | What's in it |
63
+ | ------------------ | ------------------------------------------------------------------ |
64
+ | `index.yaml` | Identity, category, status, `since`. |
65
+ | `api.yaml` | The prop contract and the per-framework adapters. |
66
+ | `anatomy.yaml` | Parts, the schematic, and the four body states. |
67
+ | `behavior.md` | Composition, how the height reaches the plot, state, localization. |
68
+ | `accessibility.md` | Roles, the placeholder's live region, header controls. |
69
+ | `tokens.yaml` | The `--ui-*` tokens the widget references. |
70
+
71
+ ## No Figma Code Connect
72
+
73
+ Deliberately absent. `ChartWidget` has no node of its own: in Figma the shape
74
+ lives inside each chart's own component set (ChartArea `8174:22232`, ChartBar
75
+ `8700:55606`, …), which wrap the `_CardLegacy` instance — and that maps to
76
+ `Card`, which is already connected.
77
+
78
+ So the connection belongs on the per-type chart components, whose examples
79
+ should show `<ChartWidget>` wrapping their plot. A `.figma.tsx` here would have
80
+ to point at one arbitrary chart set and would read as if the widget were that
81
+ chart.
82
+
83
+ ## Props
84
+
85
+ | Prop | Type | Default | Required | Description |
86
+ |------|------|---------|----------|-------------|
87
+ | `header` | `CardHeaderProps` | — | no | The card header, forwarded verbatim to `CardHeader` — `title`, `description`, `extras` (a filter chip), `actions` (the ⋯ menu), `isDraggable`, `isSwitchable`, `hasAvatar`, `hasRename`, `isCollapsible`. Omit for a header-less widget. |
88
+ | `orientation` | `'vertical' | 'horizontal'` | `vertical` | no | How the `metric` and plot are arranged inside the card body. `vertical` (default) — metric above the plot, full width; the metric takes its natural height and the plot fills the rest. `horizontal` — metric and plot side by side, each `flex-1`; use for compact (sm/288px) widgets where vertical space is at a premium. |
89
+ | `metric` | `ReactNode` | — | no | The readout beside or above the plot (depending on `orientation`), typically a `Metric`. |
90
+ | `variant` | `'area' | 'bar' | 'line' | 'donut' | 'radial' | 'funnel' | 'radar' | 'sankey' | 'scatter' | 'treemap' | 'table' | 'text'` | — | no | Which chart the widget holds. Only used while `state="empty"`, to pick the silhouette for that chart type. Forwarded to `ChartState`. |
91
+ | `state` | `'loading' | 'empty' | 'error'` | — | no | Renders `ChartState` in place of the plot, keeping the header. `error` additionally gives the Card its error border. |
92
+ | `stateDescription` | `ReactNode` | — | no | Text below the placeholder illustration or glyph. Forwarded to `ChartState` as `description`. Defaults to the per-state built-in ("Data is loading…" / "No data found" / "Something went wrong"). |
93
+ | `stateAction` | `ReactNode` | — | no | Trailing action on the `error` placeholder — e.g. a retry button. |
94
+ | `bodyClassName` | `string` | — | no | Class for the card body. Rarely needed — the body already fills the card. For the case where neither the grid nor the content gives a height, e.g. a placeholder-only widget outside a sized cell. |
95
+ | `children` | `ReactNode` | — | no | The chart. Replaced by the placeholder while `state` is set. |
96
+ | `render` | `useRender.RenderProp` | — | no | Replace the rendered `<div>` with another element or component (Base UI composition), forwarded to `Card`. How a dashboard gives each widget a landmark role — `render={<section aria-label="Sessions" />}` — which is the one thing the widget can't infer for itself. |
97
+
98
+ ## Behavior
99
+
100
+ ### Composition, not a container
101
+
102
+ `ChartWidget` is a `Card` plus two things a card doesn't know about: what the
103
+ body shows while there's no plot, and how much height to reserve for it.
104
+ Everything else is `Card`'s.
105
+
106
+ The `header` prop is typed `CardHeaderProps` and spread onto `CardHeader`. That
107
+ is the whole header contract — title, description, the `extras` filter chip, the
108
+ `actions` ⋯ menu, the drag handle, the switch, the avatar, rename, and the
109
+ collapse trigger. None of it is re-declared here, so a header feature added to
110
+ `Card` reaches this component without a change, and can't drift out of sync.
111
+
112
+ The per-type chart components stay **card-less**. They are the plot; the card is
113
+ composition. That keeps `<AreaChart>` usable inside a table cell, a popover or a
114
+ `Metric`'s sparkline slot, where a card would be wrong.
115
+
116
+ ### No size of its own
117
+
118
+ The Figma sets carry a `size` axis of `sm` / `md` / `lg`, but measuring all eight
119
+ shows it only changes the frame **width** — 288 / 592 / 896. And every card, and
120
+ every card body, is `HUG` vertically: the height is whatever the content adds up
121
+ to.
122
+
123
+ So the widget declares no size — it _passes one down_. The dashboard grid gives
124
+ the card a height; the card is a full-height flex column; the header is
125
+ `shrink-0`; the body takes the rest; and the plot slot takes what the metric row
126
+ leaves. A chart given `size-full` therefore occupies the whole remaining card.
127
+
128
+ `min-h-0` appears at every step of that chain, and it is load-bearing: without
129
+ it a flex child refuses to shrink below its content's height, so a tall chart
130
+ would push the card past the height the grid gave it instead of fitting inside.
131
+
132
+ Dropped into a parent with no definite height, `h-full` resolves to `auto` and
133
+ the card hugs its content instead — so a standalone widget still works, with the
134
+ chart bringing its own height. `bodyClassName` covers the remaining gap: a
135
+ placeholder-only widget outside a sized cell, where neither the grid nor the
136
+ content can give a height.
137
+
138
+ An earlier draft of this component reserved a fixed body height per chart type,
139
+ reverse-engineered from the mockup frames (252 for area, 205 for funnel, …).
140
+ That was wrong: those numbers are what a hug layout happened to produce, not a
141
+ design constraint, and hard-coding them would have fought the grid.
142
+
143
+ ### State
144
+
145
+ `state` renders `ChartState` **instead of** `children`. The header stays.
146
+
147
+ `state="error"` also maps to `Card`'s `hasError`, so the error border and the
148
+ error placeholder come from one prop. A caller can't get one without the other,
149
+ which is what the mockups draw.
150
+
151
+ `ChartState` owns the live-region contract (`role="status"` for loading/empty,
152
+ `role="alert"` for error), so the announcement follows the placeholder rather
153
+ than being re-declared on the widget.
154
+
155
+ ### Localization
156
+
157
+ Every string is the caller's: the header's `title` / `description`, the metric's
158
+ own labels, `stateDescription`, and the label inside `stateAction`. The only
159
+ literals are `ChartState`'s per-state defaults, which `stateDescription` overrides.
160
+
161
+ ## Accessibility
162
+
163
+ ## Roles and structure
164
+
165
+ The widget itself is a plain `div` (a `Card`) and adds no role: it is a grouping
166
+ surface, and the meaning lives in what it holds.
167
+
168
+ A dashboard that wants each widget to be a landmark uses `render`, which the
169
+ widget forwards to `Card`:
170
+
171
+ ```tsx
172
+ <ChartWidget
173
+ render={<section aria-label="Sessions" />}
174
+ header={{ title: 'Sessions' }}
175
+ >
176
+ {chart}
177
+ </ChartWidget>
178
+ ```
179
+
180
+ The accessible name has to come from the caller: the header's `title` is visible
181
+ text inside the region, not a name for it, so a landmark without an
182
+ `aria-label` (or `aria-labelledby` pointing at the title) is an unnamed region.
183
+
184
+ The heading text comes from `CardHeader`'s `title`. It is the widget's visible
185
+ name, so a chart inside should not repeat it in its own accessible name.
186
+
187
+ ## The placeholder is the live region
188
+
189
+ While `state` is set, `ChartState` is the live region — `role="status"`
190
+ (`aria-live="polite"`) for loading and empty, `role="alert"`
191
+ (`aria-live="assertive"`) for error. The widget deliberately does **not** add
192
+ `aria-busy` around it: the placeholder is unmounted and replaced by the plot
193
+ rather than un-busied, and some assistive tech defers announcing a busy region's
194
+ content, which would swallow the loading announcement.
195
+
196
+ A consumer that wants a busy signal sets `aria-busy` on the element it controls
197
+ — the widget accepts it as a passthrough prop.
198
+
199
+ ## Header controls
200
+
201
+ The header's affordances are `Card`'s and carry its labelling: the drag handle,
202
+ switch, rename button and collapse trigger each take a label prop with a
203
+ default, and the `actions` slot is the caller's own control (an icon button
204
+ needs its own `aria-label` — the mockups' ⋯ menu has no visible text).
205
+
206
+ ## Colour is not the only signal
207
+
208
+ `state="error"` changes the card's border colour, but the placeholder also
209
+ carries an icon and a message, so the error is not conveyed by colour alone.