prime-ui-kit 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/chunks/{chunk-PRP4B6OG.js → chunk-IVS3J7AS.js} +1 -1
- package/dist/chunks/{chunk-2ODXLPW3.js → chunk-QFHE5GKW.js} +1 -0
- package/dist/chunks/{chunk-7WWZYGRC.js → chunk-RZNGLELR.js} +4497 -3896
- package/dist/chunks/{chunk-A4WE3UNC.js → chunk-WYCTXD63.js} +18399 -17624
- package/dist/chunks/{chunk-SVFWPLNW.js → chunk-ZTZ2ML2B.js} +14 -3
- package/dist/color-picker.js +3 -3
- package/dist/components/badge/Badge.d.ts +17 -0
- package/dist/components/breadcrumb/Breadcrumb.d.ts +11 -2
- package/dist/components/button/Button.d.ts +1 -1
- package/dist/components/button-group/ButtonGroup.d.ts +1 -1
- package/dist/components/card/Card.d.ts +8 -4
- package/dist/components/chat/Chat.d.ts +3 -3
- package/dist/components/chat/MessageList.d.ts +5 -1
- package/dist/components/chat/groupMessages.d.ts +12 -4
- package/dist/components/chat-list/ChatList.d.ts +18 -9
- package/dist/components/chat-list/formatChatTime.d.ts +3 -3
- package/dist/components/checkbox/Checkbox.d.ts +6 -1
- package/dist/components/color-field/ColorField.d.ts +1 -1
- package/dist/components/color-picker/ColorPicker.d.ts +6 -1
- package/dist/components/color-picker/ColorPresets.d.ts +9 -1
- package/dist/components/crossfade/Crossfade.d.ts +1 -1
- package/dist/components/data-table/ColumnsMenu.d.ts +16 -20
- package/dist/components/data-table/DataTable.d.ts +16 -11
- package/dist/components/data-table/Head.d.ts +10 -1
- package/dist/components/data-table/Row.d.ts +7 -1
- package/dist/components/data-table/StateRows.d.ts +3 -1
- package/dist/components/data-table/types.d.ts +5 -0
- package/dist/components/digit-input/DigitInput.d.ts +1 -1
- package/dist/components/drawer/Drawer.d.ts +3 -2
- package/dist/components/field-list/FieldList.d.ts +24 -12
- package/dist/components/hint/Hint.d.ts +8 -2
- package/dist/components/index.d.ts +7 -4
- package/dist/components/input/Input.d.ts +27 -5
- package/dist/components/input/inputSuggestions.d.ts +39 -0
- package/dist/components/kanban/Kanban.d.ts +8 -9
- package/dist/components/list/List.d.ts +25 -6
- package/dist/components/message/Message.d.ts +15 -3
- package/dist/components/message/MessageSticker.d.ts +27 -0
- package/dist/components/message/formatMessageTime.d.ts +5 -2
- package/dist/components/modal/Modal.d.ts +3 -2
- package/dist/components/native-select/NativeSelect.d.ts +1 -1
- package/dist/components/radio/Radio.d.ts +11 -2
- package/dist/components/rich-text/RichText.d.ts +23 -0
- package/dist/components/scroll-container/ScrollContainer.d.ts +1 -1
- package/dist/components/scroll-container/scrollGlide.d.ts +26 -0
- package/dist/components/scroll-container/useScrollMotion.d.ts +25 -0
- package/dist/components/segmented-control/SegmentedControl.d.ts +1 -1
- package/dist/components/select/selectContext.d.ts +7 -0
- package/dist/components/slider/Slider.d.ts +1 -1
- package/dist/components/switch/Switch.d.ts +6 -1
- package/dist/components/tag-select/TagSelect.d.ts +1 -1
- package/dist/components/textarea/Textarea.d.ts +6 -1
- package/dist/components/timeline/Timeline.d.ts +1 -1
- package/dist/icon-set.js +2 -2
- package/dist/index.css +1 -1
- package/dist/index.js +5 -3
- package/dist/internal/ChoiceField.d.ts +3 -1
- package/dist/internal/FieldFrame.d.ts +8 -2
- package/dist/internal/fieldCell.d.ts +7 -1
- package/dist/internal/formatClock.d.ts +23 -2
- package/dist/internal/leaveGuard.d.ts +27 -0
- package/dist/internal/messageList.d.ts +2 -0
- package/dist/internal/overlay/DialogGuard.d.ts +3 -2
- package/dist/internal/overlay/DialogParts.d.ts +40 -6
- package/dist/internal/swatch.d.ts +5 -0
- package/dist/internal/tokenDefaults.d.ts +1 -0
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/page/Page.d.ts +48 -15
- package/dist/skill/anti-slop.md +28 -0
- package/dist/skill/api-contract.md +14 -7
- package/dist/skill/cheatsheet.md +28 -9
- package/dist/skill/checklist.md +9 -0
- package/dist/skill/choosing.md +25 -12
- package/dist/skill/components.md +1 -0
- package/dist/skill/composition.md +88 -16
- package/dist/skill/layouts.md +6 -4
- package/dist/skill/reference/components/badge/COMPONENT.md +14 -1
- package/dist/skill/reference/components/badge/examples/examples.module.css +7 -0
- package/dist/skill/reference/components/badge/examples/tag-row.tsx +34 -0
- package/dist/skill/reference/components/breadcrumb/COMPONENT.md +9 -4
- package/dist/skill/reference/components/breadcrumb/examples/router.tsx +17 -0
- package/dist/skill/reference/components/button/COMPONENT.md +1 -1
- package/dist/skill/reference/components/button-group/COMPONENT.md +1 -0
- package/dist/skill/reference/components/card/COMPONENT.md +7 -1
- package/dist/skill/reference/components/card/examples/states.tsx +59 -0
- package/dist/skill/reference/components/chat/COMPONENT.md +4 -1
- package/dist/skill/reference/components/chat/examples/time-zone.tsx +83 -0
- package/dist/skill/reference/components/chat-list/COMPONENT.md +12 -6
- package/dist/skill/reference/components/chat-list/examples/links.tsx +1 -1
- package/dist/skill/reference/components/chat-list/examples/router.tsx +63 -0
- package/dist/skill/reference/components/checkbox/COMPONENT.md +1 -0
- package/dist/skill/reference/components/color-field/COMPONENT.md +2 -1
- package/dist/skill/reference/components/color-picker/COMPONENT.md +14 -5
- package/dist/skill/reference/components/color-picker/examples/presets-custom.tsx +31 -0
- package/dist/skill/reference/components/color-swatches/COMPONENT.md +1 -0
- package/dist/skill/reference/components/confirm-dialog/COMPONENT.md +1 -1
- package/dist/skill/reference/components/crossfade/COMPONENT.md +1 -1
- package/dist/skill/reference/components/data-table/COMPONENT.md +46 -41
- package/dist/skill/reference/components/data-table/examples/actions-column.tsx +154 -0
- package/dist/skill/reference/components/data-table/examples/columns-visibility.tsx +3 -12
- package/dist/skill/reference/components/data-table/examples/controlled.tsx +1 -1
- package/dist/skill/reference/components/data-table/examples/narrow.tsx +4 -11
- package/dist/skill/reference/components/data-table/examples/numeric.tsx +9 -2
- package/dist/skill/reference/components/data-table/examples/row-link.tsx +18 -22
- package/dist/skill/reference/components/data-table/examples/screen.tsx +12 -1
- package/dist/skill/reference/components/data-table/examples/toolbar.tsx +26 -32
- package/dist/skill/reference/components/datepicker/COMPONENT.md +3 -1
- package/dist/skill/reference/components/datepicker/examples/deadline.tsx +33 -0
- package/dist/skill/reference/components/digit-input/COMPONENT.md +1 -0
- package/dist/skill/reference/components/dnd/COMPONENT.md +4 -3
- package/dist/skill/reference/components/dnd/examples/examples.module.css +4 -3
- package/dist/skill/reference/components/dnd/examples/handle.tsx +5 -5
- package/dist/skill/reference/components/dnd/examples/locked.tsx +1 -1
- package/dist/skill/reference/components/dnd/examples/table.tsx +6 -6
- package/dist/skill/reference/components/drawer/COMPONENT.md +38 -9
- package/dist/skill/reference/components/drawer/examples/client-card.tsx +32 -8
- package/dist/skill/reference/components/drawer/examples/header-actions.tsx +1 -1
- package/dist/skill/reference/components/drawer/examples/loading.tsx +58 -0
- package/dist/skill/reference/components/field-list/COMPONENT.md +40 -24
- package/dist/skill/reference/components/field-list/examples/base-row.tsx +2 -1
- package/dist/skill/reference/components/field-list/examples/locked.tsx +11 -11
- package/dist/skill/reference/components/field-list/examples/reorder.tsx +1 -1
- package/dist/skill/reference/components/field-list/examples/saving.tsx +83 -0
- package/dist/skill/reference/components/field-list/examples/screen.module.css +1 -1
- package/dist/skill/reference/components/field-list/examples/screen.tsx +168 -162
- package/dist/skill/reference/components/field-list/examples/totals.tsx +83 -0
- package/dist/skill/reference/components/file-upload/COMPONENT.md +1 -0
- package/dist/skill/reference/components/hint/COMPONENT.md +16 -2
- package/dist/skill/reference/components/hint/examples/variants.tsx +48 -0
- package/dist/skill/reference/components/input/COMPONENT.md +39 -9
- package/dist/skill/reference/components/input/examples/affixes.tsx +4 -2
- package/dist/skill/reference/components/input/examples/dismiss.tsx +29 -0
- package/dist/skill/reference/components/input/examples/suggestions.tsx +26 -0
- package/dist/skill/reference/components/input/examples/time.tsx +24 -0
- package/dist/skill/reference/components/input/examples/with-select.tsx +44 -0
- package/dist/skill/reference/components/kanban/COMPONENT.md +12 -8
- package/dist/skill/reference/components/kanban/examples/columns.tsx +4 -2
- package/dist/skill/reference/components/kanban/examples/states.tsx +7 -5
- package/dist/skill/reference/components/list/COMPONENT.md +39 -13
- package/dist/skill/reference/components/list/examples/reorder.tsx +42 -7
- package/dist/skill/reference/components/list/examples/row-controls.tsx +80 -0
- package/dist/skill/reference/components/list/examples/with-icon.tsx +23 -2
- package/dist/skill/reference/components/login-form/COMPONENT.md +8 -7
- package/dist/skill/reference/components/login-form/examples/states.tsx +12 -14
- package/dist/skill/reference/components/message/COMPONENT.md +30 -9
- package/dist/skill/reference/components/message/examples/preview.tsx +28 -0
- package/dist/skill/reference/components/message/examples/sticker.tsx +35 -0
- package/dist/skill/reference/components/modal/COMPONENT.md +35 -10
- package/dist/skill/reference/components/modal/examples/long-content.tsx +37 -18
- package/dist/skill/reference/components/native-select/COMPONENT.md +1 -0
- package/dist/skill/reference/components/notification/COMPONENT.md +1 -1
- package/dist/skill/reference/components/radio/COMPONENT.md +2 -0
- package/dist/skill/reference/components/rich-text/COMPONENT.md +93 -0
- package/dist/skill/reference/components/rich-text/examples/overview.tsx +19 -0
- package/dist/skill/reference/components/scroll-container/COMPONENT.md +8 -5
- package/dist/skill/reference/components/segmented-control/COMPONENT.md +4 -2
- package/dist/skill/reference/components/segmented-control/examples/in-form.tsx +1 -1
- package/dist/skill/reference/components/select/COMPONENT.md +7 -3
- package/dist/skill/reference/components/sheet/COMPONENT.md +18 -7
- package/dist/skill/reference/components/slider/COMPONENT.md +1 -0
- package/dist/skill/reference/components/slider/examples/controlled.tsx +1 -1
- package/dist/skill/reference/components/switch/COMPONENT.md +1 -0
- package/dist/skill/reference/components/tag-select/COMPONENT.md +1 -0
- package/dist/skill/reference/components/textarea/COMPONENT.md +1 -0
- package/dist/skill/reference/components/timeline/COMPONENT.md +3 -3
- package/dist/skill/reference/components/timeline/examples/structure.tsx +2 -1
- package/dist/skill/reference/layout/app-shell/COMPONENT.md +2 -2
- package/dist/skill/reference/layout/bottom-nav/COMPONENT.md +1 -0
- package/dist/skill/reference/layout/page/COMPONENT.md +42 -20
- package/dist/skill/reference/layout/page/examples/examples.module.css +7 -0
- package/dist/skill/reference/layout/page/examples/guard.tsx +87 -0
- package/dist/skill/reference/layout/page/examples/loading.tsx +57 -0
- package/dist/skill/reference/layout/page/examples/screen.module.css +1 -1
- package/dist/skill/reference/layout/page/examples/screen.tsx +93 -104
- package/dist/skill/reference/layout/page/examples/sections.tsx +2 -15
- package/dist/skill/reference/layout/sidebar/COMPONENT.md +1 -0
- package/dist/skill/responsive.md +19 -12
- package/dist/skill/ux.md +5 -3
- package/dist/tokens/semantic.d.ts +3 -1
- package/dist/tokens/themes/dark.d.ts +1 -1
- package/package.json +2 -2
- package/src/components/badge/COMPONENT.md +14 -1
- package/src/components/badge/examples/examples.module.css +7 -0
- package/src/components/badge/examples/tag-row.tsx +34 -0
- package/src/components/breadcrumb/COMPONENT.md +9 -4
- package/src/components/breadcrumb/examples/router.tsx +17 -0
- package/src/components/button/COMPONENT.md +1 -1
- package/src/components/button-group/COMPONENT.md +1 -0
- package/src/components/card/COMPONENT.md +7 -1
- package/src/components/card/examples/states.tsx +59 -0
- package/src/components/chat/COMPONENT.md +4 -1
- package/src/components/chat/examples/time-zone.tsx +83 -0
- package/src/components/chat-list/COMPONENT.md +12 -6
- package/src/components/chat-list/examples/links.tsx +1 -1
- package/src/components/chat-list/examples/router.tsx +63 -0
- package/src/components/checkbox/COMPONENT.md +1 -0
- package/src/components/color-field/COMPONENT.md +2 -1
- package/src/components/color-picker/COMPONENT.md +14 -5
- package/src/components/color-picker/examples/presets-custom.tsx +31 -0
- package/src/components/color-swatches/COMPONENT.md +1 -0
- package/src/components/confirm-dialog/COMPONENT.md +1 -1
- package/src/components/crossfade/COMPONENT.md +1 -1
- package/src/components/data-table/COMPONENT.md +46 -41
- package/src/components/data-table/examples/actions-column.tsx +154 -0
- package/src/components/data-table/examples/columns-visibility.tsx +3 -12
- package/src/components/data-table/examples/controlled.tsx +1 -1
- package/src/components/data-table/examples/narrow.tsx +4 -11
- package/src/components/data-table/examples/numeric.tsx +9 -2
- package/src/components/data-table/examples/row-link.tsx +18 -22
- package/src/components/data-table/examples/screen.tsx +12 -1
- package/src/components/data-table/examples/toolbar.tsx +26 -32
- package/src/components/datepicker/COMPONENT.md +3 -1
- package/src/components/datepicker/examples/deadline.tsx +33 -0
- package/src/components/digit-input/COMPONENT.md +1 -0
- package/src/components/dnd/COMPONENT.md +4 -3
- package/src/components/dnd/examples/examples.module.css +4 -3
- package/src/components/dnd/examples/handle.tsx +5 -5
- package/src/components/dnd/examples/locked.tsx +1 -1
- package/src/components/dnd/examples/table.tsx +6 -6
- package/src/components/drawer/COMPONENT.md +38 -9
- package/src/components/drawer/examples/client-card.tsx +32 -8
- package/src/components/drawer/examples/header-actions.tsx +1 -1
- package/src/components/drawer/examples/loading.tsx +58 -0
- package/src/components/field-list/COMPONENT.md +40 -24
- package/src/components/field-list/examples/base-row.tsx +2 -1
- package/src/components/field-list/examples/locked.tsx +11 -11
- package/src/components/field-list/examples/reorder.tsx +1 -1
- package/src/components/field-list/examples/saving.tsx +83 -0
- package/src/components/field-list/examples/screen.module.css +1 -1
- package/src/components/field-list/examples/screen.tsx +168 -162
- package/src/components/field-list/examples/totals.tsx +83 -0
- package/src/components/file-upload/COMPONENT.md +1 -0
- package/src/components/hint/COMPONENT.md +16 -2
- package/src/components/hint/examples/variants.tsx +48 -0
- package/src/components/input/COMPONENT.md +39 -9
- package/src/components/input/examples/affixes.tsx +4 -2
- package/src/components/input/examples/dismiss.tsx +29 -0
- package/src/components/input/examples/suggestions.tsx +26 -0
- package/src/components/input/examples/time.tsx +24 -0
- package/src/components/input/examples/with-select.tsx +44 -0
- package/src/components/kanban/COMPONENT.md +12 -8
- package/src/components/kanban/examples/columns.tsx +4 -2
- package/src/components/kanban/examples/states.tsx +7 -5
- package/src/components/list/COMPONENT.md +39 -13
- package/src/components/list/examples/reorder.tsx +42 -7
- package/src/components/list/examples/row-controls.tsx +80 -0
- package/src/components/list/examples/with-icon.tsx +23 -2
- package/src/components/login-form/COMPONENT.md +8 -7
- package/src/components/login-form/examples/states.tsx +12 -14
- package/src/components/message/COMPONENT.md +30 -9
- package/src/components/message/examples/preview.tsx +28 -0
- package/src/components/message/examples/sticker.tsx +35 -0
- package/src/components/modal/COMPONENT.md +35 -10
- package/src/components/modal/examples/long-content.tsx +37 -18
- package/src/components/native-select/COMPONENT.md +1 -0
- package/src/components/notification/COMPONENT.md +1 -1
- package/src/components/radio/COMPONENT.md +2 -0
- package/src/components/rich-text/COMPONENT.md +93 -0
- package/src/components/rich-text/examples/overview.tsx +19 -0
- package/src/components/scroll-container/COMPONENT.md +8 -5
- package/src/components/segmented-control/COMPONENT.md +4 -2
- package/src/components/segmented-control/examples/in-form.tsx +1 -1
- package/src/components/select/COMPONENT.md +7 -3
- package/src/components/sheet/COMPONENT.md +18 -7
- package/src/components/slider/COMPONENT.md +1 -0
- package/src/components/slider/examples/controlled.tsx +1 -1
- package/src/components/switch/COMPONENT.md +1 -0
- package/src/components/tag-select/COMPONENT.md +1 -0
- package/src/components/textarea/COMPONENT.md +1 -0
- package/src/components/timeline/COMPONENT.md +3 -3
- package/src/components/timeline/examples/structure.tsx +2 -1
- package/src/layout/app-shell/COMPONENT.md +2 -2
- package/src/layout/bottom-nav/COMPONENT.md +1 -0
- package/src/layout/page/COMPONENT.md +42 -20
- package/src/layout/page/examples/examples.module.css +7 -0
- package/src/layout/page/examples/guard.tsx +87 -0
- package/src/layout/page/examples/loading.tsx +57 -0
- package/src/layout/page/examples/screen.module.css +1 -1
- package/src/layout/page/examples/screen.tsx +93 -104
- package/src/layout/page/examples/sections.tsx +2 -15
- package/src/layout/sidebar/COMPONENT.md +1 -0
- package/src/styles/theme-dark.css +1 -1
- package/src/styles/theme-light.css +2 -1
- package/dist/components/data-table/context.d.ts +0 -14
- package/dist/components/scroll-container/useWheelToInline.d.ts +0 -10
|
@@ -177,7 +177,15 @@ const initials = (name: string) =>
|
|
|
177
177
|
.toUpperCase();
|
|
178
178
|
|
|
179
179
|
const COLUMNS: DataTableColumn<Invoice>[] = [
|
|
180
|
-
{
|
|
180
|
+
{
|
|
181
|
+
id: "id",
|
|
182
|
+
header: "Счёт",
|
|
183
|
+
accessor: "id",
|
|
184
|
+
sortable: true,
|
|
185
|
+
numeric: true,
|
|
186
|
+
align: "start",
|
|
187
|
+
hideable: false,
|
|
188
|
+
},
|
|
181
189
|
{
|
|
182
190
|
id: "client",
|
|
183
191
|
header: "Клиент",
|
|
@@ -230,6 +238,7 @@ const COLUMNS: DataTableColumn<Invoice>[] = [
|
|
|
230
238
|
id: "actions",
|
|
231
239
|
header: "Действия",
|
|
232
240
|
hideHeader: true,
|
|
241
|
+
hideable: false,
|
|
233
242
|
width: "3.5rem",
|
|
234
243
|
align: "end",
|
|
235
244
|
cell: (row) => (
|
|
@@ -320,6 +329,8 @@ export default function DataTableScreenExample() {
|
|
|
320
329
|
getRowLabel={(row) => `Счёт ${row.id}`}
|
|
321
330
|
pageSize={6}
|
|
322
331
|
stickyFirstColumn
|
|
332
|
+
stickyLastColumn
|
|
333
|
+
columnsMenu
|
|
323
334
|
defaultSort={{ columnId: "due", order: "desc" }}
|
|
324
335
|
empty={
|
|
325
336
|
<EmptyPage.Root size="s" role="status" aria-labelledby="invoices-empty">
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
/** A campaign
|
|
1
|
+
/** A campaign table with its own search and status filter above it — two table-scoped controls; the empty text when nothing matches — `toolbar`, `empty`. */
|
|
2
2
|
import {
|
|
3
3
|
Badge,
|
|
4
|
-
Button,
|
|
5
4
|
DataTable,
|
|
6
5
|
type DataTableColumn,
|
|
7
6
|
Icon,
|
|
@@ -87,36 +86,31 @@ export default function DataTableToolbarExample() {
|
|
|
87
86
|
pageSize={5}
|
|
88
87
|
empty="Ничего не найдено — измените запрос или фильтр"
|
|
89
88
|
toolbar={
|
|
90
|
-
<div className={styles.
|
|
91
|
-
<
|
|
92
|
-
<Input.
|
|
93
|
-
<Input.
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
</SegmentedControl.Root>
|
|
116
|
-
</div>
|
|
117
|
-
<Button.Root variant="outline" tone="neutral" size="s">
|
|
118
|
-
Экспорт
|
|
119
|
-
</Button.Root>
|
|
89
|
+
<div className={styles.filters}>
|
|
90
|
+
<Input.Root size="s" className={styles.search}>
|
|
91
|
+
<Input.Wrapper>
|
|
92
|
+
<Input.Icon side="start">
|
|
93
|
+
<Icon name="action.search" />
|
|
94
|
+
</Input.Icon>
|
|
95
|
+
<Input.Field
|
|
96
|
+
type="search"
|
|
97
|
+
placeholder="Поиск кампаний"
|
|
98
|
+
aria-label="Поиск кампаний"
|
|
99
|
+
value={query}
|
|
100
|
+
onValueChange={setQuery}
|
|
101
|
+
/>
|
|
102
|
+
</Input.Wrapper>
|
|
103
|
+
</Input.Root>
|
|
104
|
+
<SegmentedControl.Root
|
|
105
|
+
size="s"
|
|
106
|
+
aria-label="Статус"
|
|
107
|
+
value={status}
|
|
108
|
+
onValueChange={setStatus}
|
|
109
|
+
>
|
|
110
|
+
<SegmentedControl.Item value="all">Все</SegmentedControl.Item>
|
|
111
|
+
<SegmentedControl.Item value="active">Активные</SegmentedControl.Item>
|
|
112
|
+
<SegmentedControl.Item value="paused">На паузе</SegmentedControl.Item>
|
|
113
|
+
</SegmentedControl.Root>
|
|
120
114
|
</div>
|
|
121
115
|
}
|
|
122
116
|
/>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- An annual day and month without a year (`yearless`).
|
|
13
13
|
|
|
14
14
|
## When not to use
|
|
15
|
-
- Time of day only → use [Input](../input/COMPONENT.md) with a time
|
|
15
|
+
- Time of day only → use [Input](../input/COMPONENT.md) with `Input.Field type="time"`: the time is typed in hour and minute segments, there is no day to pick (a date with a time is `withTime`).
|
|
16
16
|
- A choice between a few fixed periods («7 дней», «30 дней») → use [SegmentedControl](../segmented-control/COMPONENT.md) or [Select](../select/COMPONENT.md).
|
|
17
17
|
- A free-text date the user types → use [Input](../input/COMPONENT.md).
|
|
18
18
|
|
|
@@ -63,6 +63,7 @@ panel presets column (range) · 1–2 month grids · promp
|
|
|
63
63
|
| `size` | `"xs" \| "s" \| "m" \| "l" \| "xl"` | host tier, else `"m"` | Field tier (height 28 · 32 · 36 · 40 · 48) and panel tier (day cell 24 · 28 · 32 · 36 · 40). Without it the tier of its host, else `m`. |
|
|
64
64
|
| `label` | `ReactNode` | — | Field label; part of the button's accessible name together with the value. |
|
|
65
65
|
| `hint` | `ReactNode` | — | Support text under the field (`aria-describedby`). |
|
|
66
|
+
| `hintTone` | `"neutral" \| "success" \| "warning" \| "danger"` | `"neutral"` | Status color of `hint` («Осталось 12 дней» in `warning`). The words carry the status, the color repeats it; an `error` replaces the hint and its tone. A validation error is `error`, never a `danger` hint. |
|
|
66
67
|
| `error` | `ReactNode` | — | Error message in place of the hint; implies `invalid`. |
|
|
67
68
|
| `required` | `boolean` | `false` | Red `*` after the label; the field button is described by `labels.required` (a button takes no `aria-required`). |
|
|
68
69
|
| `optional` | `boolean` | — | Muted `labels.optional` after the label. |
|
|
@@ -241,6 +242,7 @@ Committing: without `footer` a picked day (or the second day of a range) applies
|
|
|
241
242
|
| [inline-panel.tsx](examples/inline-panel.tsx) | A booking calendar embedded in a card: the card is its container, two months when the parent has room, the range applies at once — `Datepicker.Panel`, `months`, `prompt`. |
|
|
242
243
|
| [yearless.tsx](examples/yearless.tsx) | An annual price change date: day and month without a year, a value prefix and taken days disabled — `yearless`, `valuePrefix`, `isDayDisabled`. |
|
|
243
244
|
| [locale.tsx](examples/locale.tsx) | A delivery window on a partner portal in English: month and weekday names from the date-fns locale, the week from Sunday and the system texts translated — `locale`, `weekStartsOn`, `labels`. |
|
|
245
|
+
| [deadline.tsx](examples/deadline.tsx) | A document's expiry date whose hint counts the days left and takes the status tone — `hint`, `hintTone`. |
|
|
244
246
|
| [dismiss.tsx](examples/dismiss.tsx) | A shift period with a footer: the calendar keeps the draft on a press beside it and closes by Escape, Reset or Apply — `closeOnOutsideClick`, `closeOnEscape`. |
|
|
245
247
|
| [controlled.tsx](examples/controlled.tsx) | A report filter owns the period: quick buttons set it from outside, the field shows it — `value`, `onValueChange`. |
|
|
246
248
|
| [controlled-open.tsx](examples/controlled-open.tsx) | The parent owns the panel: a reminder button opens the calendar from code, a picked day closes it — `open`, `onOpenChange`. |
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** A document's expiry date whose hint counts the days left and takes the status tone — `hint`, `hintTone`. */
|
|
2
|
+
import { differenceInCalendarDays } from "date-fns";
|
|
3
|
+
import { Datepicker } from "prime-ui-kit";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
const TODAY = new Date(2026, 9, 15);
|
|
7
|
+
const WARN_DAYS = 30;
|
|
8
|
+
|
|
9
|
+
/** The hint and its tone: the words say the status, the tone repeats it. */
|
|
10
|
+
function expiryHint(expires: Date | null) {
|
|
11
|
+
if (!expires) return { hint: "Дата из договора страхования", tone: "neutral" as const };
|
|
12
|
+
const days = differenceInCalendarDays(expires, TODAY);
|
|
13
|
+
if (days < 0) return { hint: `Срок истёк ${-days} дн. назад`, tone: "danger" as const };
|
|
14
|
+
if (days <= WARN_DAYS) return { hint: `Осталось ${days} дн.`, tone: "warning" as const };
|
|
15
|
+
return { hint: `Действует ещё ${days} дн.`, tone: "success" as const };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function DatepickerDeadlineExample() {
|
|
19
|
+
const [expires, setExpires] = React.useState<Date | null>(new Date(2026, 9, 27));
|
|
20
|
+
const { hint, tone } = expiryHint(expires);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Datepicker.Root
|
|
24
|
+
mode="single"
|
|
25
|
+
label="Страховка действует до"
|
|
26
|
+
value={expires}
|
|
27
|
+
onValueChange={setExpires}
|
|
28
|
+
hint={hint}
|
|
29
|
+
hintTone={tone}
|
|
30
|
+
fullWidth
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -42,6 +42,7 @@ DigitInput field frame: label row → cells → support row (hint | e
|
|
|
42
42
|
| `required` | `boolean` | — | Red `*` after the label and native `required` on every cell. |
|
|
43
43
|
| `optional` | `boolean` | — | Muted marker right after the label text (`labels.optional`). |
|
|
44
44
|
| `hint` | `ReactNode` | — | Help text under the cells. Hidden while `error` is shown. |
|
|
45
|
+
| `hintTone` | `"neutral" \| "success" \| "warning" \| "danger"` | `"neutral"` | Status color of `hint` («Осталось 12 дней» in `warning`). The words carry the status, the color repeats it; an `error` replaces the hint and its tone. A validation error is `error`, never a `danger` hint. |
|
|
45
46
|
| `error` | `ReactNode` | — | Error message in the hint slot; implies `invalid`. |
|
|
46
47
|
| `invalid` | `boolean` | — | Danger ring and danger digits on every cell, `aria-invalid`. A non-empty `error` implies it. |
|
|
47
48
|
| `success` | `boolean` | `false` | The code was accepted (after the server checked it): the cells turn success one after another. Ignored while invalid. Say it in words too (`hint`). |
|
|
@@ -30,7 +30,7 @@ Dnd.Root session owner: overlay (portal), live region, l
|
|
|
30
30
|
├─ Dnd.Sortable list container and drop target; renders items in the drawn order
|
|
31
31
|
│ ├─ header optional content before the items that never moves (a group's heading row)
|
|
32
32
|
│ ├─ Dnd.SortableItem one item (li in ul/ol, div otherwise); the gap is drawn before it
|
|
33
|
-
│ │ └─ Dnd.Handle optional grip: a ghost icon-only Button (with `handle` on the list); a lock in a `locked` item; busy while the item's move is saved
|
|
33
|
+
│ │ └─ Dnd.Handle optional grip, last in the item: a ghost icon-only Button (with `handle` on the list); a lock in a `locked` item; busy while the item's move is saved
|
|
34
34
|
│ ├─ empty the `empty` content while the list has no items
|
|
35
35
|
│ └─ children optional content after the items that never moves (an add tile)
|
|
36
36
|
├─ Dnd.Draggable an item to carry elsewhere; stays in place, faded, until the drop
|
|
@@ -85,7 +85,7 @@ Generic `Dnd.Sortable<T>`; `ref` → `HTMLElement`. The list and its drop target
|
|
|
85
85
|
| `…rest` | `Omit<HTMLAttributes<HTMLElement>, "id">` | — | `aria-labelledby` / `aria-describedby` (the visible title and description) and the other attributes. |
|
|
86
86
|
|
|
87
87
|
### Dnd.Handle
|
|
88
|
-
`ref` → `HTMLButtonElement` (`HTMLSpanElement` in a `locked` item). The grip: a ghost icon-only `xs` Button that starts the drag and takes the item's keys; busy while
|
|
88
|
+
`ref` → `HTMLButtonElement` (`HTMLSpanElement` in a `locked` item). The grip: a ghost icon-only `xs` Button that starts the drag and takes the item's keys; busy while saving. In a `locked` item it is a lock. Place it last in the item, after its controls.
|
|
89
89
|
|
|
90
90
|
| Prop | Type | Default | Description |
|
|
91
91
|
|---|---|---|---|
|
|
@@ -201,6 +201,7 @@ An item's own `onPointerDown` / `onKeyDown` run before the kit's: `preventDefaul
|
|
|
201
201
|
## Layout & spacing
|
|
202
202
|
- The distance between items is `--prime-space-2`; a host sets `--dnd-sortable-gap` on the list for another one.
|
|
203
203
|
- A grid (`axis="grid"`) is `display: grid`: the host sets the tracks (`grid-template-columns`) and the cells' size; the gap takes a cell, and a carried item resizes to the cell it would land in.
|
|
204
|
+
- A `Dnd.Handle` ends its item: the last child, after the item's own trailing controls (a remove button), at the inline end (on the left in RTL); a `locked` item's lock takes the same place, so the grips of every row stand on one line. In a table it is the last cell. An on / off switch leads the item (its first child), so its state reads first and nothing stands between the text and the grip.
|
|
204
205
|
- The reason chip stands `--prime-tooltip-offset` under the carried copy, start-aligned, at most `--prime-tooltip-max-width` wide.
|
|
205
206
|
- The look of a row is the consumer's: give `Dnd.SortableItem` / `Dnd.Draggable` a radius on its own class (the gap and the clone's fill take it) and keep the row's look within its ancestors up to the nearest surface (`data-depth`) — the clone flies inside empty copies (`display: contents`) of exactly those, so their variables and selectors still apply. A table row flies in a one-row copy of its table inside them.
|
|
206
207
|
- A scrollable ancestor under the pointer auto-scrolls within 64 px of its edge (a `ScrollContainer` strip works).
|
|
@@ -251,7 +252,7 @@ An item's own `onPointerDown` / `onKeyDown` run before the kit's: `preventDefaul
|
|
|
251
252
|
| Example | Shows |
|
|
252
253
|
|---|---|
|
|
253
254
|
| [overview.tsx](examples/overview.tsx) | Project tasks reordered by dragging the whole row; one root above, the move applied with a helper — `Dnd.Root`, `Dnd.Sortable`, `onReorder`, `moveBefore`. |
|
|
254
|
-
| [handle.tsx](examples/handle.tsx) | Notification channels
|
|
255
|
+
| [handle.tsx](examples/handle.tsx) | Notification channels, each switch leading its row and the grip ending it: only the grip starts a drag, and the async save can roll the order back — `handle`, `Dnd.Handle`, `onReorder`. |
|
|
255
256
|
| [grid.tsx](examples/grid.tsx) | Product photos in a wrapping grid: a photo carried across rows lands in reading order, the gap takes a cell, Alt + ← / → steps it one place — `axis`. |
|
|
256
257
|
| [locked.tsx](examples/locked.tsx) | Sections of a workspace menu: the home section is pinned first with a lock in place of its grip, the rest reorder around nothing past it; each row is named by its visible title — `locked`, `handle`, `aria-labelledby`. |
|
|
257
258
|
| [board.tsx](examples/board.tsx) | A ticket board: columns share one kind, so a ticket lands in another column at an exact position or moves there with Alt + ← / →, an empty column says so, and a full column refuses it before the release with the reason under the ticket — `kind`, `canDrop`, `empty`, `onReorder`. |
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
box-shadow: var(--prime-shadow-raised);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/* The grip sits closer to the edge:
|
|
21
|
+
/* The grip ends the row and sits closer to the edge: a square button with its own inner room. */
|
|
22
22
|
.handleRow {
|
|
23
|
-
padding-inline-
|
|
23
|
+
padding-inline-end: var(--prime-space-2);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
.rowText {
|
|
@@ -134,9 +134,10 @@
|
|
|
134
134
|
text-align: end;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/* The grip column ends the row. */
|
|
137
138
|
.grip {
|
|
138
139
|
inline-size: var(--prime-space-10);
|
|
139
|
-
padding-inline-
|
|
140
|
+
padding-inline-start: 0;
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
/* A stage's heading row: a fill marks where a new stage starts. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Notification channels
|
|
1
|
+
/** Notification channels, each switch leading its row and the grip ending it: only the grip starts a drag, and the async save can roll the order back — `handle`, `Dnd.Handle`, `onReorder`. */
|
|
2
2
|
import { Dnd, moveBefore, Switch, Typography } from "prime-ui-kit";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
@@ -37,10 +37,6 @@ export default function DndHandleExample() {
|
|
|
37
37
|
}}
|
|
38
38
|
renderItem={(channel) => (
|
|
39
39
|
<Dnd.SortableItem id={channel.id} className={styles.handleRow}>
|
|
40
|
-
<Dnd.Handle />
|
|
41
|
-
<Typography as="span" variant="body-m" className={styles.rowText}>
|
|
42
|
-
{channel.title}
|
|
43
|
-
</Typography>
|
|
44
40
|
<Switch.Root
|
|
45
41
|
aria-label={channel.title}
|
|
46
42
|
checked={channel.enabled}
|
|
@@ -50,6 +46,10 @@ export default function DndHandleExample() {
|
|
|
50
46
|
)
|
|
51
47
|
}
|
|
52
48
|
/>
|
|
49
|
+
<Typography as="span" variant="body-m" className={styles.rowText}>
|
|
50
|
+
{channel.title}
|
|
51
|
+
</Typography>
|
|
52
|
+
<Dnd.Handle />
|
|
53
53
|
</Dnd.SortableItem>
|
|
54
54
|
)}
|
|
55
55
|
/>
|
|
@@ -40,7 +40,6 @@ export default function DndLockedExample() {
|
|
|
40
40
|
aria-labelledby={`${uid}-${section.id}`}
|
|
41
41
|
aria-describedby={`${uid}-${section.id}-note`}
|
|
42
42
|
>
|
|
43
|
-
<Dnd.Handle aria-label={`Переместить раздел «${section.title}»`} />
|
|
44
43
|
<span className={styles.rowText}>
|
|
45
44
|
<Typography as="span" variant="body-m" id={`${uid}-${section.id}`}>
|
|
46
45
|
{section.title}
|
|
@@ -49,6 +48,7 @@ export default function DndLockedExample() {
|
|
|
49
48
|
{section.description}
|
|
50
49
|
</Typography>
|
|
51
50
|
</span>
|
|
51
|
+
<Dnd.Handle aria-label={`Переместить раздел «${section.title}»`} />
|
|
52
52
|
</Dnd.SortableItem>
|
|
53
53
|
)}
|
|
54
54
|
/>
|
|
@@ -51,9 +51,6 @@ export default function DndTableExample() {
|
|
|
51
51
|
</caption>
|
|
52
52
|
<thead>
|
|
53
53
|
<tr>
|
|
54
|
-
<th scope="col" className={styles.grip}>
|
|
55
|
-
<VisuallyHidden>Порядок</VisuallyHidden>
|
|
56
|
-
</th>
|
|
57
54
|
<th scope="col" className={styles.cell}>
|
|
58
55
|
<Typography as="span" variant="caption" tone="muted">
|
|
59
56
|
Работа
|
|
@@ -64,6 +61,9 @@ export default function DndTableExample() {
|
|
|
64
61
|
Часы
|
|
65
62
|
</Typography>
|
|
66
63
|
</th>
|
|
64
|
+
<th scope="col" className={styles.grip}>
|
|
65
|
+
<VisuallyHidden>Порядок</VisuallyHidden>
|
|
66
|
+
</th>
|
|
67
67
|
</tr>
|
|
68
68
|
</thead>
|
|
69
69
|
{stages.map((stage) => (
|
|
@@ -90,9 +90,6 @@ export default function DndTableExample() {
|
|
|
90
90
|
}
|
|
91
91
|
renderItem={(line) => (
|
|
92
92
|
<Dnd.SortableItem id={line.id}>
|
|
93
|
-
<td className={styles.grip}>
|
|
94
|
-
<Dnd.Handle />
|
|
95
|
-
</td>
|
|
96
93
|
<td className={styles.cell}>
|
|
97
94
|
<Typography as="span" variant="body-m">
|
|
98
95
|
{line.title}
|
|
@@ -103,6 +100,9 @@ export default function DndTableExample() {
|
|
|
103
100
|
{line.hours}
|
|
104
101
|
</Typography>
|
|
105
102
|
</td>
|
|
103
|
+
<td className={styles.grip}>
|
|
104
|
+
<Dnd.Handle />
|
|
105
|
+
</td>
|
|
106
106
|
</Dnd.SortableItem>
|
|
107
107
|
)}
|
|
108
108
|
/>
|
|
@@ -35,6 +35,7 @@ Drawer.Root state and dismiss policy (no DOM)
|
|
|
35
35
|
│ ├─ Drawer.Description <p>
|
|
36
36
|
│ └─ Drawer.Actions row of tier-s controls before the close button
|
|
37
37
|
├─ Drawer.Body the only scrolling zone (ScrollContainer)
|
|
38
|
+
│ └─ Drawer.Section <section>: <h3> label, description, Drawer.Actions at the heading's end
|
|
38
39
|
└─ Drawer.Footer always: Close or Cancel + the main action, primary last
|
|
39
40
|
├─ Drawer.Meta <p>, muted status at the start
|
|
40
41
|
└─ Drawer.Close clones its child; closes on click
|
|
@@ -80,7 +81,7 @@ No DOM, no ref. State and dismiss policy.
|
|
|
80
81
|
| `…rest` | `HTMLAttributes<HTMLElement>` | — | `className` and the other attributes of the `<header>`. |
|
|
81
82
|
|
|
82
83
|
### Drawer.Actions
|
|
83
|
-
`ref` → `HTMLDivElement`. A row of controls at the end of the header, before the close button: a status Badge, a ⋯ Dropdown
|
|
84
|
+
`ref` → `HTMLDivElement`. A row of controls at the end of the header, before the close button: a status Badge, a ⋯ Dropdown; under the title when it would get less than 12rem; or at the end of a `Drawer.Section` heading. Controls inside take tier `s`.
|
|
84
85
|
|
|
85
86
|
| Prop | Type | Default | Description |
|
|
86
87
|
|---|---|---|---|
|
|
@@ -95,12 +96,20 @@ No DOM, no ref. State and dismiss policy.
|
|
|
95
96
|
| `children` | `ReactNode` | — (required) | An `Icon` (sized to the m icon), or the record's picture: an `Avatar.Root` (sized to the tile) or a `Thumbnail.Root`. |
|
|
96
97
|
| `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children">` | — | `className` and the other attributes of the tile. |
|
|
97
98
|
|
|
98
|
-
### Drawer.Title
|
|
99
|
-
`ref` → `HTMLHeadingElement
|
|
99
|
+
### Drawer.Title
|
|
100
|
+
`ref` → `HTMLHeadingElement`. `<h2>` (title-m); its id names the dialog.
|
|
100
101
|
|
|
101
102
|
| Prop | Type | Default | Description |
|
|
102
103
|
|---|---|---|---|
|
|
103
|
-
| `…rest` | `Omit<HTMLAttributes<HTMLHeadingElement
|
|
104
|
+
| `…rest` | `Omit<HTMLAttributes<HTMLHeadingElement>, "id">` | — | `className`, `children` and the other attributes except `id` (the dialog owns it). |
|
|
105
|
+
|
|
106
|
+
### Drawer.Description
|
|
107
|
+
`ref` → `HTMLParagraphElement`. `<p>` (body-s, muted) under the title; its id describes the dialog.
|
|
108
|
+
|
|
109
|
+
| Prop | Type | Default | Description |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `loading` | `boolean` | `false` | Its data loads: a `Skeleton` holds the line's place and `aria-busy` is set, so the header never moves; the data fades in. |
|
|
112
|
+
| `…rest` | `Omit<HTMLAttributes<HTMLParagraphElement>, "id">` | — | `className`, `children` and the other attributes except `id` (the dialog owns it). |
|
|
104
113
|
|
|
105
114
|
### Drawer.Body
|
|
106
115
|
`ref` → `HTMLDivElement`. The only scrolling zone (a ScrollContainer), 16 gap between blocks.
|
|
@@ -109,12 +118,21 @@ No DOM, no ref. State and dismiss policy.
|
|
|
109
118
|
|---|---|---|---|
|
|
110
119
|
| `…rest` | `HTMLAttributes<HTMLDivElement>` | — | `className`, `children` and the other attributes of the zone. |
|
|
111
120
|
|
|
121
|
+
### Drawer.Section
|
|
122
|
+
`ref` → `HTMLElement`. A titled `<section>` of the body: an `<h3>` (title-s, under the `<h2>` title), an optional description, a `Drawer.Actions` at the end of the heading row; the content 16 below, sections 32 apart, in any wrapper. Fields group in a `Fieldset`.
|
|
123
|
+
|
|
124
|
+
| Prop | Type | Default | Description |
|
|
125
|
+
|---|---|---|---|
|
|
126
|
+
| `label` | `ReactNode` | — (required) | The heading of the section; it names the `<section>`. |
|
|
127
|
+
| `description` | `ReactNode` | — | One line of context under the heading (body-s). |
|
|
128
|
+
| `…rest` | `Omit<HTMLAttributes<HTMLElement>, "title">` | — | `children` (the content and an optional `Drawer.Actions`), `className` and the other attributes of the `<section>`. |
|
|
129
|
+
|
|
112
130
|
### Drawer.Footer
|
|
113
131
|
`ref` → `HTMLElement`. Renders `<footer>` with the actions, primary last, and an optional `Meta` at the start.
|
|
114
132
|
|
|
115
133
|
| Prop | Type | Default | Description |
|
|
116
134
|
|---|---|---|---|
|
|
117
|
-
| `layout` | `"fill" \| "end"` | `"end"` | `fill`: equal-width buttons in one row; `end`: auto width, at the end. Stacked on phones (viewport below 640 px) and in a dialog narrower than 360 px. |
|
|
135
|
+
| `layout` | `"fill" \| "end"` | `"end"` | `fill`: equal-width buttons in one row, wrapping rather than cutting a label; `end`: auto width, at the end. Stacked on phones (viewport below 640 px) and in a dialog narrower than 360 px. |
|
|
118
136
|
| `…rest` | `HTMLAttributes<HTMLElement>` | — | `className`, `children` and the other attributes of the `<footer>`. |
|
|
119
137
|
|
|
120
138
|
### Drawer.Meta
|
|
@@ -125,7 +143,7 @@ No DOM, no ref. State and dismiss policy.
|
|
|
125
143
|
| `…rest` | `HTMLAttributes<HTMLParagraphElement>` | — | `className`, `children` and the other attributes of the `<p>`. |
|
|
126
144
|
|
|
127
145
|
### Drawer.Guard
|
|
128
|
-
No DOM, no ref. Holds the dialog from anywhere inside Content, where the form state lives; renders only the built-in «discard changes?» confirm. Closing from code is never held.
|
|
146
|
+
No DOM, no ref. Holds the dialog from anywhere inside Content, where the form state lives; renders only the built-in «discard changes?» confirm. While `busy` or `dirty` the browser also asks before the tab closes or reloads. Closing from code is never held.
|
|
129
147
|
|
|
130
148
|
| Prop | Type | Default | Description |
|
|
131
149
|
|---|---|---|---|
|
|
@@ -176,6 +194,9 @@ Below 640px of viewport every size is full width and loses its corners.
|
|
|
176
194
|
| closing | `open` turns false | the panel keeps showing the children it had open while it slides out |
|
|
177
195
|
| busy | `Drawer.Guard busy` | Escape, the scrim, the close button and `Drawer.Close` do nothing; the close button and the `Close` child are `disabled` in place |
|
|
178
196
|
| dirty | `Drawer.Guard dirty` | a dismiss opens the «Не сохранять изменения?» ConfirmDialog (`labels.discard*`); its confirm closes the drawer |
|
|
197
|
+
| leaving the tab | `Drawer.Guard busy` or `dirty` | the browser asks before the tab closes or reloads (`beforeunload`), as with `Page.Guard` |
|
|
198
|
+
| description loading | `Drawer.Description loading` | a `Skeleton` holds the line, `aria-busy` on the `<p>`; the data fades in under the same element |
|
|
199
|
+
| actions below | the title would get less than 12rem beside `Drawer.Actions` | `data-actions-below` on the header: the actions move under the title and the description |
|
|
179
200
|
|
|
180
201
|
Motion: the scrim fades, the panel slides from its side over `slow` and leaves over `base` (`overlayMotion`); under `prefers-reduced-motion` it unmounts at once. The drawer has no touch gestures: it is not dragged or swiped away — it closes by its buttons, Escape and the scrim, with the same slide-out.
|
|
181
202
|
|
|
@@ -189,6 +210,10 @@ Motion: the scrim fades, the panel slides from its side over `slow` and leaves o
|
|
|
189
210
|
- Full viewport height; only the body scrolls, with its own side padding.
|
|
190
211
|
- 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.
|
|
191
212
|
- Fields inside get the surface field fill.
|
|
213
|
+
- 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`.
|
|
214
|
+
- With a `Drawer.Meta` the buttons stay one row: when the footer runs short the status goes above them first.
|
|
215
|
+
- `Drawer.Actions` keep their place beside the title while it has 12rem; on a phone, where a status and a ⋯ beside the × would fold the title into letters, they move under it by themselves.
|
|
216
|
+
- A record whose data is still loading: the title from the list row, `Drawer.Description loading` and a `Skeleton` body in the data's geometry under a `Crossfade` — not a skeleton of your own in the header.
|
|
192
217
|
|
|
193
218
|
## Accessibility
|
|
194
219
|
|
|
@@ -228,8 +253,9 @@ Motion: the scrim fades, the panel slides from its side over `slow` and leaves o
|
|
|
228
253
|
| [structure.tsx](examples/structure.tsx) | Optional parts: an icon tile in the header and one full-width action in the footer — `Drawer.Icon`, `Drawer.Footer`, `layout`. |
|
|
229
254
|
| [sizes.tsx](examples/sizes.tsx) | Every panel width, 360 to 800 px; below 640 px of viewport the panel takes the full width — `size`. |
|
|
230
255
|
| [placement.tsx](examples/placement.tsx) | The panel slides from the right for details and from the left for filters, rounded only on the edge facing the page — `side`. |
|
|
231
|
-
| [header-actions.tsx](examples/header-actions.tsx) | A record card: its status and a ⋯ menu sit in the header before the close button, one tier smaller — `Drawer.Actions`. |
|
|
232
|
-
| [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 — `Drawer.Icon`, `Avatar.Root`. |
|
|
256
|
+
| [header-actions.tsx](examples/header-actions.tsx) | A record card: its status and a ⋯ menu sit in the header before the close button, one tier smaller; on a phone, where the title has no room beside them, they move under it — `Drawer.Actions`. |
|
|
257
|
+
| [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`. |
|
|
258
|
+
| [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`. |
|
|
233
259
|
| [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`. |
|
|
234
260
|
| [long-content.tsx](examples/long-content.tsx) | A long body scrolls on its own while the header and the footer stay in place — `Drawer.Body`. |
|
|
235
261
|
| [dismiss.tsx](examples/dismiss.tsx) | An import closes only from its buttons, and not at all while it runs — `closeOnOutsideClick`, `closeOnEscape`. |
|
|
@@ -241,6 +267,9 @@ Motion: the scrim fades, the panel slides from its side over `slow` and leaves o
|
|
|
241
267
|
- A drawer without `Drawer.Footer` → add one: «Закрыть» for details, «Отмена» + the submit button for a form. The header × alone is not the end of the panel.
|
|
242
268
|
- 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.
|
|
243
269
|
- A delete confirmation in a Drawer → use ConfirmDialog.
|
|
270
|
+
- A hand-made heading row (`Typography as="h3"` + buttons) over a group in the body → `Drawer.Section label` with `Drawer.Actions`.
|
|
271
|
+
- A wrapper with its own gap around `Drawer.Section`s inside a `Tabs.Panel` or a form → not needed: sections keep 32 between them in any wrapper.
|
|
272
|
+
- A `Skeleton` placed by hand in `Drawer.Description` while the record loads → `Drawer.Description loading`.
|
|
244
273
|
- Busy or unsaved state lifted out of the form into Root props → put `Drawer.Guard` next to the form, inside Content.
|
|
245
274
|
- 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.
|
|
246
275
|
- A Badge or a menu as a custom child of the header text column → put them in `Drawer.Actions`.
|
|
@@ -250,5 +279,5 @@ Motion: the scrim fades, the panel slides from its side over `slow` and leaves o
|
|
|
250
279
|
- Padding on a wrapper inside Body → Body already pads and scrolls.
|
|
251
280
|
|
|
252
281
|
## Related
|
|
253
|
-
- **Built from:** the dialog shared with Modal and Sheet (`internal/overlay/DialogParts`), [Button](../button/COMPONENT.md) (close button), [ScrollContainer](../scroll-container/COMPONENT.md) (`Drawer.Body`), [Avatar](../avatar/COMPONENT.md) and [Thumbnail](../thumbnail/COMPONENT.md) (a record's picture in `Drawer.Icon`), [ConfirmDialog](../confirm-dialog/COMPONENT.md) (the `Drawer.Guard` discard confirm), `Icon` (the close glyph)
|
|
282
|
+
- **Built from:** the dialog shared with Modal and Sheet (`internal/overlay/DialogParts`), [Button](../button/COMPONENT.md) (close button), [ScrollContainer](../scroll-container/COMPONENT.md) (`Drawer.Body`), [Avatar](../avatar/COMPONENT.md) and [Thumbnail](../thumbnail/COMPONENT.md) (a record's picture in `Drawer.Icon`), [Skeleton](../skeleton/COMPONENT.md) (`Drawer.Description loading`), [ConfirmDialog](../confirm-dialog/COMPONENT.md) (the `Drawer.Guard` discard confirm), `Icon` (the close glyph)
|
|
254
283
|
- **See also:** [Modal](../modal/COMPONENT.md) (the same header, body, footer and `Guard` in the centre), [Sheet](../sheet/COMPONENT.md) (the same parts raised from the bottom edge for a short task), [ConfirmDialog](../confirm-dialog/COMPONENT.md) (a destructive confirm), [Sidebar](../../layout/sidebar/COMPONENT.md), [Popover](../popover/COMPONENT.md), [Avatar](../avatar/COMPONENT.md) (a person's picture in `Drawer.Icon`)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** A client card from the list: the contact's avatar heads the panel in place of an icon tile, then the details — `Drawer.Icon`, `Avatar.Root`. */
|
|
2
|
-
import { Avatar, Button, DescriptionList, Drawer } from "prime-ui-kit";
|
|
1
|
+
/** 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`. */
|
|
2
|
+
import { Avatar, Button, DescriptionList, Drawer, Icon } from "prime-ui-kit";
|
|
3
3
|
|
|
4
4
|
const CLIENT = {
|
|
5
5
|
name: "Анна Соколова",
|
|
@@ -10,6 +10,11 @@ const CLIENT = {
|
|
|
10
10
|
manager: "Игорь Лебедев",
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
const DEALS = [
|
|
14
|
+
{ id: "D-2041", title: "Поставка серверов", amount: "1 240 000 ₽" },
|
|
15
|
+
{ id: "D-2057", title: "Сервисный контракт на год", amount: "380 000 ₽" },
|
|
16
|
+
];
|
|
17
|
+
|
|
13
18
|
export default function DrawerClientCardExample() {
|
|
14
19
|
return (
|
|
15
20
|
<Drawer.Root>
|
|
@@ -29,12 +34,31 @@ export default function DrawerClientCardExample() {
|
|
|
29
34
|
<Drawer.Description>{CLIENT.company}</Drawer.Description>
|
|
30
35
|
</Drawer.Header>
|
|
31
36
|
<Drawer.Body>
|
|
32
|
-
<
|
|
33
|
-
<DescriptionList.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
<Drawer.Section label="Контакт">
|
|
38
|
+
<DescriptionList.Root>
|
|
39
|
+
<DescriptionList.Item label="Должность">{CLIENT.role}</DescriptionList.Item>
|
|
40
|
+
<DescriptionList.Item label="Телефон">{CLIENT.phone}</DescriptionList.Item>
|
|
41
|
+
<DescriptionList.Item label="Почта">{CLIENT.email}</DescriptionList.Item>
|
|
42
|
+
<DescriptionList.Item label="Менеджер">{CLIENT.manager}</DescriptionList.Item>
|
|
43
|
+
</DescriptionList.Root>
|
|
44
|
+
</Drawer.Section>
|
|
45
|
+
<Drawer.Section label="Открытые сделки" description="2 сделки на 1 620 000 ₽">
|
|
46
|
+
<Drawer.Actions>
|
|
47
|
+
<Button.Root variant="ghost" tone="neutral">
|
|
48
|
+
<Button.Icon>
|
|
49
|
+
<Icon name="action.add" />
|
|
50
|
+
</Button.Icon>
|
|
51
|
+
Сделка
|
|
52
|
+
</Button.Root>
|
|
53
|
+
</Drawer.Actions>
|
|
54
|
+
<DescriptionList.Root>
|
|
55
|
+
{DEALS.map((deal) => (
|
|
56
|
+
<DescriptionList.Item key={deal.id} label={deal.title}>
|
|
57
|
+
{deal.amount}
|
|
58
|
+
</DescriptionList.Item>
|
|
59
|
+
))}
|
|
60
|
+
</DescriptionList.Root>
|
|
61
|
+
</Drawer.Section>
|
|
38
62
|
</Drawer.Body>
|
|
39
63
|
<Drawer.Footer>
|
|
40
64
|
<Drawer.Close>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** A record card: its status and a ⋯ menu sit in the header before the close button, one tier smaller — `Drawer.Actions`. */
|
|
1
|
+
/** A record card: its status and a ⋯ menu sit in the header before the close button, one tier smaller; on a phone, where the title has no room beside them, they move under it — `Drawer.Actions`. */
|
|
2
2
|
import { Badge, Button, DescriptionList, Drawer, Dropdown, Icon } from "prime-ui-kit";
|
|
3
3
|
|
|
4
4
|
const DEAL = {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** 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`. */
|
|
2
|
+
import { Button, Crossfade, DescriptionList, Drawer, Skeleton } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
const BIKE = {
|
|
6
|
+
title: "Велосипед №214",
|
|
7
|
+
summary: "Тур 2 · на линии с 6 октября",
|
|
8
|
+
facts: [
|
|
9
|
+
["Модель", "Tour 2, рама M"],
|
|
10
|
+
["Пробег", "1 840 км"],
|
|
11
|
+
["Последнее ТО", "28 сентября"],
|
|
12
|
+
["Арендатор", "Илья Морозов"],
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default function DrawerLoadingExample() {
|
|
17
|
+
const [loading, setLoading] = React.useState(true);
|
|
18
|
+
|
|
19
|
+
const openChange = (open: boolean) => {
|
|
20
|
+
if (!open) return;
|
|
21
|
+
setLoading(true);
|
|
22
|
+
window.setTimeout(() => setLoading(false), 1200);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Drawer.Root onOpenChange={openChange}>
|
|
27
|
+
<Drawer.Trigger>
|
|
28
|
+
<Button.Root variant="soft" tone="neutral">
|
|
29
|
+
Открыть велосипед
|
|
30
|
+
</Button.Root>
|
|
31
|
+
</Drawer.Trigger>
|
|
32
|
+
<Drawer.Content>
|
|
33
|
+
<Drawer.Header>
|
|
34
|
+
<Drawer.Title>{BIKE.title}</Drawer.Title>
|
|
35
|
+
<Drawer.Description loading={loading}>{BIKE.summary}</Drawer.Description>
|
|
36
|
+
</Drawer.Header>
|
|
37
|
+
<Drawer.Body>
|
|
38
|
+
<Crossfade state={loading ? "loading" : "data"} aria-busy={loading}>
|
|
39
|
+
<DescriptionList.Root>
|
|
40
|
+
{BIKE.facts.map(([term, value]) => (
|
|
41
|
+
<DescriptionList.Item key={term} label={term}>
|
|
42
|
+
{loading ? <Skeleton /> : value}
|
|
43
|
+
</DescriptionList.Item>
|
|
44
|
+
))}
|
|
45
|
+
</DescriptionList.Root>
|
|
46
|
+
</Crossfade>
|
|
47
|
+
</Drawer.Body>
|
|
48
|
+
<Drawer.Footer>
|
|
49
|
+
<Drawer.Close>
|
|
50
|
+
<Button.Root variant="outline" tone="neutral">
|
|
51
|
+
Закрыть
|
|
52
|
+
</Button.Root>
|
|
53
|
+
</Drawer.Close>
|
|
54
|
+
</Drawer.Footer>
|
|
55
|
+
</Drawer.Content>
|
|
56
|
+
</Drawer.Root>
|
|
57
|
+
);
|
|
58
|
+
}
|