prime-ui-kit 0.21.1 → 0.21.3
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.
- package/README.md +2 -2
- package/THIRD_PARTY_NOTICES +13 -0
- package/dist/chunks/{chunk-6L7VWEY6.js → chunk-BAJRN3QN.js} +2 -2
- package/dist/chunks/{chunk-QFHE5GKW.js → chunk-HMLQCKRL.js} +605 -556
- package/dist/chunks/{chunk-S27VR4ON.js → chunk-JO7E6SZQ.js} +105 -40
- package/dist/chunks/{chunk-S4H2ZKWJ.js → chunk-R5LI5BO3.js} +2150 -1615
- package/dist/chunks/{chunk-IVS3J7AS.js → chunk-SUGCIPD7.js} +1 -1
- package/dist/color-picker.js +3 -3
- package/dist/components/chat-list/ChatList.d.ts +4 -3
- package/dist/components/command-menu/CommandMenu.d.ts +1 -4
- package/dist/components/composer/Composer.d.ts +1 -1
- package/dist/components/data-table/DataTable.d.ts +9 -7
- package/dist/components/data-table/Footer.d.ts +7 -3
- package/dist/components/datepicker/DatepickerPanel.d.ts +2 -2
- package/dist/components/dnd/labels.d.ts +2 -0
- package/dist/components/field-list/FieldList.d.ts +2 -4
- package/dist/components/file-upload/FileUpload.d.ts +1 -9
- package/dist/components/index.d.ts +2 -2
- package/dist/components/kanban/Kanban.d.ts +0 -2
- package/dist/components/list/List.d.ts +7 -1
- package/dist/components/message/Message.d.ts +7 -3
- package/dist/components/message/MessageParts.d.ts +7 -2
- package/dist/components/rich-text/RichText.d.ts +12 -4
- package/dist/components/scroll-container/ScrollContainer.d.ts +1 -1
- package/dist/components/select/selectContext.d.ts +0 -2
- package/dist/components/smart-filter/SmartFilter.d.ts +1 -5
- package/dist/components/sparkline/Sparkline.d.ts +50 -8
- package/dist/components/tag-select/TagSelect.d.ts +0 -2
- package/dist/components/textarea/Textarea.d.ts +1 -1
- package/dist/icon-set.d.ts +6 -1
- package/dist/icon-set.js +10 -2
- package/dist/icons/glyph.d.ts +12 -1
- package/dist/icons/glyphs/app-window.d.ts +2 -0
- package/dist/icons/glyphs/telegram.d.ts +2 -0
- package/dist/icons/glyphs/vk.d.ts +2 -0
- package/dist/icons/glyphs/whatsapp.d.ts +2 -0
- package/dist/icons/registry.d.ts +10 -1
- package/dist/index.css +1 -1
- package/dist/index.js +7 -3
- package/dist/internal/ToolRow.d.ts +3 -1
- package/dist/internal/barPlan.d.ts +7 -1
- package/dist/internal/blockStack.d.ts +7 -0
- package/dist/internal/overlay/DialogParts.d.ts +9 -7
- package/dist/internal/richText.d.ts +32 -1
- package/dist/internal/selectionIndicator.d.ts +9 -3
- package/dist/internal/textFormatting.d.ts +2 -2
- package/dist/layout/page/Page.d.ts +2 -1
- package/dist/skill/SKILL.md +3 -2
- package/dist/skill/api-contract.md +3 -2
- package/dist/skill/cheatsheet.md +1 -1
- package/dist/skill/checklist.md +6 -1
- package/dist/skill/choosing.md +76 -5
- package/dist/skill/components.md +2 -2
- package/dist/skill/composition.md +108 -20
- package/dist/skill/foundations.md +1 -1
- package/dist/skill/layouts.md +4 -4
- package/dist/skill/reference/components/card/COMPONENT.md +1 -1
- package/dist/skill/reference/components/card/examples/screen.tsx +9 -11
- package/dist/skill/reference/components/chat-list/COMPONENT.md +6 -5
- package/dist/skill/reference/components/chat-list/examples/structure.tsx +4 -4
- package/dist/skill/reference/components/command-menu/COMPONENT.md +1 -2
- package/dist/skill/reference/components/command-menu/examples/states.tsx +1 -1
- package/dist/skill/reference/components/composer/COMPONENT.md +5 -4
- package/dist/skill/reference/components/composer/examples/formatting.tsx +1 -1
- package/dist/skill/reference/components/data-table/COMPONENT.md +18 -16
- package/dist/skill/reference/components/data-table/examples/server.tsx +2 -1
- package/dist/skill/reference/components/datepicker/COMPONENT.md +2 -2
- package/dist/skill/reference/components/dnd/COMPONENT.md +2 -1
- package/dist/skill/reference/components/drawer/COMPONENT.md +9 -6
- package/dist/skill/reference/components/drawer/examples/tab-switcher.tsx +158 -0
- package/dist/skill/reference/components/field-list/COMPONENT.md +7 -7
- package/dist/skill/reference/components/field-list/examples/limits.tsx +2 -2
- package/dist/skill/reference/components/file-upload/COMPONENT.md +8 -12
- package/dist/skill/reference/components/file-upload/examples/gallery.tsx +2 -2
- package/dist/skill/reference/components/file-upload/examples/in-form.tsx +2 -4
- package/dist/skill/reference/components/file-upload/examples/narrow.tsx +1 -0
- package/dist/skill/reference/components/file-upload/examples/overview.tsx +1 -5
- package/dist/skill/reference/components/file-upload/examples/sizes.tsx +1 -1
- package/dist/skill/reference/components/file-upload/examples/states.tsx +4 -5
- package/dist/skill/reference/components/file-upload/examples/validation.tsx +2 -7
- package/dist/skill/reference/components/kanban/COMPONENT.md +1 -2
- package/dist/skill/reference/components/list/COMPONENT.md +11 -6
- package/dist/skill/reference/components/list/examples/reorder.tsx +30 -3
- package/dist/skill/reference/components/message/COMPONENT.md +17 -11
- package/dist/skill/reference/components/message/examples/bot-actions.tsx +6 -2
- package/dist/skill/reference/components/message/examples/status.tsx +2 -1
- package/dist/skill/reference/components/message/examples/text-formatting.tsx +13 -3
- package/dist/skill/reference/components/modal/COMPONENT.md +5 -4
- package/dist/skill/reference/components/rich-text/COMPONENT.md +45 -7
- package/dist/skill/reference/components/rich-text/examples/entities.tsx +20 -0
- package/dist/skill/reference/components/scroll-container/COMPONENT.md +5 -5
- package/dist/skill/reference/components/scroll-container/examples/overscroll.tsx +2 -2
- package/dist/skill/reference/components/select/COMPONENT.md +1 -2
- package/dist/skill/reference/components/sheet/COMPONENT.md +5 -4
- package/dist/skill/reference/components/smart-filter/COMPONENT.md +3 -5
- package/dist/skill/reference/components/sparkline/COMPONENT.md +40 -12
- package/dist/skill/reference/components/sparkline/examples/lower-is-better.tsx +46 -0
- package/dist/skill/reference/components/sparkline/examples/own-value.tsx +41 -0
- package/dist/skill/reference/components/sparkline/examples/reference.tsx +2 -1
- package/dist/skill/reference/components/tabs/COMPONENT.md +2 -2
- package/dist/skill/reference/components/tag-select/COMPONENT.md +0 -2
- package/dist/skill/reference/components/tag-select/examples/overview.tsx +0 -1
- package/dist/skill/reference/components/tag-select/examples/sizes.tsx +0 -1
- package/dist/skill/reference/components/textarea/COMPONENT.md +4 -3
- package/dist/skill/reference/icon-set.ts +6 -1
- package/dist/skill/reference/layout/app-shell/COMPONENT.md +1 -1
- package/dist/skill/reference/layout/page/COMPONENT.md +13 -12
- package/dist/skill/reference/layout/page/examples/collapse.tsx +45 -31
- package/dist/skill/reference/layout/page/examples/router.tsx +47 -14
- package/dist/skill/ux.md +79 -2
- package/package.json +1 -1
- package/src/components/card/COMPONENT.md +1 -1
- package/src/components/card/examples/screen.tsx +9 -11
- package/src/components/chat-list/COMPONENT.md +6 -5
- package/src/components/chat-list/examples/structure.tsx +4 -4
- package/src/components/command-menu/COMPONENT.md +1 -2
- package/src/components/command-menu/examples/states.tsx +1 -1
- package/src/components/composer/COMPONENT.md +5 -4
- package/src/components/composer/examples/formatting.tsx +1 -1
- package/src/components/data-table/COMPONENT.md +18 -16
- package/src/components/data-table/examples/server.tsx +2 -1
- package/src/components/datepicker/COMPONENT.md +2 -2
- package/src/components/dnd/COMPONENT.md +2 -1
- package/src/components/drawer/COMPONENT.md +9 -6
- package/src/components/drawer/examples/tab-switcher.tsx +158 -0
- package/src/components/field-list/COMPONENT.md +7 -7
- package/src/components/field-list/examples/limits.tsx +2 -2
- package/src/components/file-upload/COMPONENT.md +8 -12
- package/src/components/file-upload/examples/gallery.tsx +2 -2
- package/src/components/file-upload/examples/in-form.tsx +2 -4
- package/src/components/file-upload/examples/narrow.tsx +1 -0
- package/src/components/file-upload/examples/overview.tsx +1 -5
- package/src/components/file-upload/examples/sizes.tsx +1 -1
- package/src/components/file-upload/examples/states.tsx +4 -5
- package/src/components/file-upload/examples/validation.tsx +2 -7
- package/src/components/kanban/COMPONENT.md +1 -2
- package/src/components/list/COMPONENT.md +11 -6
- package/src/components/list/examples/reorder.tsx +30 -3
- package/src/components/message/COMPONENT.md +17 -11
- package/src/components/message/examples/bot-actions.tsx +6 -2
- package/src/components/message/examples/status.tsx +2 -1
- package/src/components/message/examples/text-formatting.tsx +13 -3
- package/src/components/modal/COMPONENT.md +5 -4
- package/src/components/rich-text/COMPONENT.md +45 -7
- package/src/components/rich-text/examples/entities.tsx +20 -0
- package/src/components/scroll-container/COMPONENT.md +5 -5
- package/src/components/scroll-container/examples/overscroll.tsx +2 -2
- package/src/components/select/COMPONENT.md +1 -2
- package/src/components/sheet/COMPONENT.md +5 -4
- package/src/components/smart-filter/COMPONENT.md +3 -5
- package/src/components/sparkline/COMPONENT.md +40 -12
- package/src/components/sparkline/examples/lower-is-better.tsx +46 -0
- package/src/components/sparkline/examples/own-value.tsx +41 -0
- package/src/components/sparkline/examples/reference.tsx +2 -1
- package/src/components/tabs/COMPONENT.md +2 -2
- package/src/components/tag-select/COMPONENT.md +0 -2
- package/src/components/tag-select/examples/overview.tsx +0 -1
- package/src/components/tag-select/examples/sizes.tsx +0 -1
- package/src/components/textarea/COMPONENT.md +4 -3
- package/src/layout/app-shell/COMPONENT.md +1 -1
- package/src/layout/page/COMPONENT.md +13 -12
- package/src/layout/page/examples/collapse.tsx +45 -31
- package/src/layout/page/examples/router.tsx +47 -14
|
@@ -38,7 +38,7 @@ SmartFilter.Root state + context, no DOM
|
|
|
38
38
|
│ │ (groups: a caption over each group's values, role="group")
|
|
39
39
|
│ ├─ range rows field label · Input «от» – Input «до» · or a Datepicker range field
|
|
40
40
|
│ ├─ no matches compact EmptyPage naming the fields with nothing found
|
|
41
|
-
│ └─ footer
|
|
41
|
+
│ └─ footer Reset Button, while a filter is set
|
|
42
42
|
└─ SmartFilter.Chips applied filters as removable Badges · add / clear all Buttons
|
|
43
43
|
```
|
|
44
44
|
The toolbar and the chips are separate parts so a page can put them in different places; both only need to be inside `Root`, which draws nothing of its own.
|
|
@@ -171,7 +171,7 @@ same height and type.
|
|
|
171
171
|
- The panel is a flush Popover at least as wide as the toolbar (`width="min-trigger"`), by its content up to twice the panel minimum when the toolbar is narrow; every section is a full-width row with its own padding (`--prime-space-3` × `--prime-space-4`, footer `--prime-space-2` × `--prime-space-4`), Dividers run edge to edge, the field label column is at least `--prime-space-24` (a longer name takes its own width, never breaking inside a word); a row narrower than 24rem stacks the label over the values (an intrinsic wrap, no breakpoint).
|
|
172
172
|
- The toolbar is one row of controls (`--prime-space-2` gap): the search first, growing from 9rem (`--prime-space-24` × 1.5), then the filter button at its own width. When less than 9rem is left beside the labelled button, the button shows only its icon and count and stays beside the search, which gives its width instead of wrapping. Every row stays one control high. The search and the button also stand alone: `searchable={false}` keeps the button, empty `fields` keep the search. Search first, filter second — the active filters (`SmartFilter.Chips`) sit under them.
|
|
173
173
|
- Chips are Badges of the toolbar tier on the badge scale (`xs` → `s`), so a tag sits lower than the controls above it; «Добавить фильтр» is a tag of the same tier and «Сбросить все» a LinkButton with the tag's text size.
|
|
174
|
-
- Values and chips wrap with `--prime-space-2` gaps; «Сбросить все» is pushed to the end. The chips row
|
|
174
|
+
- Values and chips wrap with `--prime-space-2` gaps; «Сбросить все» is pushed to the end. The chips row, and the panel footer with its Reset, rise in with the first filter and close up in place with the last (`partPresence`).
|
|
175
175
|
- Option groups stack `--prime-space-3` apart, a caption `--prime-space-1` above each group's values. A range row holds two equal Inputs with a dash between them, centred on the field label.
|
|
176
176
|
- While typing a search a range row stays only when its field name matches.
|
|
177
177
|
- The root draws nothing: its host places Toolbar and Chips, next to what they filter. A table among other blocks (KPI cards, a chart) takes them in its own tools row — the DataTable `toolbar` (the Toolbar) and `toolbarChips` (the Chips), with `SmartFilter.Root` around the DataTable. A page that is one list takes them in its bar — the Toolbar in `Page.Tools`, the Chips in `Page.Chips`, with `SmartFilter.Root` around `Page.Root`. Elsewhere a host stacks them (a `Card.Body`, a form column).
|
|
@@ -208,9 +208,7 @@ same height and type.
|
|
|
208
208
|
| `clearSearch` | `"Очистить поиск"` | Name of the search clear button. |
|
|
209
209
|
| `searchText` | `"Искать «{query}»"` | The text search row while typing. |
|
|
210
210
|
| `noMatches` | `"{fields} — нет совпадений"` | Fields with nothing found while typing (compact EmptyPage). |
|
|
211
|
-
| `
|
|
212
|
-
| `count` | `"Фильтров: {count}"` | Panel footer counter. |
|
|
213
|
-
| `reset` | `"Сбросить"` | Panel footer reset. |
|
|
211
|
+
| `reset` | `"Сбросить"` | Panel footer reset, shown while a filter is set. |
|
|
214
212
|
| `more` | `"Ещё {count}"` | Collapsed values of a field. |
|
|
215
213
|
| `add` | `"Добавить фильтр"` | Chips row: opens the panel. |
|
|
216
214
|
| `clearAll` | `"Сбросить все"` | Chips row: clears every field. |
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**Category:** data-display
|
|
4
4
|
**Kind:** primitive
|
|
5
5
|
|
|
6
|
-
> A small chart — a smooth line over a fading area, a bare line or bars — with its headline: the value at rest, its
|
|
6
|
+
> A small chart — a smooth line over a fading area, a bare line or bars — with its headline: the value at rest, its date and an optional change; any point can be scrubbed.
|
|
7
7
|
|
|
8
8
|
## When to use
|
|
9
9
|
- A metric card on a dashboard: revenue, orders, sign-ups over the last days or weeks.
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
- A daily flow over a period — revenue, orders, sign-ups (`summary="sum"`, `compareTo` the previous period).
|
|
15
15
|
- A metric against its plan or average (`reference`), or its change over the whole period (`change="first"`).
|
|
16
16
|
- A series with missing days — a collector that was down (`value: null` breaks the line).
|
|
17
|
+
- A metric where growth is bad — response time, costs, bounce rate (`better="lower"`); a share in percent whose change is in percentage points (`changeUnit="points"`).
|
|
18
|
+
- A period value that is neither the sum nor the mean of the days — a median, unique clients (`value`, `compareTo`).
|
|
17
19
|
|
|
18
20
|
## When not to use
|
|
19
21
|
- Exact values on axes, several series, or a legend → a full chart library on the kit's chart palette (`chartColors`); Sparkline has no axes.
|
|
@@ -55,10 +57,13 @@ compact: the plot alone (no head, no value row); loading — the p
|
|
|
55
57
|
| `variant` | `"area" \| "line" \| "bars"` | `"area"` | `area` — a line over an accent gradient that fades to the bottom; `line` — the line alone, for many trends side by side; `bars` — a bar per point standing on a zero line: up when positive, down (danger) when negative. |
|
|
56
58
|
| `curve` | `"smooth" \| "linear" \| "step"` | `"smooth"` | Shape of the line (`area`, `line`): `smooth` — a monotone cubic through every point that never overshoots a peak or a trough; `linear` — straight segments; `step` — a level per point, changing halfway between points. |
|
|
57
59
|
| `layout` | `"default" \| "compact"` | `"default"` | `default` — the headline over an 80 px plot; `compact` — the plot alone at an `xs` control height with the dot at rest, for a DataTable cell; the scrubbed point shows in the tooltip and `aria-valuetext`. |
|
|
58
|
-
| `change` | `"previous" \| "first" \| "none"` | `"
|
|
60
|
+
| `change` | `"previous" \| "first" \| "none"` | `"none"` | What the change badge of a shown point compares it with: `previous` — the point before it; `first` — the first point with a value (the change over the period); `none` — no badge (`compareTo` still badges the rest). |
|
|
61
|
+
| `changeUnit` | `"percent" \| "points"` | `"percent"` | How the change badge measures: `percent` — the relative change («+12%»); `points` — the difference, for a share in percent (`labels.points`, «+0,5 п. п.»). A zero change is neutral, no arrow. |
|
|
62
|
+
| `better` | `"higher" \| "lower"` | `"higher"` | Which way of the change is good: `higher` — growth is `success`, a fall `danger` (revenue, orders); `lower` — a fall is `success`, growth `danger` (response time, costs, bounce rate). The arrow always follows the sign. |
|
|
59
63
|
| `summary` | `"latest" \| "sum" \| "average"` | `"latest"` | What the headline shows at rest: `latest` — the latest point (or `highlight`), for a level (MRR, conversion); `sum` — the period total, for a flow (revenue per day); `average` — the period mean. |
|
|
60
|
-
| `highlight` | `number` | the latest point with a value | Index of the point at rest with `summary="latest"` (the current month): the headline returns to it; `bars` draw the other bars quieter. Not allowed with `sum` / `average`. |
|
|
61
|
-
| `
|
|
64
|
+
| `highlight` | `number` | the latest point with a value | Index of the point at rest with `summary="latest"` (the current month): the headline returns to it; `bars` draw the other bars quieter. Not allowed with `sum` / `average` / `value`. |
|
|
65
|
+
| `value` | `number \| null` | — | The period's own value at rest, neither a sum nor a mean of the points (a median, unique clients), with the period as the date; `compareTo` is its previous period. Not with `summary` / `highlight`. |
|
|
66
|
+
| `compareTo` | `number` | — | The same summary (or `value`) for the previous period («прошлые 30 дней»): the badge at rest compares the headline with it. Without it a `sum` / `average` / `value` headline shows no change at rest. |
|
|
62
67
|
| `min` | `number` | — | The value axis reaches down to at least this value: a floor of zero, or one scale for several sparklines (a table column). Data below widens the scale, never gets cut. |
|
|
63
68
|
| `max` | `number` | — | The value axis reaches up to at least this value; data above widens the scale. |
|
|
64
69
|
| `reference` | `number` | — | A dashed horizontal line at this value — a plan, a target, an average; the scale holds it and the chart is described by `labels.reference`. |
|
|
@@ -86,9 +91,23 @@ compact: the plot alone (no head, no value row); loading — the p
|
|
|
86
91
|
### change
|
|
87
92
|
| Value | Looks like | Use when | Default |
|
|
88
93
|
|---|---|---|---|
|
|
89
|
-
| `previous` | change against the point before the shown one | period-over-period (this month vs the last) |
|
|
94
|
+
| `previous` | change against the point before the shown one | period-over-period (this month vs the last) | |
|
|
90
95
|
| `first` | change against the first point with a value | the change over the whole range («за полгода») | |
|
|
91
|
-
| `none` | no badge
|
|
96
|
+
| `none` | no badge for a point; with `compareTo` the badge at rest stays | the headline alone, or the change only against the previous period | yes |
|
|
97
|
+
|
|
98
|
+
### changeUnit
|
|
99
|
+
| Value | Looks like | Use when | Default |
|
|
100
|
+
|---|---|---|---|
|
|
101
|
+
| `percent` | «+12%», hidden when the base is 0 or the sign flips | counts, money, durations | yes |
|
|
102
|
+
| `points` | the difference of the values, `labels.points` («+0,5 п. п.») | a share already in percent: conversion, churn, load | |
|
|
103
|
+
|
|
104
|
+
A change that rounds to zero (one decimal) is a `neutral` badge without an arrow («0%»).
|
|
105
|
+
|
|
106
|
+
### better
|
|
107
|
+
| Value | Looks like | Use when | Default |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| `higher` | growth `success`, a fall `danger` | revenue, orders, conversion | yes |
|
|
110
|
+
| `lower` | a fall `success`, growth `danger`; the arrow still follows the sign | response time, costs, bounce rate, deal cycle | |
|
|
92
111
|
|
|
93
112
|
### summary
|
|
94
113
|
| Value | Looks like | Use when | Default |
|
|
@@ -99,6 +118,8 @@ compact: the plot alone (no head, no value row); loading — the p
|
|
|
99
118
|
|
|
100
119
|
Scrubbing always shows the point under the pointer; leaving returns to the summary. With `compareTo` (the same summary for the previous period) the badge at rest is the change against it — «+8% к прошлым 30 дням».
|
|
101
120
|
|
|
121
|
+
A period value that is neither the sum nor the mean of the points (a median, unique clients over the period) is passed as `value` (in place of `summary`): the headline rests on it like on a sum, with the period as its date and no dot; `compareTo` is the same value for the previous period.
|
|
122
|
+
|
|
102
123
|
### layout
|
|
103
124
|
| Value | Looks like | Use when | Default |
|
|
104
125
|
|---|---|---|---|
|
|
@@ -118,11 +139,12 @@ Put it in a `Card` for a dashboard tile.
|
|
|
118
139
|
## States
|
|
119
140
|
| State | Driven by | DOM |
|
|
120
141
|
|---|---|---|
|
|
121
|
-
| at rest | `summary`, `highlight`, `compareTo` | the latest point or `highlight` (`latest`), or the period total / mean with the period as date and no dot (`sum` / `average`, cursor `data-hidden`); the badge compares with `compareTo` when given; the value rolls its digits when `data` changes or scrubbing ends |
|
|
142
|
+
| at rest | `summary`, `highlight`, `value`, `compareTo` | the latest point or `highlight` (`latest`), or the period total / mean / own `value` with the period as date and no dot (`sum` / `average` / `value`, cursor `data-hidden`); the badge compares with `compareTo` when given; the value rolls its digits when `data` changes or scrubbing ends |
|
|
122
143
|
| scrubbing | pointer, touch or the arrow keys | `data-scrubbing` on the root; the guide and the tooltip (`data-state="open"`, date · value, right of the pointer at its height, left of it only where the right has no room in the viewport; from the keyboard beside the point at mid-height) fade in (`fast`), the cursor (or the bar under the pointer) and the headline follow with no easing (direct manipulation), digits change in place |
|
|
123
144
|
| keyboard | arrows, Home, End on the focused plot | `aria-valuenow` / `aria-valuetext`, `data-scrubbing`; the cursor moves to the point at once (keyboard steps are high-frequency); in `bars` the current bar keeps its fill, the others go quiet (`fast` opacity) |
|
|
124
145
|
| settle | pointer leaves, touch ends, focus leaves | back to the point at rest; the tooltip fades out (`fast`) |
|
|
125
|
-
| change hidden | `change="none"`, nothing to compare with, a missing value, a base of 0
|
|
146
|
+
| change hidden | `change="none"`, nothing to compare with, a missing value, or (`percent`) a base of 0 or a sign flip | no change Badge: the measure would mean nothing |
|
|
147
|
+
| no change | a change that rounds to zero | the change Badge is `neutral`, without an arrow |
|
|
126
148
|
| no data | a point with `value: null` | the line breaks (a point alone between gaps is a dot), no bar; shown, the value reads `labels.empty` (`data-empty`, muted) and the dot fades out, the guide marks the point |
|
|
127
149
|
| new series | a new `data` array | the chart cross-fades in (swap motion); the trend arrow turns half a turn between up and down |
|
|
128
150
|
| compact | `layout="compact"` | `data-layout="compact"` on the root; no headline: `label` names the slider, the point is spoken through `aria-valuetext` and shown in the tooltip |
|
|
@@ -152,9 +174,9 @@ Put it in a `Card` for a dashboard tile.
|
|
|
152
174
|
### ARIA
|
|
153
175
|
<!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
|
|
154
176
|
|
|
155
|
-
- The plot is `role="slider"` named by `label`; `aria-valuetext` speaks the point («8 окт: 312 400 ₽», `labels.point`); a point with no data speaks `labels.empty` («8 окт: Нет данных»). At rest with `sum` / `average` it speaks the period and the summary («9 сен – 8 окт: 8 914 000 ₽»). With `reference` the slider is described by `labels.reference` («Ориентир: 3%», `aria-describedby`). The headline follows the point under the pointer or the arrows, in text.
|
|
177
|
+
- The plot is `role="slider"` named by `label`; `aria-valuetext` speaks the point («8 окт: 312 400 ₽», `labels.point`); a point with no data speaks `labels.empty` («8 окт: Нет данных»). At rest with `sum` / `average` / `value` it speaks the period and the summary («9 сен – 8 окт: 8 914 000 ₽»). With `reference` the slider is described by `labels.reference` («Ориентир: 3%», `aria-describedby`). The headline follows the point under the pointer or the arrows, in text.
|
|
156
178
|
- The headline with the value and change reads as plain text.
|
|
157
|
-
- The change carries a sign and an arrow, never color alone; a negative bar goes down, not only red.
|
|
179
|
+
- The change carries a sign and an arrow, never color alone (a zero change is a neutral badge with no arrow); a negative bar goes down, not only red.
|
|
158
180
|
- While `loading` the root is `aria-busy` and the chart is not in the tab order yet.
|
|
159
181
|
- `layout="compact"`: no visible title; `label` is only the slider's name, the point still goes to `aria-valuetext`.
|
|
160
182
|
- The tooltip is `aria-hidden`: it repeats `aria-valuetext` for sighted users. The plot carries `data-row-click-ignore` (and is a slider), so its pointer and keys never open a DataTable row.
|
|
@@ -165,9 +187,10 @@ Put it in a `Card` for a dashboard tile.
|
|
|
165
187
|
| Key | Default | Used for |
|
|
166
188
|
|---|---|---|
|
|
167
189
|
| `point` | `"{label}: {value}"` | Spoken value of the chosen point (`aria-valuetext`); `{label}` and `{value}` are replaced. |
|
|
168
|
-
| `period` | `"{first} – {last}"` | The date line of a `sum` / `average` headline at rest; `{first}` and `{last}` are the labels of the first and the last point. |
|
|
190
|
+
| `period` | `"{first} – {last}"` | The date line of a `sum` / `average` / `value` headline at rest; `{first}` and `{last}` are the labels of the first and the last point. |
|
|
169
191
|
| `empty` | `"Нет данных"` | Shown and spoken in place of the value of a point with `value: null`. |
|
|
170
192
|
| `reference` | `"Ориентир: {value}"` | Description of the chart with a `reference` line (`aria-describedby`); `{value}` is replaced. |
|
|
193
|
+
| `points` | `"{value} п. п."` | The change badge with `changeUnit="points"`; `{value}` is the signed difference. |
|
|
171
194
|
|
|
172
195
|
## Examples
|
|
173
196
|
| Example | Shows |
|
|
@@ -178,7 +201,9 @@ Put it in a `Card` for a dashboard tile.
|
|
|
178
201
|
| [compact.tsx](examples/compact.tsx) | Revenue by month per client in a list: the plot alone at a row's height with the dot on the current month, scrubbing shows the month in a tooltip, the label is the chart's accessible name — `layout`, `label`. |
|
|
179
202
|
| [signed-bars.tsx](examples/signed-bars.tsx) | Monthly profit with a sign: losses go below the zero line, the current month rests highlighted and any bar shows its value under the pointer or the arrows — `variant`, `highlight`. |
|
|
180
203
|
| [curve.tsx](examples/curve.tsx) | The same month of active users as a smooth curve, straight segments and steps — `curve`. |
|
|
181
|
-
| [reference.tsx](examples/reference.tsx) | Checkout conversion against its plan: a dashed reference line, the scale from zero to 5% and the change over the whole period — `reference`, `min`, `max`, `change`. |
|
|
204
|
+
| [reference.tsx](examples/reference.tsx) | Checkout conversion against its plan: a dashed reference line, the scale from zero to 5% and the change over the whole period in percentage points — `reference`, `min`, `max`, `change`, `changeUnit`. |
|
|
205
|
+
| [lower-is-better.tsx](examples/lower-is-better.tsx) | First response time by day, where a fall is good: growth paints the change red, a fall green — `better`, `summary`, `compareTo`. |
|
|
206
|
+
| [own-value.tsx](examples/own-value.tsx) | Unique clients in dialogs: the period's own count is neither the sum nor the mean of the days, so the card passes it with the previous period's count — `value`, `compareTo`. |
|
|
182
207
|
| [gaps.tsx](examples/gaps.tsx) | Daily API requests with days the collector was down: the line breaks over a missing day and the headline says there is no data — `data`, `labels`. |
|
|
183
208
|
| [period.tsx](examples/period.tsx) | Another period swaps the series: the line cross-fades in and the headline digits roll to the new total — `data`, `summary`. |
|
|
184
209
|
|
|
@@ -191,6 +216,9 @@ Put it in a `Card` for a dashboard tile.
|
|
|
191
216
|
- A missing day as `0` → the line dives to zero; pass `value: null`, the line breaks and the headline says there is no data.
|
|
192
217
|
- Compact sparklines in a column, each on its own scale, when rows must be compared by size → pass the column's `min` / `max` to every row.
|
|
193
218
|
- A plan drawn as a second series → `reference`.
|
|
219
|
+
- Response time, costs or bounce rate with growth painted green → `better="lower"`.
|
|
220
|
+
- A conversion's change in percent of itself («+62%» for 2,1% → 3,4%) → `changeUnit="points"` («+1,3 п. п.»).
|
|
221
|
+
- A median or a unique count forced into `summary="sum"` / `"average"`, or a chart dropped for it → pass the period's own `value` with `compareTo`.
|
|
194
222
|
- A grey box while the series loads → `loading`.
|
|
195
223
|
- A full Sparkline squeezed into a table cell, or its headline hidden with CSS → `layout="compact"`.
|
|
196
224
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** First response time by day, where a fall is good: growth paints the change red, a fall green — `better`, `summary`, `compareTo`. */
|
|
2
|
+
import { Card, Sparkline, type SparklinePoint } from "prime-ui-kit";
|
|
3
|
+
|
|
4
|
+
import styles from "./examples.module.css";
|
|
5
|
+
|
|
6
|
+
/** Mean first response by day, minutes, 2 — 15 October 2026. */
|
|
7
|
+
const RESPONSE: SparklinePoint[] = [
|
|
8
|
+
{ label: "2 окт", value: 4.2 },
|
|
9
|
+
{ label: "3 окт", value: 3.8 },
|
|
10
|
+
{ label: "4 окт", value: 4.6 },
|
|
11
|
+
{ label: "5 окт", value: 5.1 },
|
|
12
|
+
{ label: "6 окт", value: 4.4 },
|
|
13
|
+
{ label: "7 окт", value: 3.9 },
|
|
14
|
+
{ label: "8 окт", value: 3.6 },
|
|
15
|
+
{ label: "9 окт", value: 4.8 },
|
|
16
|
+
{ label: "10 окт", value: 5.6 },
|
|
17
|
+
{ label: "11 окт", value: 5.2 },
|
|
18
|
+
{ label: "12 окт", value: 4.9 },
|
|
19
|
+
{ label: "13 окт", value: 5.4 },
|
|
20
|
+
{ label: "14 окт", value: 5.8 },
|
|
21
|
+
{ label: "15 окт", value: 5.5 },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/** The mean of the 14 days before, minutes. */
|
|
25
|
+
const PREVIOUS_AVERAGE = 4.1;
|
|
26
|
+
|
|
27
|
+
const MINUTES = new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 1 });
|
|
28
|
+
|
|
29
|
+
const formatMinutes = (value: number) => `${MINUTES.format(value)} мин`;
|
|
30
|
+
|
|
31
|
+
export default function SparklineLowerIsBetterExample() {
|
|
32
|
+
return (
|
|
33
|
+
<Card.Root className={styles.card}>
|
|
34
|
+
<Card.Body>
|
|
35
|
+
<Sparkline
|
|
36
|
+
data={RESPONSE}
|
|
37
|
+
label="Время первого ответа"
|
|
38
|
+
summary="average"
|
|
39
|
+
compareTo={PREVIOUS_AVERAGE}
|
|
40
|
+
better="lower"
|
|
41
|
+
formatValue={formatMinutes}
|
|
42
|
+
/>
|
|
43
|
+
</Card.Body>
|
|
44
|
+
</Card.Root>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Unique clients in dialogs: the period's own count is neither the sum nor the mean of the days, so the card passes it with the previous period's count — `value`, `compareTo`. */
|
|
2
|
+
import { Card, Sparkline, type SparklinePoint } from "prime-ui-kit";
|
|
3
|
+
|
|
4
|
+
import styles from "./examples.module.css";
|
|
5
|
+
|
|
6
|
+
/** Clients who wrote each day, 2 — 15 October 2026; one client writes on many days. */
|
|
7
|
+
const CLIENTS_BY_DAY: SparklinePoint[] = [
|
|
8
|
+
{ label: "2 окт", value: 184 },
|
|
9
|
+
{ label: "3 окт", value: 201 },
|
|
10
|
+
{ label: "4 окт", value: 146 },
|
|
11
|
+
{ label: "5 окт", value: 132 },
|
|
12
|
+
{ label: "6 окт", value: 218 },
|
|
13
|
+
{ label: "7 окт", value: 226 },
|
|
14
|
+
{ label: "8 окт", value: 209 },
|
|
15
|
+
{ label: "9 окт", value: 231 },
|
|
16
|
+
{ label: "10 окт", value: 240 },
|
|
17
|
+
{ label: "11 окт", value: 158 },
|
|
18
|
+
{ label: "12 окт", value: 141 },
|
|
19
|
+
{ label: "13 окт", value: 237 },
|
|
20
|
+
{ label: "14 окт", value: 252 },
|
|
21
|
+
{ label: "15 окт", value: 244 },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/** Unique clients over the period and over the 14 days before. */
|
|
25
|
+
const UNIQUE_CLIENTS = 1_386;
|
|
26
|
+
const PREVIOUS_UNIQUE_CLIENTS = 1_270;
|
|
27
|
+
|
|
28
|
+
export default function SparklineOwnValueExample() {
|
|
29
|
+
return (
|
|
30
|
+
<Card.Root className={styles.card}>
|
|
31
|
+
<Card.Body>
|
|
32
|
+
<Sparkline
|
|
33
|
+
data={CLIENTS_BY_DAY}
|
|
34
|
+
label="Клиенты в диалогах"
|
|
35
|
+
value={UNIQUE_CLIENTS}
|
|
36
|
+
compareTo={PREVIOUS_UNIQUE_CLIENTS}
|
|
37
|
+
/>
|
|
38
|
+
</Card.Body>
|
|
39
|
+
</Card.Root>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Checkout conversion against its plan: a dashed reference line, the scale from zero to 5% and the change over the whole period — `reference`, `min`, `max`, `change`. */
|
|
1
|
+
/** Checkout conversion against its plan: a dashed reference line, the scale from zero to 5% and the change over the whole period in percentage points — `reference`, `min`, `max`, `change`, `changeUnit`. */
|
|
2
2
|
import { Card, Sparkline, type SparklinePoint } from "prime-ui-kit";
|
|
3
3
|
|
|
4
4
|
import styles from "./examples.module.css";
|
|
@@ -29,6 +29,7 @@ export default function SparklineReferenceExample() {
|
|
|
29
29
|
min={0}
|
|
30
30
|
max={5}
|
|
31
31
|
change="first"
|
|
32
|
+
changeUnit="points"
|
|
32
33
|
formatValue={formatPercent}
|
|
33
34
|
labels={{ reference: "План: {value}" }}
|
|
34
35
|
/>
|
|
@@ -199,7 +199,7 @@ Other attributes: Root `data-orientation`, `data-size`, `data-tone`, `data-full-
|
|
|
199
199
|
|
|
200
200
|
## Layout & spacing
|
|
201
201
|
- Tabs are a bar, not a surface: the root has no fill, padding, radius or `data-depth`. On a page or in a `Card.Body`, put it straight into the host's padded region and the panel content straight into `Tabs.Panel`. The strip's edges and the panel content share the host's content edge; the panel stands one tier gap below the strip (`--prime-space-3` … `--prime-space-8`).
|
|
202
|
-
- In a Modal or Drawer the tabs belong to the header: `Tabs.Root` wraps `X.Header` and `X.Body`, `Tabs.List` goes last into the header, the panels into the body. The header becomes the strip (painted like it, no hairline, edge to edge — the header hands the bar its side padding as `--prime-bleed-inline`), the active tab is the body rising into it, and the root renders no box (`display: contents`), so only the body scrolls and the tabs stay in view.
|
|
202
|
+
- In a Modal or Drawer the tabs belong to the header: `Tabs.Root` wraps `X.Header` and `X.Body`, `Tabs.List` goes last into the header, the panels into the body. The header becomes the strip (painted like it, no hairline, edge to edge — the header hands the bar its side padding as `--prime-bleed-inline`), the active tab is the body rising into it, and the root renders no box (`display: contents`), so only the body scrolls and the tabs stay in view. A panel in the body stands its blocks 32 apart — the step between the body's sections — so a tab of self-titled blocks (charts, titled cards) or of `X.Section`s needs no spacing wrapper.
|
|
203
203
|
|
|
204
204
|
```tsx
|
|
205
205
|
<Drawer.Content>
|
|
@@ -216,7 +216,7 @@ Other attributes: Root `data-orientation`, `data-size`, `data-tone`, `data-full-
|
|
|
216
216
|
```
|
|
217
217
|
- The folder paints the host's `layer-current`, so the active tab is always the colour of the surface it stands on; controls in a panel read the host's field and fill tokens.
|
|
218
218
|
- Vertical: the column is as wide as its widest section (at least four control heights, at most 40% of the root) and as tall as its sections; the panel sits beside it, one gap away.
|
|
219
|
-
- The list is a switcher (foundation §9): it never wraps — it collapses, then scrolls horizontally with faded edges (hidden scrollbar,
|
|
219
|
+
- The list is a switcher (foundation §9): it never wraps — it collapses, then scrolls horizontally with faded edges (hidden scrollbar; at its sideways edge it stops, a vertical wheel goes on to the page). Give the list's container a width (`min-width: 0` on flex children).
|
|
220
220
|
- Without `size` the tabs take the tier of their host (a Card, a Drawer, a form), else `m`.
|
|
221
221
|
- A horizontal tab is never narrower than its content (`min-width: max-content`; the tab bounds that content between `minItemWidth` and `maxItemWidth`), so a label is never cut to make room. Full width, tabs tend to equal widths, not widths in proportion to their labels (`flex: 1 1 0`): a long label keeps its width, the short ones share the rest.
|
|
222
222
|
- A vertical Tabs.Root is a size container: give it a width (it measures itself to switch to a row).
|
|
@@ -30,7 +30,6 @@ TagSelect field frame: label · control · hint/error (sin
|
|
|
30
30
|
│ ├── input[type=hidden] × value with `name`
|
|
31
31
|
│ └── chevron
|
|
32
32
|
└── list (portal) role="listbox", multi-select
|
|
33
|
-
├── panel hint labels.panelHint (`""` hides it)
|
|
34
33
|
├── «Создать» row with `creatable`: plus icon · labels.create · preview Badge
|
|
35
34
|
└── option row Checkbox.Indicator · Badge [· «⋯» Button → Popover menu]; picked options first
|
|
36
35
|
```
|
|
@@ -150,7 +149,6 @@ No `variant` or `tone`. The control is the field look; chips are the kit's soft
|
|
|
150
149
|
|
|
151
150
|
| Key | Default | Used for |
|
|
152
151
|
|---|---|---|
|
|
153
|
-
| `panelHint` | `"Выберите вариант или создайте новый"` | Line above the list; `""` hides it. |
|
|
154
152
|
| `create` | `"Создать"` | Action text before the preview of a new tag. |
|
|
155
153
|
| `remove` | `"Удалить {label}"` | Accessible name of a chip's remove button; `{label}` is the tag text. |
|
|
156
154
|
| `more` | `"Показать ещё {count}"` | Accessible name of the «+N» chip; `{count}` is the number of hidden tags. |
|
|
@@ -59,7 +59,7 @@ Unlike Input, Textarea has no Wrapper / Field parts: `Textarea.Root` renders the
|
|
|
59
59
|
| `id` | `string` | — | Id of the `<textarea>` (auto-generated when omitted); hint id is `<id>-hint`, error id is `<id>-error`. |
|
|
60
60
|
| `aria-describedby` | `string` | — | Merged before the hint/error ids. |
|
|
61
61
|
| `onCommit` | `(value: string) => void \| Promise<void>` | — | Inline save: fires with a changed value on blur and Mod+Enter; Escape reverts. Return a Promise: the field shows a spinner and stays editable; a rejection restores the value — show why through `error`. |
|
|
62
|
-
| `formatting` | `boolean` | `false` | Selecting text shows a floating toolbar of
|
|
62
|
+
| `formatting` | `boolean` | `false` | Selecting text shows a floating toolbar of eight Markdown tools and enables their hotkeys; a tool wraps the selection, a second press unwraps it. Off: plain text. |
|
|
63
63
|
| `children` | `ReactNode` | — | Actions in the field's top end corner: `Textarea.CopyButton`, `Textarea.Action`. |
|
|
64
64
|
| `labels` | `Partial<TextareaLabels>` | — | Built-in strings, see Labels. |
|
|
65
65
|
| `className` | `string` | — | Class on the field frame `<div>` (label, field box, support row), as on every field root. |
|
|
@@ -116,7 +116,7 @@ Textarea has no `variant` or `tone`: one field treatment (fill, no visible borde
|
|
|
116
116
|
| Value | Looks like | Use when | Default |
|
|
117
117
|
|---|---|---|---|
|
|
118
118
|
| `focusRing` | `false`: no focus ring; focus fill and caret remain | A single composer field where focus is obvious | `true` |
|
|
119
|
-
| `formatting` | A floating toolbar over a selection (bold, italic, strikethrough, code, spoiler, link, quote) and the Mod-hotkeys; each writes Markdown around the selection | The text is shown later with its Markdown (Message renders the same subset) | `false` |
|
|
119
|
+
| `formatting` | A floating toolbar over a selection (bold, italic, underline, strikethrough, code, spoiler, link, quote) and the Mod-hotkeys; each writes Markdown around the selection | The text is shown later with its Markdown (Message renders the same subset) | `false` |
|
|
120
120
|
| `reserveSupportRow` | Empty support row of hint height | Live validation, fields side by side | `false` |
|
|
121
121
|
| `required` / `optional` | Red `*` / muted «необязательно» after the label | Mark the minority of the form | — / `false` |
|
|
122
122
|
|
|
@@ -164,7 +164,7 @@ Inline save (`onCommit`): the text at focus is the committed one; blur and Mod+E
|
|
|
164
164
|
| `Tab` | Focuses the field, then the buttons in its corner; Enter inserts a line break instead of submitting the form. |
|
|
165
165
|
| `Mod+Enter` | With `onCommit`: saves a changed text; focus stays in the field. |
|
|
166
166
|
| `Escape` | With `formatting`: first clears a selection with the toolbar. With `onCommit`: puts the committed text back. |
|
|
167
|
-
| `Mod + B` · `Mod + I` · `Mod + Shift + X` · `Mod + E` · `Mod + Shift + H` · `Mod + K` · `Mod + Shift + 9` | With `formatting`: bold, italic, strikethrough, code, spoiler, link and quote for the selection; the physical key counts, so any layout works. |
|
|
167
|
+
| `Mod + B` · `Mod + I` · `Mod + U` · `Mod + Shift + X` · `Mod + E` · `Mod + Shift + H` · `Mod + K` · `Mod + Shift + 9` | With `formatting`: bold, italic, underline, strikethrough, code, spoiler, link and quote for the selection; the physical key counts, so any layout works. |
|
|
168
168
|
| `←` · `→` | Move between the formatting buttons. |
|
|
169
169
|
| `Home` · `End` | Jump to the first / last formatting button. |
|
|
170
170
|
|
|
@@ -194,6 +194,7 @@ Inline save (`onCommit`): the text at focus is the committed one; blur and Mod+E
|
|
|
194
194
|
| `formatting` | `"Форматирование"` | Accessible name of the formatting toolbar. |
|
|
195
195
|
| `bold` | `"Жирный"` | Bold tool. |
|
|
196
196
|
| `italic` | `"Курсив"` | Italic tool. |
|
|
197
|
+
| `underline` | `"Подчёркнутый"` | Underline tool (`__…__`). |
|
|
197
198
|
| `strikethrough` | `"Зачёркнутый"` | Strikethrough tool. |
|
|
198
199
|
| `code` | `"Моноширинный"` | Code tool. |
|
|
199
200
|
| `spoiler` | `"Скрытый текст"` | Spoiler tool (`\|\|…\|\|`). |
|
|
@@ -128,7 +128,7 @@ The navigation of the nearest `AppShell.Root` for a trigger outside the rail (a
|
|
|
128
128
|
- Two full-height planes edge to edge: no inset, radius, shadow or border — the boundary is the fill change (the Sidebar rail is layer 1, the panel the page: white rail beside a gray page in light, a rail one step lighter than the near-black page in dark).
|
|
129
129
|
- Main hands the page its edge air: `--page-gutter` is the page gutter `--prime-layout-gutter` — `--prime-layout-gutter-s` (16) → `-m` (24) from 640px → `-l` (32) from 1024px; `--page-top` is `--prime-space-6` (24) → `--prime-space-8` (32) from 1024px. `Page.Root` pads itself with them (its top bar steps out to the panel's edges). Main itself pads only the bottom: `--prime-space-16`, the scroll tail of a long page. Inside Main the page gutter is the small one again, so a frame nested in the page starts from its own gutter, not the shell's.
|
|
130
130
|
- A page that fills the panel height (`Page.Root fill`): Main's bottom padding drops to the side gutter, so no long scroll tail stays under it — beside the Sidebar rail too.
|
|
131
|
-
- Main
|
|
131
|
+
- Main hands its scroll on at its end (ScrollContainer `overscrollBehavior="auto"`): without `fillViewport` it is not a scroller and the document scrolls under the pointer.
|
|
132
132
|
- Nav column: sticky at the top, viewport high, `z-index: --prime-z-sticky` (above the panel, so the compact Sidebar toggle can ride the rail edge); with `fillViewport` it fills the shell height instead.
|
|
133
133
|
- Footer: no padding of its own (BottomNav keeps the bottom safe-area inset), sticky at the bottom, `z-index: --prime-z-sticky`.
|
|
134
134
|
- A floating BottomNav (`floating`) in the footer: the footer takes no height, the capsule overlays the page, and Main keeps extra bottom room (the capsule height and the safe-area inset) so its last line never ends up under it.
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
- Every route rendered inside `AppShell.Main`: the app is navigation and the page, and the page carries its own top (`Page.TopBar`), title (`Page.Title`, the `<h1>`) and body.
|
|
10
10
|
- A work screen (a list, a board, a chat, a schedule): `Page.TopBar` with the title (`Page.Heading`), the page's tools (`Page.Tools`: sections, search and filter, view), all its buttons (`Page.Actions`) and the active filters (`Page.Chips`).
|
|
11
11
|
- A filter stands next to what it filters. `Page.Tools` holds what acts on the whole page: sections, the view, a period that drives several blocks (the KPI cards and the table), and a search and filter only when it narrows several views of one set (a board and its list). A table's search, filter, chips, «Колонки» and selection menu always belong to its own tools row (DataTable `toolbar` + `toolbarChips`), on a page that is only the table too.
|
|
12
|
-
- A section with sub-pages (the «Мессенджеры» item of the Sidebar with
|
|
13
|
-
- A document page (settings, a long form, help)
|
|
12
|
+
- A section with sub-pages (the «Мессенджеры» item of the Sidebar with «Каналы», «Шаблоны» and «Статистика»): each sub-page is a route of its own, `Page.Title` stays the section's name, and the switch is a `SegmentedControl` of links (`SegmentedControl.Item asChild`) in `Page.Tools`, on a line of its own under the title at the start (see Top bar plan), so it stays put while each sub-page brings its own `Page.Actions` or none; the body is the current sub-page. The Sidebar keeps the section's item current on every sub-route — a router `NavLink` to the section's path without `end`, or `current={pathname.startsWith("/messengers")}`.
|
|
13
|
+
- A document page (settings, a long form, help) read top to bottom with no page tools: `Page.Header` in the flow — the large title and its buttons — and `Page.TopBar collapse` with the path in its `Page.Heading`, which repeats the title once it scrolls away. A dashboard or an analytics page titles itself in `Page.TopBar` like a list.
|
|
14
14
|
- A nested record: a `Breadcrumb` path in `Page.Heading`; in the bar the title reads as its last level.
|
|
15
15
|
- A text page held to a reading measure (`maxWidth="readable"`), a wide dashboard capped at the content width (`maxWidth="wide"`).
|
|
16
16
|
- The tiles of a dashboard — KPI cards, charts, panels — as one board (`Page.Grid` + `Page.Cell`).
|
|
@@ -94,7 +94,7 @@ Page.Root the page column (<div>); maxWidth, fill, labe
|
|
|
94
94
|
| `…rest` | `HTMLAttributes<HTMLElement>` | — | `children` (`Page.Heading`, `Page.Tools`, `Page.Actions`, `Page.Chips` — direct children), `className` and the other attributes. |
|
|
95
95
|
|
|
96
96
|
### Page.Header
|
|
97
|
-
`ref` → `HTMLDivElement`. The document header in the page flow (settings, long forms
|
|
97
|
+
`ref` → `HTMLDivElement`. The document header in the page flow (settings, long forms): a `Page.Heading` and its `Page.Actions` in one row; the actions wrap under it on a narrow column.
|
|
98
98
|
|
|
99
99
|
| Prop | Type | Default | Description |
|
|
100
100
|
|---|---|---|---|
|
|
@@ -123,7 +123,7 @@ Page.Root the page column (<div>); maxWidth, fill, labe
|
|
|
123
123
|
| `…rest` | `HTMLAttributes<HTMLParagraphElement>` | — | `children`, `className` and the other paragraph attributes. |
|
|
124
124
|
|
|
125
125
|
### Page.Tools
|
|
126
|
-
`ref` → `HTMLDivElement`. What acts on the whole page:
|
|
126
|
+
`ref` → `HTMLDivElement`. What acts on the whole page: sub-page links, view, a shared period, a filter over several views (a table's tools go in its `toolbar`). Sub-page links take a line of their own at the start. Named, a `group`.
|
|
127
127
|
|
|
128
128
|
| Prop | Type | Default | Description |
|
|
129
129
|
|---|---|---|---|
|
|
@@ -226,8 +226,8 @@ No DOM, no ref. Holds the page from anywhere inside it: a press on a `Sidebar.It
|
|
|
226
226
|
### Place of Page.Heading
|
|
227
227
|
| Value | Looks like | Use when | Default |
|
|
228
228
|
|---|---|---|---|
|
|
229
|
-
| in `Page.TopBar` | one line as high as the Sidebar brand row: path › title-m, the caption description under the whole line; both truncate |
|
|
230
|
-
| in `Page.Header` in the flow | the large document header: path over a heading-m title, body-m description, the header's `Page.Actions` beside it, wrapping under it | document pages
|
|
229
|
+
| in `Page.TopBar` | one line as high as the Sidebar brand row: path › title-m, the caption description under the whole line; both truncate | screens of data: lists, boards, chats, records, dashboards and analytics | |
|
|
230
|
+
| in `Page.Header` in the flow | the large document header: path over a heading-m title, body-m description, the header's `Page.Actions` beside it, wrapping under it | document pages with no page tools: settings, long forms, help | |
|
|
231
231
|
|
|
232
232
|
The size follows the place; the consumer passes nothing, and there are no title size props. One `Page.Title` (`<h1>`) per page, in one of the two places. A bar with no visible title still holds the `<h1>`: a `Page.Title` wrapped in `VisuallyHidden`, or a flow `Page.Title` (with `collapse` the bar repeats it).
|
|
233
233
|
|
|
@@ -236,7 +236,7 @@ The size follows the place; the consumer passes nothing, and there are no title
|
|
|
236
236
|
|---|---|---|---|
|
|
237
237
|
| `fill` (Root) | the page is as high as main; the body takes what the bar and the header leave; main keeps gutter-high air under it | a full-height table, board, chat or `Page.Split` in an `AppShell.Root fillViewport` | `false` |
|
|
238
238
|
| `sticky` (TopBar) | the bar stays at the top of main while the page scrolls under it | every page; `false` for a bar that should scroll away | `true` |
|
|
239
|
-
| `collapse` (TopBar) | the flow title rises into the bar's title row once it scrolls away | a document page
|
|
239
|
+
| `collapse` (TopBar) | the flow title rises into the bar's title row once it scrolls away | a document page with the path in the bar and the title in the flow | `false` |
|
|
240
240
|
| `dirty` (Guard) | nothing until a person leaves: a press on the navigation or the path asks «leave without saving?» | a form that is the page has unsaved changes | `false` |
|
|
241
241
|
| `busy` (Guard) | nothing until a person leaves: a press on the navigation or the path does nothing | the page's save request runs | `false` |
|
|
242
242
|
|
|
@@ -245,13 +245,13 @@ One plan, chosen from the natural widths of the groups (`data-plan` on the bar's
|
|
|
245
245
|
|
|
246
246
|
| Step | Looks like | Use when | Default |
|
|
247
247
|
|---|---|---|---|
|
|
248
|
-
| `one` | `[Heading 1fr][Tools][Actions]` on one line | everything fits | yes |
|
|
249
|
-
| `below` | the tools on a line of their own under the heading and the actions — the one tool row, as the DataTable `toolbar`: a search (`SmartFilter.Toolbar`, `Input.Root`) takes the free room, switchers, pickers and buttons keep their width at the end; under 36rem the search takes a line of its own and a `SegmentedControl.Root` / `Datepicker.Root` / Select fills the line it stands on | heading + tools + actions do not fit | |
|
|
248
|
+
| `one` | `[Heading 1fr][Tools][Actions]` on one line | everything fits, and the tools hold no navigation | yes |
|
|
249
|
+
| `below` | the tools on a line of their own under the heading and the actions — the one tool row, as the DataTable `toolbar`: a search (`SmartFilter.Toolbar`, `Input.Root`) takes the free room, switchers, pickers and buttons keep their width at the end; under 36rem the search takes a line of its own and a `SegmentedControl.Root` / `Datepicker.Root` / Select fills the line it stands on | heading + tools + actions do not fit; always for a section's sub-pages (the tools hold a navigation — a `SegmentedControl` of links), gathered at the start | |
|
|
250
250
|
| `icons-secondary` | as `below`; secondary Buttons with a `Button.Icon` show only the icon, the label is their name and their tooltip | heading + actions still do not fit | |
|
|
251
251
|
| `icons` | the primary Button gives way to its icon too | still too wide | |
|
|
252
252
|
| `fold` | the secondary Buttons fold into «⋯» (`labels.more`); the primary stays | still too wide, and «⋯» is narrower than their icons (a single secondary keeps its icon); past it a Breadcrumb path folds into «…»; if the path and the title still do not share the line, the heading stacks: the path on its own line over the title, the title and the description wrapping (`data-heading="stack"`); a heading without a path truncates | |
|
|
253
253
|
|
|
254
|
-
The actions stay on the first line beside the heading at every step; `Page.Chips` is always a line under everything; nothing overflows. The primary is the first solid `Button.Root`; a Button without an icon keeps its label at every step; anything that is not a `Button.Root` stays as it is.
|
|
254
|
+
A section's sub-pages never share the first line: there they would stand where each sub-page's own heading and actions leave room, and those differ («Подключить канал» on one, none on another), so the switch would move under the pointer. On a line of their own at the start they stand at one place on every sub-page and at every width. The actions stay on the first line beside the heading at every step; `Page.Chips` is always a line under everything; nothing overflows. The primary is the first solid `Button.Root`; a Button without an icon keeps its label at every step; anything that is not a `Button.Root` stays as it is.
|
|
255
255
|
|
|
256
256
|
### Grid columns
|
|
257
257
|
| Grid width | Cell `span` 1–3 | `span` 4 | `span` 5–6 | `span` 7–12 | `rows` |
|
|
@@ -342,9 +342,9 @@ A wide cell (`span` 7–11) written next to a narrow one (`span` 4–5) is a pai
|
|
|
342
342
|
| [structure.tsx](examples/structure.tsx) | A document page in the flow: the path over the large title, its line of data and the page's buttons beside them, then the body with a section — `Page.Header`, `Page.Heading`, `Page.Description`, `Page.Section`. |
|
|
343
343
|
| [top-bar.tsx](examples/top-bar.tsx) | A nested record in the top bar: the path ending in the title, a line of data, the page's tools and its buttons — `Page.TopBar`, `Page.Heading`, `Page.Tools`, `Page.Actions`. |
|
|
344
344
|
| [loading.tsx](examples/loading.tsx) | A page whose figures load after it opens: the line of data keeps its place under the title while it loads, so nothing in the bar moves — `loading`. |
|
|
345
|
-
| [collapse.tsx](examples/collapse.tsx) |
|
|
345
|
+
| [collapse.tsx](examples/collapse.tsx) | A document page whose large title scrolls away under the top bar: the bar holds the path and repeats the title after it once the title is out of view — `collapse`, `Page.Header`. |
|
|
346
346
|
| [tools.tsx](examples/tools.tsx) | The orders page's tools in its top bar: sections with counts, the search and filter that narrow both views, the view, in the order written; the active filters on a line under everything — `Page.Tools`, `Page.Chips`. |
|
|
347
|
-
| [router.tsx](examples/router.tsx) | A section with sub-pages: the title is the section, each sub-page is a route of its own, the switch in the bar is their links and the body cross-fades between them; render it inside a router — `Page.Tools`, `asChild`. |
|
|
347
|
+
| [router.tsx](examples/router.tsx) | A section with sub-pages: the title is the section, each sub-page is a route of its own with its own actions, the switch in the bar is their links on a line of their own and the body cross-fades between them; render it inside a router — `Page.Tools`, `asChild`. |
|
|
348
348
|
| [actions-collapse.tsx](examples/actions-collapse.tsx) | The buttons of an invoices page on a tablet-wide column: the line runs short, so the secondary buttons show only their icons with the label as the name and the tooltip, the primary keeps its label — `Page.Actions`, `Button.Icon`. |
|
|
349
349
|
| [sections.tsx](examples/sections.tsx) | A page with titled sections: tiles of one screen close together, a new topic set off by air, a link at the end of the heading — `Page.Section`, `label`, `description`, `Page.Actions`. |
|
|
350
350
|
| [grid.tsx](examples/grid.tsx) | A dashboard board: four KPI tiles, a chart two rows high beside two panels, one gap on both axes — `Page.Grid`, `Page.Cell`, `span`, `rows`. |
|
|
@@ -365,6 +365,7 @@ A wide cell (`span` 7–11) written next to a narrow one (`span` 4–5) is a pai
|
|
|
365
365
|
- `SmartFilter.Root` inside the bar, around `Page.Tools` → it holds no DOM; wrap it around `Page.Root` so its Toolbar (in `Page.Tools`) and its Chips (in `Page.Chips`) share it.
|
|
366
366
|
- A table's search, filter, chips or «Колонки» in `Page.Tools` / `Page.Chips` → they read as filters of the whole page and move when the page grows a block; they go in the table's `toolbar` / `toolbarChips` on every page, the one that is only the table too. The bar keeps the title, sections, the view, a period that drives every block and the page's buttons.
|
|
367
367
|
- Sub-pages of a section as a second Sidebar level, a tab strip inside `Page.Body`, or a title that names the sub-page → the title stays the section, the sub-pages are route links in `Page.Tools` (`SegmentedControl.Item asChild`), and the section's Sidebar item stays current on every sub-route.
|
|
368
|
+
- A dashboard or analytics page with a bar of tools only (`collapse`) over a large title in the flow → the title and its line of data go in the bar's `Page.Heading` beside the tools, as on a list; the flow header is for document pages.
|
|
368
369
|
- `Page.Header` inside `Page.TopBar` → the bar takes `Page.Heading` and `Page.Actions` directly; `Page.Header` is the flow's document header.
|
|
369
370
|
- A hand-made menu button for the phone Sidebar → the bar draws it while the Sidebar is off-canvas.
|
|
370
371
|
- Hiding buttons with media queries on a phone → `Page.Actions` in the bar collapses and folds them by measured width.
|
|
@@ -1,60 +1,74 @@
|
|
|
1
|
-
/**
|
|
2
|
-
import { AppShell, Button, Card, Icon, Page,
|
|
3
|
-
import * as React from "react";
|
|
1
|
+
/** A document page whose large title scrolls away under the top bar: the bar holds the path and repeats the title after it once the title is out of view — `collapse`, `Page.Header`. */
|
|
2
|
+
import { AppShell, Breadcrumb, Button, Card, Icon, Page, Typography } from "prime-ui-kit";
|
|
4
3
|
|
|
5
4
|
import styles from "./examples.module.css";
|
|
6
5
|
|
|
7
|
-
const
|
|
6
|
+
const SECTIONS = [
|
|
8
7
|
{
|
|
9
|
-
id: "
|
|
10
|
-
title: "
|
|
11
|
-
text: "
|
|
8
|
+
id: "terms",
|
|
9
|
+
title: "Сроки",
|
|
10
|
+
text: "Клиент может вернуть заказ в течение 14 дней после получения. Срок считается от даты в акте приёмки.",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "condition",
|
|
14
|
+
title: "Состояние товара",
|
|
15
|
+
text: "Товар принимается в заводской упаковке, без следов использования, с ярлыками и полным комплектом.",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "refund",
|
|
19
|
+
title: "Возврат денег",
|
|
20
|
+
text: "Деньги возвращаются тем же способом, которым был оплачен заказ, в течение 10 рабочих дней после проверки.",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "delivery",
|
|
24
|
+
title: "Доставка при возврате",
|
|
25
|
+
text: "Если товар с браком, доставку оплачивает магазин. В остальных случаях — клиент по тарифу службы доставки.",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "exceptions",
|
|
29
|
+
title: "Исключения",
|
|
30
|
+
text: "Не возвращаются товары, изготовленные по индивидуальному заказу, и цифровые лицензии после активации.",
|
|
12
31
|
},
|
|
13
|
-
{ id: "w40", title: "29 сентября – 5 октября", text: "Выручка 1,1 млн ₽, 298 заказов." },
|
|
14
|
-
{ id: "w39", title: "22–28 сентября", text: "Выручка 0,9 млн ₽, 264 заказа." },
|
|
15
|
-
{ id: "w38", title: "15–21 сентября", text: "Выручка 1,0 млн ₽, 281 заказ." },
|
|
16
|
-
{ id: "w37", title: "8–14 сентября", text: "Выручка 0,8 млн ₽, 240 заказов." },
|
|
17
32
|
];
|
|
18
33
|
|
|
19
34
|
export default function PageCollapseExample() {
|
|
20
|
-
const [period, setPeriod] = React.useState("week");
|
|
21
|
-
|
|
22
35
|
return (
|
|
23
36
|
<div className={`${styles.window} ${styles.tall}`}>
|
|
24
37
|
<AppShell.Root fillViewport className={styles.shell}>
|
|
25
38
|
<AppShell.Main>
|
|
26
|
-
<Page.Root>
|
|
39
|
+
<Page.Root maxWidth="readable">
|
|
27
40
|
<Page.TopBar collapse>
|
|
28
|
-
<Page.
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
<Page.Heading>
|
|
42
|
+
<Breadcrumb.Root>
|
|
43
|
+
<Breadcrumb.Item href="#documents">Документы</Breadcrumb.Item>
|
|
44
|
+
</Breadcrumb.Root>
|
|
45
|
+
</Page.Heading>
|
|
46
|
+
</Page.TopBar>
|
|
47
|
+
<Page.Header>
|
|
48
|
+
<Page.Heading>
|
|
49
|
+
<Page.Title>Политика возвратов</Page.Title>
|
|
50
|
+
<Page.Description>
|
|
51
|
+
Действует с 1 сентября для всех заказов магазина.
|
|
52
|
+
</Page.Description>
|
|
53
|
+
</Page.Heading>
|
|
34
54
|
<Page.Actions>
|
|
35
55
|
<Button.Root variant="soft" tone="neutral">
|
|
36
56
|
<Button.Icon>
|
|
37
57
|
<Icon name="action.download" />
|
|
38
58
|
</Button.Icon>
|
|
39
|
-
|
|
59
|
+
Скачать PDF
|
|
40
60
|
</Button.Root>
|
|
41
61
|
</Page.Actions>
|
|
42
|
-
</Page.TopBar>
|
|
43
|
-
<Page.Header>
|
|
44
|
-
<Page.Heading>
|
|
45
|
-
<Page.Title>Продажи</Page.Title>
|
|
46
|
-
<Page.Description>Интернет-магазин · на 14 октября, 9:40</Page.Description>
|
|
47
|
-
</Page.Heading>
|
|
48
62
|
</Page.Header>
|
|
49
63
|
<Page.Body>
|
|
50
|
-
{
|
|
51
|
-
<Card.Root key={
|
|
64
|
+
{SECTIONS.map((section) => (
|
|
65
|
+
<Card.Root key={section.id} variant="panel">
|
|
52
66
|
<Card.Header>
|
|
53
|
-
<Card.Title as="h2">{
|
|
67
|
+
<Card.Title as="h2">{section.title}</Card.Title>
|
|
54
68
|
</Card.Header>
|
|
55
69
|
<Card.Body>
|
|
56
70
|
<Typography variant="body-m" tone="secondary">
|
|
57
|
-
{
|
|
71
|
+
{section.text}
|
|
58
72
|
</Typography>
|
|
59
73
|
</Card.Body>
|
|
60
74
|
</Card.Root>
|