@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,403 @@
1
+ # Dialog
2
+
3
+ > A higher-level "recipe" dialog built on top of the DialogRoot primitive parts. A single `variant` prop selects one of seven canned use-cases (default, rename, save changes, reset password, discard changes, accept, read-only) — each with its own title, body copy and footer buttons — plus a legacy `wide` escape hatch (no Figma preset, free-form footer via the `footer` prop) kept for backward compatibility with pre-Figma call sites. `children` overrides the body slot and `hasLoading` shows a `Loading` overlay. Colors, typography, and container/header/footer geometry resolve to the `--ui-dialog-*`/`--ui-footer-*` token tier (see tokens.yaml). In Figma the matching component set is named "DialogDefault"; the code-facing name is `Dialog`.
4
+
5
+ **Status:** draft · **Category:** overlays · **Since:** v0.58.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { Dialog } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <Dialog
17
+ variant="discard changes"
18
+ open={open}
19
+ onOpenChange={setOpen}
20
+ onPrimaryAction={() => { discard(); setOpen(false); }}
21
+ />
22
+
23
+ {/* Override the body slot */}
24
+ <Dialog variant="default" defaultOpen>
25
+ <p>Any custom content.</p>
26
+ </Dialog>
27
+
28
+ {/* Interpolate the real object name into the canned title/body */}
29
+ <Dialog variant="rename" objectName="Q3 Report.xlsx" open onOpenChange={setOpen} />
30
+
31
+ {/* Hide the header and/or footer chrome (beyond the Figma contract) */}
32
+ <Dialog hasHeader={false} open>
33
+ <p>Body-only content — no title bar, no close button.</p>
34
+ </Dialog>
35
+
36
+ {/* Legacy wide variant — free-form footer, kept for backward compatibility */}
37
+ <Dialog variant="wide" size="large" title="Configure discovery agent" open
38
+ footer={<>
39
+ <DialogClose render={<Button variant="ghost">Cancel</Button>} />
40
+ <Button>Configure</Button>
41
+ </>}
42
+ />
43
+ ```
44
+
45
+ A higher-level "recipe" dialog built on top of the DialogRoot primitive parts.
46
+ One `variant` prop selects a ready-made title, body copy and footer for the
47
+ seven most common dialog use-cases, so product code doesn't reassemble the
48
+ same `DialogRoot` + `DialogHeader` + `DialogFooter` boilerplate each time. In
49
+ Figma the matching component set is named "DialogDefault"; the code-facing
50
+ name is `Dialog`.
51
+
52
+ > **Status: draft.** Container/header/footer geometry and color resolve to the
53
+ > `--ui-dialog-*`/`--ui-footer-*` token tier (reconciled against the Figma
54
+ > "DialogDefault" node). `size="large"` (832px) is the one exception — a legacy
55
+ > backward-compatibility value with no matching token. See `tokens.yaml`.
56
+
57
+ ## When to use
58
+
59
+ - One of the seven canned use-cases fits: `default`, `rename`, `save changes`,
60
+ `reset password`, `discard changes`, `accept`, `read-only`.
61
+ - You want a consistent confirmation/decision dialog without hand-composing the
62
+ DialogRoot parts.
63
+ - You have a legacy call site that needs a wider popup and fully custom footer
64
+ buttons — use `variant="wide"` (see below). This is a backward-compatibility
65
+ escape hatch, not a recommended pattern for new work.
66
+ - You need a body-only dialog with no title bar and/or no action bar — set
67
+ `hasHeader`/`hasFooter` to `false`. Both default to `true`; this is beyond
68
+ the strict Figma contract (all seven canned variants always show both).
69
+
70
+ ## When not to use
71
+
72
+ - You need a bespoke layout or extra parts none of the seven variants (or the
73
+ `wide` escape hatch) cover — this is a gap to raise with the design system
74
+ team (the underlying `DialogRoot` primitive is internal-only, not a public
75
+ escape hatch).
76
+ - For transient, non-blocking feedback — use a toast.
77
+ - For contextual hints anchored to an element — use a tooltip or popover.
78
+
79
+ ## Variants
80
+
81
+ | Variant | Title | Footer |
82
+ | ----------------- | ------------------ | ----------------------------------------- |
83
+ | `default` | Dialog title | Cancel · Label |
84
+ | `rename` | Rename object name | Cancel · Rename (body: text field) |
85
+ | `save changes` | Save changes | Go back · Save |
86
+ | `reset password` | Reset password | Cancel · Reset |
87
+ | `discard changes` | Discard changes | Go back · **Confirm** (destructive) |
88
+ | `accept` | Accept object name | Cancel · Accept |
89
+ | `read-only` | License agreement | Done (single primary action) |
90
+ | `wide` | (caller-supplied) | Free-form, via `footer` — no Figma preset |
91
+
92
+ `rename`, `discard changes`, and `accept`'s canned title/body embed a generic
93
+ "object name" placeholder — pass `objectName` to interpolate the real name
94
+ (e.g. a file name) instead of overriding `title`/`children` by hand.
95
+
96
+ ## Parts
97
+
98
+ | Part | Element (default) | Purpose |
99
+ | ----------------- | ------------------ | ---------------------------------------------------- |
100
+ | `container` | `div[role=dialog]` | The portaled, centered popup card. |
101
+ | `header` | `div` | Top bar; title + close button. |
102
+ | `title` | `h2` | Accessible dialog name (`aria-labelledby`). |
103
+ | `close-button` | `button` | Icon button that dismisses the dialog. |
104
+ | `body` | `div` | Variant copy or the `children` override. |
105
+ | `footer` | `div` | Right-aligned action bar; or the free-form `footer`. |
106
+ | `loading-overlay` | `div` | Spinner scrim shown while `hasLoading`. |
107
+
108
+ ## Example
109
+
110
+ ```tsx
111
+ import { Dialog } from '@acronis-platform/ui-react';
112
+
113
+ // Canned use-case, controlled open state
114
+ <Dialog variant="discard changes" open={open} onOpenChange={setOpen} />;
115
+
116
+ // Override the body slot (header + footer chrome preserved)
117
+ <Dialog variant="default" defaultOpen>
118
+ <p>Any custom body content.</p>
119
+ </Dialog>;
120
+
121
+ // Busy state
122
+ <Dialog variant="save changes" hasLoading open />;
123
+
124
+ // Localize the canned copy (title/secondaryLabel/primaryLabel/closeLabel all
125
+ // override the variant's default English string)
126
+ <Dialog
127
+ variant="discard changes"
128
+ title="Modifications non enregistrées"
129
+ secondaryLabel="Retour"
130
+ primaryLabel="Confirmer"
131
+ closeLabel="Fermer"
132
+ open
133
+ />;
134
+
135
+ // Interpolate the real object name into the rename/discard changes/accept
136
+ // variants' canned title/body, instead of the generic placeholder text
137
+ <Dialog
138
+ variant="rename"
139
+ objectName="Q3 Report.xlsx"
140
+ open
141
+ onOpenChange={setOpen}
142
+ />;
143
+
144
+ // Hide the header and/or footer chrome (beyond the Figma contract)
145
+ <Dialog hasHeader={false} open>
146
+ <p>Body-only content — no title bar, no close button.</p>
147
+ </Dialog>;
148
+
149
+ // Legacy wide variant — free-form footer, kept for backward compatibility
150
+ import { Button, DialogClose } from '@acronis-platform/ui-react';
151
+
152
+ <Dialog
153
+ variant="wide"
154
+ size="large"
155
+ title="Configure discovery agent"
156
+ open
157
+ footer={
158
+ <>
159
+ <DialogClose render={<Button variant="ghost">Cancel</Button>} />
160
+ <Button>Configure</Button>
161
+ </>
162
+ }
163
+ >
164
+ <p>The discovery agent will obtain the neighbor IP addresses…</p>
165
+ </Dialog>;
166
+ ```
167
+
168
+ ## Props
169
+
170
+ | Prop | Type | Default | Required | Description |
171
+ |------|------|---------|----------|-------------|
172
+ | `variant` | `'default' | 'rename' | 'save changes' | 'reset password' | 'discard changes' | 'accept' | 'read-only' | 'wide'` | `'default'` | no | Selects the canned title, body copy and footer buttons. `discard changes` uses a destructive primary action; `read-only` shows a single primary action (no secondary button). `wide` has no Figma preset — a legacy escape hatch kept for backward compatibility, meant to be paired with `size="large"` and the `footer` prop. |
173
+ | `hasLoading` | `boolean` | `false` | no | Shows a spinner overlay across the body + footer (inset from just below the header to the bottom, or from the very top when `hasHeader` is `false`), and makes both `inert` (unfocusable, unclickable, hidden from assistive tech) so a keyboard user can't activate the obscured footer buttons while busy. |
174
+ | `loadingLabel` | `string` | `'Data is loading…'` | no | Overrides the loading overlay's accessible label. Ignored when `hasLoading` is `false`. |
175
+ | `hasHeader` | `boolean` | `true` | no | Shows the header (title + close button). When `false`, the title is still rendered off-screen so the dialog keeps an accessible name — only the visible bar and close button are omitted. Beyond the strict Figma contract (all seven canned variants always show the header) — a ui-react-only extension, like `wide`/`size="large"`. |
176
+ | `hasFooter` | `boolean` | `true` | no | Shows the footer (action buttons). Beyond the strict Figma contract (all seven canned variants always show the footer) — a ui-react-only extension, like `wide`/`size="large"`. When `false`, `footer` is not rendered either. |
177
+ | `title` | `string` | — | no | Overrides the variant's default title, for localization or copy changes. |
178
+ | `objectName` | `string` | — | no | The real name of the object being acted on (e.g. a file or resource name). Interpolated into the `rename`/`discard changes`/`accept` variants' canned title/body in place of the generic placeholder text (e.g. `rename` → "Rename {objectName}" and the field's prefilled value); ignored by variants that don't reference an object name. |
179
+ | `objectNameLabel` | `string` | `'Object name'` | no | Overrides the accessible name of the `rename` variant's text field. Ignored by every other variant. |
180
+ | `secondaryLabel` | `string` | — | no | Overrides the variant's default secondary (dismiss) button label. Passing this on a variant with no secondary button by default (e.g. `read-only`) also makes the button appear. Ignored when `footer` is provided. |
181
+ | `primaryLabel` | `string` | — | no | Overrides the variant's default primary button label. Ignored when `footer` is provided. |
182
+ | `footer` | `ReactNode` | — | no | Replaces the footer's action content entirely with free-form buttons — the escape hatch the `wide` variant is meant to be paired with, for legacy call sites that don't fit one of the seven canned presets. |
183
+ | `closeLabel` | `string` | `'Close'` | no | Overrides the close button's accessible name. |
184
+ | `open` | `boolean` | — | no | Controlled open state. Pair with the change event. |
185
+ | `default-open` | `boolean` | `false` | no | Initial open state when uncontrolled (React `defaultOpen`). |
186
+ | `modal` | `boolean | 'trap-focus'` | `true` | no | Modal behavior — traps focus and locks background scroll while open. `'trap-focus'` keeps the focus trap but allows background interaction. |
187
+ | `size` | `'sm' | 'large'` | `'sm'` | no | Popup max-width (forwarded to the underlying content). Defaults to `sm` (512px, the Figma-defined size), or to `large` (832px) when `variant` is `wide`. `large` predates the Figma node and has no design token — a legacy backward-compatibility size, not a design-system width scale. |
188
+ | `portal` | `boolean` | `true` | no | Render the content inside a portal (forwarded to the underlying content). Required for correct stacking; disable only when supplying your own portal ancestor. |
189
+ | `keepMounted` | `boolean` | — | no | Keep the content mounted while closed (forwarded to the underlying content's portal). |
190
+
191
+ ## Events
192
+
193
+ | Event | Payload | Description |
194
+ |-------|---------|-------------|
195
+ | `open-change` | `boolean` | Fired when the dialog opens or closes (React: `onOpenChange`). Payload is the new open value. |
196
+ | `primary-action` | `void` | Fired when the primary footer button is clicked (React: `onPrimaryAction`). The dialog does not close automatically afterwards — pair it with the open-change event to close it once the action completes. Ignored when `footer` is provided. |
197
+
198
+ ## Content slots
199
+
200
+ | Slot | Description |
201
+ |------|-------------|
202
+ | `default` | Body slot. When provided, it replaces the variant's canned body copy while keeping the header and footer chrome. |
203
+
204
+ ## Behavior
205
+
206
+ Dialog is a recipe over the DialogRoot primitive: it owns the same `open`
207
+ state and modal focus/scroll management, and adds a `variant`-driven preset for
208
+ the title, body and footer.
209
+
210
+ ### Variants
211
+
212
+ ```gherkin
213
+ Scenario: A variant selects its canned content
214
+ Given a Dialog with variant = "discard changes"
215
+ When it opens
216
+ Then the title reads "Discard changes"
217
+ And the body asks to confirm discarding changes
218
+ And the footer shows a "Go back" secondary and a destructive "Confirm"
219
+ ```
220
+
221
+ ```gherkin
222
+ Scenario: The rename variant embeds a text field
223
+ Given a Dialog with variant = "rename"
224
+ When it opens
225
+ Then the body renders a text field prefilled with the current name
226
+ ```
227
+
228
+ ```gherkin
229
+ Scenario: The read-only variant has a single action
230
+ Given a Dialog with variant = "read-only"
231
+ When it opens
232
+ Then the footer shows only a primary "Done" button (no secondary button)
233
+ ```
234
+
235
+ ```gherkin
236
+ Scenario: The wide variant is a free-form escape hatch
237
+ Given a Dialog with variant = "wide" and a footer override
238
+ When it opens
239
+ Then the popup defaults to the large (832px) size
240
+ And the footer renders exactly the caller-supplied buttons, not a canned
241
+ secondary/primary pair
242
+ ```
243
+
244
+ ### Body slot
245
+
246
+ ```gherkin
247
+ Scenario: Children override the canned body
248
+ Given a Dialog with children provided
249
+ When it opens
250
+ Then the body renders the children instead of the variant's default copy
251
+ And the header title and footer buttons are unchanged
252
+ ```
253
+
254
+ ### Object name interpolation
255
+
256
+ ```gherkin
257
+ Scenario: objectName replaces the generic placeholder
258
+ Given a Dialog with variant = "rename" and objectName = "Q3 Report.xlsx"
259
+ When it opens
260
+ Then the title reads "Rename Q3 Report.xlsx"
261
+ And the body's text field is prefilled with "Q3 Report.xlsx"
262
+ ```
263
+
264
+ ```gherkin
265
+ Scenario: objectName is ignored by variants with no placeholder
266
+ Given a Dialog with variant = "default" and objectName provided
267
+ When it opens
268
+ Then the canned title and body render unchanged
269
+ ```
270
+
271
+ ### Footer override
272
+
273
+ ```gherkin
274
+ Scenario: The footer prop replaces the canned buttons
275
+ Given a Dialog with a footer override provided
276
+ When it opens
277
+ Then the footer renders the override content
278
+ And secondaryLabel/primaryLabel are ignored
279
+ ```
280
+
281
+ ### Loading
282
+
283
+ ```gherkin
284
+ Scenario: The loading overlay covers body and footer
285
+ Given an open Dialog with hasLoading = true
286
+ Then a spinner overlay is shown from just below the header to the bottom
287
+ And it is hidden when hasLoading is false
288
+ ```
289
+
290
+ ```gherkin
291
+ Scenario: The loading overlay actually blocks interaction, not just the click
292
+ Given an open Dialog with hasLoading = true
293
+ Then the body and footer are inert — unfocusable, unclickable, and hidden
294
+ from assistive tech, not merely visually obscured
295
+ And the popup carries aria-busy="true"
296
+ And loadingLabel overrides the overlay's accessible name
297
+ ```
298
+
299
+ ### Header and footer optionality
300
+
301
+ ```gherkin
302
+ Scenario: Hiding the header keeps the dialog's accessible name
303
+ Given a Dialog with hasHeader = false
304
+ When it opens
305
+ Then the visible title bar and close button are not rendered
306
+ And the dialog still has an accessible name (the title renders off-screen)
307
+ ```
308
+
309
+ ```gherkin
310
+ Scenario: Hiding the footer removes its actions
311
+ Given a Dialog with hasFooter = false
312
+ When it opens
313
+ Then no secondary/primary/footer-override content is rendered
314
+ ```
315
+
316
+ ```gherkin
317
+ Scenario: Hiding both leaves only the body
318
+ Given a Dialog with hasHeader = false and hasFooter = false
319
+ When it opens
320
+ Then only the body content is visible
321
+ And the dialog still has an accessible name
322
+ ```
323
+
324
+ ```gherkin
325
+ Scenario: The loading overlay adapts to a hidden header
326
+ Given a Dialog with hasHeader = false and hasLoading = true
327
+ When it opens
328
+ Then the spinner overlay covers from the very top of the popup, not from
329
+ below a (non-existent) header
330
+ ```
331
+
332
+ ### Opening & closing
333
+
334
+ ```gherkin
335
+ Scenario: Dismissing via the secondary button
336
+ Given an open Dialog with a secondary button
337
+ When the user activates it
338
+ Then the dialog closes and open-change(false) fires
339
+ ```
340
+
341
+ ```gherkin
342
+ Scenario: Dismissing with the keyboard
343
+ Given an open modal Dialog
344
+ When the user presses Escape
345
+ Then the dialog closes and open-change(false) fires
346
+ ```
347
+
348
+ ```gherkin
349
+ Scenario: Controlled
350
+ Given a Dialog with a fixed open prop
351
+ When the user attempts to dismiss it
352
+ Then internal state does NOT change on its own
353
+ And open-change fires so the consumer can update the open prop
354
+ ```
355
+
356
+ ## Accessibility
357
+
358
+ Dialog inherits the Base UI Dialog primitive's modal semantics, focus
359
+ management and keyboard handling (via `DialogContent`/`DialogTitle`/
360
+ `DialogClose`). The recipe supplies the accessible name and the action controls.
361
+
362
+ ## Roles & semantics
363
+
364
+ - The popup (`container`) has `role="dialog"` and `aria-modal="true"` when modal.
365
+ - **Name.** Every variant renders a `DialogTitle`, wired to the popup via
366
+ `aria-labelledby`, so the dialog always has an accessible name.
367
+ - The close button exposes an accessible name ("Close") via an `sr-only` label
368
+ alongside the icon, and is present whenever the header is shown (the
369
+ default). Setting `hasHeader = false` removes the header bar, and with it
370
+ the close button — the dialog is then dismissed via `Escape` or a
371
+ caller-supplied action instead.
372
+ - The loading overlay's spinner carries `role="status"` with an accessible name
373
+ (`loadingLabel`, defaulting to `Loading`'s own `'Data is loading…'`) so the
374
+ busy state is announced. The popup itself carries `aria-busy="true"`, and the
375
+ body + footer are made `inert` — not just visually obscured — so a keyboard
376
+ user cannot tab to or activate the footer buttons while `hasLoading` is set.
377
+ - The `rename` variant's text field has a built-in accessible name
378
+ (`aria-label="Object name"`) — the field renders without a visible label in
379
+ the design, so the component supplies the label itself.
380
+ - The `wide` variant's `footer` override is free-form — the caller is
381
+ responsible for giving each action button an accessible name (e.g. visible
382
+ label text), the same as any other Button usage.
383
+
384
+ ## Keyboard
385
+
386
+ - **Tab / Shift+Tab** — focus cycles within the dialog only; the focus trap
387
+ prevents reaching background content while modal.
388
+ - **Escape** — closes the dialog (fires the change event).
389
+ - On open, focus moves into the dialog; on close, focus returns to the opener.
390
+
391
+ ## Screen reader
392
+
393
+ - Entering the dialog announces its role and name (title).
394
+ - While modal, background content is inert, so assistive tech stays within the
395
+ dialog.
396
+
397
+ ## Contrast
398
+
399
+ - Title and body text use `--ui-text-on-surface-primary`; the idle close icon
400
+ uses `--ui-text-on-surface-secondary`; both meet WCAG AA against the
401
+ `--ui-background-surface-secondary` container and `--ui-background-surface-primary`
402
+ header/footer in light and dark themes. The close button shows a
403
+ `--ui-focus-primary` ring on keyboard focus.
@@ -0,0 +1,193 @@
1
+ # DropdownMenu
2
+
3
+ > A menu of actions anchored to a trigger (typically a `ButtonMenu`), opened on demand. Composable parts — trigger, content, group (section), item, shortcut, submenu — built on the Base UI Menu primitive. Themed by the `--ui-button-menu-dropdown-*` token tier from @acronis-platform/tokens-pd.
4
+
5
+ **Status:** stable · **Category:** overlays · **Since:** v0.14.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, ButtonMenu } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <DropdownMenu>
17
+ <DropdownMenuTrigger render={<ButtonMenu variant="secondary">Actions</ButtonMenu>} />
18
+ <DropdownMenuContent>
19
+ <DropdownMenuGroup>
20
+ <DropdownMenuItem>
21
+ Profile <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
22
+ </DropdownMenuItem>
23
+ <DropdownMenuItem>Settings</DropdownMenuItem>
24
+ </DropdownMenuGroup>
25
+ </DropdownMenuContent>
26
+ </DropdownMenu>
27
+ ```
28
+
29
+ A menu of actions anchored to a trigger, opened on demand. Composable from
30
+ a set of parts — trigger, content, group (section), item, shortcut, submenu —
31
+ built on the Base UI Menu primitive. Themed by the `--ui-button-menu-dropdown-*`
32
+ token tier from `@acronis-platform/tokens-pd`.
33
+
34
+ ## When to use
35
+
36
+ - A compact list of **actions** triggered from a `ButtonMenu` (row actions,
37
+ account menu, "more" overflow).
38
+
39
+ ## When not to use
40
+
41
+ - For selecting a value into a field — use `InputSelect` / `Select`.
42
+ - For secondary content or a form — use a `Popover`.
43
+ - For primary page navigation — use a nav, not a menu.
44
+
45
+ ## Parts
46
+
47
+ | Part | Element | Purpose |
48
+ | ----------------------------------------------------------------------- | -------------------- | -------------------------------------------------- |
49
+ | `DropdownMenu` | — (Root) | Owns the open state. |
50
+ | `DropdownMenuTrigger` | `button` | Opens the menu (compose with `ButtonMenu`). |
51
+ | `DropdownMenuContent` | `div[role=menu]` | The portaled, positioned popup. |
52
+ | `DropdownMenuGroup` | `div` | A section of items (auto border-top on non-first). |
53
+ | `DropdownMenuItem` | `div[role=menuitem]` | An action row (`inset` to align). |
54
+ | `DropdownMenuShortcut` | `span` | Right-aligned shortcut hint. |
55
+ | `DropdownMenuSub` / `DropdownMenuSubTrigger` / `DropdownMenuSubContent` | — | A nested submenu. |
56
+ | `DropdownMenuPortal` | — | Portal wrapper. |
57
+
58
+ ## Example
59
+
60
+ ```tsx
61
+ import {
62
+ DropdownMenu,
63
+ DropdownMenuTrigger,
64
+ DropdownMenuContent,
65
+ DropdownMenuGroup,
66
+ DropdownMenuItem,
67
+ DropdownMenuShortcut,
68
+ ButtonMenu,
69
+ } from '@acronis-platform/ui-react';
70
+
71
+ <DropdownMenu>
72
+ <DropdownMenuTrigger
73
+ render={<ButtonMenu variant="secondary">Actions</ButtonMenu>}
74
+ />
75
+ <DropdownMenuContent>
76
+ <DropdownMenuGroup>
77
+ <DropdownMenuItem>
78
+ Profile <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
79
+ </DropdownMenuItem>
80
+ <DropdownMenuItem>Settings</DropdownMenuItem>
81
+ </DropdownMenuGroup>
82
+ </DropdownMenuContent>
83
+ </DropdownMenu>;
84
+ ```
85
+
86
+ ## Props
87
+
88
+ | Prop | Type | Default | Required | Description |
89
+ |------|------|---------|----------|-------------|
90
+ | `open` | `boolean` | — | no | Controlled open state. Pair with the change event. |
91
+ | `default-open` | `boolean` | `false` | no | Initial open state when uncontrolled. |
92
+ | `side` | `'top' | 'bottom' | 'left' | 'right' | 'inline-start' | 'inline-end'` | `'bottom'` | no | On the content — which side of the trigger to render on. |
93
+ | `align` | `'start' | 'center' | 'end'` | — | no | On the content — alignment along the chosen side. |
94
+ | `side-offset` | `number` | `4` | no | On the content — distance in px from the trigger. |
95
+ | `inset` | `boolean` | `false` | no | On an item / submenu-trigger — left-pad to align with rows that have a leading indicator. |
96
+
97
+ ## Events
98
+
99
+ | Event | Payload | Description |
100
+ |-------|---------|-------------|
101
+ | `open-change` | `boolean` | Fired when the menu opens or closes (React `onOpenChange`). |
102
+
103
+ ## Content slots
104
+
105
+ | Slot | Description |
106
+ |------|-------------|
107
+ | `default` | A `trigger` (typically a `ButtonMenu`) plus the `content` popup of `group`s containing `item`s — optionally with `shortcut` hints and nested submenus (`submenu-trigger` + `submenu-content`). |
108
+
109
+ ## Behavior
110
+
111
+ DropdownMenu shows a menu of actions anchored to a trigger. It owns the open
112
+ state (or the consumer controls it via `open`).
113
+
114
+ ### Opening & closing
115
+
116
+ ```gherkin
117
+ Scenario: Opening from the trigger
118
+ Given a closed menu
119
+ When the user activates the trigger (click / Enter / Space / Arrow)
120
+ Then the popup opens, focus moves into it, and open-change(true) fires
121
+ ```
122
+
123
+ ```gherkin
124
+ Scenario: Selecting an item closes the menu
125
+ Given an open menu
126
+ When the user activates an item
127
+ Then the item's action runs and the menu closes (open-change(false))
128
+ ```
129
+
130
+ ```gherkin
131
+ Scenario: Dismissing
132
+ Given an open menu
133
+ When the user presses Escape or presses outside the popup
134
+ Then the menu closes and focus returns to the trigger
135
+ ```
136
+
137
+ ### Keyboard
138
+
139
+ ```gherkin
140
+ Scenario: Roving navigation and typeahead
141
+ Given an open menu
142
+ When the user presses Arrow keys
143
+ Then highlight moves between items (data-[highlighted])
144
+ And typing characters jumps to the matching item
145
+ ```
146
+
147
+ ### Submenus
148
+
149
+ ```gherkin
150
+ Scenario: Opening a submenu
151
+ Given a DropdownMenuSubTrigger
152
+ When the user highlights it (hover or ArrowRight)
153
+ Then the submenu (data-[popup-open]) opens beside it
154
+ ```
155
+
156
+ ## Accessibility
157
+
158
+ DropdownMenu leans on the Base UI Menu primitive for the WAI-ARIA menu pattern:
159
+ roles, roving focus, typeahead, and dismissal.
160
+
161
+ ## Roles & semantics
162
+
163
+ - The trigger exposes `aria-haspopup="menu"` and `aria-expanded`; the popup has
164
+ `role="menu"`.
165
+ - Items are `role="menuitem"`. The shortcut hint is decorative text inside its
166
+ item. Groups (`DropdownMenuGroup`) visually section items; non-first groups
167
+ render a top-border separator.
168
+
169
+ ## Keyboard
170
+
171
+ - **Enter / Space / Arrow Down** on the trigger opens the menu and moves focus
172
+ to the first item.
173
+ - **Arrow Up / Down** move the highlight (roving); **Home / End** jump to
174
+ first / last; **typeahead** jumps to a matching item.
175
+ - **Arrow Right** opens a submenu from its trigger; **Arrow Left** / **Escape**
176
+ closes it (Escape closes the whole menu from the root and returns focus to the
177
+ trigger).
178
+ - **Enter / Space** activates the highlighted item.
179
+ - **Focus ring** (`--ui-focus-primary`, 3px inset) appears only on keyboard
180
+ navigation (`focus-visible:not(:hover)`), not on pointer interaction.
181
+
182
+ ## Screen reader
183
+
184
+ - Opening announces the menu and its first item ("Profile, menu item 1 of 4").
185
+
186
+ ## Contrast
187
+
188
+ - The popup uses `--ui-button-menu-dropdown-item-label-color` on
189
+ `--ui-button-menu-dropdown-container-color` with a
190
+ `--ui-button-menu-dropdown-container-border-color` border; the highlighted
191
+ item uses `--ui-button-menu-dropdown-item-container-color-hover`; shortcuts
192
+ use `--ui-button-menu-dropdown-extras-shortcut-label-color` — all meeting
193
+ WCAG AA in light and dark.