@acronis-platform/ui-react 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/dist/components/ui/accordion-container/accordion-container.js +14 -14
  2. package/dist/components/ui/accordion-container/accordion-container.js.map +1 -1
  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/bar-chart/bar-chart.js +295 -293
  7. package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
  8. package/dist/components/ui/category-bar/category-bar.js +109 -93
  9. package/dist/components/ui/category-bar/category-bar.js.map +1 -1
  10. package/dist/components/ui/chart/chart-palette.js +113 -0
  11. package/dist/components/ui/chart/chart-palette.js.map +1 -0
  12. package/dist/components/ui/chart/chart.js +176 -129
  13. package/dist/components/ui/chart/chart.js.map +1 -1
  14. package/dist/components/ui/chart-state/chart-state-illustrations.js +310 -0
  15. package/dist/components/ui/chart-state/chart-state-illustrations.js.map +1 -0
  16. package/dist/components/ui/chart-state/chart-state.js +48 -34
  17. package/dist/components/ui/chart-state/chart-state.js.map +1 -1
  18. package/dist/components/ui/chart-widget/chart-widget.js +87 -0
  19. package/dist/components/ui/chart-widget/chart-widget.js.map +1 -0
  20. package/dist/components/ui/composed-chart/composed-chart.js +117 -115
  21. package/dist/components/ui/composed-chart/composed-chart.js.map +1 -1
  22. package/dist/components/ui/confidence-cone/confidence-cone.js +166 -158
  23. package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -1
  24. package/dist/components/ui/funnel-chart/funnel-chart.js +162 -161
  25. package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -1
  26. package/dist/components/ui/histogram/histogram.js +94 -92
  27. package/dist/components/ui/histogram/histogram.js.map +1 -1
  28. package/dist/components/ui/input/input.js.map +1 -1
  29. package/dist/components/ui/label/label.js +2 -9
  30. package/dist/components/ui/label/label.js.map +1 -1
  31. package/dist/components/ui/line-chart/line-chart.js +143 -141
  32. package/dist/components/ui/line-chart/line-chart.js.map +1 -1
  33. package/dist/components/ui/metric/metric.js +74 -185
  34. package/dist/components/ui/metric/metric.js.map +1 -1
  35. package/dist/components/ui/page-content/page-content.js.map +1 -1
  36. package/dist/components/ui/pie-chart/pie-chart.js +201 -195
  37. package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
  38. package/dist/components/ui/radar-chart/radar-chart.js +133 -131
  39. package/dist/components/ui/radar-chart/radar-chart.js.map +1 -1
  40. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +297 -265
  41. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -1
  42. package/dist/components/ui/sankey-chart/sankey-chart.js +123 -101
  43. package/dist/components/ui/sankey-chart/sankey-chart.js.map +1 -1
  44. package/dist/components/ui/scatter-chart/scatter-chart.js +76 -74
  45. package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
  46. package/dist/components/ui/section/section.js +157 -31
  47. package/dist/components/ui/section/section.js.map +1 -1
  48. package/dist/components/ui/stepper/stepper.js +24 -24
  49. package/dist/components/ui/stepper/stepper.js.map +1 -1
  50. package/dist/components/ui/stepper-item/stepper-item.js +43 -24
  51. package/dist/components/ui/stepper-item/stepper-item.js.map +1 -1
  52. package/dist/components/ui/treemap/treemap.js +133 -124
  53. package/dist/components/ui/treemap/treemap.js.map +1 -1
  54. package/dist/components/ui/trend-indicator/trend-indicator.js +34 -69
  55. package/dist/components/ui/trend-indicator/trend-indicator.js.map +1 -1
  56. package/dist/components/ui/wizard/wizard.js +59 -0
  57. package/dist/components/ui/wizard/wizard.js.map +1 -0
  58. package/dist/hooks/use-wizard.js +75 -0
  59. package/dist/hooks/use-wizard.js.map +1 -0
  60. package/dist/index.js +502 -497
  61. package/dist/index.js.map +1 -1
  62. package/dist/llms/accordion-container.md +181 -0
  63. package/dist/llms/accordion.md +124 -0
  64. package/dist/llms/alert.md +225 -0
  65. package/dist/llms/app-shell-chat.md +415 -0
  66. package/dist/llms/area-chart.md +355 -0
  67. package/dist/llms/avatar.md +208 -0
  68. package/dist/llms/bar-chart.md +406 -0
  69. package/dist/llms/breadcrumb.md +326 -0
  70. package/dist/llms/button-group.md +298 -0
  71. package/dist/llms/button-icon-input.md +142 -0
  72. package/dist/llms/button-icon-menu.md +209 -0
  73. package/dist/llms/button-icon.md +128 -0
  74. package/dist/llms/button-menu.md +172 -0
  75. package/dist/llms/button.md +185 -0
  76. package/dist/llms/calendar-panel.md +733 -0
  77. package/dist/llms/calendar.md +188 -0
  78. package/dist/llms/card-filter.md +214 -0
  79. package/dist/llms/card-section.md +293 -0
  80. package/dist/llms/card.md +447 -0
  81. package/dist/llms/carousel-dialog.md +156 -0
  82. package/dist/llms/category-bar.md +193 -0
  83. package/dist/llms/chart-state.md +190 -0
  84. package/dist/llms/chart-widget.md +209 -0
  85. package/dist/llms/chart.md +182 -0
  86. package/dist/llms/checkbox.md +257 -0
  87. package/dist/llms/chip.md +195 -0
  88. package/dist/llms/combobox.md +164 -0
  89. package/dist/llms/composed-chart.md +508 -0
  90. package/dist/llms/confidence-cone.md +277 -0
  91. package/dist/llms/data-table.md +474 -0
  92. package/dist/llms/date-range-picker.md +272 -0
  93. package/dist/llms/description-list.md +131 -0
  94. package/dist/llms/dialog-footer-carousel.md +147 -0
  95. package/dist/llms/dialog-footer-default.md +142 -0
  96. package/dist/llms/dialog-root.md +246 -0
  97. package/dist/llms/dialog-welcome.md +223 -0
  98. package/dist/llms/dialog.md +403 -0
  99. package/dist/llms/dropdown-menu.md +193 -0
  100. package/dist/llms/empty-overlay.md +146 -0
  101. package/dist/llms/empty.md +156 -0
  102. package/dist/llms/field.md +185 -0
  103. package/dist/llms/filter-cards.md +157 -0
  104. package/dist/llms/filter-chips.md +323 -0
  105. package/dist/llms/filter-search.md +512 -0
  106. package/dist/llms/form.md +154 -0
  107. package/dist/llms/funnel-chart.md +333 -0
  108. package/dist/llms/grid.md +80 -0
  109. package/dist/llms/histogram.md +190 -0
  110. package/dist/llms/input-date-picker.md +203 -0
  111. package/dist/llms/input-otp.md +173 -0
  112. package/dist/llms/input-password.md +197 -0
  113. package/dist/llms/input-search.md +170 -0
  114. package/dist/llms/input-select.md +278 -0
  115. package/dist/llms/input-text-area.md +271 -0
  116. package/dist/llms/input-text.md +192 -0
  117. package/dist/llms/line-chart.md +369 -0
  118. package/dist/llms/link.md +209 -0
  119. package/dist/llms/loading.md +130 -0
  120. package/dist/llms/meter.md +177 -0
  121. package/dist/llms/metric.md +204 -0
  122. package/dist/llms/number-field.md +122 -0
  123. package/dist/llms/page-content.md +69 -0
  124. package/dist/llms/page-header.md +182 -0
  125. package/dist/llms/pagination.md +133 -0
  126. package/dist/llms/pie-chart.md +307 -0
  127. package/dist/llms/popover.md +252 -0
  128. package/dist/llms/progress-circle.md +126 -0
  129. package/dist/llms/progress.md +111 -0
  130. package/dist/llms/radar-chart.md +316 -0
  131. package/dist/llms/radial-bar-chart.md +319 -0
  132. package/dist/llms/radio.md +255 -0
  133. package/dist/llms/resizable.md +195 -0
  134. package/dist/llms/sankey-chart.md +208 -0
  135. package/dist/llms/scatter-chart.md +239 -0
  136. package/dist/llms/scroll-area.md +183 -0
  137. package/dist/llms/section.md +401 -0
  138. package/dist/llms/select.md +210 -0
  139. package/dist/llms/separator.md +87 -0
  140. package/dist/llms/sheet.md +202 -0
  141. package/dist/llms/sidebar-primary.md +360 -0
  142. package/dist/llms/sidebar-secondary.md +417 -0
  143. package/dist/llms/skeleton.md +81 -0
  144. package/dist/llms/slider.md +97 -0
  145. package/dist/llms/spinner.md +100 -0
  146. package/dist/llms/stack.md +80 -0
  147. package/dist/llms/stepper-item.md +309 -0
  148. package/dist/llms/stepper.md +251 -0
  149. package/dist/llms/switch.md +140 -0
  150. package/dist/llms/table.md +436 -0
  151. package/dist/llms/tabs.md +197 -0
  152. package/dist/llms/tag.md +139 -0
  153. package/dist/llms/timeline.md +361 -0
  154. package/dist/llms/timer.md +259 -0
  155. package/dist/llms/toast.md +241 -0
  156. package/dist/llms/toggle-group.md +121 -0
  157. package/dist/llms/toolbar.md +453 -0
  158. package/dist/llms/tooltip.md +164 -0
  159. package/dist/llms/treemap.md +254 -0
  160. package/dist/llms/trend-indicator.md +132 -0
  161. package/dist/llms/widget-placeholder.md +169 -0
  162. package/dist/llms/wizard.md +412 -0
  163. package/dist/llms.txt +142 -0
  164. package/dist/react.js +502 -497
  165. package/dist/react.js.map +1 -1
  166. package/dist/src/components/ui/area-chart/area-chart.d.ts +9 -4
  167. package/dist/src/components/ui/avatar/avatar.d.ts +1 -1
  168. package/dist/src/components/ui/bar-chart/bar-chart.d.ts +9 -4
  169. package/dist/src/components/ui/category-bar/category-bar.d.ts +10 -4
  170. package/dist/src/components/ui/chart/__stories__/palette-control.d.ts +48 -0
  171. package/dist/src/components/ui/chart/__tests__/chart-palette.test.d.ts +1 -0
  172. package/dist/src/components/ui/chart/chart-palette.d.ts +128 -0
  173. package/dist/src/components/ui/chart/chart.d.ts +66 -15
  174. package/dist/src/components/ui/chart/index.d.ts +4 -2
  175. package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +6 -0
  176. package/dist/src/components/ui/chart-state/chart-state.d.ts +13 -2
  177. package/dist/src/components/ui/chart-state/index.d.ts +1 -0
  178. package/dist/src/components/ui/chart-widget/chart-widget.d.ts +64 -0
  179. package/dist/src/components/ui/chart-widget/index.d.ts +1 -0
  180. package/dist/src/components/ui/composed-chart/composed-chart.d.ts +12 -7
  181. package/dist/src/components/ui/confidence-cone/confidence-cone.d.ts +10 -5
  182. package/dist/src/components/ui/empty-overlay/empty-overlay.d.ts +1 -1
  183. package/dist/src/components/ui/funnel-chart/funnel-chart.d.ts +11 -5
  184. package/dist/src/components/ui/histogram/histogram.d.ts +10 -4
  185. package/dist/src/components/ui/label/index.d.ts +1 -1
  186. package/dist/src/components/ui/label/label.d.ts +1 -5
  187. package/dist/src/components/ui/line-chart/line-chart.d.ts +9 -4
  188. package/dist/src/components/ui/metric/metric.d.ts +13 -14
  189. package/dist/src/components/ui/pie-chart/pie-chart.d.ts +17 -8
  190. package/dist/src/components/ui/radar-chart/radar-chart.d.ts +10 -5
  191. package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +17 -6
  192. package/dist/src/components/ui/sankey-chart/sankey-chart.d.ts +10 -5
  193. package/dist/src/components/ui/scatter-chart/scatter-chart.d.ts +10 -5
  194. package/dist/src/components/ui/section/index.d.ts +1 -1
  195. package/dist/src/components/ui/section/section.d.ts +75 -6
  196. package/dist/src/components/ui/stepper-item/stepper-item.d.ts +6 -5
  197. package/dist/src/components/ui/tag/tag.d.ts +1 -1
  198. package/dist/src/components/ui/treemap/treemap.d.ts +11 -5
  199. package/dist/src/components/ui/trend-indicator/trend-indicator.d.ts +3 -7
  200. package/dist/src/components/ui/wizard/index.d.ts +1 -0
  201. package/dist/src/components/ui/wizard/wizard.d.ts +6 -0
  202. package/dist/src/hooks/index.d.ts +1 -0
  203. package/dist/src/hooks/use-wizard.d.ts +80 -0
  204. package/dist/src/index.d.ts +2 -3
  205. package/dist/ui-react.css +1 -1
  206. package/package.json +9 -3
  207. package/dist/components/ui/app-shell/app-shell.js +0 -75
  208. package/dist/components/ui/app-shell/app-shell.js.map +0 -1
  209. package/dist/components/ui/auth-layout/auth-layout.js +0 -56
  210. package/dist/components/ui/auth-layout/auth-layout.js.map +0 -1
  211. package/dist/components/ui/collapsible/collapsible.js +0 -22
  212. package/dist/components/ui/collapsible/collapsible.js.map +0 -1
  213. package/dist/src/components/ui/app-shell/app-shell.d.ts +0 -8
  214. package/dist/src/components/ui/app-shell/index.d.ts +0 -1
  215. package/dist/src/components/ui/auth-layout/auth-layout.d.ts +0 -6
  216. package/dist/src/components/ui/auth-layout/index.d.ts +0 -1
  217. package/dist/src/components/ui/collapsible/collapsible.d.ts +0 -5
  218. package/dist/src/components/ui/collapsible/index.d.ts +0 -1
  219. package/dist/src/components/ui/label/label.docs.d.ts +0 -10
