prime-ui-kit 0.21.2 → 0.21.4
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-T7IEJSTH.js → chunk-HMLQCKRL.js} +36 -0
- package/dist/chunks/{chunk-7JYPFRZ3.js → chunk-PFASPQSJ.js} +393 -354
- package/dist/chunks/{chunk-SQGPQOUO.js → chunk-SUGCIPD7.js} +1 -1
- package/dist/chunks/{chunk-ME4VEAQ5.js → chunk-UNNQITFE.js} +2132 -1619
- package/dist/chunks/{chunk-VQ3UCVDV.js → chunk-WGXXPJDR.js} +5 -2
- 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/input/Input.d.ts +1 -1
- package/dist/components/kanban/Kanban.d.ts +0 -2
- package/dist/components/list/List.d.ts +7 -1
- package/dist/components/login-form/LoginForm.d.ts +5 -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 +2 -2
- 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/autofill.d.ts +30 -0
- 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 +8 -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/button/examples/in-form.tsx +0 -1
- 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/screen.tsx +1 -5
- 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/input/COMPONENT.md +3 -0
- package/dist/skill/reference/components/input/examples/in-form.tsx +0 -1
- package/dist/skill/reference/components/input/examples/validation.tsx +1 -1
- 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/login-form/COMPONENT.md +2 -2
- package/dist/skill/reference/components/login-form/examples/sizes.tsx +6 -2
- 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/tabs/examples/orientation.tsx +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 +6 -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 +98 -4
- package/package.json +1 -1
- package/src/components/button/examples/in-form.tsx +0 -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/screen.tsx +1 -5
- 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/input/COMPONENT.md +3 -0
- package/src/components/input/examples/in-form.tsx +0 -1
- package/src/components/input/examples/validation.tsx +1 -1
- 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/login-form/COMPONENT.md +2 -2
- package/src/components/login-form/examples/sizes.tsx +6 -2
- 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/tabs/examples/orientation.tsx +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 +6 -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
|
@@ -29,7 +29,7 @@ ChatList.Root <div> column filling its host (container `chat-lis
|
|
|
29
29
|
│ └─ Crossfade loading (placeholder List rows) ⇄ empty (EmptyPage compact) ⇄ the list
|
|
30
30
|
│ └─ List.Root <ul> aria-label «Диалоги»; spacer <li aria-hidden> stand for rows out of view
|
|
31
31
|
│ └─ ChatList.Item List.Item <li> + <button> | <a href> | your element (asChild); current from value; one Tab stop
|
|
32
|
-
│ ├─ ChatList.ItemIcon before the name: avatar slot (aria-hidden); after it: a glyph
|
|
32
|
+
│ ├─ ChatList.ItemIcon before the name: avatar slot (aria-hidden); after it: a glyph (role="img" with aria-label)
|
|
33
33
|
│ ├─ ChatList.ItemText the name; muted glyph after it
|
|
34
34
|
│ ├─ ChatList.ItemDescription author · media word · text — cross-fades with draft and «печатает…»
|
|
35
35
|
│ ├─ (meta) delivery ticks · time
|
|
@@ -89,12 +89,12 @@ ChatList.Root <div> column filling its host (container `chat-lis
|
|
|
89
89
|
| `…rest` | `ButtonHTMLAttributes<HTMLButtonElement>` | — | `className` and the native attributes of the row element. |
|
|
90
90
|
|
|
91
91
|
### ChatList.ItemIcon
|
|
92
|
-
`ref` → `HTMLSpanElement`. Before the name: the avatar slot (`aria-hidden`), an `Avatar.Root` sized by the row. After the name: a glyph beside it (verified, bot,
|
|
92
|
+
`ref` → `HTMLSpanElement`. Before the name: the avatar slot (`aria-hidden`), an `Avatar.Root` sized by the row. After the name: a glyph beside it (verified, bot, channel type), any kit icon sized by the row; with `aria-label` it is `role="img"` of that name.
|
|
93
93
|
|
|
94
94
|
| Prop | Type | Default | Description |
|
|
95
95
|
|---|---|---|---|
|
|
96
|
-
| `children` | `ReactNode` | — (required) | An `Avatar.Root` (with `Avatar.Status`), or after the name an
|
|
97
|
-
| `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children">` | — | `className` and the other `span` attributes. |
|
|
96
|
+
| `children` | `ReactNode` | — (required) | An `Avatar.Root` (with `Avatar.Status`), or after the name an icon: `<Icon name>` or a glyph of `prime-ui-kit/icons` (`<SendIcon />`), as is. |
|
|
97
|
+
| `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children">` | — | `className`, `aria-label` (names a glyph after the name: its channel, «Официальный аккаунт») and the other `span` attributes. |
|
|
98
98
|
|
|
99
99
|
### ChatList.ItemText
|
|
100
100
|
`ref` → `HTMLSpanElement`. The name of the dialog: one line, truncated, the glyphs after it kept whole; the search query is marked.
|
|
@@ -200,7 +200,7 @@ Root attribute: `data-size`. Every motion honours reduced motion (durations coll
|
|
|
200
200
|
|
|
201
201
|
- The list is a `<ul>` named «Диалоги» (`labels.list`); each row an `<li>` with a button, a link with `href`, or your link with `asChild`.
|
|
202
202
|
- The open dialog is `aria-current="true"` on a button, `"page"` on a link. Selection does not follow the focus: arrows only move it (a dialog may load a conversation); Enter, Space and a click open.
|
|
203
|
-
- The row is named by its content: the name, glyphs
|
|
203
|
+
- The row is named by its content: the name, the glyphs (`ChatList.ItemIcon` `aria-label`, «Без звука»), the preview, the delivery mark («Прочитано»), the time, then «упоминание» and «непрочитанных: 3» instead of the bare badges; the avatar is `aria-hidden`.
|
|
204
204
|
- While the other side types, the preview is `aria-hidden` and «печатает…» is read; a draft reads «Черновик: …».
|
|
205
205
|
- A row with a menu has `aria-keyshortcuts="Shift+F10"`; the menu is a `Dropdown` (`menu` / `menuitem`).
|
|
206
206
|
- The search field is `type="search"` named «Поиск по диалогам»; after a search a polite status says «Найдено: 4» or «Ничего не найдено».
|
|
@@ -257,6 +257,7 @@ Root attribute: `data-size`. Every motion honours reduced motion (durations coll
|
|
|
257
257
|
- Filtering inside `renderItem` (returning `null`) → filter `items`: the list counts, announces and glides by `items`.
|
|
258
258
|
- Pinned dialogs mixed into the time order → put them first in `items`; `pinned` only draws the pin.
|
|
259
259
|
- `onValueChange` used to mark a dialog read on focus → arrows only move the focus; mark read when the conversation shows it (`Chat.Messages onRead`) or on open.
|
|
260
|
+
- A glyph after the name cloned with `label` and `size` → put the icon in `ChatList.ItemIcon` as is and name the part with `aria-label`: the row sizes it.
|
|
260
261
|
- An `Avatar.Root size="s"` to fit the row → leave `size` out; the slot sizes the avatar to the tier.
|
|
261
262
|
- A «⋯» button nested inside the row for the menu → `ChatList.ItemMenu`: it opens on right click, a long press and Shift+F10.
|
|
262
263
|
- Deleting from the menu without a question → open a `ConfirmDialog` from the menu item.
|
|
@@ -111,13 +111,13 @@ export default function ChatListStructureExample() {
|
|
|
111
111
|
</ChatList.ItemIcon>
|
|
112
112
|
<ChatList.ItemText>{item.name}</ChatList.ItemText>
|
|
113
113
|
{item.verified ? (
|
|
114
|
-
<ChatList.ItemIcon>
|
|
115
|
-
<Icon name="status.success" tone="accent"
|
|
114
|
+
<ChatList.ItemIcon aria-label="Официальный аккаунт">
|
|
115
|
+
<Icon name="status.success" tone="accent" />
|
|
116
116
|
</ChatList.ItemIcon>
|
|
117
117
|
) : null}
|
|
118
118
|
{item.group ? (
|
|
119
|
-
<ChatList.ItemIcon>
|
|
120
|
-
<Icon name="object.users"
|
|
119
|
+
<ChatList.ItemIcon aria-label="Группа">
|
|
120
|
+
<Icon name="object.users" />
|
|
121
121
|
</ChatList.ItemIcon>
|
|
122
122
|
) : null}
|
|
123
123
|
<ChatList.ItemDescription
|
|
@@ -136,7 +136,7 @@ CommandMenu.Root Modal + top-aligned palette panel, query and lo
|
|
|
136
136
|
| `…rest` | `HTMLAttributes<HTMLSpanElement>` | — | `children` (the title), `className` and the other span attributes. |
|
|
137
137
|
|
|
138
138
|
### CommandMenu.Empty
|
|
139
|
-
`ref` → `HTMLDivElement`. A compact EmptyPage (`role="status"`) shown while nothing matches and nothing loads: `labels.empty
|
|
139
|
+
`ref` → `HTMLDivElement`. A compact EmptyPage (`role="status"`) shown while nothing matches and nothing loads: `labels.empty`, text only. An action for the query is a `persistent` `CommandMenu.Item` at the end of the list.
|
|
140
140
|
|
|
141
141
|
| Prop | Type | Default | Description |
|
|
142
142
|
|---|---|---|---|
|
|
@@ -236,7 +236,6 @@ One look: the floating layer of the ladder (it is a Modal), `--prime-modal-radiu
|
|
|
236
236
|
|---|---|---|
|
|
237
237
|
| `search` | `"Поиск"` | Default placeholder and accessible name of `CommandMenu.Input`. |
|
|
238
238
|
| `empty` | `"Ничего не найдено"` | `CommandMenu.Empty`: nothing matches the query. |
|
|
239
|
-
| `emptyHint` | `"Попробуйте изменить запрос"` | Second line of `CommandMenu.Empty`; `""` hides it. |
|
|
240
239
|
| `loading` | `"Загрузка…"` | Announced while `loading` or `loadingMore`. |
|
|
241
240
|
| `retry` | `"Повторить"` | The retry button under `loadError` (`onRetry`). |
|
|
242
241
|
|
|
@@ -46,7 +46,7 @@ export default function CommandMenuStatesExample() {
|
|
|
46
46
|
onSearchChange={setSearch}
|
|
47
47
|
loading={state === "loading"}
|
|
48
48
|
aria-label="Выбор склада"
|
|
49
|
-
labels={{ empty: "
|
|
49
|
+
labels={{ empty: "В этом городе склада пока нет" }}
|
|
50
50
|
>
|
|
51
51
|
<CommandMenu.Input placeholder="Город склада" />
|
|
52
52
|
<CommandMenu.List>
|
|
@@ -109,7 +109,7 @@ In an optimistic chat the send returns at once: add the message to the list with
|
|
|
109
109
|
| `defaultRecordMode` | `"voice" \| "video"` | `"voice"` | Initial record mode, uncontrolled. |
|
|
110
110
|
| `onRecordModeChange` | `(mode: "voice" \| "video") => void` | — | A double click / double tap on the record button (or ↑ / ↓ on it) switched voice and video. |
|
|
111
111
|
| `queue` | `Pick<UploadQueue, "items" \| "add" \| "retry" \| "remove" \| "clear" \| "move">` | — | Attachments, usually `useUploadQueue(…)`: the paperclip, a paste and a drop add to it, and a tray above the row shows them as one reorderable strip of tiles. Without `queue` paste and drop of files do nothing. |
|
|
112
|
-
| `formatting` | `boolean` | `true` | Selecting text shows a floating toolbar of
|
|
112
|
+
| `formatting` | `boolean` | `true` | Selecting text shows a floating toolbar of eight Markdown tools and enables their hotkeys; a tool wraps the selection, a second press unwraps it. `false` turns both off. |
|
|
113
113
|
| `size` | `"xs" \| "s" \| "m" \| "l" \| "xl"` | host tier, else `"m"` | Tier of the field, the buttons and the bar text. Without it the tier of its host, else `m`. |
|
|
114
114
|
| `disabled` | `boolean` | `false` | A closed conversation: the field, the paperclip and the button are disabled; nothing is pasted or dropped. A disabled `Fieldset` around it sets it too (`data-disabled` included). |
|
|
115
115
|
| `inputRef` | `Ref<HTMLTextAreaElement>` | — | The message `<textarea>`: focus it after «Ответить» in a message menu. |
|
|
@@ -197,7 +197,7 @@ Composer has no `variant`; what the row shows follows its state.
|
|
|
197
197
|
### Flags
|
|
198
198
|
| Value | Looks like | Use when | Default |
|
|
199
199
|
|---|---|---|---|
|
|
200
|
-
| `formatting` | a floating toolbar over a selection (bold, italic, strikethrough, code, spoiler, link, quote); Mod-hotkeys | always, unless the chat shows plain text only | `true` |
|
|
200
|
+
| `formatting` | a floating toolbar over a selection (bold, italic, underline, strikethrough, code, spoiler, link, quote); Mod-hotkeys | always, unless the chat shows plain text only | `true` |
|
|
201
201
|
| `disabled` | the field, paperclip and button disabled | a closed conversation, no permission to write | `false` |
|
|
202
202
|
| `queue` | the tray above the field: one strip of tiles — photos, videos and files; paste and drop take files | the chat accepts files | — |
|
|
203
203
|
| `onSendVoice` / `onSendVideoNote` | an empty field offers the record button | the chat takes voice / video messages | — |
|
|
@@ -250,7 +250,7 @@ Motion: the tray opens and closes as a Crossfade (fade and height glide, the row
|
|
|
250
250
|
| `Mod + Enter` | Always sends, on a touch screen too. |
|
|
251
251
|
| `↑` | In an empty field opens your last message for editing (`onEditLast`). |
|
|
252
252
|
| `Escape` | Clears a selection with the toolbar, then cancels the reply or edit; while recording throws the recording away and stops there, so an enclosing Modal or Drawer stays open. |
|
|
253
|
-
| `Mod + B` · `Mod + I` · `Mod + Shift + X` · `Mod + E` · `Mod + Shift + H` · `Mod + K` · `Mod + Shift + 9` | Bold, italic, strikethrough, code, spoiler, link and quote for the selection; the physical key counts, so any layout works. |
|
|
253
|
+
| `Mod + B` · `Mod + I` · `Mod + U` · `Mod + Shift + X` · `Mod + E` · `Mod + Shift + H` · `Mod + K` · `Mod + Shift + 9` | Bold, italic, underline, strikethrough, code, spoiler, link and quote for the selection; the physical key counts, so any layout works. |
|
|
254
254
|
| `Enter` · `Space` | On the record button: record hands-free at once. During a hands-free recording, wherever the focus is in the composer: Enter sends the recording (a paused one too), Space pauses and resumes it — the timer and the waveform stand, the dot turns into a pause glyph. |
|
|
255
255
|
| `↑` · `↓` | On the record button, with voice and video both offered: switches voice and video (`aria-keyshortcuts`, named in the button's description). |
|
|
256
256
|
| `←` · `→` | Move between the formatting buttons. |
|
|
@@ -310,6 +310,7 @@ Motion: the tray opens and closes as a Crossfade (fade and height glide, the row
|
|
|
310
310
|
| `formatting` | `"Форматирование"` | Accessible name of the formatting toolbar. |
|
|
311
311
|
| `bold` | `"Жирный"` | Bold tool. |
|
|
312
312
|
| `italic` | `"Курсив"` | Italic tool. |
|
|
313
|
+
| `underline` | `"Подчёркнутый"` | Underline tool (`__…__`). |
|
|
313
314
|
| `strikethrough` | `"Зачёркнутый"` | Strikethrough tool. |
|
|
314
315
|
| `code` | `"Моноширинный"` | Code tool. |
|
|
315
316
|
| `spoiler` | `"Скрытый текст"` | Spoiler tool (`\|\|…\|\|`). |
|
|
@@ -333,7 +334,7 @@ Motion: the tray opens and closes as a Crossfade (fade and height glide, the row
|
|
|
333
334
|
| [attach-menu.tsx](examples/attach-menu.tsx) | The paperclip as a menu of attachment kinds: photos and videos, documents, any file, and an invoice from the system that puts its link into the message and returns the focus to the field — `Composer.Item`, `Composer.ItemIcon`, `accept`, `onSelect`, `inputRef`. |
|
|
334
335
|
| [reply-edit.tsx](examples/reply-edit.tsx) | Answering a client's message and correcting your own: the bar above the field quotes the message, Escape or the cross cancels, ↑ in an empty field opens your last message, send becomes save and hands over the message id — `Composer.Context`, `mode`, `messageId`, `onDismiss`, `onEditLast`. |
|
|
335
336
|
| [typing.tsx](examples/typing.tsx) | The other side's status line follows what you do: typing, recording a voice or a video message, then nothing — `onActivityChange`. |
|
|
336
|
-
| [formatting.tsx](examples/formatting.tsx) | Selected text gets a toolbar of bold, italic, strikethrough, code, spoiler, link and quote, each with its shortcut, written as Markdown around the selection — `formatting`. |
|
|
337
|
+
| [formatting.tsx](examples/formatting.tsx) | Selected text gets a toolbar of bold, italic, underline, strikethrough, code, spoiler, link and quote, each with its shortcut, written as Markdown around the selection — `formatting`. |
|
|
337
338
|
| [voice.tsx](examples/voice.tsx) | Voice and round video messages: hold the button to record and release to send, slide left to cancel, slide up or tap to record hands-free; a double tap or double click switches voice and video — `onSendVoice`, `onSendVideoNote`, `recordMode`. |
|
|
338
339
|
| [bot-keyboard.tsx](examples/bot-keyboard.tsx) | A bot's reply keyboard under the field: a key sends its text as a message, the field asks for a key, the keyboard hides after use and the toggle brings it back — `Composer.Keyboard`, `rows`, `placeholder`, `oneTime`. |
|
|
339
340
|
| [states.tsx](examples/states.tsx) | A closed conversation is disabled, a slow send keeps the text with a busy button until the server answers — `disabled`, `onSend`. |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Selected text gets a toolbar of bold, italic, strikethrough, code, spoiler, link and quote, each with its shortcut, written as Markdown around the selection — `formatting`. */
|
|
1
|
+
/** Selected text gets a toolbar of bold, italic, underline, strikethrough, code, spoiler, link and quote, each with its shortcut, written as Markdown around the selection — `formatting`. */
|
|
2
2
|
import { Composer } from "prime-ui-kit";
|
|
3
3
|
|
|
4
4
|
import styles from "./examples.module.css";
|
|
@@ -50,7 +50,7 @@ DataTable card-fill block, radius 12, clips its conten
|
|
|
50
50
|
│ (one per group) with getRowGroup: a <th scope="rowgroup"> header row, then its rows
|
|
51
51
|
│ with onReorder: a Dnd.Sortable tbody; a Dnd.Handle ends each row (reorderHandle
|
|
52
52
|
│ "end"), leads it ("start"), or the row itself is the grip ("row")
|
|
53
|
-
└── footer
|
|
53
|
+
└── footer (when it has something) Pagination · range «Показано 1–5 из 23» (`showRange`) · «Догружаем строки…» while more rows load
|
|
54
54
|
```
|
|
55
55
|
```
|
|
56
56
|
DataTableColumnsMenu the column chooser outside the table, for the page's tools
|
|
@@ -65,7 +65,7 @@ Columns are data (`DataTableColumn<Row>[]`), not child parts; the table has no p
|
|
|
65
65
|
<!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
|
|
66
66
|
|
|
67
67
|
### DataTable
|
|
68
|
-
Generic `DataTable<Row>`; `ref` → `HTMLDivElement`. A card-fill block with an optional toolbar, a scrolling `<table>` and a footer (
|
|
68
|
+
Generic `DataTable<Row>`; `ref` → `HTMLDivElement`. A card-fill block with an optional toolbar, a scrolling `<table>` and a footer when it has something to show (Pagination, the range with `showRange`, the status while more rows load); passes its tier to the controls inside.
|
|
69
69
|
|
|
70
70
|
| Prop | Type | Default | Description |
|
|
71
71
|
|---|---|---|---|
|
|
@@ -79,7 +79,7 @@ Generic `DataTable<Row>`; `ref` → `HTMLDivElement`. A card-fill block with an
|
|
|
79
79
|
| `columnsMenu` | `boolean` | `false` | The column chooser in the head of the row-actions column (the last one, `hideHeader`); reorders with `columnOrder`. Without that column it renders nothing and warns: use `DataTableColumnsMenu` in the page's tools. |
|
|
80
80
|
| `rows` | `Row[]` | — (required) | Data; sorted in memory when a sort is set. With `source="server"` — the page (or the loaded batches) as the server returned it. |
|
|
81
81
|
| `source` | `"client" \| "server"` | `"client"` | Who sorts and pages the rows. `client`: the table sorts `rows` and slices pages. `server`: `rows` is one page, already sorted; `sort` / `page` changes are the request, `rowCount` drives Pagination. |
|
|
82
|
-
| `rowCount` | `number` | `rows.length` | Total rows on the server (`source="server"`): the page count and
|
|
82
|
+
| `rowCount` | `number` | `rows.length` | Total rows on the server (`source="server"`): the page count and the range line (`showRange`). |
|
|
83
83
|
| `getRowKey` | `(row: Row, index: number) => Key` | — | Stable row id (position by default; sub-rows too, `index` among siblings); selection, expansion and the enter animation of new rows are keyed by it. Pass it whenever sorting, selection or expansion is used. |
|
|
84
84
|
| `getRowLabel` | `(row: Row) => string` | — | Human row name for the checkbox and toggle names (`{label}` in `labels`). |
|
|
85
85
|
| `size` | `"xs" \| "s" \| "m" \| "l" \| "xl"` | host tier, else `"m"` | Density and control tier: rows from 36 · 36 · 44 · 52 · 52 px (`--prime-table-row-height-<size>`); passed to checkboxes, toggles, Pagination and nested controls. Without it the tier of the host. |
|
|
@@ -88,6 +88,7 @@ Generic `DataTable<Row>`; `ref` → `HTMLDivElement`. A card-fill block with an
|
|
|
88
88
|
| `page` | `number` | — | Current page (1-based), controlled. |
|
|
89
89
|
| `defaultPage` | `number` | `1` | Initial page, uncontrolled. |
|
|
90
90
|
| `onPageChange` | `(page: number) => void` | — | Page change from the pager; a sort change also returns to page 1. |
|
|
91
|
+
| `showRange` | `boolean` | `false` | A range line in the footer (`labels.range`) when the rows do not fit one page: paged with more than one page, or `paging="infinite"`. |
|
|
91
92
|
| `sort` | `DataTableSortState` | — | Current sort, controlled: `{ columnId, order: "asc" \| "desc" }` or `null`. |
|
|
92
93
|
| `defaultSort` | `DataTableSortState` | `null` | Initial sort, uncontrolled. |
|
|
93
94
|
| `onSortChange` | `(sort: DataTableSortState) => void` | — | A head click cycles asc → desc → none. |
|
|
@@ -112,7 +113,7 @@ Generic `DataTable<Row>`; `ref` → `HTMLDivElement`. A card-fill block with an
|
|
|
112
113
|
| `onExpandedChange` | `(expanded: Key[]) => void` | — | Expansion change. |
|
|
113
114
|
| `infiniteBatchSize` | `number` | `20` | Rows revealed per scroll step. |
|
|
114
115
|
| `hasMore` | `boolean` | `false` | More rows can be loaded from the server. |
|
|
115
|
-
| `loadingMore` | `boolean` | `false` | A server batch is loading: footer
|
|
116
|
+
| `loadingMore` | `boolean` | `false` | A server batch is loading: the footer shows `labels.loadingMore` with a Spinner, `aria-busy`. |
|
|
116
117
|
| `onLoadMore` | `() => void \| Promise<void>` | — | Called at the end when every loaded row is shown and `hasMore` is set. A returned promise shows the loading status until it settles; a rejected one stops asking until the rows change. |
|
|
117
118
|
| `scrollHeight` | `number \| string \| "fill"` | `360` with `paging="infinite"`, else none | Max height of the scroll viewport (number = px); `"fill"` — the table takes the height of its flex / grid parent (or a sized block), the rows scroll inside, infinite paging follows it. |
|
|
118
119
|
| `stickyHeader` | `boolean` | `false` | The head sticks while the body scrolls (with `scrollHeight`); on screens lower than 480px it scrolls with the rows. |
|
|
@@ -172,7 +173,7 @@ One column of `columns`: data, not a part.
|
|
|
172
173
|
| `description` | `string` | — | What the column means: an info glyph after the header opens it as a tooltip; the header cell is described by it (`aria-describedby`). |
|
|
173
174
|
| `numeric` | `boolean` | — | Tabular figures, no wrapping, end alignment unless `align` is set. |
|
|
174
175
|
| `truncate` | `boolean` | — | One line with an ellipsis; width from `maxWidth` (or `width`); string values get a `title`. |
|
|
175
|
-
| `grow` | `boolean` | — | Takes the free width
|
|
176
|
+
| `grow` | `boolean` | — | Takes the free width; the table then fills its container. Its cells fit the width they get: text wraps, `truncate` ellipsizes, a `Badge.Group` with `overflow="count"` shows «+N». It never gets narrower than its header or `minWidth`: below that the table scrolls instead of cells overlapping. |
|
|
176
177
|
| `hideable` | `boolean` | `true` | The column can be hidden through `hiddenColumns` and `columnsMenu`; `false` for the key column that names the row and for the row-actions column. |
|
|
177
178
|
| `reorderable` | `boolean` | `true` | The column moves when columns reorder (`columnOrder`); `false` holds its place: no column moves past it, the chooser shows a lock. A `hideHeader` column and a pinned first or last column (`stickyFirstColumn`, `stickyLastColumn`) hold theirs too. |
|
|
178
179
|
| `scale` | `boolean \| { min?: number; max?: number }` | — | Heatmap: each cell is shaded by its numeric value on the five-step chart scale. `true` spans the column's minimum to maximum over all rows; `{ min, max }` fixes either end. |
|
|
@@ -193,13 +194,13 @@ Without `size` the table takes the tier of its host (a sized form, panel or popo
|
|
|
193
194
|
|
|
194
195
|
Row height is a minimum: cells pad vertically by (row height − control one tier down) / 2, so one text line or a control one tier down keeps the tier height and taller content grows the row. Checkboxes use the table tier; expand toggles are square ghost Buttons of a fixed small tier (`xs` for xs–m, `s` for l–xl).
|
|
195
196
|
|
|
196
|
-
Column widths come from the content and then hold still: once rows are laid out the widths are frozen, so searching, filtering, paging and opening sub-rows do not move the columns. A column widens when later content does not fit it, never narrows; widths are measured again when the column set or the container width changes. `grow` columns keep taking the free width.
|
|
197
|
+
Column widths come from the content and then hold still: once rows are laid out the widths are frozen, so searching, filtering, paging and opening sub-rows do not move the columns. A column widens when later content does not fit it, never narrows; widths are measured again when the column set or the container width changes. `grow` columns keep taking the free width; their body never widens them (only the header and `minWidth` set their narrowest), so a `truncate` value, a counted `Badge.Group` or wrapping text fits whatever width the column gets.
|
|
197
198
|
|
|
198
199
|
### paging
|
|
199
200
|
| Value | Looks like | Use when | Default |
|
|
200
201
|
|---|---|---|---|
|
|
201
|
-
| `pages` | `pageSize` rows,
|
|
202
|
-
| `infinite` | rows revealed while scrolling in a `scrollHeight` window
|
|
202
|
+
| `pages` | `pageSize` rows, Pagination in the footer (compact on narrow widths) | tables people return to and reference | yes |
|
|
203
|
+
| `infinite` | rows revealed while scrolling in a `scrollHeight` window; no footer at rest, only a Spinner and `labels.loadingMore` while a request runs | logs and feeds | |
|
|
203
204
|
| `none` | every row, no footer | short lists, reports in a scroll window | |
|
|
204
205
|
|
|
205
206
|
### source
|
|
@@ -265,6 +266,7 @@ Heads keep `start` whatever the cell alignment, with the sort icon at the end ed
|
|
|
265
266
|
| `columnOrder` · `defaultColumnOrder` · `onColumnOrderChange` | any of the three makes columns reorder: a press that moves on a header carries the header alone (a finger holds still) while the body stays, Alt + ← / → on the header, grips in the column chooser plus «Исходный порядок» | tables people arrange for themselves; `defaultColumnOrder={[]}` starts in the order of `columns` | off |
|
|
266
267
|
| column `reorderable` | `false`: the column holds its place — its header carries nothing, no column moves past it, the chooser shows a lock. A `hideHeader` column and a pinned first / last column hold theirs too | the key column, a total that must stay last | `true` |
|
|
267
268
|
| `showHeader` | head row shown | off only for self-explanatory key/value tables | `true` |
|
|
269
|
+
| `showRange` | «Показано 1–5 из 23» on the left of the footer, with more than one page or with infinite scroll | the count matters to the task (a register people page through, a server list with a total) | `false` |
|
|
268
270
|
| `fullWidth` | spans the container; `false` = content width | `false` for short lookup tables in wide layouts | `true` |
|
|
269
271
|
| column `numeric` | tabular figures, end-aligned, no wrap | numbers, money | — |
|
|
270
272
|
| column `truncate` | one line with an ellipsis and a `title` | long names in narrow columns | — |
|
|
@@ -277,11 +279,11 @@ Heads keep `start` whatever the cell alignment, with the sort icon at the end ed
|
|
|
277
279
|
| State | Driven by | DOM |
|
|
278
280
|
|---|---|---|
|
|
279
281
|
| loading (no rows) | `loading` | skeleton rows (`data-skeleton`), `role="status"` with `labels.loading`, `aria-busy` on the table, `data-loading` on the root |
|
|
280
|
-
| loading more | `hasMore` with every loaded row shown calls `onLoadMore`; `loadingMore`, or its pending promise | `aria-busy`; footer `labels.loadingMore
|
|
282
|
+
| loading more | `hasMore` with every loaded row shown calls `onLoadMore`; `loadingMore`, or its pending promise | `aria-busy`; the footer rises in with a Spinner and `labels.loadingMore` (announced by a live region that stays mounted) and closes up in place when it settles; no new request until it settles (after a rejection, until the rows change) |
|
|
281
283
|
| refreshing | `loading` with rows on screen (a refetch, another server page or sort) | the rows, head, toolbar and footer stay; `aria-busy` on the table, `data-loading` on the root — no skeleton |
|
|
282
284
|
| new server page | `source="server"` and `rows` replaced (not appended) | the body remounts with the swap fade (`swapMotion`), the viewport scrolls to its top; no per-row enter |
|
|
283
285
|
| empty | no rows and not loading | compact EmptyPage with `empty` or `labels.empty` (`role="status"`), or the `empty` node; centred in the visible width, not across every column of a wide table |
|
|
284
|
-
| error | `loadError` | body replaced by a `role="alert"` message in `danger-text`, centred in the visible width; range and
|
|
286
|
+
| error | `loadError` | body replaced by a `role="alert"` message in `danger-text`, centred in the visible width; range and loading status hidden |
|
|
285
287
|
| sorted | `sort` / `defaultSort` | `aria-sort` and `data-sorted` on the head; the sort icon: unsorted `text-disabled`, hover `text-muted`, sorted `text-secondary` — never accent |
|
|
286
288
|
| selected | `selectable` with `selected` / `defaultSelected` | `data-selectable` on the root, a leading checkbox column; `aria-selected` rows with `accent-soft` fill; polite `labels.selectedCount` |
|
|
287
289
|
| expanded | `expanded` / `defaultExpanded` | `data-expanded` on the parent row (one step darker); toggle `aria-expanded`, chevron turns 90° |
|
|
@@ -311,7 +313,7 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
311
313
|
- Pass the tools as direct children of `toolbar` or a fragment: the row lays out each element as a cell. A wrapper component that returns several controls is one cell — no gaps between them, no growing search.
|
|
312
314
|
- The selection actions menu (`renderSelectionActions`): a soft neutral Button at the table tier — `labels.selectionActions`, a Badge with the count of selected rows, a chevron — named «Действия N» as read aloud. It enters last in the tool row (after the `toolbar`) once a row is selected and closes up in place when the selection empties; without a `toolbar` the tools row comes and goes with it. Its Dropdown opens below, at the end edge: `labels.clearSelection` first, a separator, then the items `renderSelectionActions(selected)` returns for the selected keys.
|
|
313
315
|
- Toolbar padding `--prime-space-3` × cell padding, items `gap: --prime-space-2`, chips 12 under the row; below 30rem of table width the footer stacks.
|
|
314
|
-
- The footer has a hairline on top:
|
|
316
|
+
- The footer has a hairline on top and exists only when it has something to show: Pagination on the right, the range on the left with `showRange` (only with more than one page or with infinite scroll), the loading status of infinite scroll while a request runs. An empty label draws nothing.
|
|
315
317
|
- Columns scroll sideways inside the table, never the page; pin the identifying column with `stickyFirstColumn` and the row actions column with `stickyLastColumn` (it pins the last visible column, so hiding it pins the one before). A 320 px column holds the table without breaking it.
|
|
316
318
|
- While columns are hidden past an edge, that edge shows a `--prime-space-4` shadow (`--prime-color-bg-edge-shadow`) over the content; it takes no space and follows RTL. With `stickyFirstColumn` the start shadow falls from the pinned column's end edge; with `stickyLastColumn` the end shadow falls from the pinned column's start edge. With `stickyHeader` the pinned head cells hold both corners.
|
|
317
319
|
- Layers, bottom to top: every cell that scrolls · the pinned columns (lead cells, the first and the last column, the end grip cell) · sticky group headers · the sticky head · the corners (head cells pinned to a column edge). Each cell keeps what it holds inside its own layer, so a control in a scrolled cell — a Switch, an icon Button, a focus ring — always passes under the pinned columns and the head, whatever its own stacking. A head label truncates inside its cell and never runs under the next one.
|
|
@@ -370,9 +372,8 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
370
372
|
|---|---|---|
|
|
371
373
|
| `loading` | `"Загрузка данных…"` | Screen-reader status while skeleton rows are shown. |
|
|
372
374
|
| `empty` | `"Нет данных для отображения."` | Default empty-state text. |
|
|
373
|
-
| `range` | `"Показано {from}–{to} из {total}"` | Footer range line
|
|
374
|
-
| `loadingMore` | `"Догружаем строки…"` | Footer status while `loadingMore
|
|
375
|
-
| `scrollForMore` | `"Прокрутите вниз для загрузки"` | Footer hint when more rows can appear by scrolling. |
|
|
375
|
+
| `range` | `"Показано {from}–{to} из {total}"` | Footer range line with `showRange`; `{from}`, `{to}`, `{total}` are replaced. |
|
|
376
|
+
| `loadingMore` | `"Догружаем строки…"` | Footer status while more rows load (`loadingMore` or a pending `onLoadMore`); announced politely. |
|
|
376
377
|
| `selectAll` | `"Выбрать все строки"` | Name of the head «select all» checkbox. |
|
|
377
378
|
| `selectRow` | `"Выбрать строку {label}"` | Name of a row checkbox; `{label}` is `getRowLabel(row)`, empty without it. |
|
|
378
379
|
| `selectedCount` | `"Выбрано: {count}"` | Polite announcement after the selection changes. |
|
|
@@ -414,7 +415,7 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
414
415
|
| [columns-visibility.tsx](examples/columns-visibility.tsx) | Orders without a row-actions column: the column chooser stands in the tools above the table with the count of shown columns; the choice outlives a reload in local storage, the order number always stays — `DataTableColumnsMenu`, `hiddenColumns`, `onHiddenColumnsChange`, `hideable`. |
|
|
415
416
|
| [column-order.tsx](examples/column-order.tsx) | Orders whose columns a manager puts in place by dragging a header — it moves alone and the gap opens among the headers where it lands — or with Alt + ← / →, and shows, hides and reorders in the chooser over the row menu; the order number stays first and the row menu last, and the choice outlives a reload — `columnOrder`, `onColumnOrderChange`, `reorderable`, `columnsMenu`, `hiddenColumns`. |
|
|
416
417
|
| [infinite-scroll.tsx](examples/infinite-scroll.tsx) | An audit log that reveals loaded rows in batches and then asks the server for more — `paging`, `infiniteBatchSize`, `hasMore`, `loadingMore`, `onLoadMore`. |
|
|
417
|
-
| [server.tsx](examples/server.tsx) | A CRM deal list the server sorts, pages and searches, with its tools in its own row — search, filter and column chooser, the active filters under them; a new search returns to page 1 — `source`, `rowCount`, `sort`, `page`, `loading`, `toolbar`, `toolbarChips`. |
|
|
418
|
+
| [server.tsx](examples/server.tsx) | A CRM deal list the server sorts, pages and searches, with its tools in its own row — search, filter and column chooser, the active filters under them; a new search returns to page 1, and the footer counts the deals shown out of the total — `source`, `rowCount`, `showRange`, `sort`, `page`, `loading`, `toolbar`, `toolbarChips`. |
|
|
418
419
|
| [fill-height.tsx](examples/fill-height.tsx) | An audit log that fills the height of its panel and loads older entries while scrolling; the status shows while the request is pending — `scrollHeight`, `paging`, `onLoadMore`. |
|
|
419
420
|
| [states.tsx](examples/states.tsx) | Loading skeleton, an empty period and a load error with a retry: the head stays, only the body changes — `loading`, `loadingRows`, `empty`, `loadError`. |
|
|
420
421
|
| [controlled.tsx](examples/controlled.tsx) | Sort, page and the column choice owned by the parent (a URL or a store): a header click goes asc → desc → none, a new sort returns to page 1 — `sort`, `onSortChange`, `page`, `onPageChange`, `hiddenColumns`, `onHiddenColumnsChange`. |
|
|
@@ -461,5 +462,5 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
461
462
|
- A new search that keeps the current page → set `page` to 1 together with the search.
|
|
462
463
|
|
|
463
464
|
## Related
|
|
464
|
-
- **Built from:** [Checkbox](../checkbox/COMPONENT.md), [Button](../button/COMPONENT.md), [Dropdown](../dropdown/COMPONENT.md) (the column chooser, the selection actions menu), [Pagination](../pagination/COMPONENT.md), [ScrollContainer](../scroll-container/COMPONENT.md), [EmptyPage](../empty-page/COMPONENT.md), [VisuallyHidden](../visually-hidden/COMPONENT.md) (loading status, selection announcement), [Skeleton](../skeleton/COMPONENT.md) (loading rows, in the table tier), [Dnd](../dnd/COMPONENT.md) (`onReorder`: `Dnd.Sortable as="tbody"`, `Dnd.Handle`; `columnOrder`: the head row as a `Dnd.Sortable as="tr"`, the chooser's `Dnd.Sortable` list, `Dnd.Handle`, the lock), [Badge](../badge/COMPONENT.md) (the count on `DataTableColumnsMenu` and on the selection actions menu), [Tooltip](../tooltip/COMPONENT.md) (handle hint, a column `description`, the column chooser), Icon (sort arrows, the info glyph of a column `description`, the expand chevron, the column chooser trigger); `useVirtualRows` (the window of a long body); `useFlipList` (the head cells glide to an order set from outside the head row); `enterMotion` (the moved column fades in); `ToolRow` (the `toolbar` row, the selection actions cell and the `toolbarChips` line) with [SmartFilter](../smart-filter/COMPONENT.md), [Input](../input/COMPONENT.md) and [Typography](../typography/COMPONENT.md) (what grows in the tool row), [SegmentedControl](../segmented-control/COMPONENT.md), [Datepicker](../datepicker/COMPONENT.md) and [Select](../select/COMPONENT.md) (what fills a short line; the first two get `fullWidth`); `partPresence` (the selection actions menu and the tools row it brings enter and close up)
|
|
465
|
+
- **Built from:** [Checkbox](../checkbox/COMPONENT.md), [Button](../button/COMPONENT.md), [Dropdown](../dropdown/COMPONENT.md) (the column chooser, the selection actions menu), [Pagination](../pagination/COMPONENT.md), [ScrollContainer](../scroll-container/COMPONENT.md), [EmptyPage](../empty-page/COMPONENT.md), [VisuallyHidden](../visually-hidden/COMPONENT.md) (loading status, selection announcement), [Skeleton](../skeleton/COMPONENT.md) (loading rows, in the table tier), [Spinner](../spinner/COMPONENT.md) (the footer status while more rows load), [Dnd](../dnd/COMPONENT.md) (`onReorder`: `Dnd.Sortable as="tbody"`, `Dnd.Handle`; `columnOrder`: the head row as a `Dnd.Sortable as="tr"`, the chooser's `Dnd.Sortable` list, `Dnd.Handle`, the lock), [Badge](../badge/COMPONENT.md) (the count on `DataTableColumnsMenu` and on the selection actions menu), [Tooltip](../tooltip/COMPONENT.md) (handle hint, a column `description`, the column chooser), Icon (sort arrows, the info glyph of a column `description`, the expand chevron, the column chooser trigger); `useVirtualRows` (the window of a long body); `useFlipList` (the head cells glide to an order set from outside the head row); `enterMotion` (the moved column fades in); `ToolRow` (the `toolbar` row, the selection actions cell and the `toolbarChips` line) with [SmartFilter](../smart-filter/COMPONENT.md), [Input](../input/COMPONENT.md) and [Typography](../typography/COMPONENT.md) (what grows in the tool row), [SegmentedControl](../segmented-control/COMPONENT.md), [Datepicker](../datepicker/COMPONENT.md) and [Select](../select/COMPONENT.md) (what fills a short line; the first two get `fullWidth`); `partPresence` (the selection actions menu and the tools row it brings enter and close up)
|
|
465
466
|
- **See also:** [Sparkline](../sparkline/COMPONENT.md) (`layout="compact"` in a cell), [SmartFilter](../smart-filter/COMPONENT.md), [Page](../../layout/page/COMPONENT.md) (`Page.Tools`, `Page.Chips`), [Gantt](../gantt/COMPONENT.md), [Kanban](../kanban/COMPONENT.md), [Badge](../badge/COMPONENT.md), [Avatar](../avatar/COMPONENT.md), [Card](../card/COMPONENT.md)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** A CRM deal list the server sorts, pages and searches, with its tools in its own row — search, filter and column chooser, the active filters under them; a new search returns to page 1 — `source`, `rowCount`, `sort`, `page`, `loading`, `toolbar`, `toolbarChips`. */
|
|
1
|
+
/** A CRM deal list the server sorts, pages and searches, with its tools in its own row — search, filter and column chooser, the active filters under them; a new search returns to page 1, and the footer counts the deals shown out of the total — `source`, `rowCount`, `showRange`, `sort`, `page`, `loading`, `toolbar`, `toolbarChips`. */
|
|
2
2
|
import {
|
|
3
3
|
Badge,
|
|
4
4
|
DataTable,
|
|
@@ -174,6 +174,7 @@ export default function DataTableServerExample() {
|
|
|
174
174
|
hiddenColumns={hidden}
|
|
175
175
|
rows={result.rows}
|
|
176
176
|
rowCount={result.total}
|
|
177
|
+
showRange
|
|
177
178
|
getRowKey={(row) => row.id}
|
|
178
179
|
pageSize={PAGE_SIZE}
|
|
179
180
|
page={page}
|
|
@@ -150,7 +150,7 @@ A date as a form submits it, in local time: `yyyy-MM-dd`, with `withTime` `yyyy-
|
|
|
150
150
|
### Flags
|
|
151
151
|
| Value | Looks like | Use when | Default |
|
|
152
152
|
|---|---|---|---|
|
|
153
|
-
| `presets` |
|
|
153
|
+
| `presets` | a column of preset buttons on the left (a scrolling row above when narrow); the active preset is pressed | analytics periods | `false` |
|
|
154
154
|
| `prompt` | muted step line under the grid | infrequent use of ranges | `false` |
|
|
155
155
|
| `footer` | dates (dd.MM.yyyy, dd.MM with `yearless`) and «Сбросить» / «Применить» | the user confirms, or with time | `false` |
|
|
156
156
|
| `withTime` | HH:mm inputs in the footer | timestamps (with `footer`) | `false` |
|
|
@@ -227,7 +227,7 @@ Committing: without `footer` a picked day (or the second day of a range) applies
|
|
|
227
227
|
| `timeEnd` | `"Время конца"` | Name of the end time input. |
|
|
228
228
|
| `time` | `"Время"` | Name of the time input of a single date. |
|
|
229
229
|
| `until` | `"до"` | Prefix of a range without a start: «до 6 окт». |
|
|
230
|
-
| `
|
|
230
|
+
| `presetsGroup` | `"Период"` | Accessible name of the presets group (no visible heading). |
|
|
231
231
|
| `optional` | `"необязательно"` | Muted marker after the label when `optional`. |
|
|
232
232
|
| `required` | `"обязательное поле"` | Read after the field name of a `required` field: its trigger is a button, which takes no `aria-required`. |
|
|
233
233
|
|
|
@@ -226,7 +226,7 @@ An item's own `onPointerDown` / `onKeyDown` run before the kit's: `preventDefaul
|
|
|
226
226
|
### ARIA
|
|
227
227
|
<!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
|
|
228
228
|
|
|
229
|
-
- A live region (`role="status"`, `aria-live="polite"`) announces grabbed, dropped, returned, cancelled, «position N of M», a move to another list by its name
|
|
229
|
+
- A live region (`role="status"`, `aria-live="polite"`) announces grabbed, dropped, returned, cancelled, «position N of M», a move to another list by its name, a refusal with its reason and a keyboard step a `locked` item stops, from `labels`.
|
|
230
230
|
- The keyboard target of a sortable item has `aria-keyshortcuts` (with the across keys in linked lists) and `aria-roledescription` and is in the tab order: the item itself (`tabIndex={0}`; a named `<div>` item is a `button`, an `li` or a `tr` keeps its role), or with `handle` its grip. A role description stands only on an element with a role. The clone is `aria-hidden`.
|
|
231
231
|
- While `onReorder`'s promise is pending, the item has `aria-busy` and holds still, and its `Dnd.Handle` is busy.
|
|
232
232
|
- In lists sharing a `kind`, Alt + the across arrows move the item to the same row of the neighbouring list in document order; focus follows the item.
|
|
@@ -249,6 +249,7 @@ An item's own `onPointerDown` / `onKeyDown` run before the kit's: `preventDefaul
|
|
|
249
249
|
| `cancelled` | `"{label}: перемещение отменено"` | Live region: cancelled (Escape, interruption). |
|
|
250
250
|
| `moved` | `"{label}: позиция {position} из {total}"` | Live region: moved with the keyboard. |
|
|
251
251
|
| `movedTo` | `"{label}: «{list}», позиция {position} из {total}"` | Live region: moved to a linked list with the keyboard; `{list}` is its accessible name. |
|
|
252
|
+
| `blocked` | `"{label}: «{locked}» закреплено, дальше не переместить"` | Live region: a keyboard step refused because a `locked` item holds that place; `{locked}` is its label. |
|
|
252
253
|
| `refused` | `"{label}: «{list}» не принимает элемент"` | Live region: a linked list refused a keyboard move (`canDrop` answered `false`). |
|
|
253
254
|
| `refusedReason` | `"{label}: {reason}"` | Live region: a target refused the item and said why (`canDrop` answered a string). |
|
|
254
255
|
|
|
@@ -33,7 +33,7 @@ Drawer.Root state and dismiss policy (no DOM)
|
|
|
33
33
|
│ ├─ Drawer.Icon tone tile around an Icon, or the record's Avatar / Thumbnail, no tile, 40
|
|
34
34
|
│ ├─ Drawer.Title line 1: <h2>; `loading` holds it with a skeleton
|
|
35
35
|
│ ├─ Drawer.Description line 2: <p>; the record's status Badges lead it (tier s), then the text
|
|
36
|
-
│ └─ Drawer.Actions buttons
|
|
36
|
+
│ └─ Drawer.Actions one tab-wide switcher, then buttons (icon buttons, a ⋯ Dropdown), tier s, on the title's line
|
|
37
37
|
├─ Drawer.Body the only scrolling zone (ScrollContainer)
|
|
38
38
|
│ └─ Drawer.Section <section>: <h3> label, description, Drawer.Actions at the heading's end
|
|
39
39
|
└─ Drawer.Footer always: Close or Cancel + the main action, primary last
|
|
@@ -82,7 +82,7 @@ No DOM, no ref. State and dismiss policy.
|
|
|
82
82
|
| `…rest` | `HTMLAttributes<HTMLElement>` | — | `className` and the other attributes of the `<header>`. |
|
|
83
83
|
|
|
84
84
|
### Drawer.Actions
|
|
85
|
-
`ref` → `HTMLDivElement`. Header
|
|
85
|
+
`ref` → `HTMLDivElement`. Header: a tab-wide switcher (`SegmentedControl`, `Datepicker`), then buttons (a status goes in `Drawer.Description`), tier `s`; under the title below 12rem; at the end of a `Drawer.Section` heading. In `Drawer.Footer`: the start group at the footer tier.
|
|
86
86
|
|
|
87
87
|
| Prop | Type | Default | Description |
|
|
88
88
|
|---|---|---|---|
|
|
@@ -208,13 +208,13 @@ Motion: the scrim fades, the panel slides from its side over `slow` and leaves o
|
|
|
208
208
|
## Layout & spacing
|
|
209
209
|
- The header has one anatomy, the same in every drawer, modal and sheet:
|
|
210
210
|
```
|
|
211
|
-
[mark] Title [icon buttons] [⋯] [×]
|
|
211
|
+
[mark] Title [tab switcher] [icon buttons] [⋯] [×]
|
|
212
212
|
[status badges] description text
|
|
213
213
|
```
|
|
214
214
|
- mark — `Drawer.Icon`: a glyph on the tone tile, or the record's picture (`Avatar.Root`, `Thumbnail.Root`) with no tile; both are 40 px, so the header never changes its height between a person and a setting.
|
|
215
215
|
- line 1 — `Drawer.Title`, the record's name.
|
|
216
216
|
- line 2 — `Drawer.Description`: the record's statuses as `Badge.Root`s first, then its text (an email, a handle, «Заказ № 1042 · 3 коробки»). The kit sizes the badges (chip tier `s`, the line's own 20 px), keeps them first and one gap apart, and wraps the text under them on a narrow panel. A status belongs to the record, so it lives on its line, never next to the buttons.
|
|
217
|
-
- end — `Drawer.Actions`:
|
|
217
|
+
- end — `Drawer.Actions`: first one switcher that applies to the whole open tab (a `SegmentedControl` — the text language RU / EN; a `Datepicker` — the period, the same one as on the page), then the buttons — ghost icon buttons, each named and with a Tooltip, and a ⋯ Dropdown for the rest — all at the close button's tier, centred on the title's line. A switcher that applies only to some tabs is rendered only on them: each control stands in a cell that rises in and closes up, so the buttons and the title glide instead of jumping.
|
|
218
218
|
- A drawer has three zones: `Drawer.Header` (title, the × close button), `Drawer.Body` (the content, the only zone that scrolls) and `Drawer.Footer` (the actions). The footer is not optional decoration — it is where the drawer is finished:
|
|
219
219
|
- read-only details: `Drawer.Close` around `<Button.Root variant="outline" tone="neutral">Закрыть</Button.Root>`, plus the record's main action (solid, last) when it has one;
|
|
220
220
|
- a form: «Отмена» (`Drawer.Close`, outline neutral) and the submit button (solid, last; `type="submit" form={formId}`), `Drawer.Meta` for «Есть несохранённые изменения»;
|
|
@@ -225,7 +225,7 @@ Motion: the scrim fades, the panel slides from its side over `slow` and leaves o
|
|
|
225
225
|
- Full viewport height; only the body scrolls, with its own side padding.
|
|
226
226
|
- Tabs in the header: a `Tabs.List` inside `Drawer.Header` (with `Tabs.Root` around the header and the body) spans the header's last row. The header becomes the tab strip, edge to edge and without the hairline; the active tab is the body rising into it, the panels sit in the body.
|
|
227
227
|
- Fields inside get the surface field fill.
|
|
228
|
-
- Titled groups of the body (a record's contacts, its deals, its history): `Drawer.Section label` — an `<h3>` under the drawer's `<h2>` title, an optional `description`, a `Drawer.Actions` at the end of its heading row; content 16 below, sections 32 apart — directly in the body, in a `Tabs.Panel` or in a form, with no spacing wrapper of your own. A group of fields is a `Fieldset`; a page section is `Page.Section`.
|
|
228
|
+
- Titled groups of the body (a record's contacts, its deals, its history): `Drawer.Section label` — an `<h3>` under the drawer's `<h2>` title, an optional `description`, a `Drawer.Actions` at the end of its heading row; content 16 below, sections 32 apart — directly in the body, in a `Tabs.Panel` or in a form, with no spacing wrapper of your own. A tab's blocks: a `Drawer.Section` with a label, or a self-titled block (a `ChartFrame`, a `Card` with its `Card.Title`) directly in the panel — a `Tabs.Panel` in the body stands its blocks 32 apart, the step between sections, by itself. A group of fields is a `Fieldset`; a page section is `Page.Section`.
|
|
229
229
|
- With a `Drawer.Meta` the buttons stay one row: when the footer runs short the status goes above them first.
|
|
230
230
|
- `Drawer.Actions` keep their place beside the title while it has 12rem; on a phone, where the buttons beside the × would fold the title into letters, they move under it by themselves.
|
|
231
231
|
- Opening a record: the header and its tabs come at once, the body as a `Skeleton` in the data's geometry. A record opened from a list row comes with the data the row already has (its title, often its status) at once, and only the rest loads (`Drawer.Description loading`); a record opened by a link knows only its id, so `Drawer.Title loading` holds the title line too. Read-only data swaps under a `Crossfade`; a form gets no `Crossfade` around it — its fields render once the record is there, so typing and focus are never cross-faded. Never a skeleton of your own in the header.
|
|
@@ -272,6 +272,7 @@ Motion: the scrim fades, the panel slides from its side over `slow` and leaves o
|
|
|
272
272
|
| [header-actions.tsx](examples/header-actions.tsx) | The one header of a record: the avatar, the name, its statuses leading the line under it, and only buttons at the end — `Drawer.Icon`, `Drawer.Description`, `Drawer.Actions`. |
|
|
273
273
|
| [footer-actions.tsx](examples/footer-actions.tsx) | A profile panel: Sign out stands apart at the start of the footer, the main pair keeps the end; on a phone the column puts it first and the primary last — `Drawer.Footer`, `Drawer.Actions`. |
|
|
274
274
|
| [client-card.tsx](examples/client-card.tsx) | A client card from the list: the contact's avatar heads the panel in place of an icon tile, then the details in titled sections, one with its own action — `Drawer.Icon`, `Avatar.Root`, `Drawer.Section`, `Drawer.Actions`. |
|
|
275
|
+
| [tab-switcher.tsx](examples/tab-switcher.tsx) | A model card with tabs: one switcher for the whole open tab stands in the header before ⋯ — the text language on the description tab, the period on the analytics tab, whose two self-titled charts the panel spaces by itself — `Drawer.Actions`, `Tabs.Root`, `Tabs.Panel`. |
|
|
275
276
|
| [loading.tsx](examples/loading.tsx) | A record card that opens before its data arrives: the title comes from the list row, the line under it and the body hold their place in skeletons, then the data fades in — `Drawer.Description`, `loading`. |
|
|
276
277
|
| [link-loading.tsx](examples/link-loading.tsx) | A record opened by a link knows only its id: the title and the line under it hold their place in skeletons, the body keeps the data's geometry, then the name and the data fade in — `Drawer.Title`, `loading`. |
|
|
277
278
|
| [guard.tsx](examples/guard.tsx) | A form that holds the drawer from inside: after an edit Escape, the scrim and Cancel ask before discarding, the footer says so, and saving locks every way out — `Drawer.Guard`, `Drawer.Meta`. |
|
|
@@ -286,12 +287,14 @@ Motion: the scrim fades, the panel slides from its side over `slow` and leaves o
|
|
|
286
287
|
- Save or Cancel buttons at the end of the body, or in the header → they go in `Drawer.Footer`, which stays in place while the body scrolls.
|
|
287
288
|
- A delete confirmation in a Drawer → use ConfirmDialog.
|
|
288
289
|
- A hand-made heading row (`Typography as="h3"` + buttons) over a group in the body → `Drawer.Section label` with `Drawer.Actions`.
|
|
289
|
-
- A wrapper with its own gap around `Drawer.Section`s inside a `Tabs.Panel
|
|
290
|
+
- A wrapper with its own gap around `Drawer.Section`s or self-titled blocks inside a `Tabs.Panel`, or around sections in a form → not needed: the panel in the body stands its blocks 32 apart, and sections keep 32 between them in any wrapper.
|
|
291
|
+
- A `Drawer.Section` only to space a block that already names itself (a chart, a titled card) → put it directly in the `Tabs.Panel`; the panel spaces it.
|
|
290
292
|
- A `Skeleton` placed by hand in `Drawer.Title` or `Drawer.Description` while the record loads → their `loading`; a placeholder title that shifts the header when the name arrives → `Drawer.Title loading`.
|
|
291
293
|
- A start-side button («Выйти») put inside `Drawer.Meta` → `Drawer.Actions` inside `Drawer.Footer`; `Meta` is status text only.
|
|
292
294
|
- Busy or unsaved state lifted out of the form into Root props → put `Drawer.Guard` next to the form, inside Content.
|
|
293
295
|
- Clearing the record together with `open` and rendering a skeleton for «no record» → not needed: the panel keeps its last content while it slides out.
|
|
294
296
|
- A status `Badge` in the header's `Drawer.Actions`, beside the buttons → put it in `Drawer.Description`, before the text: the kit sizes it to the line and keeps it first.
|
|
297
|
+
- 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 `Drawer.Actions`, before the buttons, rendered only on the tabs it applies to.
|
|
295
298
|
- A menu or a button as a custom child of the header text column → put it in `Drawer.Actions`.
|
|
296
299
|
- A Badge with its own `size` in `Drawer.Description` → leave `size` out: the line gives every header the same chip tier.
|
|
297
300
|
- An avatar wrapped in its own box, or given its own `size`, in `Drawer.Icon` → pass the `Avatar.Root` (or your component that renders it) as it is: the slot drops the tile and sets 40 px.
|
|
@@ -180,11 +180,7 @@ export default function DrawerScreenExample() {
|
|
|
180
180
|
</Radio.Group>
|
|
181
181
|
<Input.Root label="Название" error={errors.name}>
|
|
182
182
|
<Input.Wrapper>
|
|
183
|
-
<Input.Field
|
|
184
|
-
name="name"
|
|
185
|
-
placeholder="ООО «Ромашка»"
|
|
186
|
-
autoComplete="organization"
|
|
187
|
-
/>
|
|
183
|
+
<Input.Field name="name" placeholder="ООО «Ромашка»" />
|
|
188
184
|
</Input.Wrapper>
|
|
189
185
|
</Input.Root>
|
|
190
186
|
<div className={styles.pair}>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/** A model card with tabs: one switcher for the whole open tab stands in the header before ⋯ — the text language on the description tab, the period on the analytics tab, whose two self-titled charts the panel spaces by itself — `Drawer.Actions`, `Tabs.Root`, `Tabs.Panel`. */
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
Card,
|
|
5
|
+
Datepicker,
|
|
6
|
+
type DatepickerRange,
|
|
7
|
+
DEFAULT_DATEPICKER_PRESETS,
|
|
8
|
+
DescriptionList,
|
|
9
|
+
Drawer,
|
|
10
|
+
Dropdown,
|
|
11
|
+
Icon,
|
|
12
|
+
SegmentedControl,
|
|
13
|
+
Sparkline,
|
|
14
|
+
type SparklinePoint,
|
|
15
|
+
Tabs,
|
|
16
|
+
} from "prime-ui-kit";
|
|
17
|
+
import * as React from "react";
|
|
18
|
+
|
|
19
|
+
const MODEL = {
|
|
20
|
+
name: "Кресло «Орион»",
|
|
21
|
+
sku: "OR-240",
|
|
22
|
+
description: {
|
|
23
|
+
ru: "Офисное кресло с сетчатой спинкой и поддержкой поясницы. Подлокотники регулируются в четырёх направлениях, механизм качания фиксируется в пяти положениях.",
|
|
24
|
+
en: "An office chair with a mesh back and lumbar support. The armrests adjust in four directions, the tilt locks in five positions.",
|
|
25
|
+
},
|
|
26
|
+
title: { ru: "Кресло «Орион», графит", en: "Orion chair, graphite" },
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const SOLD: SparklinePoint[] = [
|
|
30
|
+
{ label: "май", value: 52 },
|
|
31
|
+
{ label: "июн", value: 61 },
|
|
32
|
+
{ label: "июл", value: 58 },
|
|
33
|
+
{ label: "авг", value: 74 },
|
|
34
|
+
{ label: "сен", value: 83 },
|
|
35
|
+
{ label: "окт", value: 90 },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const RETURNS: SparklinePoint[] = [
|
|
39
|
+
{ label: "май", value: 2 },
|
|
40
|
+
{ label: "июн", value: 1 },
|
|
41
|
+
{ label: "июл", value: 0 },
|
|
42
|
+
{ label: "авг", value: 1 },
|
|
43
|
+
{ label: "сен", value: 1 },
|
|
44
|
+
{ label: "окт", value: 1 },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const formatPieces = (value: number) => `${value.toLocaleString("ru-RU")} шт.`;
|
|
48
|
+
|
|
49
|
+
const ALL_TIME: DatepickerRange = { from: null, to: null };
|
|
50
|
+
|
|
51
|
+
type Lang = "ru" | "en";
|
|
52
|
+
|
|
53
|
+
export default function DrawerTabSwitcherExample() {
|
|
54
|
+
const [tab, setTab] = React.useState("description");
|
|
55
|
+
const [lang, setLang] = React.useState<Lang>("ru");
|
|
56
|
+
const [period, setPeriod] = React.useState<DatepickerRange>(ALL_TIME);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Drawer.Root>
|
|
60
|
+
<Drawer.Trigger>
|
|
61
|
+
<Button.Root variant="soft" tone="neutral">
|
|
62
|
+
Открыть модель
|
|
63
|
+
</Button.Root>
|
|
64
|
+
</Drawer.Trigger>
|
|
65
|
+
<Drawer.Content>
|
|
66
|
+
<Tabs.Root value={tab} onValueChange={setTab}>
|
|
67
|
+
<Drawer.Header>
|
|
68
|
+
<Drawer.Title>{MODEL.name}</Drawer.Title>
|
|
69
|
+
<Drawer.Description>Артикул {MODEL.sku}</Drawer.Description>
|
|
70
|
+
<Drawer.Actions>
|
|
71
|
+
{tab === "description" ? (
|
|
72
|
+
<SegmentedControl.Root
|
|
73
|
+
aria-label="Язык текстов"
|
|
74
|
+
value={lang}
|
|
75
|
+
onValueChange={(next) => setLang(next as Lang)}
|
|
76
|
+
>
|
|
77
|
+
<SegmentedControl.Item value="ru">RU</SegmentedControl.Item>
|
|
78
|
+
<SegmentedControl.Item value="en">EN</SegmentedControl.Item>
|
|
79
|
+
</SegmentedControl.Root>
|
|
80
|
+
) : (
|
|
81
|
+
<Datepicker.Root
|
|
82
|
+
mode="range"
|
|
83
|
+
aria-label="Период"
|
|
84
|
+
placeholder="Всё время"
|
|
85
|
+
value={period}
|
|
86
|
+
onValueChange={setPeriod}
|
|
87
|
+
resetValue={ALL_TIME}
|
|
88
|
+
presets={DEFAULT_DATEPICKER_PRESETS}
|
|
89
|
+
disableFuture
|
|
90
|
+
align="end"
|
|
91
|
+
/>
|
|
92
|
+
)}
|
|
93
|
+
<Dropdown.Root>
|
|
94
|
+
<Dropdown.Trigger>
|
|
95
|
+
<Button.Root variant="ghost" tone="neutral" aria-label="Действия с моделью">
|
|
96
|
+
<Button.Icon>
|
|
97
|
+
<Icon name="action.more" />
|
|
98
|
+
</Button.Icon>
|
|
99
|
+
</Button.Root>
|
|
100
|
+
</Dropdown.Trigger>
|
|
101
|
+
<Dropdown.Content>
|
|
102
|
+
<Dropdown.Item>Дублировать</Dropdown.Item>
|
|
103
|
+
<Dropdown.Item>Снять с продажи</Dropdown.Item>
|
|
104
|
+
</Dropdown.Content>
|
|
105
|
+
</Dropdown.Root>
|
|
106
|
+
</Drawer.Actions>
|
|
107
|
+
<Tabs.List aria-label={MODEL.name}>
|
|
108
|
+
<Tabs.Item value="description">Описание</Tabs.Item>
|
|
109
|
+
<Tabs.Item value="analytics">Аналитика</Tabs.Item>
|
|
110
|
+
</Tabs.List>
|
|
111
|
+
</Drawer.Header>
|
|
112
|
+
<Drawer.Body>
|
|
113
|
+
<Tabs.Panel value="description">
|
|
114
|
+
<DescriptionList.Root>
|
|
115
|
+
<DescriptionList.Item label="Название на сайте">
|
|
116
|
+
{MODEL.title[lang]}
|
|
117
|
+
</DescriptionList.Item>
|
|
118
|
+
<DescriptionList.Item label="Описание">
|
|
119
|
+
{MODEL.description[lang]}
|
|
120
|
+
</DescriptionList.Item>
|
|
121
|
+
</DescriptionList.Root>
|
|
122
|
+
</Tabs.Panel>
|
|
123
|
+
<Tabs.Panel value="analytics">
|
|
124
|
+
<Card.Root variant="panel">
|
|
125
|
+
<Card.Header>
|
|
126
|
+
<Card.Title>Продажи</Card.Title>
|
|
127
|
+
<Card.Description>По месяцам, в штуках</Card.Description>
|
|
128
|
+
</Card.Header>
|
|
129
|
+
<Card.Body>
|
|
130
|
+
<Sparkline
|
|
131
|
+
data={SOLD}
|
|
132
|
+
label="Продано за период"
|
|
133
|
+
summary="sum"
|
|
134
|
+
formatValue={formatPieces}
|
|
135
|
+
/>
|
|
136
|
+
</Card.Body>
|
|
137
|
+
</Card.Root>
|
|
138
|
+
<Card.Root variant="panel">
|
|
139
|
+
<Card.Header>
|
|
140
|
+
<Card.Title>Возвраты</Card.Title>
|
|
141
|
+
<Card.Description>По месяцам, в штуках</Card.Description>
|
|
142
|
+
</Card.Header>
|
|
143
|
+
<Card.Body>
|
|
144
|
+
<Sparkline
|
|
145
|
+
data={RETURNS}
|
|
146
|
+
label="Возвращено за период"
|
|
147
|
+
summary="sum"
|
|
148
|
+
formatValue={formatPieces}
|
|
149
|
+
/>
|
|
150
|
+
</Card.Body>
|
|
151
|
+
</Card.Root>
|
|
152
|
+
</Tabs.Panel>
|
|
153
|
+
</Drawer.Body>
|
|
154
|
+
</Tabs.Root>
|
|
155
|
+
</Drawer.Content>
|
|
156
|
+
</Drawer.Root>
|
|
157
|
+
);
|
|
158
|
+
}
|