prime-ui-kit 0.21.2 → 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-VQ3UCVDV.js → chunk-BAJRN3QN.js} +2 -2
- package/dist/chunks/{chunk-T7IEJSTH.js → chunk-HMLQCKRL.js} +36 -0
- package/dist/chunks/{chunk-7JYPFRZ3.js → chunk-JO7E6SZQ.js} +29 -18
- package/dist/chunks/{chunk-ME4VEAQ5.js → chunk-R5LI5BO3.js} +2122 -1614
- package/dist/chunks/{chunk-SQGPQOUO.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 +5 -1
- package/dist/components/message/MessageParts.d.ts +7 -2
- package/dist/components/rich-text/RichText.d.ts +12 -4
- 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 +5 -1
- package/dist/icon-set.js +8 -2
- package/dist/icons/glyph.d.ts +12 -1
- 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 +8 -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 +8 -6
- package/dist/internal/richText.d.ts +32 -1
- 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 +2 -1
- 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 +17 -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 +12 -7
- 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/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 +1 -1
- 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 +5 -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 +17 -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 +12 -7
- 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/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 +1 -1
- 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/page/COMPONENT.md +13 -12
- package/src/layout/page/examples/collapse.tsx +45 -31
- package/src/layout/page/examples/router.tsx +47 -14
|
@@ -32,7 +32,7 @@ Modal.Root state and dismiss policy (no DOM)
|
|
|
32
32
|
│ ├── Modal.Icon 40 tile with a tone fill; the record's Avatar / Thumbnail with no tile, 40
|
|
33
33
|
│ ├── Modal.Title line 1: <h2>, names the dialog; `loading` holds it with a skeleton
|
|
34
34
|
│ ├── Modal.Description line 2: <p>, describes the dialog; status Badges lead it (tier s)
|
|
35
|
-
│ └── Modal.Actions buttons
|
|
35
|
+
│ └── Modal.Actions one tab-wide switcher, then buttons (icon buttons, a ⋯ Dropdown), tier s, on the title's line
|
|
36
36
|
├── Modal.Body the only scrolling zone
|
|
37
37
|
│ └── Modal.Section <section>: <h3> label, description, Modal.Actions at the heading's end
|
|
38
38
|
└── Modal.Footer actions, primary last
|
|
@@ -85,7 +85,7 @@ No DOM, no ref. State and dismiss policy.
|
|
|
85
85
|
| `…rest` | `HTMLAttributes<HTMLElement>` | — | `className` and the other attributes of the `<header>`. |
|
|
86
86
|
|
|
87
87
|
### Modal.Actions
|
|
88
|
-
`ref` → `HTMLDivElement`. Header
|
|
88
|
+
`ref` → `HTMLDivElement`. Header: a tab-wide switcher (`SegmentedControl`, `Datepicker`), then buttons (a status goes in `Modal.Description`), tier `s`; under the title below 12rem; at the end of a `Modal.Section` heading. In `Modal.Footer`: the start group at the footer tier.
|
|
89
89
|
|
|
90
90
|
| Prop | Type | Default | Description |
|
|
91
91
|
|---|---|---|---|
|
|
@@ -245,13 +245,13 @@ Motion (`overlayMotion`): the scrim fades in and out over `base`; the dialog fad
|
|
|
245
245
|
## Layout & spacing
|
|
246
246
|
- The header has one anatomy, the same in every modal, drawer and sheet:
|
|
247
247
|
```
|
|
248
|
-
[mark] Title [icon buttons] [⋯] [×]
|
|
248
|
+
[mark] Title [tab switcher] [icon buttons] [⋯] [×]
|
|
249
249
|
[status badges] description text
|
|
250
250
|
```
|
|
251
251
|
- mark — `Modal.Icon`: a glyph on the tone tile, or the record's picture (`Avatar.Root`, `Thumbnail.Root`, also inside your own wrapper component) with no tile; both are 40 px.
|
|
252
252
|
- line 1 — `Modal.Title`; `loading` holds it with a Skeleton while the name loads.
|
|
253
253
|
- line 2 — `Modal.Description`: the record's statuses as `Badge.Root`s first, then the text. The kit sizes the badges (chip tier `s`, the line's 20 px), keeps them first and one gap apart, and wraps the text under them on a narrow panel.
|
|
254
|
-
- end — `Modal.Actions`:
|
|
254
|
+
- end — `Modal.Actions`: first one switcher for the whole open tab (a `SegmentedControl`, a `Datepicker` period), then the buttons — ghost icon buttons with a Tooltip and a ⋯ Dropdown — at the close button's tier, centred on the title's line; a status never goes here. A control rendered only on some tabs rises in and closes up in its own cell.
|
|
255
255
|
- An action apart from the main pair («Удалить» of a record, «Выйти»): `Modal.Actions` inside `Modal.Footer` puts it at the start, at the footer's tier; stacked on a phone it comes first and the primary stays last.
|
|
256
256
|
- Dialog padding `--prime-modal-padding` (24) on every zone; header 24 top / 20 bottom, body 20 with a 16 gap between blocks, footer 20 top / 24 bottom.
|
|
257
257
|
- Max height = viewport minus `--prime-modal-viewport-padding` on both sides; only the body scrolls.
|
|
@@ -317,6 +317,7 @@ Motion (`overlayMotion`): the scrim fades in and out over `base`; the dialog fad
|
|
|
317
317
|
- Busy or unsaved state lifted out of the form into Root props → put `Modal.Guard` next to the form, inside Content.
|
|
318
318
|
- `key` or a conditional around `Modal.Root` to reset a form → the dialog unmounts at once, without its exit; keep the Root mounted and drive `open`.
|
|
319
319
|
- A status `Badge` in the header's `Modal.Actions`, beside the buttons → put it in `Modal.Description`, before the text: the kit sizes it to the line and keeps it first.
|
|
320
|
+
- A control that applies to the whole tab (the text language, the period) repeated in section headings or put in one section → one switcher in the header's `Modal.Actions`, before the buttons, rendered only on the tabs it applies to.
|
|
320
321
|
- A Badge with its own `size` in `Modal.Description` → leave `size` out: the line gives every header the same chip tier.
|
|
321
322
|
- A `Skeleton` placed by hand in `Modal.Title` or `Modal.Description` → their `loading`.
|
|
322
323
|
- A start-side button («Выйти», «Удалить») put inside `Modal.Meta` → `Modal.Actions` inside `Modal.Footer`; `Meta` is status text only.
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
**Category:** data-display
|
|
4
4
|
**Kind:** primitive
|
|
5
5
|
|
|
6
|
-
> Text in the chat's Markdown subset
|
|
6
|
+
> Text in the chat's Markdown subset — or a messenger's plain text with its entities — outside a message (a bot's welcome text, a template, a comment), rendered as elements, never as HTML.
|
|
7
7
|
|
|
8
8
|
## When to use
|
|
9
9
|
- A text written with the formatting toolbar of `Composer` or `Textarea formatting` is shown somewhere other than a chat: a bot's settings, a template list, a card.
|
|
10
10
|
- Text from users or bots that may carry `**bold**`, links or a `||spoiler||` and must be rendered safely.
|
|
11
|
+
- A text stored as a messenger sends it — plain text and formatted ranges (Telegram `entities`, VK `format_data`) → `entities`: the text is never read as Markdown, so `**` in it stays text and a range may start inside a word.
|
|
12
|
+
- Moving between the two forms: `getTextEntities(source)` turns a Composer message into `{ text, entities }` for the messenger's API; `getRichTextSource({ text, entities })` turns a stored text back into Markdown for the editor, escaping what would read as markup (`\*`).
|
|
11
13
|
|
|
12
14
|
## When not to use
|
|
13
15
|
- The text of a chat message → [Message](../message/COMPONENT.md) `Message.Text` (it marks search matches and holds the meta's room).
|
|
@@ -17,13 +19,13 @@
|
|
|
17
19
|
|
|
18
20
|
## Import
|
|
19
21
|
```tsx
|
|
20
|
-
import { RichText } from "prime-ui-kit";
|
|
22
|
+
import { getRichTextSource, getTextEntities, RichText, type TextEntity } from "prime-ui-kit";
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
## Anatomy
|
|
24
26
|
```
|
|
25
|
-
RichText <div>; the source as children
|
|
26
|
-
├─ <p> a run of text, line breaks kept
|
|
27
|
+
RichText <div>; the source (or the plain text of `entities`) as children
|
|
28
|
+
├─ <p> a run of text, line breaks kept; <strong> <em> <u> <s> <code> <a> inside
|
|
27
29
|
├─ <blockquote> lines starting with "> "
|
|
28
30
|
└─ <pre> fenced code
|
|
29
31
|
```
|
|
@@ -37,17 +39,51 @@ RichText <div>; the source as children
|
|
|
37
39
|
|
|
38
40
|
| Prop | Type | Default | Description |
|
|
39
41
|
|---|---|---|---|
|
|
40
|
-
| `children` | `string` | — (required) | The
|
|
42
|
+
| `children` | `string` | — (required) | The Markdown subset (`**`, `_`, `__`, `~~`, `\|\|`, code, links, `> ` quotes; `\*` escapes a mark), or with `entities` the plain text they format. |
|
|
43
|
+
| `entities` | `readonly TextEntity[]` | — | The formatted ranges of a messenger's text (Telegram, VK): `children` is then plain text, never read as Markdown, and a range may start inside a word. Bare URLs in it still link. |
|
|
41
44
|
| `labels` | `Partial<RichTextLabels>` | — | Built-in strings, see Labels. |
|
|
42
45
|
| `…rest` | `Omit<HTMLAttributes<HTMLDivElement>, "children">` | — | `className`, `id`, `data-*` and the other div attributes. |
|
|
43
46
|
|
|
47
|
+
### Types
|
|
48
|
+
|
|
49
|
+
#### `TextEntity`
|
|
50
|
+
One formatted range of a plain text, as messengers send it (Telegram `MessageEntity`, VK `format_data`): map their type names onto these.
|
|
51
|
+
|
|
52
|
+
| Field | Type | Default | Description |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| `type` | `"bold" \| "italic" \| "underline" \| "strikethrough" \| "spoiler" \| "code" \| "pre" \| "link" \| "quote"` | — (required) | The format; `pre` and `quote` are blocks (the line breaks at their edges are theirs). A type the kit does not know shows its text. |
|
|
55
|
+
| `offset` | `number` | — (required) | Start in UTF-16 code units (a JS string index, as in Telegram). |
|
|
56
|
+
| `length` | `number` | — (required) | Length in UTF-16 code units; ranges nest and may cross (a crossing one is cut). |
|
|
57
|
+
| `url` | `string` | — | The address of a `link`; without it the range itself is the address. Only http(s), `mailto:` and `tel:` link. |
|
|
58
|
+
| `language` | `string` | — | The language of a `pre` block, kept through the conversions. |
|
|
59
|
+
|
|
60
|
+
#### `FormattedText`
|
|
61
|
+
A plain text with its formatted ranges: what a messenger sends and takes.
|
|
62
|
+
|
|
63
|
+
| Field | Type | Default | Description |
|
|
64
|
+
|---|---|---|---|
|
|
65
|
+
| `text` | `string` | — (required) | The plain text. |
|
|
66
|
+
| `entities` | `TextEntity[]` | — (required) | Its formatted ranges. |
|
|
67
|
+
|
|
68
|
+
### Functions
|
|
69
|
+
|
|
70
|
+
#### `getTextEntities(source)`
|
|
71
|
+
`(source: string) => FormattedText`
|
|
72
|
+
|
|
73
|
+
The plain `text` and `entities` of a Markdown source — what a messenger's API takes from a Composer message. Bare URLs make no entity (the messenger links them).
|
|
74
|
+
|
|
75
|
+
#### `getRichTextSource(value)`
|
|
76
|
+
`(value: FormattedText) => string`
|
|
77
|
+
|
|
78
|
+
The Markdown source of `{ text, entities }` — to edit a received or stored text in Composer or `Textarea formatting`. Text that would read as markup is escaped (`\*`); an italic or underline inside a word, which the subset cannot hold, is dropped and its text kept.
|
|
79
|
+
|
|
44
80
|
## Variants
|
|
45
81
|
One look: the type of its host. Wrap it in `Typography as="div"` to pick a text role.
|
|
46
82
|
|
|
47
83
|
## States
|
|
48
84
|
| State | Driven by | DOM |
|
|
49
85
|
|---|---|---|
|
|
50
|
-
| masked spoiler | `\|\|text\|\|` in `children` | `role="button"`, `data-state="masked"`: a wash over transparent words |
|
|
86
|
+
| masked spoiler | `\|\|text\|\|` in `children`, or a `spoiler` entity | `role="button"`, `data-state="masked"`: a wash over transparent words |
|
|
51
87
|
| revealed spoiler | a press, `Enter` or `Space` | `data-state="revealed"`: the wash and the words cross-fade on the fade clock; one way, like a chat |
|
|
52
88
|
|
|
53
89
|
## Layout & spacing
|
|
@@ -82,12 +118,14 @@ One look: the type of its host. Wrap it in `Typography as="div"` to pick a text
|
|
|
82
118
|
| Example | Shows |
|
|
83
119
|
|---|---|
|
|
84
120
|
| [overview.tsx](examples/overview.tsx) | The welcome text of a support bot shown outside the chat: bold, a link, a quote, code and a spoiler from the chat's Markdown — `children`. |
|
|
121
|
+
| [entities.tsx](examples/entities.tsx) | A bot's greeting stored as a messenger sends it — plain text with formatted ranges — shown with its stars kept as text — `entities`. |
|
|
85
122
|
|
|
86
123
|
## Mistakes
|
|
87
124
|
- `dangerouslySetInnerHTML` with a Markdown library for a chat text → `RichText`: the same subset as the chat, never HTML.
|
|
88
125
|
- A `Message.Root` with no time and no author only to render formatted text → `RichText`.
|
|
89
126
|
- Sizing the text with a class on `RichText` → put it inside the host that sets the type.
|
|
127
|
+
- Converting a messenger's entities to Markdown by hand, or showing a text `plain` because it may hold `**` → `entities`; for the editor, `getRichTextSource`.
|
|
90
128
|
|
|
91
129
|
## Related
|
|
92
|
-
- **Built from:** `internal/richText` (the parser and the elements, shared with `Message.Text`)
|
|
130
|
+
- **Built from:** `internal/richText` (the Markdown parser, the entities, the conversions and the elements, shared with `Message.Text`)
|
|
93
131
|
- **See also:** [Message](../message/COMPONENT.md), [Composer](../composer/COMPONENT.md), [Textarea](../textarea/COMPONENT.md), [Typography](../typography/COMPONENT.md)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** A bot's greeting stored as a messenger sends it — plain text with formatted ranges — shown with its stars kept as text — `entities`. */
|
|
2
|
+
import { Card, RichText, type TextEntity } from "prime-ui-kit";
|
|
3
|
+
|
|
4
|
+
const GREETING = "Здравствуйте! Счета — в личном кабинете. Поле *ИНН* обязательно.";
|
|
5
|
+
|
|
6
|
+
const GREETING_ENTITIES: TextEntity[] = [
|
|
7
|
+
{ type: "bold", offset: 0, length: 13 },
|
|
8
|
+
{ type: "link", offset: 24, length: 15, url: "https://prime.app/billing" },
|
|
9
|
+
{ type: "underline", offset: 52, length: 11 },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export default function RichTextEntitiesExample() {
|
|
13
|
+
return (
|
|
14
|
+
<Card.Root>
|
|
15
|
+
<Card.Body>
|
|
16
|
+
<RichText entities={GREETING_ENTITIES}>{GREETING}</RichText>
|
|
17
|
+
</Card.Body>
|
|
18
|
+
</Card.Root>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -224,7 +224,7 @@ No `variant` or `tone`. The trigger is the field look (fill, inset control borde
|
|
|
224
224
|
| load error | `loadError` | the list is `hidden`; a compact EmptyPage with `role="alert"` and a retry Button (`onRetry`) |
|
|
225
225
|
| state swap | loading ↔ items ↔ load error | the new content fades in (`swapMotion`), the first render stays still |
|
|
226
226
|
| searching | typing in the search | panel `data-searching="true"`; groups without matches and separators hide; the match in an option's title, description and plain text is a `<mark>` in `accent-text` (not in the trigger); the query clears when the list closes (`onSearchChange("")`) |
|
|
227
|
-
| nothing to show | no option matches (and not loading) | a compact EmptyPage with `role="status"`: `labels.empty
|
|
227
|
+
| nothing to show | no option matches (and not loading) | a compact EmptyPage with `role="status"`: `labels.empty` alone; a `Select.Action` stays under it, highlighted |
|
|
228
228
|
| sheet | viewport below 640px | a scrim and a full-width bottom sheet with an `aria-hidden` grab handle around the panel; the sheet carries `data-state`, the panel keeps its role and ref; page scroll locked; a swipe down from the handle closes it |
|
|
229
229
|
| segment | inside `Input.Wrapper` | no frame (label, hint, error, support row): the root is `data-field-segment`, the trigger `data-segment="true"` — a tinted section flush with the box's start edge, a stronger fill on hover and while open, an inset focus ring; named «`aria-label`: value» |
|
|
230
230
|
| option highlighted / selected / disabled | keyboard and pointer / value / `disabled` | `data-highlighted`, `data-selected` + `aria-selected`, `data-disabled` + `aria-disabled`; highlight is a fill only |
|
|
@@ -312,7 +312,6 @@ No `variant` or `tone`. The trigger is the field look (fill, inset control borde
|
|
|
312
312
|
|---|---|---|
|
|
313
313
|
| `search` | `"Поиск"` | Placeholder and accessible name of the search field. |
|
|
314
314
|
| `empty` | `"Ничего не найдено"` | Empty state: no items or nothing matches. |
|
|
315
|
-
| `emptyHint` | `"Попробуйте изменить запрос"` | Second line of the empty state while searching; `""` hides it. |
|
|
316
315
|
| `loading` | `"Загрузка…"` | Announced while `loading` or `loadingMore`. |
|
|
317
316
|
| `retry` | `"Повторить"` | The retry button under `loadError` (`onRetry`). |
|
|
318
317
|
| `clear` | `"Очистить"` | Tooltip of the clear segment. |
|
|
@@ -32,7 +32,7 @@ Sheet.Root state and dismiss policy (no DOM)
|
|
|
32
32
|
│ ├─ Sheet.Icon tone tile around an Icon, or the record's Avatar / Thumbnail, no tile, 40
|
|
33
33
|
│ ├─ Sheet.Title line 1: <h2>; `loading` holds it with a skeleton
|
|
34
34
|
│ ├─ Sheet.Description line 2: <p>; the record's status Badges lead it (tier s), then the text
|
|
35
|
-
│ └─ Sheet.Actions buttons
|
|
35
|
+
│ └─ Sheet.Actions one tab-wide switcher, then buttons (icon buttons, a ⋯ Dropdown), tier s, on the title's line
|
|
36
36
|
├─ Sheet.Body the content; the only scrolling zone (ScrollContainer)
|
|
37
37
|
└─ Sheet.Footer optional: only for a form with an explicit submit
|
|
38
38
|
├─ Sheet.Actions the start group: an action apart from the main pair
|
|
@@ -78,7 +78,7 @@ No DOM, no ref. State and dismiss policy.
|
|
|
78
78
|
| `…rest` | `HTMLAttributes<HTMLElement>` | — | `className` and the other attributes of the `<header>`. |
|
|
79
79
|
|
|
80
80
|
### Sheet.Actions
|
|
81
|
-
`ref` → `HTMLDivElement`. Header
|
|
81
|
+
`ref` → `HTMLDivElement`. Header: a tab-wide switcher (`SegmentedControl`, `Datepicker`), then buttons (a status goes in `Sheet.Description`), tier `s`; under the title below 12rem. In `Sheet.Footer`: the start group at the footer tier.
|
|
82
82
|
|
|
83
83
|
| Prop | Type | Default | Description |
|
|
84
84
|
|---|---|---|---|
|
|
@@ -190,13 +190,13 @@ Motion: the scrim fades, the sheet slides up over `slow` and down over `base` (`
|
|
|
190
190
|
- **The header is optional.** Keep it when the content needs a title to be understood (a list of actions on «Заказ № 1042»). Drop it — and with it the × and the hairline — when the content speaks for itself (a calendar opened from a «Дата отгрузки» button): name the sheet with `aria-label` on `Sheet.Content`.
|
|
191
191
|
- The header has one anatomy, the same in every modal, drawer and sheet:
|
|
192
192
|
```
|
|
193
|
-
[mark] Title [icon buttons] [⋯] [×]
|
|
193
|
+
[mark] Title [tab switcher] [icon buttons] [⋯] [×]
|
|
194
194
|
[status badges] description text
|
|
195
195
|
```
|
|
196
196
|
- mark — `Sheet.Icon`: a glyph on the tone tile, or the record's picture (`Avatar.Root`, `Thumbnail.Root`, also inside your own wrapper component) with no tile; both are 40 px.
|
|
197
197
|
- line 1 — `Sheet.Title`; `loading` holds it with a Skeleton while the name loads.
|
|
198
198
|
- line 2 — `Sheet.Description`: the record's statuses as `Badge.Root`s first, then the text. The kit sizes the badges (chip tier `s`, the line's 20 px), keeps them first and one gap apart, and wraps the text under them on a narrow panel.
|
|
199
|
-
- end — `Sheet.Actions`:
|
|
199
|
+
- end — `Sheet.Actions`: first one switcher for the whole open tab (a `SegmentedControl`, a `Datepicker` period), then the buttons — ghost icon buttons with a Tooltip and a ⋯ Dropdown — at the close button's tier, centred on the title's line; a status never goes here. A control rendered only on some tabs rises in and closes up in its own cell.
|
|
200
200
|
- A form's action apart from its submit pair: `Sheet.Actions` inside `Sheet.Footer` puts it at the start, at the footer's tier; stacked on a phone it comes first and the primary stays last.
|
|
201
201
|
- The content is the sheet's own, across its width: `Datepicker.Panel` or a column of actions straight in `Sheet.Body` — never a Card or a padded box inside it.
|
|
202
202
|
- Zone padding `--prime-sheet-padding`, width `--prime-sheet-width`. The bottom-sheet surface (`src/internal/sheet.module.css`, shared with a Modal and the floating layers on a phone) gives the height by content up to the top gap, the rounded top, the handle strip and the bottom safe-area inset below the content.
|
|
@@ -259,6 +259,7 @@ Motion: the scrim fades, the sheet slides up over `slow` and down over `base` (`
|
|
|
259
259
|
- A header with only a title over a calendar or a list that already says what it is → drop the header and name the sheet with `aria-label`.
|
|
260
260
|
- No header and no `aria-label` → the dialog has no name; give `Sheet.Content` an `aria-label`.
|
|
261
261
|
- A status `Badge` in the header's `Sheet.Actions`, beside the buttons → put it in `Sheet.Description`, before the text: the kit sizes it to the line and keeps it first.
|
|
262
|
+
- A control that applies to the whole tab (the text language, the period) repeated in section headings or put in one section → one switcher in the header's `Sheet.Actions`, before the buttons, rendered only on the tabs it applies to.
|
|
262
263
|
- A Badge with its own `size` in `Sheet.Description` → leave `size` out: the line gives every header the same chip tier.
|
|
263
264
|
- A `Skeleton` placed by hand in `Sheet.Title` or `Sheet.Description` → their `loading`.
|
|
264
265
|
- A start-side button («Выйти», «Удалить») put inside `Sheet.Meta` → `Sheet.Actions` inside `Sheet.Footer`; `Meta` is status text only.
|
|
@@ -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>
|
|
@@ -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 (`\|\|…\|\|`). |
|