@@ -0,0 +1,254 @@
1
+ # Treemap
2
+
3
+ > A typed treemap composition over the shared `Chart` primitives. Tiles a flat set of leaves into nested rectangles sized by `dataKey` and colored per `nameKey`, inside a `ChartContainer`, with one- or two-line on-cell labels, a tooltip and an opt-in legend. No axes or grid — and no variant axes; `aspectRatio` is a plain prop. Themed through a custom cell renderer (recharts' default has no token hooks). Ported from the apps/demo `TreemapChartPlayground`.
4
+
5
+ **Status:** draft · **Category:** data-display · **Since:** v0.58.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { Treemap } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <Treemap
17
+ config={config}
18
+ data={data}
19
+ dataKey="size"
20
+ nameKey="name"
21
+ className="h-[320px] w-[520px]"
22
+ />
23
+ ```
24
+
25
+ A typed treemap built on the shared `Chart` primitives. Give it `data`, a
26
+ per-leaf `config`, the size field (`dataKey`), and the label field (`nameKey`);
27
+ it renders a themed recharts `Treemap` — cells sized by value, colored + labelled
28
+ per name, with a tooltip — so you don't hand-compose recharts children.
29
+
30
+ Cell colors come from the `palette` prop — a dataviz palette resolving to the
31
+ `--ui-dataviz-*` tokens, `categorical` by default — not from `config`.
32
+
33
+ > **Design-pending v1.** Ported from the apps/demo `TreemapChartPlayground`.
34
+ > On-cell labels still use `--ui-text-on-status-strong-neutral` — a generic
35
+ > "text on a colored surface" color — over the palette's cell fills, so the
36
+ > palette owes matched per-stop label colors. The chrome is reconciled with Figma
37
+ > later; Code Connect is deferred.
38
+
39
+ ## When to use
40
+
41
+ - Showing part-to-whole across many items where the **relative size** matters
42
+ more than precise values (disk usage, portfolio weight, category volume).
43
+ - A compact, space-filling overview of a flat set of categories.
44
+
45
+ ## When not to use
46
+
47
+ - Precise value comparison — area is hard to judge; a bar chart reads better.
48
+ - Few categories — a bar or pie chart is clearer.
49
+ - Hierarchical drill-down — this v1 renders a **flat** set of leaves only.
50
+
51
+ ## Variants
52
+
53
+ None. Treemap has no CVA variant axes — `aspectRatio` (the tiling ratio) is a
54
+ plain prop, and nesting is out of scope for v1.
55
+
56
+ ## Example
57
+
58
+ ```tsx
59
+ import { Treemap } from '@acronis-platform/ui-react';
60
+ import type { ChartConfig } from '@acronis-platform/ui-react';
61
+
62
+ const data = [
63
+ { name: 'React', size: 2400 },
64
+ { name: 'Vue', size: 1600 },
65
+ { name: 'Angular', size: 1200 },
66
+ { name: 'Svelte', size: 800 },
67
+ ];
68
+
69
+ const config = {
70
+ React: { label: 'React', color: 'var(--ui-background-brand-secondary)' },
71
+ Vue: { label: 'Vue', color: 'var(--ui-background-status-strong-success)' },
72
+ Angular: {
73
+ label: 'Angular',
74
+ color: 'var(--ui-background-status-strong-danger)',
75
+ },
76
+ Svelte: {
77
+ label: 'Svelte',
78
+ color: 'var(--ui-background-status-strong-warning)',
79
+ },
80
+ } satisfies ChartConfig;
81
+
82
+ <Treemap
83
+ config={config}
84
+ data={data}
85
+ dataKey="size"
86
+ nameKey="name"
87
+ className="h-[320px] w-[520px]"
88
+ />;
89
+ ```
90
+
91
+ Cell colors reference existing semantic `--ui-*` tokens, keyed by each leaf's
92
+ `nameKey` value. `--ui-background-status-strong-*` is chromatic in every brand;
93
+ `--ui-background-brand-secondary` is brand-dependent (blue in `default`, neutral
94
+ in some white-label brands), so it is not color-stable across brands until the
95
+ real data-viz palette lands.
96
+
97
+ ## Props
98
+
99
+ | Prop | Type | Default | Required | Description |
100
+ |------|------|---------|----------|-------------|
101
+ | `data` | `ReadonlyArray<Record<string, string | number>>` | — | yes | Row-per-leaf data — each row holds a `nameKey` label and a `dataKey` numeric size. |
102
+ | `config` | `ChartConfig` | — | yes | Leaves take their colour from the container's `palette`; each entry, keyed by the leaf's `nameKey` value, maps that leaf to a `label` and an optional `tone`. Resolved into the `--color-<name>` custom properties the cells fill from. |
103
+ | `dataKey` | `string` | — | yes | The numeric field that sizes each leaf's rectangle. |
104
+ | `nameKey` | `string` | — | yes | The field that keys each leaf (drives the legend, tooltip, and `--color-<name>` lookup). Values should be unique and CSS-safe (they become part of a custom-property name) — the human name shown on the tile is the leaf's config label. |
105
+ | `aspectRatio` | `number` | `1.3333` | no | Width-to-height ratio the tiling targets (default 4 / 3). |
106
+ | `showLabels` | `boolean` | `true` | no | Render each leaf's label inside its cell (when it fits). |
107
+ | `labelAlign` | `'bottom-start' | 'top-start' | 'center'` | `'bottom-start'` | no | Where a cell's label block sits — anchored at the tile's bottom start corner (the design's placement), at its top start corner, or centered in the tile. Named for the start edge, not a physical one: the corner variants mirror under dir="rtl". |
108
+ | `secondaryKeys` | `ReadonlyArray<string>` | — | no | Fields whose values make up a second label line under the leaf's name (e.g. a value and a count). Dropped on cells too short to hold it. |
109
+ | `secondaryFormatter` | `(value: number | string, index?: number) => string` | — | no | Format each secondary value. Receives the field's index in secondaryKeys, so one formatter can cover fields of different kinds. |
110
+ | `secondarySeparator` | `string` | `' · '` | no | Separator drawn between multiple secondary values. |
111
+ | `showTooltip` | `boolean` | `true` | no | Render the hover tooltip. |
112
+ | `showLegend` | `boolean` | `false` | no | Render the legend — one entry per distinct leaf name. Off by default: a treemap labels its own cells. |
113
+ | `legendPos` | `'top' | 'bottom'` | `'bottom'` | no | Which edge the legend sits on. |
114
+ | `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. |
115
+ | `animate` | `boolean` | `false` | no | Enable entrance animation. Off by default so output/baselines are unchanged. |
116
+ | `animationDuration` | `number` | — | no | Animation duration in ms. |
117
+ | `animationBegin` | `number` | — | no | Delay before the animation starts, in ms. |
118
+ | `animationEasing` | `'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'` | — | no | Easing curve. |
119
+
120
+ ## Behavior
121
+
122
+ `Treemap` is a typed [recharts](https://recharts.org) composition over the shared
123
+ `Chart` primitives. It tiles a flat set of leaves into rectangles sized by
124
+ `dataKey` and colored per `nameKey`, inside a `ChartContainer`.
125
+
126
+ ```gherkin
127
+ Scenario: Render cells from data and config
128
+ Given data rows and a config mapping each leaf name to a label and color
129
+ And dataKey "size" and nameKey "name"
130
+ Then one rectangle renders per row, its area proportional to the dataKey value
131
+ And each cell fills from its injected --color-<name> custom property
132
+ ```
133
+
134
+ ```gherkin
135
+ Scenario: On-cell labels
136
+ Given showLabels is true
137
+ Then each cell large enough shows its name at the tile's bottom start corner
138
+ And the name shown is the leaf's config label, falling back to its nameKey value
139
+ But small cells omit the label to avoid overflow
140
+ And when showLabels is false no labels render
141
+ ```
142
+
143
+ ```gherkin
144
+ Scenario: Tiles are separated by the surface, not a stroke
145
+ Given any data
146
+ Then each tile is inset inside its node's rectangle and its corners are rounded
147
+ And the gap between neighbouring tiles is the chart's surface showing through
148
+ ```
149
+
150
+ ```gherkin
151
+ Scenario: Label alignment
152
+ Given labelAlign is "top-start" or "center"
153
+ Then the label block is anchored at the tile's top start corner, or centered in it
154
+ And what the alignment aligns is the block, not its first line
155
+ And a corner-anchored block hugs the tile's start edge, so it mirrors under dir="rtl"
156
+ And every alignment leaves each line as wide as the tile, so it can still ellipsize
157
+ ```
158
+
159
+ ```gherkin
160
+ Scenario: A second label line
161
+ Given secondaryKeys naming other fields on the row
162
+ Then a second line renders under the leaf's name, one entry per field
163
+ And the entries are joined by secondarySeparator
164
+ And each entry is formatted by secondaryFormatter, which receives the field's index
165
+ But a field that is missing or empty on a row is skipped rather than left as a gap
166
+ And a field the formatter returns empty for is skipped the same way
167
+ ```
168
+
169
+ ```gherkin
170
+ Scenario: Labels degrade with the tile
171
+ Given a cell too short to hold two lines
172
+ Then only the leaf's name renders
173
+ And the height a line needs is its rendered line box, not its font size, so a
174
+ label is never drawn into a tile that would clip it
175
+ And given a line wider than its cell
176
+ Then that line is truncated with an ellipsis
177
+ And given a cell below the size a label needs at all
178
+ Then the cell renders no label
179
+ ```
180
+
181
+ ```gherkin
182
+ Scenario: Legend is opt-in
183
+ Given showLegend is true
184
+ Then one legend entry renders per distinct leaf name, labelled and colored from config
185
+ And legendPos places it on the top or the bottom edge
186
+ And it is laid out as a row beside the tiled surface, which gives up that height,
187
+ so the legend never covers the tiles
188
+ And it wraps onto further rows when its entries outgrow the chart's width
189
+ But when showLegend is false no legend renders and no row is added
190
+ ```
191
+
192
+ ```gherkin
193
+ Scenario: Aspect ratio
194
+ Given an aspectRatio
195
+ Then the tiling targets that width-to-height ratio per cell
196
+ ```
197
+
198
+ ```gherkin
199
+ Scenario: Tooltip on hover
200
+ Given showTooltip is true
201
+ When the user hovers a cell
202
+ Then a card shows that leaf's name and value
203
+ ```
204
+
205
+ ```gherkin
206
+ Scenario: Empty data
207
+ Given data is an empty array
208
+ Then the chart renders no cells and does not throw
209
+ ```
210
+
211
+ ```gherkin
212
+ Scenario: Entrance animation is opt-in
213
+ Given animate is unset
214
+ Then the series render at their final geometry with no entrance animation
215
+ And the committed visual baselines are unaffected
216
+ ```
217
+
218
+ ```gherkin
219
+ Scenario: Reduced motion
220
+ Given animate is true
221
+ And a user with prefers-reduced-motion
222
+ Then the entrance animation does not play and the series render at their final geometry
223
+ And the same applies when rendering on the server
224
+ ```
225
+
226
+ ## Accessibility
227
+
228
+ - recharts' `accessibilityLayer` is **on by default** (recharts v3), giving the
229
+ chart keyboard focus and an accessible description of the plotted cells.
230
+ - A treemap is inherently visual (area is a weak quantitative encoding, small
231
+ cells lose their labels). **Pair it with a text alternative** — a caption, a
232
+ summary, or an adjacent data table carrying the same numbers — and give the
233
+ chart an accessible name (`aria-label` / `aria-labelledby` referencing a visible
234
+ heading). The wrapper forwards native `div` attributes, so `aria-*` pass
235
+ through.
236
+ - Do **not** rely on color alone to distinguish cells. The on-cell labels name
237
+ the larger cells; keep `showTooltip` on so small (unlabelled) cells are still
238
+ identifiable on hover.
239
+ - On-cell labels use the `--ui-text-on-status-strong-neutral` token ("text on a
240
+ strong colored surface", constant white in both themes) to read over the
241
+ saturated series colors. This is legible on the chromatic brand/status tokens
242
+ used here but is **not** guaranteed for arbitrary caller colors — pick cell
243
+ colors dark enough for the light label text (matched per-cell label colors are a
244
+ design-pending item for the `--ui-chart-*` palette). The tooltip chrome and cell
245
+ separators resolve to semantic `--ui-*` tokens that meet contrast in both themes.
246
+ - Watch recharts issue [#4809](https://github.com/recharts/recharts/issues/4809)
247
+ on the a11y layer for heavily-customized charts.
248
+
249
+ ## Contrast
250
+
251
+ Tooltip chrome and the surface-colored cell separators meet contrast in both
252
+ themes via the semantic tokens. Cell fills come from `config` and are the
253
+ caller's responsibility — keep them dark enough for the white on-cell labels, and
254
+ adjacent cells distinguishable from each other.
@@ -0,0 +1,132 @@
1
+ # TrendIndicator
2
+
3
+ > A small presentational trend/delta indicator — a direction glyph, an already-formatted change value, and an optional comparison label. It separates `direction` (what happened mathematically) from `sentiment` (whether that is good or bad), so the kit never assumes up = good. Inline or compact badge, two sizes. Design-pending v1 (no Figma node yet).
4
+
5
+ **Status:** draft · **Category:** data-display
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { TrendIndicator } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <TrendIndicator
17
+ direction="up"
18
+ sentiment="positive"
19
+ value="12%"
20
+ ariaLabel="Revenue up 12% vs previous quarter"
21
+ />
22
+ ```
23
+
24
+ A small presentational primitive that shows **how a metric changed** versus a
25
+ reference — a direction glyph, an already-formatted change value, and an optional
26
+ comparison label.
27
+
28
+ ## When to use
29
+
30
+ - Next to a [Metric](../metric/README.md) value ("↑ 12% vs last quarter").
31
+ - In a Data Table cell to show per-row movement.
32
+ - In compact headers/summaries as a tinted `badge`.
33
+
34
+ ## When not to use
35
+
36
+ - To plot a trend over time — use a Line / Area / Composed chart or
37
+ ConfidenceCone.
38
+ - To compute a delta — the consumer computes `direction`, `sentiment`, and the
39
+ formatted `value`; this component only renders them.
40
+
41
+ ## Key idea: direction ≠ sentiment
42
+
43
+ `direction` is the maths (up / down / flat); `sentiment` is whether that's good
44
+ (positive / negative / neutral). The kit can't assume up = good — revenue ↑ is
45
+ positive, threats ↑ is negative, MTTR ↓ is positive. Always pass both.
46
+
47
+ ```tsx
48
+ <TrendIndicator direction="up" sentiment="positive" value="8%" comparisonLabel="revenue QoQ" />
49
+ <TrendIndicator direction="up" sentiment="negative" value="35%" comparisonLabel="threats" />
50
+ <TrendIndicator direction="down" sentiment="positive" value="1.4 h" comparisonLabel="MTTR" />
51
+ ```
52
+
53
+ ## Notes
54
+
55
+ - **Design-pending v1** — no Figma node yet; sentiment colors reuse the semantic
56
+ status tokens.
57
+ - Value may be numeric or qualitative (`"Improving"`, `"4.2 h → 2.8 h"`).
58
+ - Pass `ariaLabel` for a full accessible sentence; the glyph is decorative.
59
+
60
+ ## Props
61
+
62
+ | Prop | Type | Default | Required | Description |
63
+ |------|------|---------|----------|-------------|
64
+ | `direction` | `'up' | 'down' | 'flat'` | — | yes | What changed mathematically — selects the direction glyph (up/down trend arrows, or a horizontal arrow for flat). |
65
+ | `sentiment` | `'positive' | 'negative' | 'neutral'` | `neutral` | no | Whether the change is good, bad, or neutral — drives the color. Separate from `direction`, because up/down is not inherently good/bad. |
66
+ | `value` | `ReactNode` | — | no | Already-formatted change to display — numeric (`12%`, `3.5 h`) or qualitative (`Improving`, `4.2 h → 2.8 h`). Never computed by the kit. |
67
+ | `tooltip` | `ReactNode` | — | no | Contextual hint shown on hover/focus (Base UI Tooltip). |
68
+ | `showIcon` | `boolean` | `true` | no | Show the leading direction glyph (color alone isn't enough). |
69
+ | `ariaLabel` | `string` | — | no | Full accessible sentence describing the trend, applied via `role="img"`. Without it, assistive tech reads the visible value text and the glyph stays decorative. |
70
+
71
+ ## Behavior
72
+
73
+ ### Renders a direction + change
74
+
75
+ - **Given** `direction="up"`, `value="12%"`
76
+ **When** rendered
77
+ **Then** an up trend arrow precedes `12%`.
78
+
79
+ - **Given** `direction="flat"`
80
+ **Then** a horizontal arrow glyph is shown (the "no meaningful change" mark).
81
+
82
+ ### Direction is not sentiment
83
+
84
+ - **Given** `direction="up"`, `sentiment="negative"` (e.g. threats rose)
85
+ **Then** the arrow points up but the color is the negative/danger family — the
86
+ kit does **not** infer good/bad from the arrow.
87
+
88
+ - **Given** `direction="down"`, `sentiment="positive"` (e.g. MTTR fell)
89
+ **Then** the arrow points down but the color is the positive/success family.
90
+
91
+ ### Value is caller-formatted
92
+
93
+ - **Given** `value="4.2 h → 2.8 h"` (or `"Improving"`)
94
+ **Then** the text is rendered verbatim — the kit never diffs numbers, rounds,
95
+ adds a sign, or converts units.
96
+
97
+ ### Icon visibility
98
+
99
+ - **Given** `showIcon={false}`
100
+ **Then** no glyph renders (use sparingly — color alone should not carry
101
+ meaning).
102
+
103
+ ### Tooltip
104
+
105
+ - **Given** a `tooltip`
106
+ **When** the indicator is hovered or focused
107
+ **Then** the hint appears; the trigger is keyboard-focusable.
108
+
109
+ ## Accessibility
110
+
111
+ - The direction glyph is **decorative** (`aria-hidden`) — meaning must exist in
112
+ text, never in the icon or color alone.
113
+ - **Color is not the only differentiator.** The direction glyph and the visible
114
+ value carry the meaning; sentiment color is reinforcement.
115
+ - **`ariaLabel`** sets a full, caller-authored sentence as the element's label
116
+ via `role="img"` (e.g. "Revenue increased 12% compared with the previous
117
+ quarter, a positive trend"). The kit can't build a correct localized sentence
118
+ from `direction` + `value` alone, so it never synthesizes one.
119
+ - **Without `ariaLabel`**, assistive tech reads the visible `value` and
120
+ `comparisonLabel` (both caller-supplied). Pass `ariaLabel` when the direction
121
+ itself must be announced, since the glyph is hidden.
122
+ - A `flat` direction should be paired with a value/label like "Stable" or "No
123
+ change" — a lone horizontal arrow is ambiguous to assistive tech.
124
+ - When a `tooltip` is set the trigger is focusable (`tabIndex={0}`), so the hint
125
+ is reachable by keyboard, not hover only. The tooltip must not be the sole
126
+ source of essential information.
127
+
128
+ ## Contrast
129
+
130
+ Sentiment text/glyph colors resolve `--ui-text-on-status-*` tokens that meet text
131
+ contrast on the base surface in both themes; the badge tint pairs each with its
132
+ `--ui-background-status-*` background.
@@ -0,0 +1,169 @@
1
+ # WidgetPlaceholder
2
+
3
+ > An empty-state placeholder for a dashboard widget — a bordered card with a header (icon + title), a centered illustration / message / action, and an optional footer. Ported from the legacy shadcn-uikit `WidgetPlaceholder`; composable parts, optionally interactive.
4
+
5
+ **Status:** draft · **Category:** data-display · **Since:** v0.41.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { WidgetPlaceholder } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <WidgetPlaceholder>
17
+ <WidgetPlaceholderHeader>
18
+ <WidgetPlaceholderIcon><ChartBarVerticalIcon /></WidgetPlaceholderIcon>
19
+ <WidgetPlaceholderTitle>Backup statistics</WidgetPlaceholderTitle>
20
+ </WidgetPlaceholderHeader>
21
+ <WidgetPlaceholderContent>
22
+ <WidgetPlaceholderImage><ChartBarVerticalIcon /></WidgetPlaceholderImage>
23
+ <WidgetPlaceholderText>No data available yet</WidgetPlaceholderText>
24
+ <WidgetPlaceholderAction>Set up backup plan</WidgetPlaceholderAction>
25
+ </WidgetPlaceholderContent>
26
+ </WidgetPlaceholder>
27
+ ```
28
+
29
+ An empty-state placeholder for a dashboard widget — a bordered card with a
30
+ header, a centered illustration / message / action, and an optional footer.
31
+
32
+ > **Status: draft (design-pending v1).** Ported from the legacy
33
+ > `@acronis-platform/shadcn-uikit` `WidgetPlaceholder`. No
34
+ > `--ui-widget-placeholder-*` tier; it maps onto semantic tokens (the muted
35
+ > illustration tone is a stand-in for the legacy soft-brand tint). Reconcile with
36
+ > `/figma-component WidgetPlaceholder <url> --update` once a mockup lands.
37
+
38
+ ## When to use
39
+
40
+ - A dashboard widget that has no data yet (and a way to get started).
41
+ - A widget tile that is configurable / clickable as a whole (`interactive`).
42
+
43
+ ## When not to use
44
+
45
+ - A full-page or in-table empty state — use `Empty`.
46
+ - A loading state — use `Loading` (busy) or `Progress` (determinate).
47
+
48
+ ## Parts
49
+
50
+ | Part | Element | Purpose |
51
+ | -------------------------- | ------- | -------------------------------------------- |
52
+ | `WidgetPlaceholder` | div | Root card; `interactive` makes it focusable. |
53
+ | `WidgetPlaceholderHeader` | div | Icon + title row. |
54
+ | `WidgetPlaceholderIcon` | div | Leading 16px header glyph. |
55
+ | `WidgetPlaceholderTitle` | div | Widget name (truncates). |
56
+ | `WidgetPlaceholderContent` | div | Centered column for the body. |
57
+ | `WidgetPlaceholderImage` | div | Large 72px decorative illustration. |
58
+ | `WidgetPlaceholderText` | div | Short supporting message. |
59
+ | `WidgetPlaceholderAction` | div | Inline call-to-action. |
60
+ | `WidgetPlaceholderFooter` | div | Optional metadata line. |
61
+
62
+ ## Example
63
+
64
+ ```tsx
65
+ import {
66
+ WidgetPlaceholder,
67
+ WidgetPlaceholderHeader,
68
+ WidgetPlaceholderIcon,
69
+ WidgetPlaceholderTitle,
70
+ WidgetPlaceholderContent,
71
+ WidgetPlaceholderImage,
72
+ WidgetPlaceholderText,
73
+ WidgetPlaceholderAction,
74
+ } from '@acronis-platform/ui-react';
75
+ import { ChartBarVerticalIcon } from '@acronis-platform/icons-react/stroke-mono';
76
+
77
+ <WidgetPlaceholder className="h-[220px] w-[320px]">
78
+ <WidgetPlaceholderHeader>
79
+ <WidgetPlaceholderIcon>
80
+ <ChartBarVerticalIcon />
81
+ </WidgetPlaceholderIcon>
82
+ <WidgetPlaceholderTitle>Backup statistics</WidgetPlaceholderTitle>
83
+ </WidgetPlaceholderHeader>
84
+ <WidgetPlaceholderContent>
85
+ <WidgetPlaceholderImage>
86
+ <ChartBarVerticalIcon />
87
+ </WidgetPlaceholderImage>
88
+ <WidgetPlaceholderText>No data available yet</WidgetPlaceholderText>
89
+ <WidgetPlaceholderAction>Set up backup plan</WidgetPlaceholderAction>
90
+ </WidgetPlaceholderContent>
91
+ </WidgetPlaceholder>;
92
+ ```
93
+
94
+ ## Props
95
+
96
+ | Prop | Type | Default | Required | Description |
97
+ |------|------|---------|----------|-------------|
98
+ | `interactive` | `boolean` | `false` | no | Make the whole widget focusable (`tabindex=0`) and clickable — adds hover/active surface tints and a focus ring. Wire the behavior with `onClick`. |
99
+
100
+ ## Content slots
101
+
102
+ | Slot | Description |
103
+ |------|-------------|
104
+ | `children` | The composed parts — WidgetPlaceholderHeader / Title / Icon, Content / Image / Text / Action, and an optional Footer. |
105
+
106
+ ## Behavior
107
+
108
+ WidgetPlaceholder is a composable empty-state for a dashboard widget. It has no
109
+ internal state; the only behavioral switch is the `interactive` prop on the root.
110
+
111
+ ```gherkin
112
+ Scenario: Static placeholder
113
+ Given a WidgetPlaceholder without interactive
114
+ Then the root is not focusable (no tabindex) and shows no hover affordance
115
+ ```
116
+
117
+ ```gherkin
118
+ Scenario: Interactive placeholder
119
+ Given a WidgetPlaceholder with interactive
120
+ Then the root is focusable (tabindex=0)
121
+ And it shows a hover surface tint on pointer-over
122
+ And a pressed surface tint while active
123
+ And a focus ring when focused via keyboard
124
+ And clicking it invokes the supplied onClick
125
+ ```
126
+
127
+ ```gherkin
128
+ Scenario: Truncating title
129
+ Given a WidgetPlaceholderTitle whose text overflows the header
130
+ Then the title truncates with an ellipsis rather than wrapping
131
+ ```
132
+
133
+ ```gherkin
134
+ Scenario: Optional footer
135
+ Given a WidgetPlaceholder with a WidgetPlaceholderFooter
136
+ Then a muted metadata line renders below the content
137
+ ```
138
+
139
+ ### Not `ChartState`
140
+
141
+ `ChartState` is the placeholder that goes _inside_ a chart's slot: it fills the
142
+ slot, renders `loading` / `empty` / `error`, and for `empty` draws the silhouette
143
+ the design defines for that chart type. `WidgetPlaceholder` is the composable
144
+ skeleton for a whole tile — its own header, footer and action parts, and an
145
+ `interactive` affordance — for a widget that isn't a chart, or one still being
146
+ set up.
147
+
148
+ Reach for `ChartState` when a chart has no data; for this when the widget itself
149
+ isn't there yet.
150
+
151
+ ## Accessibility
152
+
153
+ - The static placeholder is presentational — its parts are plain elements with no
154
+ imposed roles, so its text content is read in document order.
155
+ - When `interactive`, the root is focusable (`tabindex=0`) and paints a visible
156
+ focus ring (`--ui-focus-primary`). The whole-card click is a convenience target;
157
+ for a primary action prefer the `WidgetPlaceholderAction` element (or a real
158
+ `Button`/`Link` inside it) so the action has an accessible name and keyboard
159
+ semantics. If you make the whole card the only control, give it an
160
+ `aria-label`/`role="button"` and handle Enter/Space.
161
+ - The illustration is decorative; keep meaning in `WidgetPlaceholderText`, not the
162
+ icon alone.
163
+
164
+ ## Contrast
165
+
166
+ Text uses `--ui-text-on-surface-primary` and the muted image/footer use
167
+ `--ui-text-on-surface-secondary`, both over `--ui-background-surface-primary` —
168
+ meeting text contrast in light and dark. The icon/action use the brand action
169
+ color (`--ui-background-brand-secondary`), which meets contrast against the card.