prime-ui-kit 0.13.0 → 0.14.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 (215) hide show
  1. package/README.md +7 -6
  2. package/dist/chunks/{chunk-ZSJMNA46.js → chunk-P6ZO37JM.js} +1 -1
  3. package/dist/chunks/{chunk-Z3WKKAS6.js → chunk-SYEX7OVT.js} +3572 -2865
  4. package/dist/chunks/{chunk-E6SQBSDF.js → chunk-WXKRUVSE.js} +199 -162
  5. package/dist/color-picker.js +2 -2
  6. package/dist/components/banner/Banner.d.ts +5 -2
  7. package/dist/components/chart-frame/ChartFrame.d.ts +19 -6
  8. package/dist/components/chat/Chat.d.ts +2 -2
  9. package/dist/components/chat/ChatPinned.d.ts +2 -2
  10. package/dist/components/chat/MessageList.d.ts +1 -1
  11. package/dist/components/chat/chatContext.d.ts +1 -1
  12. package/dist/components/field-list/FieldList.d.ts +90 -0
  13. package/dist/components/index.d.ts +3 -1
  14. package/dist/components/input/Input.d.ts +2 -2
  15. package/dist/components/kanban/Kanban.d.ts +6 -3
  16. package/dist/components/page-content/PageContent.d.ts +55 -4
  17. package/dist/components/sparkline/Sparkline.d.ts +64 -15
  18. package/dist/components/sparkline/geometry.d.ts +26 -0
  19. package/dist/components/tooltip/Tooltip.d.ts +5 -2
  20. package/dist/hooks/usePosition.d.ts +15 -4
  21. package/dist/index.css +1 -1
  22. package/dist/index.js +4 -2
  23. package/dist/internal/ChartTooltip.d.ts +26 -0
  24. package/dist/internal/FieldAction.d.ts +6 -7
  25. package/dist/internal/fieldCell.d.ts +21 -0
  26. package/dist/internal/messageList.d.ts +2 -2
  27. package/dist/internal/palette.d.ts +7 -0
  28. package/dist/skill/SKILL.md +24 -14
  29. package/dist/skill/anti-slop.md +77 -4
  30. package/dist/skill/cheatsheet.md +24 -5
  31. package/dist/skill/checklist.md +19 -1
  32. package/dist/skill/choosing.md +10 -5
  33. package/dist/skill/components.md +4 -3
  34. package/dist/skill/composition.md +28 -15
  35. package/dist/skill/foundations.md +8 -6
  36. package/dist/skill/layouts.md +6 -6
  37. package/dist/skill/patterns/app-shell.module.css +1 -8
  38. package/dist/skill/patterns/app-shell.tsx +11 -9
  39. package/dist/skill/patterns/chat.tsx +23 -5
  40. package/dist/skill/patterns/dashboard.module.css +1 -31
  41. package/dist/skill/patterns/dashboard.tsx +150 -136
  42. package/dist/skill/patterns/detail-page.module.css +0 -3
  43. package/dist/skill/patterns/detail-page.tsx +4 -7
  44. package/dist/skill/patterns/form-drawer.tsx +2 -2
  45. package/dist/skill/patterns/kanban-board.tsx +4 -2
  46. package/dist/skill/patterns/list-page.tsx +2 -2
  47. package/dist/skill/patterns/media-library.module.css +0 -22
  48. package/dist/skill/patterns/media-library.tsx +17 -18
  49. package/dist/skill/patterns/record-editor.module.css +18 -0
  50. package/dist/skill/patterns/record-editor.tsx +356 -0
  51. package/dist/skill/patterns/screen-states.module.css +3 -17
  52. package/dist/skill/patterns/screen-states.tsx +70 -69
  53. package/dist/skill/patterns/settings-page.module.css +1 -21
  54. package/dist/skill/patterns/settings-page.tsx +58 -79
  55. package/dist/skill/reference/components/avatar/COMPONENT.md +4 -4
  56. package/dist/skill/reference/components/avatar/examples/presence.tsx +1 -1
  57. package/dist/skill/reference/components/avatar/examples/states.tsx +1 -1
  58. package/dist/skill/reference/components/banner/COMPONENT.md +6 -6
  59. package/dist/skill/reference/components/banner/examples/dismissible.tsx +22 -11
  60. package/dist/skill/reference/components/banner/examples/examples.module.css +3 -3
  61. package/dist/skill/reference/components/banner/examples/narrow.tsx +1 -1
  62. package/dist/skill/reference/components/breadcrumb/COMPONENT.md +1 -1
  63. package/dist/skill/reference/components/breadcrumb/examples/overflow.tsx +1 -1
  64. package/dist/skill/reference/components/card/COMPONENT.md +5 -4
  65. package/dist/skill/reference/components/card/examples/examples.module.css +0 -28
  66. package/dist/skill/reference/components/card/examples/kpi-media.tsx +13 -11
  67. package/dist/skill/reference/components/card/examples/narrow.tsx +1 -1
  68. package/dist/skill/reference/components/card/examples/panel-chart.tsx +45 -22
  69. package/dist/skill/reference/components/chart-frame/COMPONENT.md +10 -7
  70. package/dist/skill/reference/components/chart-frame/examples/tooltip.tsx +20 -24
  71. package/dist/skill/reference/components/chat/COMPONENT.md +3 -3
  72. package/dist/skill/reference/components/chat/examples/search.tsx +1 -1
  73. package/dist/skill/reference/components/chat-list/COMPONENT.md +1 -1
  74. package/dist/skill/reference/components/checkbox/examples/indicator.tsx +24 -2
  75. package/dist/skill/reference/components/color-picker/COMPONENT.md +2 -2
  76. package/dist/skill/reference/components/color-swatches/COMPONENT.md +1 -0
  77. package/dist/skill/reference/components/datepicker/COMPONENT.md +1 -0
  78. package/dist/skill/reference/components/dnd/COMPONENT.md +3 -3
  79. package/dist/skill/reference/components/field-list/COMPONENT.md +164 -0
  80. package/dist/skill/reference/components/field-list/examples/base-row.tsx +76 -0
  81. package/dist/skill/reference/components/field-list/examples/controlled.tsx +72 -0
  82. package/dist/skill/reference/components/field-list/examples/errors.tsx +85 -0
  83. package/dist/skill/reference/components/field-list/examples/examples.module.css +34 -0
  84. package/dist/skill/reference/components/field-list/examples/limits.tsx +55 -0
  85. package/dist/skill/reference/components/field-list/examples/locked.tsx +139 -0
  86. package/dist/skill/reference/components/field-list/examples/narrow.tsx +68 -0
  87. package/dist/skill/reference/components/field-list/examples/overview.tsx +66 -0
  88. package/dist/skill/reference/components/field-list/examples/reorder.tsx +42 -0
  89. package/dist/skill/reference/components/field-list/examples/sizes.tsx +42 -0
  90. package/dist/skill/reference/components/field-list/examples/states.tsx +51 -0
  91. package/dist/skill/reference/components/input/examples/examples.module.css +2 -7
  92. package/dist/skill/reference/components/kanban/COMPONENT.md +13 -9
  93. package/dist/skill/reference/components/kanban/examples/limits.tsx +2 -1
  94. package/dist/skill/reference/components/kanban/examples/variants.tsx +16 -8
  95. package/dist/skill/reference/components/list/COMPONENT.md +1 -1
  96. package/dist/skill/reference/components/media-viewer/COMPONENT.md +2 -2
  97. package/dist/skill/reference/components/message/COMPONENT.md +2 -2
  98. package/dist/skill/reference/components/notification/COMPONENT.md +1 -0
  99. package/dist/skill/reference/components/page-content/COMPONENT.md +70 -14
  100. package/dist/skill/reference/components/page-content/examples/grid.tsx +107 -0
  101. package/dist/skill/reference/components/page-content/examples/overview.tsx +2 -2
  102. package/dist/skill/reference/components/page-content/examples/sections.tsx +81 -0
  103. package/dist/skill/reference/components/page-toolbar/COMPONENT.md +1 -1
  104. package/dist/skill/reference/components/segmented-control/COMPONENT.md +1 -0
  105. package/dist/skill/reference/components/select/COMPONENT.md +4 -2
  106. package/dist/skill/reference/components/select/examples/icon-only.tsx +38 -0
  107. package/dist/skill/reference/components/sparkline/COMPONENT.md +83 -30
  108. package/dist/skill/reference/components/sparkline/examples/compact.tsx +1 -1
  109. package/dist/skill/reference/components/sparkline/examples/curve.tsx +28 -0
  110. package/dist/skill/reference/components/sparkline/examples/gaps.tsx +33 -0
  111. package/dist/skill/reference/components/sparkline/examples/overview.tsx +11 -2
  112. package/dist/skill/reference/components/sparkline/examples/period.tsx +7 -2
  113. package/dist/skill/reference/components/sparkline/examples/reference.tsx +37 -0
  114. package/dist/skill/reference/components/sparkline/examples/variants.tsx +2 -2
  115. package/dist/skill/reference/components/tabs/examples/examples.module.css +0 -6
  116. package/dist/skill/reference/components/tabs/examples/in-drawer.tsx +11 -13
  117. package/dist/skill/reference/components/timeline/examples/examples.module.css +0 -8
  118. package/dist/skill/reference/components/timeline/examples/narrow.tsx +2 -2
  119. package/dist/skill/reference/components/timeline/examples/sizes.tsx +3 -3
  120. package/dist/skill/reference/components/timeline/examples/variants.tsx +4 -4
  121. package/dist/skill/reference/components/tooltip/COMPONENT.md +6 -4
  122. package/dist/skill/reference/components/tooltip/examples/placement.tsx +2 -1
  123. package/dist/skill/reference/components/video-note/COMPONENT.md +1 -1
  124. package/dist/skill/reference/components/voice-player/COMPONENT.md +1 -1
  125. package/dist/skill/reference/layout/app-header/examples/in-app-shell.tsx +2 -2
  126. package/dist/skill/reference/layout/app-shell/COMPONENT.md +1 -1
  127. package/dist/skill/reference/layout/app-shell/examples/footer.tsx +2 -2
  128. package/dist/skill/reference/layout/app-shell/examples/overview.tsx +2 -2
  129. package/dist/skill/reference/layout/app-shell/examples/template.tsx +2 -2
  130. package/dist/skill/reference/layout/bottom-nav/examples/floating-action.tsx +2 -2
  131. package/dist/skill/reference/layout/bottom-nav/examples/floating.tsx +2 -2
  132. package/dist/skill/reference/layout/bottom-nav/examples/in-app-shell.tsx +2 -2
  133. package/dist/skill/responsive.md +5 -3
  134. package/dist/skill/ux.md +145 -0
  135. package/dist/tokens/semantic.d.ts +2 -0
  136. package/package.json +1 -1
  137. package/src/components/avatar/COMPONENT.md +4 -4
  138. package/src/components/avatar/examples/presence.tsx +1 -1
  139. package/src/components/avatar/examples/states.tsx +1 -1
  140. package/src/components/banner/COMPONENT.md +6 -6
  141. package/src/components/banner/examples/dismissible.tsx +22 -11
  142. package/src/components/banner/examples/examples.module.css +3 -3
  143. package/src/components/banner/examples/narrow.tsx +1 -1
  144. package/src/components/breadcrumb/COMPONENT.md +1 -1
  145. package/src/components/breadcrumb/examples/overflow.tsx +1 -1
  146. package/src/components/card/COMPONENT.md +5 -4
  147. package/src/components/card/examples/examples.module.css +0 -28
  148. package/src/components/card/examples/kpi-media.tsx +13 -11
  149. package/src/components/card/examples/narrow.tsx +1 -1
  150. package/src/components/card/examples/panel-chart.tsx +45 -22
  151. package/src/components/chart-frame/COMPONENT.md +10 -7
  152. package/src/components/chart-frame/examples/tooltip.tsx +20 -24
  153. package/src/components/chat/COMPONENT.md +3 -3
  154. package/src/components/chat/examples/search.tsx +1 -1
  155. package/src/components/chat-list/COMPONENT.md +1 -1
  156. package/src/components/checkbox/examples/indicator.tsx +24 -2
  157. package/src/components/color-picker/COMPONENT.md +2 -2
  158. package/src/components/color-swatches/COMPONENT.md +1 -0
  159. package/src/components/datepicker/COMPONENT.md +1 -0
  160. package/src/components/dnd/COMPONENT.md +3 -3
  161. package/src/components/field-list/COMPONENT.md +164 -0
  162. package/src/components/field-list/examples/base-row.tsx +76 -0
  163. package/src/components/field-list/examples/controlled.tsx +72 -0
  164. package/src/components/field-list/examples/errors.tsx +85 -0
  165. package/src/components/field-list/examples/examples.module.css +34 -0
  166. package/src/components/field-list/examples/limits.tsx +55 -0
  167. package/src/components/field-list/examples/locked.tsx +139 -0
  168. package/src/components/field-list/examples/narrow.tsx +68 -0
  169. package/src/components/field-list/examples/overview.tsx +66 -0
  170. package/src/components/field-list/examples/reorder.tsx +42 -0
  171. package/src/components/field-list/examples/sizes.tsx +42 -0
  172. package/src/components/field-list/examples/states.tsx +51 -0
  173. package/src/components/input/examples/examples.module.css +2 -7
  174. package/src/components/kanban/COMPONENT.md +13 -9
  175. package/src/components/kanban/examples/limits.tsx +2 -1
  176. package/src/components/kanban/examples/variants.tsx +16 -8
  177. package/src/components/list/COMPONENT.md +1 -1
  178. package/src/components/media-viewer/COMPONENT.md +2 -2
  179. package/src/components/message/COMPONENT.md +2 -2
  180. package/src/components/notification/COMPONENT.md +1 -0
  181. package/src/components/page-content/COMPONENT.md +70 -14
  182. package/src/components/page-content/examples/grid.tsx +107 -0
  183. package/src/components/page-content/examples/overview.tsx +2 -2
  184. package/src/components/page-content/examples/sections.tsx +81 -0
  185. package/src/components/page-toolbar/COMPONENT.md +1 -1
  186. package/src/components/segmented-control/COMPONENT.md +1 -0
  187. package/src/components/select/COMPONENT.md +4 -2
  188. package/src/components/select/examples/icon-only.tsx +38 -0
  189. package/src/components/sparkline/COMPONENT.md +83 -30
  190. package/src/components/sparkline/examples/compact.tsx +1 -1
  191. package/src/components/sparkline/examples/curve.tsx +28 -0
  192. package/src/components/sparkline/examples/gaps.tsx +33 -0
  193. package/src/components/sparkline/examples/overview.tsx +11 -2
  194. package/src/components/sparkline/examples/period.tsx +7 -2
  195. package/src/components/sparkline/examples/reference.tsx +37 -0
  196. package/src/components/sparkline/examples/variants.tsx +2 -2
  197. package/src/components/tabs/examples/examples.module.css +0 -6
  198. package/src/components/tabs/examples/in-drawer.tsx +11 -13
  199. package/src/components/timeline/examples/examples.module.css +0 -8
  200. package/src/components/timeline/examples/narrow.tsx +2 -2
  201. package/src/components/timeline/examples/sizes.tsx +3 -3
  202. package/src/components/timeline/examples/variants.tsx +4 -4
  203. package/src/components/tooltip/COMPONENT.md +6 -4
  204. package/src/components/tooltip/examples/placement.tsx +2 -1
  205. package/src/components/video-note/COMPONENT.md +1 -1
  206. package/src/components/voice-player/COMPONENT.md +1 -1
  207. package/src/layout/app-header/examples/in-app-shell.tsx +2 -2
  208. package/src/layout/app-shell/COMPONENT.md +1 -1
  209. package/src/layout/app-shell/examples/footer.tsx +2 -2
  210. package/src/layout/app-shell/examples/overview.tsx +2 -2
  211. package/src/layout/app-shell/examples/template.tsx +2 -2
  212. package/src/layout/bottom-nav/examples/floating-action.tsx +2 -2
  213. package/src/layout/bottom-nav/examples/floating.tsx +2 -2
  214. package/src/layout/bottom-nav/examples/in-app-shell.tsx +2 -2
  215. package/src/styles/theme-light.css +1 -0
