@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,259 @@
1
+ # Timer
2
+
3
+ > An elapsed-time readout paired with a hairline-separated cluster of icon-only actions, in a single bordered 32px box. Displays a caller-formatted value; it does not tick.
4
+
5
+ **Status:** ready · **Category:** data-display · **Since:** v0.63.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { Timer, ButtonGroupItem } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ const [elapsed, setElapsed] = useState(0);
17
+
18
+ <Timer value={formatDuration(elapsed)} actionsLabel="Time tracking">
19
+ <ButtonGroupItem aria-label="Pause" onClick={pause}>
20
+ <CirclePauseIcon size={16} />
21
+ </ButtonGroupItem>
22
+ <ButtonGroupItem aria-label="Rename" onClick={rename}>
23
+ <PencilIcon size={16} />
24
+ </ButtonGroupItem>
25
+ <ButtonGroupItem aria-label="Add entry" onClick={addEntry}>
26
+ <PlusIcon size={16} />
27
+ </ButtonGroupItem>
28
+ </Timer>
29
+
30
+ {/* Read-only: no actions, so no toolbar and no divider */}
31
+ <Timer value="00:12:30" />
32
+ ```
33
+
34
+ An elapsed-time readout paired with a hairline-separated cluster of icon-only
35
+ actions, in a single bordered 32px box. The readout displays a value the caller
36
+ formats; the actions are ButtonGroup items the caller supplies.
37
+
38
+ ## When to use
39
+
40
+ - A **time-tracking control** in a toolbar, table row, or card header: show the
41
+ elapsed time on a task and offer the two or three actions that operate on it
42
+ (pause, rename, add an entry).
43
+ - A **stopwatch or session counter** the user can act on — pause, resume, stop —
44
+ where the count and its controls should read as one compact unit.
45
+ - A **read-only duration** that belongs in the same visual family as the
46
+ interactive ones. Omit the actions and the box becomes just the readout.
47
+
48
+ ## When not to use
49
+
50
+ - **A duration inside prose or a data table cell.** A bordered 32px box next to
51
+ plain text is chrome for nothing; render the formatted string directly.
52
+ - **A countdown that must announce itself.** `role="timer"` is deliberately
53
+ silent (`aria-live="off"`) so it doesn't flood a screen reader once a second.
54
+ If a threshold matters, announce that event from a separate status region.
55
+ - **Actions unrelated to the time shown.** The shared box claims the readout and
56
+ the actions are one control; unrelated actions belong outside it.
57
+ - **Labelled actions.** The cluster is icon-only by design. For text actions
58
+ next to a duration, compose a `Toolbar` with plain `Button`s.
59
+ - **As a clock.** The component holds no timer of its own (see Notes).
60
+
61
+ ## Example (React — implemented)
62
+
63
+ ```tsx
64
+ import { Timer, ButtonGroupItem } from '@acronis-platform/ui-react';
65
+ import {
66
+ CirclePauseIcon,
67
+ PencilIcon,
68
+ PlusIcon,
69
+ } from '@acronis-platform/icons-react/stroke-mono';
70
+
71
+ <Timer value={formatDuration(elapsed)} actionsLabel="Time tracking">
72
+ <ButtonGroupItem aria-label="Pause" onClick={pause}>
73
+ <CirclePauseIcon size={16} />
74
+ </ButtonGroupItem>
75
+ <ButtonGroupItem aria-label="Rename" onClick={rename}>
76
+ <PencilIcon size={16} />
77
+ </ButtonGroupItem>
78
+ <ButtonGroupItem aria-label="Add entry" onClick={addEntry}>
79
+ <PlusIcon size={16} />
80
+ </ButtonGroupItem>
81
+ </Timer>;
82
+ ```
83
+
84
+ Read-only — no actions, so no toolbar and no divider:
85
+
86
+ ```tsx
87
+ <Timer value="00:12:30" />
88
+ ```
89
+
90
+ Vue and Web Component implementations are planned and will target the same
91
+ contract — see `api.yaml` `adapters`.
92
+
93
+ ## Parts
94
+
95
+ | Part | Element | Notes |
96
+ | --------- | ---------- | --------------------------------------------------------------------------------------------------- |
97
+ | container | `div` | The bordered box. No role — it owns the border, radius, background, height, and the clipping. |
98
+ | `readout` | `div` | `role="timer"`. The formatted value, in tabular figures. |
99
+ | `divider` | _(border)_ | The readout's inline-end border, dropped when there are no actions. Not an element, so not in a11y. |
100
+ | `actions` | `div` | `role="toolbar"` — a `ButtonGroup` in its `inlined` style, holding the caller's `ButtonGroupItem`s. |
101
+
102
+ ## Notes
103
+
104
+ - **It does not tick.** The component holds no clock and starts no interval: it
105
+ renders whatever `value` it is handed. The caller owns the interval, the
106
+ format, and the state the actions mutate — which is what lets the same
107
+ component serve a stopwatch, a countdown, and a static duration.
108
+ - **Formatting is yours too**, including whether hours appear and how the locale
109
+ renders the separators. The readout only guarantees that whatever you pass
110
+ keeps its width as digits change, via tabular figures.
111
+ - **Actions are ButtonGroup items**, imported from `ButtonGroup` rather than
112
+ re-exported under a Timer name. The Timer renders the group itself so it can
113
+ always pick the `inlined` style — its own box already draws the border and
114
+ radius an `outlined` group would duplicate — but everything about an item
115
+ (separators, one Tab stop, arrow-key roving, hover/active/focus/disabled) is
116
+ ButtonGroup's contract, unchanged.
117
+ - **Name the actions.** Each icon-only item needs its own `aria-label`, and a
118
+ play/pause control's name must track its current function rather than its
119
+ icon. The toolbar's own name comes from `actionsLabel`, whose default is an
120
+ untranslated English string. See `accessibility.md`.
121
+ - **The box hugs its content.** Figma's 224px frame is the measured width of the
122
+ sample value plus three actions, not a fixed size — a longer value simply
123
+ makes the box wider.
124
+ - **RTL-safe.** The divider is an inline-end border, so the whole layout mirrors
125
+ under `dir="rtl"` with no extra work.
126
+ - **No variants, no states.** Figma models this as a single component, not a
127
+ component set: there is no size or style axis, and every interaction state
128
+ lives on the individual actions.
129
+
130
+ ## Props
131
+
132
+ | Prop | Type | Default | Required | Description |
133
+ |------|------|---------|----------|-------------|
134
+ | `value` | `string | Node` | — | yes | The formatted time to display, e.g. `12:01:45`. The component holds no clock: it neither ticks nor formats, so the caller owns the interval and the format (including whether hours are shown and how a locale renders the separators). Rendered with tabular figures so the readout keeps its width as the digits change. |
135
+ | `actions-label` | `string` | `'Timer actions'` | no | Accessible name for the action cluster's toolbar. Exists because the caller cannot reach that element directly — the Timer renders it — and the default is an unlocalized English string, so translate it. |
136
+
137
+ ## Content slots
138
+
139
+ | Slot | Description |
140
+ |------|-------------|
141
+ | `default` | The actions, as ButtonGroup items in visual order. Each is icon-only and needs its own accessible name. Omit them for a read-only readout: the toolbar is then not rendered at all and the divider goes with it. |
142
+
143
+ ## Behavior
144
+
145
+ ### The readout
146
+
147
+ **Given** a timer
148
+ **When** it renders
149
+ **Then** the `value` it was given is displayed verbatim inside the readout, with
150
+ tabular figures so every digit occupies the same width.
151
+
152
+ **Given** a running timer whose `value` changes from `12:01:45` to `12:01:46`
153
+ **When** the new value renders
154
+ **Then** the readout's width is unchanged and nothing to its inline-end shifts,
155
+ because the figures are tabular and the box hugs its content rather than
156
+ re-measuring per frame.
157
+
158
+ **Given** a timer
159
+ **When** time passes without the caller supplying a new `value`
160
+ **Then** the readout does not change. The component owns no clock and starts no
161
+ interval — ticking, formatting, pausing, and resetting all belong to the caller,
162
+ who also owns whatever the actions operate on.
163
+
164
+ **Given** a `value` longer than the design's sample (e.g. `128:59:59`)
165
+ **When** it renders
166
+ **Then** the box grows to fit it. The readout hugs its content plus the
167
+ horizontal padding; the Figma frame's 224px width is the measured result of the
168
+ sample content, not a fixed width.
169
+
170
+ ### The action cluster
171
+
172
+ **Given** a timer with one or more actions
173
+ **When** it renders
174
+ **Then** they appear inline-end of the readout as a single toolbar, in the order
175
+ given, separated from the readout by a hairline divider and from each other by
176
+ the ButtonGroup's own separators.
177
+
178
+ **Given** a timer with no actions
179
+ **When** it renders
180
+ **Then** no toolbar element is rendered at all, and the divider is dropped too —
181
+ the readout is the last child, and the divider is its own `:last-child`-reset
182
+ inline-end border.
183
+
184
+ **Given** an action
185
+ **When** it is hovered, activated, focused by keyboard, or disabled
186
+ **Then** it behaves exactly as a ButtonGroup item does; the timer adds nothing
187
+ and overrides nothing. See the ButtonGroup spec.
188
+
189
+ **Given** the action cluster
190
+ **When** it renders
191
+ **Then** it is always the `inlined` ButtonGroup style — never `outlined`. The
192
+ timer's own box already draws the border and radius an outlined group would
193
+ duplicate, and this is how the design instantiates it. It is not configurable.
194
+
195
+ ### Container
196
+
197
+ **Given** a timer whose trailing action is hovered or focused
198
+ **When** the fill or the inset focus ring paints
199
+ **Then** it is clipped to the container's radius, so no square corner escapes
200
+ the rounded box.
201
+
202
+ **Given** a timer under `dir="rtl"`
203
+ **When** it renders
204
+ **Then** the readout sits at the inline start and the actions at the inline end,
205
+ mirrored — the divider is an inline-end border, not a right-hand one. The value
206
+ itself is not reordered; formatting the time for the locale is the caller's job.
207
+
208
+ ## Accessibility
209
+
210
+ - **Roles:** the readout is `role="timer"` — a live-region role for a numeric
211
+ counter of elapsed or remaining time. The action cluster is the ButtonGroup's
212
+ `role="toolbar"`. The container itself carries no role; it is a box, not a
213
+ widget. The divider is a CSS border rather than an element, so it correctly
214
+ contributes nothing to the accessibility tree.
215
+
216
+ - **Live-region politeness:** `role="timer"` is implicitly `aria-live="off"`,
217
+ which is the right default: a per-second announcement would flood a screen
218
+ reader and drown out everything else on the page. Do **not** raise it to
219
+ `polite` for a ticking clock. If a milestone matters (a limit reached, a
220
+ session about to expire), announce that event from a separate status region
221
+ rather than making the timer itself chatty.
222
+
223
+ - **Accessible name — readout:** optional. The value speaks for itself in most
224
+ contexts; where several timers appear together, name each one by putting
225
+ `aria-labelledby` on the container's own visible label, or pass `aria-label`
226
+ through to the container.
227
+
228
+ - **Accessible name — action cluster:** supplied by `actions-label`, which
229
+ defaults to the English string `Timer actions`. Translate it. The property
230
+ exists precisely because the caller cannot reach the toolbar element directly.
231
+
232
+ - **Accessible name — actions:** each action is icon-only, so it has no text to
233
+ name it. Every one MUST be given an `aria-label` (or `aria-labelledby`);
234
+ decorative glyphs inside stay `aria-hidden`. A play/pause action's name must
235
+ track its current function ("Pause" while running, "Resume" while paused), not
236
+ its icon.
237
+
238
+ - **Keyboard:** the timer adds no key handling of its own. The action cluster is
239
+ a **single Tab stop** with a roving tabindex — Tab enters it and the next Tab
240
+ leaves; the arrow keys move between actions; Enter and Space activate. See the
241
+ ButtonGroup spec for the full pattern, including how a disabled action stays
242
+ focusable.
243
+
244
+ - **Focus visibility:** an action's focus ring is drawn inset, so the
245
+ container's clipping cannot shave it off at the rounded corners.
246
+
247
+ - **Contrast:** the readout uses `--ui-timer-value-color` on
248
+ `--ui-timer-container-color` and the action glyphs use
249
+ `--ui-glyph-on-surface-primary`; both pairs meet WCAG AA in the light and dark
250
+ themes of every shipped brand. The divider and the container border are
251
+ decorative hairlines and are not held to a text-contrast ratio.
252
+
253
+ - **Text sizing:** the readout is 18px with a 24px line-height inside a 32px
254
+ box. It scales with the user's font size; the container's height token is the
255
+ design's fixed 32px, so a large browser zoom scales the whole box rather than
256
+ clipping the digits.
257
+
258
+ - **Motion:** none. The value updates are plain text changes with no transition,
259
+ so nothing here is affected by `prefers-reduced-motion`.
@@ -0,0 +1,241 @@
1
+ # Toast
2
+
3
+ > A transient notification in a corner stack: a neutral card carrying its severity through a status-colored border and a status line down the leading edge, with a fixed status icon per severity, an optional description, and optional action buttons. Triggered imperatively via the `toast(...)` API and rendered by a single `<Toaster>` region.
4
+
5
+ **Status:** stable · **Category:** feedback · **Since:** v0.41.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { Toaster, toast } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ // once, near the app root:
17
+ <Toaster />
18
+
19
+ // anywhere:
20
+ toast.success('Profile saved', { description: 'Your changes were saved.' });
21
+ toast.danger('Delete failed', {
22
+ description: 'Please try again or contact support.',
23
+ actions: [{ label: 'Retry', onClick: retry }],
24
+ });
25
+ ```
26
+
27
+ A transient notification shown in a corner stack — triggered imperatively with
28
+ the `toast(...)` API and rendered by a single `<Toaster>` region.
29
+
30
+ Visually it is `Alert` plus a drop shadow: a neutral card whose severity is
31
+ carried by a 1px status-colored border, a 6px status line down the leading edge,
32
+ and a fixed status icon. Every value comes from the `--ui-toast-*` tier, mirroring
33
+ the Figma "Toast" component set (node `7421:126262`). Built on the Base UI toast
34
+ manager — no Sonner dependency.
35
+
36
+ ## When to use
37
+
38
+ - Brief, low-priority feedback for an action (saved, copied, undo).
39
+ - Background operation status (loading → success/danger via `toast.promise`).
40
+
41
+ ## When not to use
42
+
43
+ - For information the user must act on or must not miss — use a `Dialog` or an
44
+ inline message; toasts auto-dismiss.
45
+ - For persistent, in-page status — use `Alert`, which is the same card without the
46
+ shadow and without the auto-dismiss.
47
+
48
+ ## Setup
49
+
50
+ Render one `<Toaster>` near the app root, then call `toast` anywhere. **Exactly
51
+ one** region per page: the queue is a module-level singleton scoped to the package
52
+ instance, so a micro-frontend host must share a single `@acronis-platform/ui-react`
53
+ instance. With two copies loaded, `toast(...)` from one is invisible to the other's
54
+ region (queued, never rendered) and two regions overlap in the same corner.
55
+
56
+ ```tsx
57
+ import { Toaster, toast } from '@acronis-platform/ui-react';
58
+
59
+ // app root
60
+ <Toaster />;
61
+
62
+ // anywhere
63
+ toast.success('Profile saved', { description: 'Your changes were saved.' });
64
+ toast.danger('Delete failed', {
65
+ description: 'Please try again or contact support.',
66
+ actions: [
67
+ { label: 'Retry', onClick: retry },
68
+ { label: 'Get help', onClick: openSupport },
69
+ ],
70
+ });
71
+
72
+ // tie to a promise
73
+ toast.promise(save(), {
74
+ loading: 'Saving…',
75
+ success: 'Saved',
76
+ error: 'Could not save',
77
+ });
78
+ ```
79
+
80
+ The severity comes from the method: `toast.info` / `success` / `warning` /
81
+ `critical` / `danger` match the Figma variants, a bare `toast(...)` is `info`, and
82
+ `toast.loading` shows a persistent spinner (no Figma variant of its own).
83
+
84
+ ## Parts
85
+
86
+ | Part | Element | Purpose |
87
+ | ------------------- | ---------- | ------------------------------------------- |
88
+ | `root` | div | The `role="region"` viewport / toast stack. |
89
+ | `toast` | div | A single notification card. |
90
+ | `toast-status-line` | `::before` | The 6px status bar on the leading edge. |
91
+ | `toast-icon` | div | Status glyph, or a spinner while loading. |
92
+ | `toast-content` | div | The column beside the icon. |
93
+ | `toast-text` | div | Title + description block. |
94
+ | `toast-title` | h5 | The notification's heading. |
95
+ | `toast-description` | div | Optional supporting text (3-line clamp). |
96
+ | `toast-actions` | div | Optional wrapping row of action buttons. |
97
+ | `toast-close` | button | Optional dismiss (✕) button. |
98
+
99
+ ## Props
100
+
101
+ | Prop | Type | Default | Required | Description |
102
+ |------|------|---------|----------|-------------|
103
+ | `timeout` | `number` | `5000` | no | Default auto-dismiss delay (ms) for toasts that don't set their own. `0` keeps a toast until dismissed. |
104
+ | `limit` | `number` | `3` | no | Maximum toasts shown at once; the oldest is dropped past it. |
105
+ | `label` | `string` | `'Notifications'` | no | Accessible name for the toast region. |
106
+ | `closeAriaLabel` | `string` | `'Close'` | no | Accessible name for each toast's dismiss control. |
107
+
108
+ ## Content slots
109
+
110
+ | Slot | Description |
111
+ |------|-------------|
112
+ | `options.dismissable` | Whether the toast renders its dismiss control and can be swiped away. Defaults to true, matching the Figma's `dismissable` boolean. Setting it false also revokes swipe-to-dismiss, so the control and the capability never disagree; combined with `timeout: 0` the toast can then only be removed with `toast.dismiss(id)`. |
113
+ | `options.actions` | Action-button descriptors — `{ label, onClick?, variant? }` — rendered in a wrapping row under the text. The first defaults to the `secondary` button style and the rest to `ghost`, matching the Figma. Clicking one runs its handler; it does not dismiss the toast. |
114
+
115
+ ## Behavior
116
+
117
+ Toast is imperative: render one `<Toaster>` region, then push notifications with
118
+ the `toast(...)` API from anywhere (including outside React — the manager is
119
+ module-level). The severity is chosen by which method is called, not by a prop.
120
+
121
+ ```gherkin
122
+ Scenario: Show a toast
123
+ Given a mounted <Toaster>
124
+ When toast('Event created', { description: 'Monday at 6:00 PM' }) is called
125
+ Then a card with the title and description appears in the bottom-end stack
126
+ And it uses the info severity (the Figma's default variant)
127
+ And it auto-dismisses after the timeout (default 5000ms)
128
+ ```
129
+
130
+ ```gherkin
131
+ Scenario: Severities
132
+ Given toast.info / success / warning / critical / danger is called
133
+ Then the card keeps its neutral surface
134
+ And its border and 6px leading status line take that severity's color
135
+ And it shows that severity's multicolor status icon
136
+ ```
137
+
138
+ ```gherkin
139
+ Scenario: No description
140
+ Given a toast created without a description
141
+ Then only the title renders, and the icon stays aligned with its first line
142
+ ```
143
+
144
+ ```gherkin
145
+ Scenario: Long description
146
+ Given a description longer than three lines
147
+ Then it is clamped to three lines with a trailing ellipsis
148
+ ```
149
+
150
+ ```gherkin
151
+ Scenario: Loading
152
+ Given toast.loading('Processing…') is called
153
+ Then the card shows a spinner in place of the status glyph
154
+ And it borrows the info border and status line (there is no Figma loading variant)
155
+ And it does NOT auto-dismiss
156
+ And toast.dismiss(id) or toast.promise resolution removes it
157
+ ```
158
+
159
+ ```gherkin
160
+ Scenario: Actions
161
+ Given a toast created with actions: [{ label: 'View' }, { label: 'Undo' }]
162
+ Then a wrapping row renders under the text
163
+ And the first action uses the secondary button style, the rest ghost
164
+ When an action is clicked
165
+ Then its onClick runs and the toast stays open
166
+ ```
167
+
168
+ ```gherkin
169
+ Scenario: Promise lifecycle
170
+ Given toast.promise(p, { loading, success, error }) is called
171
+ Then a persistent loading toast appears
172
+ And on fulfilment it becomes a success toast
173
+ And on rejection it becomes a danger toast
174
+ ```
175
+
176
+ ```gherkin
177
+ Scenario: Manual dismiss
178
+ Given a visible toast
179
+ When the close (✕) button is clicked
180
+ Then the toast animates out and is removed
181
+ ```
182
+
183
+ ```gherkin
184
+ Scenario: Not dismissable
185
+ Given a toast created with dismissable: false
186
+ Then no close (✕) button renders
187
+ And swiping it does not dismiss it either
188
+ And it leaves only on its timeout, or via toast.dismiss(id)
189
+ ```
190
+
191
+ ```gherkin
192
+ Scenario: Direction
193
+ Given dir="rtl" on an ancestor
194
+ Then the stack pins to the bottom-left corner
195
+ And the status line sits on the right (leading) edge
196
+ And toasts slide in from the left
197
+ And the status icons do NOT mirror
198
+ ```
199
+
200
+ ```gherkin
201
+ Scenario: Limit
202
+ Given more than `limit` toasts (default 3) are active
203
+ Then the oldest is dropped to make room for the newest
204
+ ```
205
+
206
+ ```gherkin
207
+ Scenario: Update in place
208
+ Given toast(..., { id: 'x' }) is called twice with the same id
209
+ Then the existing toast updates and its auto-dismiss timer resets
210
+ ```
211
+
212
+ ## Accessibility
213
+
214
+ - The `<Toaster>` region is a live region (`role="region"`, `aria-live="polite"`)
215
+ named by the `label` prop (default `"Notifications"`), so screen readers
216
+ announce new toasts without moving focus. Pass `label` to localize it.
217
+ - Base UI manages the announcement: it exposes an off-screen copy of each toast's
218
+ text to assistive tech and marks the visible card `aria-hidden`, so the message
219
+ is announced once and not duplicated. The card's accessible name comes from its
220
+ title.
221
+ - The dismiss and action buttons are reachable by keyboard via the viewport's
222
+ focus management (F6 / Tab into the region). The dismiss control's accessible
223
+ name comes from `closeAriaLabel` (default `"Close"`) — pass it to localize —
224
+ and it shows a visible focus ring (`--ui-focus-primary`) via ButtonIcon.
225
+ - Severity is never conveyed by color alone: each severity has its own status
226
+ icon, and the title/description carry the message in words.
227
+ - Don't put essential, time-critical information only in a toast — it
228
+ auto-dismisses. Pair destructive confirmations with a persistent surface, and
229
+ prefer `Alert` (the same visual, inline and persistent) when the message must
230
+ stay on screen.
231
+
232
+ ## Contrast
233
+
234
+ The card uses `--ui-toast-global-content-text-container-title-color` (title) and
235
+ `--ui-toast-global-content-text-container-description-color` (description) over
236
+ the neutral `--ui-toast-global-container-background`, meeting text contrast in
237
+ light and dark. The severity border and status line
238
+ (`--ui-toast-<severity>-border-color` / `-left-line`) are decorative — they
239
+ reinforce the status icon rather than carrying meaning on their own — so they are
240
+ not held to text contrast. Action buttons and the dismiss control inherit the
241
+ contrast-checked `--ui-button-*` / `--ui-button-icon-*` tiers.
@@ -0,0 +1,121 @@
1
+ # ToggleGroup
2
+
3
+ > A set of pressable toggle buttons (single or multiple selection) — plus a standalone Toggle. Built on Base UI's Toggle / ToggleGroup. Ported from the legacy shadcn-uikit `toggle-group`.
4
+
5
+ **Status:** draft · **Category:** forms · **Since:** v0.51.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { Toggle, ToggleGroup, ToggleGroupItem } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <ToggleGroup defaultValue={['grid']}>
17
+ <ToggleGroupItem value="grid" aria-label="Grid">▦</ToggleGroupItem>
18
+ <ToggleGroupItem value="list" aria-label="List">≣</ToggleGroupItem>
19
+ </ToggleGroup>
20
+ ```
21
+
22
+ A set of pressable toggle buttons (single or multiple selection), plus a
23
+ standalone `Toggle`. Built on Base UI's Toggle / ToggleGroup.
24
+
25
+ > Design-pending v1, ported from the legacy shadcn-uikit `toggle-group`.
26
+
27
+ ## When to use
28
+
29
+ - A small set of mutually-exclusive (or multi-select) view/format options —
30
+ view-mode switchers, text-formatting toolbars.
31
+
32
+ ## When not to use
33
+
34
+ - Choosing one option from a list / form value — use `RadioGroup` or `InputSelect`.
35
+ - A single on/off setting with a label — use `Switch`.
36
+
37
+ ## Parts
38
+
39
+ | Export | Purpose |
40
+ | ----------------- | -------------------------------------------------- |
41
+ | `ToggleGroup` | The group — tracks the pressed value(s). |
42
+ | `ToggleGroupItem` | A pressable button in the group (needs a `value`). |
43
+ | `Toggle` | A standalone pressable button (same styling). |
44
+
45
+ ## Example
46
+
47
+ ```tsx
48
+ import { ToggleGroup, ToggleGroupItem } from '@acronis-platform/ui-react';
49
+
50
+ <ToggleGroup defaultValue={['grid']} aria-label="View mode">
51
+ <ToggleGroupItem value="grid" aria-label="Grid view">
52
+ <GridIcon />
53
+ </ToggleGroupItem>
54
+ <ToggleGroupItem value="list" aria-label="List view">
55
+ <ListIcon />
56
+ </ToggleGroupItem>
57
+ </ToggleGroup>;
58
+ ```
59
+
60
+ ## Props
61
+
62
+ | Prop | Type | Default | Required | Description |
63
+ |------|------|---------|----------|-------------|
64
+ | `value` | `string[]` | — | no | The pressed item value(s) (controlled, on ToggleGroup). |
65
+ | `defaultValue` | `string[]` | — | no | The initially pressed value(s) (uncontrolled, on ToggleGroup). |
66
+ | `pressed` | `boolean` | — | no | Whether a standalone Toggle is pressed (controlled). |
67
+ | `defaultPressed` | `boolean` | `false` | no | Whether a standalone Toggle is initially pressed (uncontrolled). |
68
+ | `disabled` | `boolean` | `false` | no | Disables the group / toggle. |
69
+
70
+ ## Events
71
+
72
+ | Event | Payload | Description |
73
+ |-------|---------|-------------|
74
+ | `onValueChange` | `(value: string[], eventDetails) => void` | Fired when the pressed set changes (ToggleGroup). |
75
+ | `onPressedChange` | `(pressed: boolean, eventDetails) => void` | Fired when a standalone Toggle's pressed state changes. |
76
+
77
+ ## Behavior
78
+
79
+ ```gherkin
80
+ Scenario: Press an item
81
+ Given a ToggleGroup with items
82
+ When the user clicks an item
83
+ Then it becomes pressed (tinted) and its value is added to the group value
84
+ ```
85
+
86
+ ```gherkin
87
+ Scenario: Single vs. multiple
88
+ Given a single-selection group
89
+ When the user presses another item
90
+ Then the previously pressed item is released
91
+ (a multiple-selection group keeps both pressed)
92
+ ```
93
+
94
+ ```gherkin
95
+ Scenario: Standalone toggle
96
+ Given a standalone Toggle
97
+ When the user clicks it
98
+ Then it flips its pressed state (onPressedChange fires)
99
+ ```
100
+
101
+ ```gherkin
102
+ Scenario: Disabled
103
+ Given disabled is set
104
+ Then the toggle(s) cannot be pressed or focused
105
+ ```
106
+
107
+ ## Accessibility
108
+
109
+ - Built on Base UI's Toggle / ToggleGroup: each toggle is a `<button>` with
110
+ `aria-pressed`, operable with Enter/Space; the group supports arrow-key roving
111
+ focus. Give each toggle an accessible name (`aria-label` for icon-only toggles)
112
+ and the group an `aria-label`.
113
+ - Don't rely on the pressed tint alone — for icon-only toggles the `aria-pressed`
114
+ state conveys selection to assistive tech.
115
+
116
+ ## Contrast
117
+
118
+ Idle is transparent (text/icon use the foreground); hover uses
119
+ `--ui-background-surface-hover` and pressed uses `--ui-background-surface-active`,
120
+ both meeting contrast against the page in light and dark; the focus ring uses
121
+ `--ui-focus-primary`.