prime-ui-kit 0.13.1 → 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-XNOJEX55.js → chunk-P6ZO37JM.js} +1 -1
  3. package/dist/chunks/{chunk-JV2KB5JZ.js → chunk-SYEX7OVT.js} +3572 -2865
  4. package/dist/chunks/{chunk-PK2GC77E.js → chunk-WXKRUVSE.js} +164 -140
  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
@@ -156,7 +156,7 @@ export default function DashboardPattern() {
156
156
  const [period, setPeriod] = React.useState<Period>("month");
157
157
 
158
158
  return (
159
- <PageContent.Section aria-labelledby="dashboard-title">
159
+ <PageContent.Page aria-labelledby="dashboard-title">
160
160
  <PageContent.Header>
161
161
  <PageContent.Title id="dashboard-title">Обзор</PageContent.Title>
162
162
  <PageContent.Description>Продажи и оплаты по всем менеджерам.</PageContent.Description>
@@ -179,148 +179,162 @@ export default function DashboardPattern() {
179
179
  </PageContent.Actions>
180
180
  </PageContent.Header>
181
181
  <PageContent.Body>
182
- {/* Another period is another set of values: the row cross-fades instead of flipping. */}
183
- <Crossfade state={period}>
184
- <div className={styles.kpis}>
185
- {KPIS[period].map((kpi) => (
186
- <Card.Root key={kpi.label} variant="stat-trend">
187
- <Card.Label>{kpi.label}</Card.Label>
188
- <Card.Value>{kpi.value}</Card.Value>
189
- <Card.Delta tone={kpi.tone}>{kpi.delta}</Card.Delta>
190
- </Card.Root>
191
- ))}
192
- </div>
193
- </Crossfade>
182
+ {/* One board: every tile of the screen in one grid, 16 apart on both axes. */}
183
+ <PageContent.Grid>
184
+ {KPIS[period].map((kpi) => (
185
+ <PageContent.Cell key={kpi.label} span={3}>
186
+ {/* Another period is another set of values: the tile cross-fades instead of flipping. */}
187
+ <Crossfade state={period}>
188
+ <Card.Root variant="stat-trend">
189
+ <Card.Label>{kpi.label}</Card.Label>
190
+ <Card.Value>{kpi.value}</Card.Value>
191
+ <Card.Delta tone={kpi.tone}>{kpi.delta}</Card.Delta>
192
+ </Card.Root>
193
+ </Crossfade>
194
+ </PageContent.Cell>
195
+ ))}
194
196
 
195
- <Card.Root variant="panel">
196
- <Card.Body>
197
- <Sparkline data={REVENUE[period]} label="Выручка по дням" formatValue={formatRubles} />
198
- </Card.Body>
199
- </Card.Root>
197
+ <PageContent.Cell>
198
+ <Card.Root variant="panel">
199
+ <Card.Body>
200
+ <Sparkline
201
+ data={REVENUE[period]}
202
+ label="Выручка по дням"
203
+ summary="sum"
204
+ formatValue={formatRubles}
205
+ />
206
+ </Card.Body>
207
+ </Card.Root>
208
+ </PageContent.Cell>
200
209
 
201
- <ChartFrame.Root series={BILLING_SERIES} formatValue={formatMillionsShort}>
202
- <ChartFrame.Header>
203
- <ChartFrame.Title as="h2">Выставлено и оплачено</ChartFrame.Title>
204
- <ChartFrame.Description>Счета за период, млн ₽</ChartFrame.Description>
205
- </ChartFrame.Header>
206
- <ChartFrame.Plot
207
- renderPlot={({ width, height, series }) => {
208
- const rows = BILLING[period];
209
- const top = Math.ceil(Math.max(...rows.map((row) => row.invoiced)));
210
- const innerH = height - BILLING_MARGIN.top - BILLING_MARGIN.bottom;
211
- const band = (width - BILLING_MARGIN.left) / rows.length;
212
- const bar = Math.min(28, (band - 16) / series.length);
213
- const y = (value: number) => BILLING_MARGIN.top + innerH * (1 - value / top);
214
- return (
215
- <svg width={width} height={height} aria-hidden="true">
216
- {[0, top / 2, top].map((tick) => (
217
- <g key={tick}>
218
- <line
219
- x1={BILLING_MARGIN.left}
220
- x2={width}
221
- y1={y(tick)}
222
- y2={y(tick)}
223
- stroke={tick === 0 ? chartColors.axis : chartColors.grid}
224
- />
225
- <text
226
- x={BILLING_MARGIN.left - 8}
227
- y={y(tick)}
228
- textAnchor="end"
229
- dominantBaseline="middle"
230
- >
231
- {tick.toLocaleString("ru-RU")}
232
- </text>
233
- </g>
234
- ))}
235
- {rows.map((row, i) => {
236
- const center = BILLING_MARGIN.left + band * i + band / 2;
237
- return (
238
- <g key={row.label}>
239
- {series.map((s, j) => {
240
- const value = s.id === "paid" ? row.paid : row.invoiced;
241
- return (
242
- <rect
243
- key={s.id}
244
- data-hidden={s.hidden}
245
- x={center - (bar * series.length) / 2 + bar * j}
246
- y={y(value)}
247
- width={bar - 2}
248
- height={y(0) - y(value)}
249
- rx={2}
250
- fill={s.color}
251
- />
252
- );
253
- })}
254
- <text x={center} y={height - 4} textAnchor="middle">
255
- {row.label}
256
- </text>
257
- </g>
258
- );
259
- })}
260
- </svg>
261
- );
262
- }}
263
- />
264
- <ChartFrame.Legend />
265
- <ChartFrame.Table
266
- rows={BILLING[period].map((row) => ({
267
- label: row.label,
268
- values: { invoiced: row.invoiced, paid: row.paid },
269
- }))}
270
- />
271
- </ChartFrame.Root>
210
+ {/* The chart spans two rows beside the two panels and fills their height. */}
211
+ <PageContent.Cell span={8} rows={2}>
212
+ <ChartFrame.Root series={BILLING_SERIES} formatValue={formatMillionsShort}>
213
+ <ChartFrame.Header>
214
+ <ChartFrame.Title as="h2">Выставлено и оплачено</ChartFrame.Title>
215
+ <ChartFrame.Description>Счета за период, млн ₽</ChartFrame.Description>
216
+ </ChartFrame.Header>
217
+ <ChartFrame.Plot
218
+ height="fill"
219
+ renderPlot={({ width, height, series }) => {
220
+ const rows = BILLING[period];
221
+ const top = Math.ceil(Math.max(...rows.map((row) => row.invoiced)));
222
+ const innerH = height - BILLING_MARGIN.top - BILLING_MARGIN.bottom;
223
+ const band = (width - BILLING_MARGIN.left) / rows.length;
224
+ const bar = Math.min(28, (band - 16) / series.length);
225
+ const y = (value: number) => BILLING_MARGIN.top + innerH * (1 - value / top);
226
+ return (
227
+ <svg width={width} height={height} aria-hidden="true">
228
+ {[0, top / 2, top].map((tick) => (
229
+ <g key={tick}>
230
+ <line
231
+ x1={BILLING_MARGIN.left}
232
+ x2={width}
233
+ y1={y(tick)}
234
+ y2={y(tick)}
235
+ stroke={tick === 0 ? chartColors.axis : chartColors.grid}
236
+ />
237
+ <text
238
+ x={BILLING_MARGIN.left - 8}
239
+ y={y(tick)}
240
+ textAnchor="end"
241
+ dominantBaseline="middle"
242
+ >
243
+ {tick.toLocaleString("ru-RU")}
244
+ </text>
245
+ </g>
246
+ ))}
247
+ {rows.map((row, i) => {
248
+ const center = BILLING_MARGIN.left + band * i + band / 2;
249
+ return (
250
+ <g key={row.label}>
251
+ {series.map((s, j) => {
252
+ const value = s.id === "paid" ? row.paid : row.invoiced;
253
+ return (
254
+ <rect
255
+ key={s.id}
256
+ data-hidden={s.hidden}
257
+ x={center - (bar * series.length) / 2 + bar * j}
258
+ y={y(value)}
259
+ width={bar - 2}
260
+ height={y(0) - y(value)}
261
+ rx={2}
262
+ fill={s.color}
263
+ />
264
+ );
265
+ })}
266
+ <text x={center} y={height - 4} textAnchor="middle">
267
+ {row.label}
268
+ </text>
269
+ </g>
270
+ );
271
+ })}
272
+ </svg>
273
+ );
274
+ }}
275
+ />
276
+ <ChartFrame.Legend />
277
+ <ChartFrame.Table
278
+ rows={BILLING[period].map((row) => ({
279
+ label: row.label,
280
+ values: { invoiced: row.invoiced, paid: row.paid },
281
+ }))}
282
+ />
283
+ </ChartFrame.Root>
284
+ </PageContent.Cell>
272
285
 
273
- <div className={styles.panels}>
274
- <Card.Root variant="panel">
275
- <Card.Header>
276
- <Card.Title as="h2">План продаж</Card.Title>
277
- <Typography as="span" variant="caption" tone="muted">
278
- Октябрь
279
- </Typography>
280
- </Card.Header>
281
- <Card.Body>
282
- <div className={styles.bars}>
283
- {PLAN.map((row) => (
284
- <ProgressBar
285
- key={row.manager}
286
- value={row.value}
287
- label={row.manager}
288
- tone={row.value < 50 ? "warning" : "accent"}
289
- showValue
290
- />
291
- ))}
292
- </div>
293
- </Card.Body>
294
- </Card.Root>
286
+ <PageContent.Cell span={4}>
287
+ <Card.Root variant="panel">
288
+ <Card.Header>
289
+ <Card.Title as="h2">План продаж</Card.Title>
290
+ <Typography as="span" variant="caption" tone="muted">
291
+ Октябрь
292
+ </Typography>
293
+ </Card.Header>
294
+ <Card.Body>
295
+ <div className={styles.bars}>
296
+ {PLAN.map((row) => (
297
+ <ProgressBar
298
+ key={row.manager}
299
+ value={row.value}
300
+ label={row.manager}
301
+ tone={row.value < 50 ? "warning" : "accent"}
302
+ showValue
303
+ />
304
+ ))}
305
+ </div>
306
+ </Card.Body>
307
+ </Card.Root>
308
+ </PageContent.Cell>
295
309
 
296
- <Card.Root variant="panel">
297
- <Card.Header>
298
- <Card.Title as="h2">Дебиторка</Card.Title>
299
- <Typography as="span" variant="caption" tone="muted">
300
- 8,2 млн ₽
301
- </Typography>
302
- </Card.Header>
303
- <Card.Body>
304
- <ProgressBar
305
- segments={RECEIVABLES}
306
- label="По сроку оплаты"
307
- legend
308
- formatValue={formatMillions}
309
- />
310
- </Card.Body>
311
- </Card.Root>
312
- </div>
310
+ <PageContent.Cell span={4}>
311
+ <Card.Root variant="panel">
312
+ <Card.Header>
313
+ <Card.Title as="h2">Дебиторка</Card.Title>
314
+ <Typography as="span" variant="caption" tone="muted">
315
+ 8,2 млн ₽
316
+ </Typography>
317
+ </Card.Header>
318
+ <Card.Body>
319
+ <ProgressBar
320
+ segments={RECEIVABLES}
321
+ label="По сроку оплаты"
322
+ legend
323
+ formatValue={formatMillions}
324
+ />
325
+ </Card.Body>
326
+ </Card.Root>
327
+ </PageContent.Cell>
328
+ </PageContent.Grid>
313
329
 
314
- <section className={styles.recent} aria-labelledby="payments-title">
315
- <div className={styles.recentHeader}>
316
- <Typography as="h2" variant="title-m" id="payments-title">
317
- Последние оплаты
318
- </Typography>
330
+ {/* A new topic: a titled section, set off by more air than the tiles. */}
331
+ <PageContent.Section label="Последние оплаты">
332
+ <PageContent.Actions>
319
333
  <LinkButton href="#invoices">Все счета</LinkButton>
320
- </div>
334
+ </PageContent.Actions>
321
335
  <DataTable columns={COLUMNS} rows={PAYMENTS} getRowKey={(row) => row.id} paging="none" />
322
- </section>
336
+ </PageContent.Section>
323
337
  </PageContent.Body>
324
- </PageContent.Section>
338
+ </PageContent.Page>
325
339
  );
326
340
  }
@@ -17,10 +17,7 @@
17
17
  }