@@ -78,21 +78,12 @@ export default function SettingsPagePattern() {
78
78
  </PageContent.Description>
79
79
  </PageContent.Header>
80
80
  <PageContent.Body>
81
- <section className={styles.section} aria-labelledby="company-title">
82
- <div className={styles.lead}>
83
- <Typography as="h2" variant="title-m" id="company-title">
84
- Компания
85
- </Typography>
86
- <Typography variant="body-m" tone="secondary">
87
- Попадают в счета, акты и письма клиентам.
88
- </Typography>
89
- </div>
90
- <form
91
- className={styles.panel}
92
- noValidate
93
- onSubmit={save}
94
- onReset={() => setInnError(undefined)}
95
- >
81
+ <PageContent.Section
82
+ layout="aside"
83
+ label="Компания"
84
+ description="Попадают в счета, акты и письма клиентам."
85
+ >
86
+ <form noValidate onSubmit={save} onReset={() => setInnError(undefined)}>
96
87
  <Card.Root variant="panel">
97
88
  <Card.Body>
98
89
  <Fieldset disabled={saving}>
@@ -144,73 +135,61 @@ export default function SettingsPagePattern() {
144
135
  </Card.Footer>
145
136
  </Card.Root>
146
137
  </form>
147
- </section>
138
+ </PageContent.Section>
148
139
 
149
- <section className={styles.section} aria-labelledby="alerts-title">
150
- <div className={styles.lead}>
151
- <Typography as="h2" variant="title-m" id="alerts-title">
152
- Уведомления
153
- </Typography>
154
- <Typography variant="body-m" tone="secondary">
155
- Включаются сразу, без сохранения.
156
- </Typography>
157
- </div>
158
- <div className={styles.panel}>
159
- <Card.Root variant="panel">
160
- <Card.Body>
161
- <Fieldset aria-labelledby="alerts-title">
162
- {ALERTS.map((alert) => (
163
- <Switch.Root
164
- key={alert.name}
165
- name={alert.name}
166
- defaultChecked={alert.defaultChecked}
167
- hint={alert.hint}
168
- >
169
- <Switch.Label>{alert.label}</Switch.Label>
170
- </Switch.Root>
171
- ))}
172
- </Fieldset>
173
- </Card.Body>
174
- </Card.Root>
175
- </div>
176
- </section>
177
-
178
- <section className={styles.section} aria-labelledby="danger-title">
179
- <div className={styles.lead}>
180
- <Typography as="h2" variant="title-m" id="danger-title">
181
- Рабочее пространство
182
- </Typography>
183
- <Typography variant="body-m" tone="secondary">
184
- Действия, которые нельзя отменить.
185
- </Typography>
186
- </div>
187
- <div className={styles.panel}>
188
- <Card.Root variant="panel">
189
- <Card.Body>
190
- <div className={styles.danger}>
191
- <div className={styles.dangerText}>
192
- <Typography variant="title-s">Удалить пространство</Typography>
193
- <Typography variant="body-s" tone="secondary">
194
- Счета, клиенты и история будут удалены для всех участников.
195
- </Typography>
196
- </div>
197
- <ConfirmDialog.Root
198
- title="Удалить «Прайм Софт»?"
199
- description="312 счетов, 48 клиентов и вся история удалятся без возможности восстановления."
200
- confirmLabel="Удалить пространство"
201
- onConfirm={removeWorkspace}
140
+ <PageContent.Section
141
+ layout="aside"
142
+ label="Уведомления"
143
+ description="Включаются сразу, без сохранения."
144
+ >
145
+ <Card.Root variant="panel">
146
+ <Card.Body>
147
+ <Fieldset aria-label="Уведомления">
148
+ {ALERTS.map((alert) => (
149
+ <Switch.Root
150
+ key={alert.name}
151
+ name={alert.name}
152
+ defaultChecked={alert.defaultChecked}
153
+ hint={alert.hint}
202
154
  >
203
- <ConfirmDialog.Trigger>
204
- <Button.Root variant="outline" tone="danger">
205
- Удалить
206
- </Button.Root>
207
- </ConfirmDialog.Trigger>
208
- </ConfirmDialog.Root>
155
+ <Switch.Label>{alert.label}</Switch.Label>
156
+ </Switch.Root>
157
+ ))}
158
+ </Fieldset>
159
+ </Card.Body>
160
+ </Card.Root>
161
+ </PageContent.Section>
162
+
163
+ <PageContent.Section
164
+ layout="aside"
165
+ label="Рабочее пространство"
166
+ description="Действия, которые нельзя отменить."
167
+ >
168
+ <Card.Root variant="panel">
169
+ <Card.Body>
170
+ <div className={styles.danger}>
171
+ <div className={styles.dangerText}>
172
+ <Typography variant="title-s">Удалить пространство</Typography>
173
+ <Typography variant="body-s" tone="secondary">
174
+ Счета, клиенты и история будут удалены для всех участников.
175
+ </Typography>
209
176
  </div>
210
- </Card.Body>
211
- </Card.Root>
212
- </div>
213
- </section>
177
+ <ConfirmDialog.Root
178
+ title="Удалить «Прайм Софт»?"
179
+ description="312 счетов, 48 клиентов и вся история удалятся без возможности восстановления."
180
+ confirmLabel="Удалить пространство"
181
+ onConfirm={removeWorkspace}
182
+ >
183
+ <ConfirmDialog.Trigger>
184
+ <Button.Root variant="outline" tone="danger">
185
+ Удалить
186
+ </Button.Root>
187
+ </ConfirmDialog.Trigger>
188
+ </ConfirmDialog.Root>
189
+ </div>
190
+ </Card.Body>
191
+ </Card.Root>
192
+ </PageContent.Section>
214
193
  </PageContent.Body>
215
194
  </PageContent.Root>
216
195
  );
@@ -67,7 +67,7 @@ Avatar.Group overlapping row, gives its size to members
67
67
 
68
68
  | Prop | Type | Default | Description |
69
69
  |---|---|---|---|
70
- | `status` | `"online" \| "offline" \| "away" \| "busy"` | — (required) | Presence state: green, gray, warning or danger dot. |
70
+ | `status` | `"online" \| "offline" \| "away" \| "busy"` | — (required) | Presence state, told by shape and color: `online` a full green dot, `away` a warning crescent, `busy` a danger dot with a bar, `offline` a hollow gray ring. |
71
71
  | `labels` | `Partial<AvatarStatusLabels>` | — | Accessible names of the states, see Labels. |
72
72
  | `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children">` | — | `className` and the other span attributes. |
73
73
 
@@ -129,7 +129,7 @@ No keyboard interaction.
129
129
 
130
130
  ### ARIA
131
131
  - `Avatar.Image` `alt`: empty when the name is written next to it, otherwise the name. Without a photo and without a name nearby, put `aria-label` on `Avatar.Root`.
132
- - `Avatar.Status` is `role="img"` with `aria-label` from `labels`.
132
+ - `Avatar.Status` is `role="img"` with `aria-label` from `labels`. Every state has its own shape (full dot, crescent, dot with a bar, hollow ring), so presence never rests on color alone.
133
133
  - `Avatar.Group` is `role="group"`; give it `aria-label`. Give `Avatar.Overflow` an `aria-label` («Ещё 3 участника»).
134
134
 
135
135
  ### Labels
@@ -148,8 +148,8 @@ No keyboard interaction.
148
148
  | [overview.tsx](examples/overview.tsx) | A photo with initials underneath while it loads, and initials alone — `Avatar.Image`, `Avatar.Fallback`. |
149
149
  | [variants.tsx](examples/variants.tsx) | Every palette hue of the fallback; derive it from a stable user id — `color`. |
150
150
  | [sizes.tsx](examples/sizes.tsx) | Every diameter, 20 to 64 px; initials take 40% of it — `size`. |
151
- | [states.tsx](examples/states.tsx) | A loaded photo, a broken URL that falls back to initials, and an icon fallback for a guest. |
152
- | [presence.tsx](examples/presence.tsx) | A presence dot on the avatar edge, announced by its state name — `Avatar.Status`, `labels`. |
151
+ | [states.tsx](examples/states.tsx) | A loaded photo, a broken URL that falls back to initials, and an icon fallback for a guest — `Avatar.Image`, `Avatar.Fallback`. |
152
+ | [presence.tsx](examples/presence.tsx) | A presence dot on the avatar edge: each state has its own shape and color and is announced by its name — `Avatar.Status`, `labels`. |
153
153
  | [team-group.tsx](examples/team-group.tsx) | Project members as an overlapping row with a «+N» cell; the group size goes to every member — `Avatar.Group`, `Avatar.Overflow`, `size`. |
154
154
  | [src-from-state.tsx](examples/src-from-state.tsx) | A new photo source restarts loading; initials show until the new photo arrives — `src`. |
155
155
 
@@ -1,4 +1,4 @@
1
- /** A presence dot on the avatar edge, announced by its state name — `Avatar.Status`, `labels`. */
1
+ /** A presence dot on the avatar edge: each state has its own shape and color and is announced by its name — `Avatar.Status`, `labels`. */
2
2
  import { Avatar, Typography } from "prime-ui-kit";
3
3
 
4
4
  import styles from "./examples.module.css";
@@ -1,4 +1,4 @@
1
- /** A loaded photo, a broken URL that falls back to initials, and an icon fallback for a guest. */
1
+ /** A loaded photo, a broken URL that falls back to initials, and an icon fallback for a guest — `Avatar.Image`, `Avatar.Fallback`. */
2
2
  import { Avatar, Icon, Typography } from "prime-ui-kit";
3
3
 
4
4
  const PHOTO = "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=128&h=128&fit=crop";
@@ -46,7 +46,7 @@ Banner.Root <div>; variant, tone, size, placement
46
46
  | `tone` | `"neutral" \| "accent" \| "success" \| "warning" \| "danger" \| "info"` | `"info"` | Semantic color of the message. |
47
47
  | `size` | `"xs" \| "s" \| "m" \| "l" \| "xl"` | `"m"` | Spacing, icon and title follow the control tier; the description is one step smaller. |
48
48
  | `placement` | `"inset" \| "page"` | `"inset"` | `inset` — rounded block in the flow or in a card; `page` — edge-to-edge strip above a page, content aligned to the page column. |
49
- | `onDismiss` | `() => void` | — | Renders a close button (top-right, or last in `Banner.Actions`) and calls this on click; the parent unmounts the banner. |
49
+ | `onDismiss` | `() => void` | — | Renders a close button (top-right, or last in `Banner.Actions`) and calls this on click; the parent unmounts the banner, which closes up in place, and moves focus to a sensible place. |
50
50
  | `labels` | `Partial<BannerLabels>` | — | Built-in strings, see Labels. |
51
51
  | `…rest` | `HTMLAttributes<HTMLDivElement>` | — | `className`, `role` (`alert`, `status`, `region`), `aria-label` and the other div attributes. |
52
52
 
@@ -117,7 +117,7 @@ Banner.Root <div>; variant, tone, size, placement
117
117
  | dismissible | `onDismiss` | corner ghost Button (`xs`, `s` on `l` / `xl`), or the last outline Button in `Banner.Actions` |
118
118
  | narrow | own width < 36rem (a banner with `Banner.Actions` is a size container) | actions move under the text |
119
119
 
120
- No `open` prop: the parent mounts and unmounts the banner.
120
+ No `open` prop: the parent mounts and unmounts the banner. A mounted banner enters (a short fade and rise); an unmounted one closes up in place, so the content below glides up instead of jumping. To swap the banner for another block in the same spot (a «restore» button), wrap both in [Crossfade](../crossfade/COMPONENT.md).
121
121
 
122
122
  ## Layout & spacing
123
123
  - `width: 100%`; text column capped at `--prime-layout-reading-max-width`.
@@ -134,7 +134,7 @@ No `open` prop: the parent mounts and unmounts the banner.
134
134
  ### ARIA
135
135
  - The banner has no role of its own: give an error after an action `role="alert"`, a calm status `role="status"`, a page strip `role="region"` with `aria-label`.
136
136
  - The icon is `aria-hidden`: the title carries the meaning, not the color.
137
- - The close button is named by `labels.dismiss`; after dismissing, move focus somewhere sensible.
137
+ - The close button is named by `labels.dismiss`. It leaves with the banner, so after dismissing the parent moves focus to a sensible place: the control that takes the banner's spot, or the next heading or action of the section (see `dismissible.tsx`).
138
138
 
139
139
  ### Labels
140
140
  <!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
@@ -150,9 +150,9 @@ No `open` prop: the parent mounts and unmounts the banner.
150
150
  | [variants.tsx](examples/variants.tsx) | Every treatment on every tone: soft by default, solid for urgent, outline for calm — `variant`, `tone`. |
151
151
  | [sizes.tsx](examples/sizes.tsx) | Every tier: padding, icon and title follow the control tier, the description is one step smaller — `size`. |
152
152
  | [structure.tsx](examples/structure.tsx) | Optional parts: a one-line title, a title with a description, and actions where the close button joins the row — `Banner.Description`, `Banner.Actions`, `onDismiss`. |
153
- | [dismissible.tsx](examples/dismissible.tsx) | The parent owns visibility: the close button calls back and the parent unmounts the banner; the button name comes from labels — `onDismiss`, `labels`. |
153
+ | [dismissible.tsx](examples/dismissible.tsx) | The parent owns visibility: the close button calls back, the banner closes up and focus moves to the restore button; the button name comes from labels — `onDismiss`, `labels`. |
154
154
  | [page-strip.tsx](examples/page-strip.tsx) | An edge-to-edge strip above a page and a rounded block inside a card — `placement`. |
155
- | [narrow.tsx](examples/narrow.tsx) | Below 36rem of its own width the banner moves the actions under the text. |
155
+ | [narrow.tsx](examples/narrow.tsx) | Below 36rem of its own width the banner moves the actions under the text — `Banner.Actions`. |
156
156
 
157
157
  ## Mistakes
158
158
  - A hand-made × next to the banner → pass `onDismiss`.
@@ -162,4 +162,4 @@ No `open` prop: the parent mounts and unmounts the banner.
162
162
 
163
163
  ## Related
164
164
  - **Built from:** [Button](../button/COMPONENT.md) (close button), Icon (`action.close`)
165
- - **See also:** [Notification](../notification/COMPONENT.md), [Hint](../hint/COMPONENT.md), [EmptyPage](../empty-page/COMPONENT.md), [Badge](../badge/COMPONENT.md)
165
+ - **See also:** [Crossfade](../crossfade/COMPONENT.md), [Notification](../notification/COMPONENT.md), [Hint](../hint/COMPONENT.md), [EmptyPage](../empty-page/COMPONENT.md), [Badge](../badge/COMPONENT.md)
@@ -1,18 +1,34 @@
1
- /** The parent owns visibility: the close button calls back and the parent unmounts the banner; the button name comes from labels — `onDismiss`, `labels`. */
2
- import { Banner, Button, Icon } from "prime-ui-kit";
1
+ /** The parent owns visibility: the close button calls back, the banner closes up and focus moves to the restore button; the button name comes from labels — `onDismiss`, `labels`. */
2
+ import { Banner, Button, Crossfade, Icon } from "prime-ui-kit";
3
3
  import * as React from "react";
4
4
 
5
5
  import styles from "./examples.module.css";
6
6
 
7
7
  export default function BannerDismissibleExample() {
8
8
  const [visible, setVisible] = React.useState(true);
9
+ const bannerRef = React.useRef<HTMLDivElement>(null);
10
+ const restoreRef = React.useRef<HTMLButtonElement>(null);
11
+ const toggled = React.useRef(false);
12
+
13
+ // The pressed button leaves with its block: focus goes to the control that takes its place.
14
+ React.useEffect(() => {
15
+ if (!toggled.current) return;
16
+ if (visible) bannerRef.current?.querySelector("button")?.focus();
17
+ else restoreRef.current?.focus();
18
+ }, [visible]);
19
+
20
+ const toggle = (next: boolean) => {
21
+ toggled.current = true;
22
+ setVisible(next);
23
+ };
9
24
 
10
25
  return (
11
- <div className={styles.stack}>
26
+ <Crossfade state={visible ? "banner" : "restore"} className={styles.region}>
12
27
  {visible ? (
13
28
  <Banner.Root
29
+ ref={bannerRef}
14
30
  tone="accent"
15
- onDismiss={() => setVisible(false)}
31
+ onDismiss={() => toggle(false)}
16
32
  labels={{ dismiss: "Скрыть объявление" }}
17
33
  >
18
34
  <Banner.Content>
@@ -24,15 +40,10 @@ export default function BannerDismissibleExample() {
24
40
  </Banner.Content>
25
41
  </Banner.Root>
26
42
  ) : (
27
- <Button.Root
28
- variant="outline"
29
- tone="neutral"
30
- className={styles.restore}
31
- onClick={() => setVisible(true)}
32
- >
43
+ <Button.Root ref={restoreRef} variant="outline" tone="neutral" onClick={() => toggle(true)}>
33
44
  Вернуть баннер
34
45
  </Button.Root>
35
46
  )}
36
- </div>
47
+ </Crossfade>
37
48
  );
38
49
  }
@@ -26,7 +26,7 @@
26
26
  width: min(100%, calc(var(--prime-space-20) * 4));
27
27
  }
28
28
 
29
- /* dismiss.tsx */
30
- .restore {
31
- align-self: flex-start;
29
+ /* dismissible.tsx: the swapped region takes the row's width. */
30
+ .region {
31
+ width: 100%;
32
32
  }
@@ -1,4 +1,4 @@
1
- /** Below 36rem of its own width the banner moves the actions under the text. */
1
+ /** Below 36rem of its own width the banner moves the actions under the text — `Banner.Actions`. */
2
2
  import { Banner, Button, Icon } from "prime-ui-kit";
3
3
 
4
4
  import styles from "./examples.module.css";
@@ -123,7 +123,7 @@ Root attributes: `data-size`, `data-collapsible` (`"true"` / `"false"`).
123
123
  | [overview.tsx](examples/overview.tsx) | The path to this page: links to the levels above and the current page last — `href`, `current`. |
124
124
  | [sizes.tsx](examples/sizes.tsx) | Every size tier; text and chevrons follow the control tier — `size`. |
125
125
  | [with-icon.tsx](examples/with-icon.tsx) | The root level as a home icon; a link without text is named for screen readers — `aria-label`. |
126
- | [overflow.tsx](examples/overflow.tsx) | A long path: levels truncate, and in a 320px container the middle ones collapse into «…», which screen readers announce as hidden levels. |
126
+ | [overflow.tsx](examples/overflow.tsx) | A long path: levels truncate, and in a 320px container the middle ones collapse into «…», which screen readers announce as hidden levels — `Breadcrumb.Item`, `current`. |
127
127
  | [ellipsis.tsx](examples/ellipsis.tsx) | Levels skipped on purpose shown as «…» with hidden text for screen readers — `Breadcrumb.Ellipsis`. |
128
128
 
129
129
  ## Mistakes
@@ -1,4 +1,4 @@
1
- /** A long path: levels truncate, and in a 320px container the middle ones collapse into «…», which screen readers announce as hidden levels. */
1
+ /** A long path: levels truncate, and in a 320px container the middle ones collapse into «…», which screen readers announce as hidden levels — `Breadcrumb.Item`, `current`. */
2
2
  import { Breadcrumb } from "prime-ui-kit";
3
3
 
4
4
  import styles from "./examples.module.css";
@@ -163,7 +163,7 @@ Card.Root asChild <a> or <button> (the only child) holding the pa
163
163
  Without `asChild` a card is static: no hover, focus or press of its own. With it: `data-interactive` on the element.
164
164
 
165
165
  ## Layout & spacing
166
- - Grid of cards: `repeat(auto-fit, minmax(min(100%, 14rem), 1fr))`, gap `--prime-space-4`.
166
+ - Cards on a page: one `PageContent.Grid` with a `PageContent.Cell span` per card (16 on both axes, the cards of a row share one height); a grid inside one block is the block's own `repeat(auto-fit, minmax(min(100%, 14rem), 1fr))`, gap `--prime-space-4`.
167
167
  - Radius `--prime-card-radius`, no border. In `panel` and `list` the `Header` is a padded zone with a faint inset hairline below; in `stat-trend` it can pair a leading Badge or icon with a compact `Card.Value`.
168
168
  - Padding `--prime-card-padding-m` (20); `Body` gap `--prime-card-gap` (16); fields inside `Body` 20 apart, one ladder step off the card.
169
169
  - Depth comes from nesting (foundation §4): on the page — the AppShell panel is the page — a card is layer 1 (white in light) with the raised whisper; inside another card it is the next layer, a flat tile. The ladder stops at layer 4.
@@ -196,11 +196,11 @@ No `labels`.
196
196
  | [overview.tsx](examples/overview.tsx) | A company settings panel: a header, fields on the surface fill and buttons in the footer — `Card.Header`, `Card.Body`, `Card.Footer`. |
197
197
  | [variants.tsx](examples/variants.tsx) | KPI templates: an icon tile with a value, a badge with a value in the header row, and a large value with its change and a reference line — `variant`, `Card.Delta`, `Card.Meta`. |
198
198
  | [kpi-media.tsx](examples/kpi-media.tsx) | A KPI with a sparkline or a fill level in the bottom slot of `mini` — `Card.Media`. |
199
- | [panel-chart.tsx](examples/panel-chart.tsx) | A chart widget: a header with a short description and a period switch, a summary line and an edge-to-edge chart — `Card.Header`, `Card.Description`, `Card.Media`. |
199
+ | [panel-chart.tsx](examples/panel-chart.tsx) | A chart widget: a header with a short description and a period switch, and a Sparkline with the period total over its chart — `Card.Header`, `Card.Description`, `Card.Body`. |
200
200
  | [interactive.tsx](examples/interactive.tsx) | Whole cards as links to their reports: one tab stop each, a lift on hover, a press and the focus ring — `asChild`. |
201
201
  | [content-templates.tsx](examples/content-templates.tsx) | Content templates: a call to action, an events list and a campaign tile with a cover — `Card.Footer`, `Card.List`, `Card.Media`. |
202
202
  | [flat.tsx](examples/flat.tsx) | A flat tile without the raised shadow next to the default one, for dense grids — `flat`. |
203
- | [narrow.tsx](examples/narrow.tsx) | The card is a size container: the split template stacks its cells below 22rem and the trend value shrinks below 20rem. |
203
+ | [narrow.tsx](examples/narrow.tsx) | The card is a size container: the split template stacks its cells below 22rem and the trend value shrinks below 20rem — `variant`. |
204
204
 
205
205
  ## Mistakes
206
206
  - A `<div>` with a border styled as a card → use `Card.Root`; depth comes from fill, not lines.
@@ -209,10 +209,11 @@ No `labels`.
209
209
  - Custom padding on a `panel` root → padding belongs to `Header` / `Body` / `Footer`.
210
210
  - A wrapper `<div>` around the label and the value of `mini` → put `Icon`, `Label`, `Value` straight into the root (or into a `split` cell); the template lays them out.
211
211
  - A chart SVG with only a `viewBox` → give it a CSS height.
212
+ - A hand-drawn trend SVG in a chart widget → `Sparkline` in the `Body` (or `Media`); a chart with a legend and states → `ChartFrame`.
212
213
  - A clickable `<div>` card with `onClick` → `asChild` with a real `<a href>` (Cmd-click, a new tab) or a `<button>`.
213
214
  - Buttons or links inside an `asChild` card → nested interactive elements; move them out of the card.
214
215
  - The reference figures written into `Card.Delta` or `Card.Label` → `Card.Meta`.
215
216
 
216
217
  ## Related
217
218
  - **Built from:** —
218
- - **See also:** [PageContent](../page-content/COMPONENT.md), [DescriptionList](../description-list/COMPONENT.md) (record details inside a `Card.Body`), [Fieldset](../fieldset/COMPONENT.md) (groups of fields inside a settings card), [DataTable](../data-table/COMPONENT.md), [Badge](../badge/COMPONENT.md), [ProgressBar](../progress-bar/COMPONENT.md), [SegmentedControl](../segmented-control/COMPONENT.md)
219
+ - **See also:** [PageContent](../page-content/COMPONENT.md), [DescriptionList](../description-list/COMPONENT.md) (record details inside a `Card.Body`), [Fieldset](../fieldset/COMPONENT.md) (groups of fields inside a settings card), [DataTable](../data-table/COMPONENT.md), [Badge](../badge/COMPONENT.md), [ProgressBar](../progress-bar/COMPONENT.md), [SegmentedControl](../segmented-control/COMPONENT.md), [Sparkline](../sparkline/COMPONENT.md) (a trend with its total inside a `panel`), [ChartFrame](../chart-frame/COMPONENT.md) (a full chart with legend and states)
@@ -28,34 +28,6 @@
28
28
  width: min(100%, calc(var(--prime-space-24) * 6));
29
29
  }
30
30
 
31
- /* Sparkline: fixed height from tokens, stretches to the card width. */
32
- .spark {
33
- display: block;
34
- width: 100%;
35
- height: var(--prime-space-10);
36
- color: var(--prime-color-accent-default);
37
- overflow: visible;
38
- }
39
-
40
- .chart {
41
- display: block;
42
- width: 100%;
43
- /* Fixed height: a viewBox-only SVG would otherwise scale to a square of the card width. */
44
- height: calc(var(--prime-space-24) * 2);
45
- color: var(--prime-color-accent-default);
46
- }
47
-
48
- .chartFill {
49
- fill: var(--prime-color-accent-soft);
50
- }
51
-
52
- .chartLine {
53
- fill: none;
54
- stroke: currentColor;
55
- stroke-width: calc(2 * var(--prime-border-width));
56
- vector-effect: non-scaling-stroke;
57
- }
58
-
59
31
  .cover {
60
32
  background: linear-gradient(
61
33
  135deg,
@@ -1,9 +1,19 @@
1
1
  /** A KPI with a sparkline or a fill level in the bottom slot of `mini` — `Card.Media`. */
2
- import { Card, Icon, ProgressBar } from "prime-ui-kit";
2
+ import { Card, Icon, ProgressBar, Sparkline, type SparklinePoint } from "prime-ui-kit";
3
3
 
4
4
  import styles from "./examples.module.css";
5
5
 
6
- const SPARKLINE = "M0 32 L15 28 L30 30 L45 18 L60 22 L75 12 L90 16 L105 8 L120 4";
6
+ const REQUESTS: SparklinePoint[] = [
7
+ { label: "12:00", value: 9_120 },
8
+ { label: "12:10", value: 9_640 },
9
+ { label: "12:20", value: 9_380 },
10
+ { label: "12:30", value: 10_910 },
11
+ { label: "12:40", value: 10_470 },
12
+ { label: "12:50", value: 11_760 },
13
+ { label: "13:00", value: 11_290 },
14
+ { label: "13:10", value: 12_310 },
15
+ { label: "13:20", value: 12_840 },
16
+ ];
7
17
 
8
18
  export default function CardKpiMediaExample() {
9
19
  return (
@@ -15,15 +25,7 @@ export default function CardKpiMediaExample() {
15
25
  <Card.Label>Запросы в минуту</Card.Label>
16
26
  <Card.Value>12 840</Card.Value>
17
27
  <Card.Media>
18
- {/* The SVG stretches to the card width; CSS sets its height, not the viewBox. */}
19
- <svg
20
- className={styles.spark}
21
- viewBox="0 0 120 40"
22
- preserveAspectRatio="none"
23
- aria-hidden="true"
24
- >
25
- <path d={SPARKLINE} className={styles.chartLine} />
26
- </svg>
28
+ <Sparkline layout="compact" data={REQUESTS} label="Запросы в минуту за час" />
27
29
  </Card.Media>
28
30
  </Card.Root>
29
31
 
@@ -1,4 +1,4 @@
1
- /** The card is a size container: the split template stacks its cells below 22rem and the trend value shrinks below 20rem. */
1
+ /** The card is a size container: the split template stacks its cells below 22rem and the trend value shrinks below 20rem — `variant`. */
2
2
  import { Card, Icon } from "prime-ui-kit";
3
3
 
4
4
  import styles from "./examples.module.css";
@@ -1,43 +1,66 @@
1
- /** A chart widget: a header with a short description and a period switch, a summary line and an edge-to-edge chart — `Card.Header`, `Card.Description`, `Card.Media`. */
2
- import { Card, SegmentedControl, Typography } from "prime-ui-kit";
1
+ /** A chart widget: a header with a short description and a period switch, and a Sparkline with the period total over its chart — `Card.Header`, `Card.Description`, `Card.Body`. */
2
+ import { Card, SegmentedControl, Sparkline, type SparklinePoint } from "prime-ui-kit";
3
+ import * as React from "react";
3
4
 
4
5
  import styles from "./examples.module.css";
5
6
 
6
- const POINTS = [42, 48, 45, 60, 58, 72, 66, 80, 76, 88, 84, 96];
7
+ type Period = "week" | "month";
7
8
 
8
- /** An area chart path in a 100 × 100 box; the SVG stretches over the whole `Card.Media`. */
9
- const LINE = POINTS.map(
10
- (value, i) => `${i === 0 ? "M" : "L"}${(i * 100) / (POINTS.length - 1)} ${100 - value}`,
11
- ).join(" ");
9
+ const REVENUE: Record<Period, SparklinePoint[]> = {
10
+ week: [
11
+ { label: "6 окт", value: 412_000 },
12
+ { label: "7 окт", value: 468_000 },
13
+ { label: "8 окт", value: 445_000 },
14
+ { label: "9 окт", value: 603_000 },
15
+ { label: "10 окт", value: 581_000 },
16
+ { label: "11 окт", value: 214_000 },
17
+ { label: "12 окт", value: 187_000 },
18
+ ],
19
+ month: [
20
+ { label: "сен, 1 нед.", value: 2_480_000 },
21
+ { label: "сен, 2 нед.", value: 2_760_000 },
22
+ { label: "сен, 3 нед.", value: 2_610_000 },
23
+ { label: "сен, 4 нед.", value: 3_140_000 },
24
+ { label: "окт, 1 нед.", value: 2_910_000 },
25
+ ],
26
+ };
27
+
28
+ /** The same total for the period before: the badge at rest compares the headline with it. */
29
+ const PREVIOUS: Record<Period, number> = {
30
+ week: 2_640_000,
31
+ month: 12_900_000,
32
+ };
33
+
34
+ const formatRubles = (value: number) => `${value.toLocaleString("ru-RU")} ₽`;
12
35
 
13
36
  export default function CardPanelChartExample() {
37
+ const [period, setPeriod] = React.useState<Period>("month");
38
+
14
39
  return (
15
40
  <div className={styles.panel}>
16
41
  <Card.Root variant="panel">
17
42
  <Card.Header>
18
43
  <Card.Title>Выручка</Card.Title>
19
44
  <Card.Description>Оплаченные счета, без возвратов</Card.Description>
20
- <SegmentedControl.Root size="xs" defaultValue="month" aria-label="Период">
45
+ <SegmentedControl.Root
46
+ size="xs"
47
+ value={period}
48
+ onValueChange={(value) => setPeriod(value as Period)}
49
+ aria-label="Период"
50
+ >
21
51
  <SegmentedControl.Item value="week">Неделя</SegmentedControl.Item>
22
52
  <SegmentedControl.Item value="month">Месяц</SegmentedControl.Item>
23
53
  </SegmentedControl.Root>
24
54
  </Card.Header>
25
55
  <Card.Body>
26
- <Typography variant="body-s" tone="secondary">
27
- С начала квартала: 12,6 млн ₽, план выполнен на 84%.
28
- </Typography>
56
+ <Sparkline
57
+ data={REVENUE[period]}
58
+ label="Оплачено за период"
59
+ summary="sum"
60
+ compareTo={PREVIOUS[period]}
61
+ formatValue={formatRubles}
62
+ />
29
63
  </Card.Body>
30
- <Card.Media>
31
- <svg
32
- className={styles.chart}
33
- viewBox="0 0 100 100"
34
- preserveAspectRatio="none"
35
- aria-hidden="true"
36
- >
37
- <path className={styles.chartFill} d={`${LINE} L100 100 L0 100 Z`} />
38
- <path className={styles.chartLine} d={LINE} />
39
- </svg>
40
- </Card.Media>
41
64
  </Card.Root>
42
65
  </div>
43
66
  );