18
18
 
19
19
  .main {
20
- display: grid;
21
20
  flex: 999 1 calc(var(--prime-space-24) * 5);
22
- gap: var(--prime-space-4);
23
- min-width: 0;
24
21
  }
25
22
 
26
23
  .aside {
@@ -74,7 +74,7 @@ const COLUMNS: DataTableColumn<Line>[] = [
74
74
 
75
75
  export default function DetailPagePattern() {
76
76
  return (
77
- <PageContent.Section aria-labelledby="invoice-title">
77
+ <PageContent.Page aria-labelledby="invoice-title">
78
78
  <PageContent.Header>
79
79
  <Breadcrumb.Root>
80
80
  <Breadcrumb.Item href="#invoices">Счета</Breadcrumb.Item>
@@ -114,10 +114,7 @@ export default function DetailPagePattern() {
114
114
  </PageContent.Header>
115
115
  <PageContent.Body>
116
116
  <div className={styles.columns}>
117
- <section className={styles.main} aria-labelledby="invoice-lines">
118
- <Typography as="h2" variant="title-m" id="invoice-lines">
119
- Позиции
120
- </Typography>
117
+ <PageContent.Section label="Позиции" className={styles.main}>
121
118
  <DataTable
122
119
  columns={COLUMNS}
123
120
  rows={LINES}
@@ -152,7 +149,7 @@ export default function DetailPagePattern() {
152
149
  </dd>
153
150
  </div>
154
151
  </dl>
155
- </section>
152
+ </PageContent.Section>
156
153
  <aside className={styles.aside} aria-label="Клиент и история">
157
154
  <Card.Root variant="panel">
158
155
  <Card.Header>
@@ -204,6 +201,6 @@ export default function DetailPagePattern() {
204
201
  </aside>
205
202
  </div>
206
203
  </PageContent.Body>
207
- </PageContent.Section>
204
+ </PageContent.Page>
208
205
  );
209
206
  }
@@ -107,7 +107,7 @@ export default function FormDrawerPattern() {
107
107
  };
108
108
 
109
109
  return (
110
- <PageContent.Section aria-labelledby="clients-title">
110
+ <PageContent.Page aria-labelledby="clients-title">
111
111
  <PageContent.Header>
112
112
  <PageContent.Title id="clients-title">Клиенты</PageContent.Title>
113
113
  <PageContent.Description>
@@ -235,6 +235,6 @@ export default function FormDrawerPattern() {
235
235
  <PageContent.Body>
236
236
  <DataTable columns={COLUMNS} rows={clients} getRowKey={(row) => row.id} paging="none" />
237
237
  </PageContent.Body>
238
- </PageContent.Section>
238
+ </PageContent.Page>
239
239
  );
240
240
  }
@@ -386,7 +386,7 @@ export default function KanbanBoardPattern() {
386
386
  const nextStage = STAGE_BY_ID.get(stageOrder[stageOrder.indexOf(dealStage) + 1] ?? "");
387
387
 
388
388
  return (
389
- <PageContent.Section aria-labelledby="deals-title">
389
+ <PageContent.Page aria-labelledby="deals-title">
390
390
  <PageContent.Header>
391
391
  <PageContent.Title id="deals-title">Сделки</PageContent.Title>
392
392
  <PageContent.Description>
@@ -443,6 +443,8 @@ export default function KanbanBoardPattern() {
443
443
  </SmartFilter.Root>
444
444
  <Crossfade state={view}>
445
445
  {view === "board" ? (
446
+ // `plain` (the default): the board is the page's main region, so the stages are no
447
+ // boxes — their headers and the deal cards stand on the page, one level of nesting.
446
448
  <Kanban.Root
447
449
  aria-label="Воронка продаж"
448
450
  className={styles.board}
@@ -607,6 +609,6 @@ export default function KanbanBoardPattern() {
607
609
  </Drawer.Content>
608
610
  </Drawer.Root>
609
611
  </PageContent.Body>
610
- </PageContent.Section>
612
+ </PageContent.Page>
611
613
  );
612
614
  }
@@ -268,7 +268,7 @@ export default function ListPagePattern() {
268
268
  };
269
269
 
270
270
  return (
271
- <PageContent.Section aria-labelledby="invoices-title">
271
+ <PageContent.Page aria-labelledby="invoices-title">
272
272
  <PageContent.Header>
273
273
  <PageContent.Title id="invoices-title">Счета</PageContent.Title>
274
274
  <PageContent.Description>
@@ -329,6 +329,6 @@ export default function ListPagePattern() {
329
329
  }
330
330
  />
331
331
  </PageContent.Body>
332
- </PageContent.Section>
332
+ </PageContent.Page>
333
333
  );
334
334
  }
@@ -1,27 +1,5 @@
1
1
  /* Media library: layout inside the library block only; the page rhythm comes from PageContent. */
2
2
 
3
- /* A section without a card: the heading row 16 above the grid. */
4
- .library {
5
- display: grid;
6
- gap: var(--prime-space-4);
7
- min-width: 0;
8
- }
9
-
10
- /* Heading and count on the start side, the selection actions on the end; they wrap on a phone. */
11
- .libraryHeader {
12
- display: flex;
13
- flex-wrap: wrap;
14
- align-items: center;
15
- justify-content: space-between;
16
- gap: var(--prime-space-3) var(--prime-space-4);
17
- }
18
-
19
- .heading {
20
- display: grid;
21
- gap: var(--prime-space-1);
22
- min-width: 0;
23
- }
24
-
25
3
  .bar {
26
4
  display: flex;
27
5
  flex-wrap: wrap;
@@ -362,7 +362,7 @@ export default function MediaLibraryPattern() {
362
362
  const totalSize = visible.reduce((sum, media) => sum + media.size, 0);
363
363
 
364
364
  return (
365
- <PageContent.Section aria-labelledby="media-title">
365
+ <PageContent.Page aria-labelledby="media-title">
366
366
  <PageContent.Header>
367
367
  <PageContent.Title id="media-title">Медиатека объекта</PageContent.Title>
368
368
  <PageContent.Description>
@@ -437,20 +437,19 @@ export default function MediaLibraryPattern() {
437
437
  inputRef={inputRef}
438
438
  />
439
439
 
440
- <section className={styles.library} aria-labelledby="files-title">
441
- <div className={styles.libraryHeader}>
442
- <div className={styles.heading}>
443
- <Typography as="h2" variant="title-m" id="files-title">
444
- Файлы
445
- </Typography>
446
- <Typography as="p" variant="caption" tone="muted" aria-live="polite">
447
- {selecting
448
- ? `Выбрано: ${selected.size}`
449
- : loading
450
- ? "Загружаем…"
451
- : `${visible.length} ${filesWord(visible.length)} · ${formatFileSize(totalSize)}`}
452
- </Typography>
453
- </div>
440
+ <PageContent.Section
441
+ label="Файлы"
442
+ description={
443
+ <span aria-live="polite">
444
+ {selecting
445
+ ? `Выбрано: ${selected.size}`
446
+ : loading
447
+ ? "Загружаем…"
448
+ : `${visible.length} ${filesWord(visible.length)} · ${formatFileSize(totalSize)}`}
449
+ </span>
450
+ }
451
+ >
452
+ <PageContent.Actions>
454
453
  <Crossfade state={selecting ? "select" : "browse"}>
455
454
  {selecting ? (
456
455
  <div className={styles.bar}>
@@ -500,7 +499,7 @@ export default function MediaLibraryPattern() {
500
499
  </div>
501
500
  )}
502
501
  </Crossfade>
503
- </div>
502
+ </PageContent.Actions>
504
503
 
505
504
  <Crossfade state={state} aria-busy={loading}>
506
505
  {loading ? (
@@ -612,7 +611,7 @@ export default function MediaLibraryPattern() {
612
611
  </EmptyPage.Root>
613
612
  ) : null}
614
613
  </Crossfade>
615
- </section>
614
+ </PageContent.Section>
616
615
 
617
616
  <MediaViewer
618
617
  items={viewerItems}
@@ -633,6 +632,6 @@ export default function MediaLibraryPattern() {
633
632
  )}
634
633
  />
635
634
  </PageContent.Body>
636
- </PageContent.Section>
635
+ </PageContent.Page>
637
636
  );
638
637
  }
@@ -0,0 +1,18 @@
1
+ /* Record editor: the sections are PageContent.Section layout="aside"; here only the panels' insides. */
2
+
3
+ /* Two groups in one panel, a group apart. */
4
+ .groups {
5
+ display: grid;
6
+ gap: var(--prime-space-8);
7
+ }
8
+
9
+ /* One value in three currencies: one row while there is room, the unit inside each field. */
10
+ .currencies {
11
+ display: grid;
12
+ grid-template-columns: repeat(
13
+ auto-fit,
14
+ minmax(min(100%, calc(var(--prime-space-24) + var(--prime-space-8))), 1fr)
15
+ );
16
+ align-items: start;
17
+ gap: var(--prime-space-3);
18
+ }