prime-ui-kit 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/dist/chunks/{chunk-XNOJEX55.js → chunk-P6ZO37JM.js} +1 -1
- package/dist/chunks/{chunk-JV2KB5JZ.js → chunk-SYEX7OVT.js} +3572 -2865
- package/dist/chunks/{chunk-PK2GC77E.js → chunk-WXKRUVSE.js} +164 -140
- package/dist/color-picker.js +2 -2
- package/dist/components/banner/Banner.d.ts +5 -2
- package/dist/components/chart-frame/ChartFrame.d.ts +19 -6
- package/dist/components/chat/Chat.d.ts +2 -2
- package/dist/components/chat/ChatPinned.d.ts +2 -2
- package/dist/components/chat/MessageList.d.ts +1 -1
- package/dist/components/chat/chatContext.d.ts +1 -1
- package/dist/components/field-list/FieldList.d.ts +90 -0
- package/dist/components/index.d.ts +3 -1
- package/dist/components/input/Input.d.ts +2 -2
- package/dist/components/kanban/Kanban.d.ts +6 -3
- package/dist/components/page-content/PageContent.d.ts +55 -4
- package/dist/components/sparkline/Sparkline.d.ts +64 -15
- package/dist/components/sparkline/geometry.d.ts +26 -0
- package/dist/components/tooltip/Tooltip.d.ts +5 -2
- package/dist/hooks/usePosition.d.ts +15 -4
- package/dist/index.css +1 -1
- package/dist/index.js +4 -2
- package/dist/internal/ChartTooltip.d.ts +26 -0
- package/dist/internal/FieldAction.d.ts +6 -7
- package/dist/internal/fieldCell.d.ts +21 -0
- package/dist/internal/messageList.d.ts +2 -2
- package/dist/internal/palette.d.ts +7 -0
- package/dist/skill/SKILL.md +24 -14
- package/dist/skill/anti-slop.md +77 -4
- package/dist/skill/cheatsheet.md +24 -5
- package/dist/skill/checklist.md +19 -1
- package/dist/skill/choosing.md +10 -5
- package/dist/skill/components.md +4 -3
- package/dist/skill/composition.md +28 -15
- package/dist/skill/foundations.md +8 -6
- package/dist/skill/layouts.md +6 -6
- package/dist/skill/patterns/app-shell.module.css +1 -8
- package/dist/skill/patterns/app-shell.tsx +11 -9
- package/dist/skill/patterns/chat.tsx +23 -5
- package/dist/skill/patterns/dashboard.module.css +1 -31
- package/dist/skill/patterns/dashboard.tsx +150 -136
- package/dist/skill/patterns/detail-page.module.css +0 -3
- package/dist/skill/patterns/detail-page.tsx +4 -7
- package/dist/skill/patterns/form-drawer.tsx +2 -2
- package/dist/skill/patterns/kanban-board.tsx +4 -2
- package/dist/skill/patterns/list-page.tsx +2 -2
- package/dist/skill/patterns/media-library.module.css +0 -22
- package/dist/skill/patterns/media-library.tsx +17 -18
- package/dist/skill/patterns/record-editor.module.css +18 -0
- package/dist/skill/patterns/record-editor.tsx +356 -0
- package/dist/skill/patterns/screen-states.module.css +3 -17
- package/dist/skill/patterns/screen-states.tsx +70 -69
- package/dist/skill/patterns/settings-page.module.css +1 -21
- package/dist/skill/patterns/settings-page.tsx +58 -79
- package/dist/skill/reference/components/avatar/COMPONENT.md +4 -4
- package/dist/skill/reference/components/avatar/examples/presence.tsx +1 -1
- package/dist/skill/reference/components/avatar/examples/states.tsx +1 -1
- package/dist/skill/reference/components/banner/COMPONENT.md +6 -6
- package/dist/skill/reference/components/banner/examples/dismissible.tsx +22 -11
- package/dist/skill/reference/components/banner/examples/examples.module.css +3 -3
- package/dist/skill/reference/components/banner/examples/narrow.tsx +1 -1
- package/dist/skill/reference/components/breadcrumb/COMPONENT.md +1 -1
- package/dist/skill/reference/components/breadcrumb/examples/overflow.tsx +1 -1
- package/dist/skill/reference/components/card/COMPONENT.md +5 -4
- package/dist/skill/reference/components/card/examples/examples.module.css +0 -28
- package/dist/skill/reference/components/card/examples/kpi-media.tsx +13 -11
- package/dist/skill/reference/components/card/examples/narrow.tsx +1 -1
- package/dist/skill/reference/components/card/examples/panel-chart.tsx +45 -22
- package/dist/skill/reference/components/chart-frame/COMPONENT.md +10 -7
- package/dist/skill/reference/components/chart-frame/examples/tooltip.tsx +20 -24
- package/dist/skill/reference/components/chat/COMPONENT.md +3 -3
- package/dist/skill/reference/components/chat/examples/search.tsx +1 -1
- package/dist/skill/reference/components/chat-list/COMPONENT.md +1 -1
- package/dist/skill/reference/components/checkbox/examples/indicator.tsx +24 -2
- package/dist/skill/reference/components/color-picker/COMPONENT.md +2 -2
- package/dist/skill/reference/components/color-swatches/COMPONENT.md +1 -0
- package/dist/skill/reference/components/datepicker/COMPONENT.md +1 -0
- package/dist/skill/reference/components/dnd/COMPONENT.md +3 -3
- package/dist/skill/reference/components/field-list/COMPONENT.md +164 -0
- package/dist/skill/reference/components/field-list/examples/base-row.tsx +76 -0
- package/dist/skill/reference/components/field-list/examples/controlled.tsx +72 -0
- package/dist/skill/reference/components/field-list/examples/errors.tsx +85 -0
- package/dist/skill/reference/components/field-list/examples/examples.module.css +34 -0
- package/dist/skill/reference/components/field-list/examples/limits.tsx +55 -0
- package/dist/skill/reference/components/field-list/examples/locked.tsx +139 -0
- package/dist/skill/reference/components/field-list/examples/narrow.tsx +68 -0
- package/dist/skill/reference/components/field-list/examples/overview.tsx +66 -0
- package/dist/skill/reference/components/field-list/examples/reorder.tsx +42 -0
- package/dist/skill/reference/components/field-list/examples/sizes.tsx +42 -0
- package/dist/skill/reference/components/field-list/examples/states.tsx +51 -0
- package/dist/skill/reference/components/input/examples/examples.module.css +2 -7
- package/dist/skill/reference/components/kanban/COMPONENT.md +13 -9
- package/dist/skill/reference/components/kanban/examples/limits.tsx +2 -1
- package/dist/skill/reference/components/kanban/examples/variants.tsx +16 -8
- package/dist/skill/reference/components/list/COMPONENT.md +1 -1
- package/dist/skill/reference/components/media-viewer/COMPONENT.md +2 -2
- package/dist/skill/reference/components/message/COMPONENT.md +2 -2
- package/dist/skill/reference/components/notification/COMPONENT.md +1 -0
- package/dist/skill/reference/components/page-content/COMPONENT.md +70 -14
- package/dist/skill/reference/components/page-content/examples/grid.tsx +107 -0
- package/dist/skill/reference/components/page-content/examples/overview.tsx +2 -2
- package/dist/skill/reference/components/page-content/examples/sections.tsx +81 -0
- package/dist/skill/reference/components/page-toolbar/COMPONENT.md +1 -1
- package/dist/skill/reference/components/segmented-control/COMPONENT.md +1 -0
- package/dist/skill/reference/components/select/COMPONENT.md +4 -2
- package/dist/skill/reference/components/select/examples/icon-only.tsx +38 -0
- package/dist/skill/reference/components/sparkline/COMPONENT.md +83 -30
- package/dist/skill/reference/components/sparkline/examples/compact.tsx +1 -1
- package/dist/skill/reference/components/sparkline/examples/curve.tsx +28 -0
- package/dist/skill/reference/components/sparkline/examples/gaps.tsx +33 -0
- package/dist/skill/reference/components/sparkline/examples/overview.tsx +11 -2
- package/dist/skill/reference/components/sparkline/examples/period.tsx +7 -2
- package/dist/skill/reference/components/sparkline/examples/reference.tsx +37 -0
- package/dist/skill/reference/components/sparkline/examples/variants.tsx +2 -2
- package/dist/skill/reference/components/tabs/examples/examples.module.css +0 -6
- package/dist/skill/reference/components/tabs/examples/in-drawer.tsx +11 -13
- package/dist/skill/reference/components/timeline/examples/examples.module.css +0 -8
- package/dist/skill/reference/components/timeline/examples/narrow.tsx +2 -2
- package/dist/skill/reference/components/timeline/examples/sizes.tsx +3 -3
- package/dist/skill/reference/components/timeline/examples/variants.tsx +4 -4
- package/dist/skill/reference/components/tooltip/COMPONENT.md +6 -4
- package/dist/skill/reference/components/tooltip/examples/placement.tsx +2 -1
- package/dist/skill/reference/components/video-note/COMPONENT.md +1 -1
- package/dist/skill/reference/components/voice-player/COMPONENT.md +1 -1
- package/dist/skill/reference/layout/app-header/examples/in-app-shell.tsx +2 -2
- package/dist/skill/reference/layout/app-shell/COMPONENT.md +1 -1
- package/dist/skill/reference/layout/app-shell/examples/footer.tsx +2 -2
- package/dist/skill/reference/layout/app-shell/examples/overview.tsx +2 -2
- package/dist/skill/reference/layout/app-shell/examples/template.tsx +2 -2
- package/dist/skill/reference/layout/bottom-nav/examples/floating-action.tsx +2 -2
- package/dist/skill/reference/layout/bottom-nav/examples/floating.tsx +2 -2
- package/dist/skill/reference/layout/bottom-nav/examples/in-app-shell.tsx +2 -2
- package/dist/skill/responsive.md +5 -3
- package/dist/skill/ux.md +145 -0
- package/dist/tokens/semantic.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/avatar/COMPONENT.md +4 -4
- package/src/components/avatar/examples/presence.tsx +1 -1
- package/src/components/avatar/examples/states.tsx +1 -1
- package/src/components/banner/COMPONENT.md +6 -6
- package/src/components/banner/examples/dismissible.tsx +22 -11
- package/src/components/banner/examples/examples.module.css +3 -3
- package/src/components/banner/examples/narrow.tsx +1 -1
- package/src/components/breadcrumb/COMPONENT.md +1 -1
- package/src/components/breadcrumb/examples/overflow.tsx +1 -1
- package/src/components/card/COMPONENT.md +5 -4
- package/src/components/card/examples/examples.module.css +0 -28
- package/src/components/card/examples/kpi-media.tsx +13 -11
- package/src/components/card/examples/narrow.tsx +1 -1
- package/src/components/card/examples/panel-chart.tsx +45 -22
- package/src/components/chart-frame/COMPONENT.md +10 -7
- package/src/components/chart-frame/examples/tooltip.tsx +20 -24
- package/src/components/chat/COMPONENT.md +3 -3
- package/src/components/chat/examples/search.tsx +1 -1
- package/src/components/chat-list/COMPONENT.md +1 -1
- package/src/components/checkbox/examples/indicator.tsx +24 -2
- package/src/components/color-picker/COMPONENT.md +2 -2
- package/src/components/color-swatches/COMPONENT.md +1 -0
- package/src/components/datepicker/COMPONENT.md +1 -0
- package/src/components/dnd/COMPONENT.md +3 -3
- package/src/components/field-list/COMPONENT.md +164 -0
- package/src/components/field-list/examples/base-row.tsx +76 -0
- package/src/components/field-list/examples/controlled.tsx +72 -0
- package/src/components/field-list/examples/errors.tsx +85 -0
- package/src/components/field-list/examples/examples.module.css +34 -0
- package/src/components/field-list/examples/limits.tsx +55 -0
- package/src/components/field-list/examples/locked.tsx +139 -0
- package/src/components/field-list/examples/narrow.tsx +68 -0
- package/src/components/field-list/examples/overview.tsx +66 -0
- package/src/components/field-list/examples/reorder.tsx +42 -0
- package/src/components/field-list/examples/sizes.tsx +42 -0
- package/src/components/field-list/examples/states.tsx +51 -0
- package/src/components/input/examples/examples.module.css +2 -7
- package/src/components/kanban/COMPONENT.md +13 -9
- package/src/components/kanban/examples/limits.tsx +2 -1
- package/src/components/kanban/examples/variants.tsx +16 -8
- package/src/components/list/COMPONENT.md +1 -1
- package/src/components/media-viewer/COMPONENT.md +2 -2
- package/src/components/message/COMPONENT.md +2 -2
- package/src/components/notification/COMPONENT.md +1 -0
- package/src/components/page-content/COMPONENT.md +70 -14
- package/src/components/page-content/examples/grid.tsx +107 -0
- package/src/components/page-content/examples/overview.tsx +2 -2
- package/src/components/page-content/examples/sections.tsx +81 -0
- package/src/components/page-toolbar/COMPONENT.md +1 -1
- package/src/components/segmented-control/COMPONENT.md +1 -0
- package/src/components/select/COMPONENT.md +4 -2
- package/src/components/select/examples/icon-only.tsx +38 -0
- package/src/components/sparkline/COMPONENT.md +83 -30
- package/src/components/sparkline/examples/compact.tsx +1 -1
- package/src/components/sparkline/examples/curve.tsx +28 -0
- package/src/components/sparkline/examples/gaps.tsx +33 -0
- package/src/components/sparkline/examples/overview.tsx +11 -2
- package/src/components/sparkline/examples/period.tsx +7 -2
- package/src/components/sparkline/examples/reference.tsx +37 -0
- package/src/components/sparkline/examples/variants.tsx +2 -2
- package/src/components/tabs/examples/examples.module.css +0 -6
- package/src/components/tabs/examples/in-drawer.tsx +11 -13
- package/src/components/timeline/examples/examples.module.css +0 -8
- package/src/components/timeline/examples/narrow.tsx +2 -2
- package/src/components/timeline/examples/sizes.tsx +3 -3
- package/src/components/timeline/examples/variants.tsx +4 -4
- package/src/components/tooltip/COMPONENT.md +6 -4
- package/src/components/tooltip/examples/placement.tsx +2 -1
- package/src/components/video-note/COMPONENT.md +1 -1
- package/src/components/voice-player/COMPONENT.md +1 -1
- package/src/layout/app-header/examples/in-app-shell.tsx +2 -2
- package/src/layout/app-shell/COMPONENT.md +1 -1
- package/src/layout/app-shell/examples/footer.tsx +2 -2
- package/src/layout/app-shell/examples/overview.tsx +2 -2
- package/src/layout/app-shell/examples/template.tsx +2 -2
- package/src/layout/bottom-nav/examples/floating-action.tsx +2 -2
- package/src/layout/bottom-nav/examples/floating.tsx +2 -2
- package/src/layout/bottom-nav/examples/in-app-shell.tsx +2 -2
- package/src/styles/theme-light.css +1 -0
|
@@ -15,10 +15,11 @@ is, and is shown live with its code in the playground (category «Компози
|
|
|
15
15
|
| One record | [detail-page.tsx](patterns/detail-page.tsx) | Breadcrumb + status in the header, actions by weight, main column + side column of Cards, Timeline |
|
|
16
16
|
| Settings | [settings-page.tsx](patterns/settings-page.tsx) | heading column + panel per section, form with its own save, Switches that apply at once, danger zone with a confirm |
|
|
17
17
|
| Create in a side panel | [form-drawer.tsx](patterns/form-drawer.tsx) | Drawer form: groups, errors on submit, focus on the first invalid field, loading, toast |
|
|
18
|
+
| Record editor | [record-editor.tsx](patterns/record-editor.tsx) | a plan card edited on a page: name and description, specs in a `FieldList` (icon-only `Select` and `ColorPresets` in compact columns, pinned rows, reorder), selling points as one reorderable column, the price and its changes by month in one list with the base row pinned, a setup fee in three currencies in one row under one heading, save with «Сохранено» on the button |
|
|
18
19
|
| Dashboard | [dashboard.tsx](patterns/dashboard.tsx) | period switch, KPI row that cross-fades to the new period, a Sparkline revenue trend, invoiced vs paid in a ChartFrame, panels with progress and a split, short table + link to the list |
|
|
19
20
|
| Screen states | [screen-states.tsx](patterns/screen-states.tsx) | page error in a Banner with retry, region loading with a Skeleton, Crossfade between states, table error in place, first-run EmptyPage |
|
|
20
21
|
| Messenger | [chat.tsx](patterns/chat.tsx) | PageContent.Split of a `ChatList` (live last message, ticks, unread counts, «печатает…») and a `Chat`: `Chat.Header` with the back button, avatar, title, the status that turns into «печатает…» (`typing`) and the search button, `Chat.Search`, `Chat.Pinned`, `Chat.Messages` from `items` + a stable `renderMessage` (dates, runs, the unread band and service events from the data, history loaded above with `hasOlder` / `onLoadOlder`), every kind of Message (photos, file, contact, voice in a VoicePlayer with a transcript, round video), reactions, reply and edit from the menu through `Composer.Context value` (`replyTo` / `editOf`), the jump to the quoted message (`Message.Reply target`), optimistic sends whose messages rise from the composer while the client's answer slides in, «печатает…» both ways (`onActivityChange` → `Chat.Root typing`), unread counts from `onNewMessage` and `onRead`, selection with `Chat.SelectionBar`, delivery ticks, the Composer in `Chat.Footer` |
|
|
21
|
-
| Sales pipeline board | [kanban-board.tsx](patterns/kanban-board.tsx) | header with one primary action, PageToolbar with SmartFilter search and a board / list SegmentedControl, Crossfade between a Kanban and a DataTable of the same deals, column totals in `renderColumnMeta`, moves saved through an `onValueChange` promise that the server can refuse, movable stages with locked first and last, the deal in a Drawer with a DescriptionList and stage actions, skeleton first load |
|
|
22
|
+
| Sales pipeline board | [kanban-board.tsx](patterns/kanban-board.tsx) | header with one primary action, PageToolbar with SmartFilter search and a board / list SegmentedControl, Crossfade between a `plain` Kanban (stages without wells, deal cards on the page) and a DataTable of the same deals, column totals in `renderColumnMeta`, moves saved through an `onValueChange` promise that the server can refuse, movable stages with locked first and last, the deal in a Drawer with a DescriptionList and stage actions, skeleton first load |
|
|
22
23
|
| App frame for desktop and phone (root layout) | [app-shell.tsx](patterns/app-shell.tsx) | Sidebar rail on a wide screen, floating BottomNav in `AppShell.Footer` on a phone («Ещё» opens the Sidebar panel), AppHeader with the menu button, title and search to CommandMenu, counts on items, a home page that cross-fades between sections |
|
|
23
24
|
| Media library | [media-library.tsx](patterns/media-library.tsx) | PageToolbar with sections and counts, search (`SmartFilter` with `fields={[]}`), order and «Загрузить»; uploads through `useUploadQueue` in `FileUpload` tiles (`trigger="tile"`, `layout="grid"`) with progress, a failure and retry, a done file moving into the library; a grid of `Thumbnail` / `Thumbnail.Video` tiles that open `MediaViewer` out of the tile (`getOrigin`, `thumbnails`, `renderActions`) or, in selection mode, are picked (`checked`) for bulk download and a `ConfirmDialog` delete; a Skeleton first load and a filtered `EmptyPage` in a `Crossfade` |
|
|
24
25
|
| Resource schedule | [schedule.tsx](patterns/schedule.tsx) | full-height `Gantt` (`scrollHeight="fill"`) under a `PageToolbar` with search, role / client filters and team sections; people with avatars, client hues, lanes for overlaps, locked absences and milestones; moves saved through an async `onBarChange` or refused and glided back, new work drawn with `onBarCreate` and named in a Modal (cancel fades the ghost), `onSelect` details in a Drawer with a `DescriptionList` and a confirmed removal, `loading` and filtered `empty` |
|
|
@@ -33,21 +34,24 @@ AppShell.Root once per app (layouts.md#app-frame-once-per-a
|
|
|
33
34
|
├─ AppShell.Nav → Sidebar navigation rail, one layer above the page
|
|
34
35
|
├─ AppHeader.Root menu button / title or path / ⌘K search / app actions
|
|
35
36
|
└─ AppShell.Main gutters; the page renders here
|
|
36
|
-
└─ PageContent.
|
|
37
|
-
├─ PageContent.Header
|
|
37
|
+
└─ PageContent.Page | Root the page you deliver
|
|
38
|
+
├─ PageContent.Header 24 above the body
|
|
38
39
|
│ ├─ Breadcrumb only below a top-level section; first child → 8 above the title
|
|
39
40
|
│ ├─ PageContent.Title the one h1
|
|
40
41
|
│ ├─ PageContent.Description one sentence of context (or a status Badge + the key fact)
|
|
41
42
|
│ └─ PageContent.Actions page buttons; wrap under the title on narrow screens
|
|
42
|
-
└─ PageContent.Body blocks
|
|
43
|
+
└─ PageContent.Body blocks 24 apart: Banner, the tile grid, a table, Cards
|
|
44
|
+
├─ PageContent.Grid every tile of the screen, 16 on both axes
|
|
45
|
+
│ └─ PageContent.Cell span (of 12), rows; the tile fills it
|
|
46
|
+
└─ PageContent.Section a new topic: h2 label (+ Actions), 40 from its neighbours
|
|
43
47
|
```
|
|
44
48
|
|
|
45
49
|
- A screen component renders only its page. Styles, `applyTheme`, `NotificationProvider` and
|
|
46
50
|
`AppShell` live at the app root.
|
|
47
|
-
- `PageContent.
|
|
51
|
+
- `PageContent.Page` for app pages (full panel width); `PageContent.Root maxWidth="wide"` for
|
|
48
52
|
settings, `maxWidth="readable"` for one narrow column of text or fields.
|
|
49
|
-
- No outer padding, no margins: `AppShell.Main` has the gutters, `PageContent` has header → body
|
|
50
|
-
block → block 40. Your CSS lays out only the inside of a block.
|
|
53
|
+
- No outer padding, no margins: `AppShell.Main` has the gutters, `PageContent` has header → body 24,
|
|
54
|
+
block → block 24, section → section 40 and tile → tile 16. Your CSS lays out only the inside of a block.
|
|
51
55
|
|
|
52
56
|
```tsx
|
|
53
57
|
import { Button, Dropdown, Icon, PageContent } from "prime-ui-kit";
|
|
@@ -90,16 +94,19 @@ Every gap is a `--prime-space-N` token (N × 4px) set as `gap` on the parent. Cl
|
|
|
90
94
|
| label → field, field → hint | 4–8 | built into fields — never add |
|
|
91
95
|
| buttons, chips, filters in a row | 8 | `--prime-space-2` |
|
|
92
96
|
| text lines of one item (name over meta) | 0–4 | `--prime-space-1` |
|
|
93
|
-
| heading → its content (group, section
|
|
94
|
-
| tile → tile
|
|
97
|
+
| heading → its content (group, section) | 16 | `--prime-space-4` (`PageContent.Section` does it) |
|
|
98
|
+
| tile → tile, both axes (KPI, charts, panels) | 16 | `PageContent.Grid` |
|
|
95
99
|
| field → field, switch → switch | 20 | `--prime-space-5` |
|
|
96
100
|
| main column → side column | 24 | `--prime-space-6` |
|
|
101
|
+
| page header → body | 24 | `PageContent` |
|
|
102
|
+
| block → block on a page | 24 | `PageContent.Body` |
|
|
97
103
|
| group → group in a form | 32 | `--prime-space-8` |
|
|
98
|
-
|
|
|
99
|
-
| block → block on a page | 40 | `PageContent.Body` |
|
|
104
|
+
| section → section (a new topic with an `h2`) | 40 | `PageContent.Section` |
|
|
100
105
|
|
|
101
106
|
A screen with one gap everywhere reads as a list of unrelated things. Use at least three levels: inside
|
|
102
|
-
an item, between items, between blocks.
|
|
107
|
+
an item, between items, between blocks. The tiles of one screen are one board: put them all in one
|
|
108
|
+
`PageContent.Grid` (one gap on both axes), never a grid per row — and keep the large air for a new topic
|
|
109
|
+
with its own heading (`PageContent.Section`).
|
|
103
110
|
|
|
104
111
|
## 3. Hierarchy — air first, then type
|
|
105
112
|
|
|
@@ -108,7 +115,7 @@ Hierarchy comes from space and a handful of type roles, not from size jumps, col
|
|
|
108
115
|
| Text | Use |
|
|
109
116
|
|---|---|
|
|
110
117
|
| page title | `PageContent.Title` (`heading-m`, the only `h1`) |
|
|
111
|
-
| section heading without a card | `
|
|
118
|
+
| section heading without a card | `PageContent.Section label` (an `h2` in `title-m`) |
|
|
112
119
|
| card / panel title | `Card.Title as="h2"` (under the page title) |
|
|
113
120
|
| heading of a group of fields | `Fieldset label` (`title-s` in the `<legend>`) |
|
|
114
121
|
| other group heading inside a card | `Typography as="h3" variant="title-s"` |
|
|
@@ -162,6 +169,12 @@ Hierarchy comes from space and a handful of type roles, not from size jumps, col
|
|
|
162
169
|
|
|
163
170
|
- Labels above every field (`label`), placeholder is an example value, help in `hint`, problems in
|
|
164
171
|
`error` — after blur or on submit. Mark the minority (`required` or `optional`).
|
|
172
|
+
- Records of one shape that a person adds, edits and removes are a `FieldList`: one row per record,
|
|
173
|
+
the column headings once above the rows, small attributes (an icon, a color) in `compact`
|
|
174
|
+
columns. Never a label in every row, never numbered labels, never one record split over lines.
|
|
175
|
+
- One value in several units is one row of fields under one heading, each unit in its field's
|
|
176
|
+
`Input.InlineAffix`; a base value with overrides is one `FieldList` with the base pinned first.
|
|
177
|
+
The shape of the data decides the layout: [ux.md](ux.md#2-the-shape-of-the-data-decides-the-layout).
|
|
165
178
|
- Fields 20 apart; related short fields side by side in an `auto-fit` grid with `align-items: start`
|
|
166
179
|
(`reserveSupportRow` on Input / Textarea keeps neighbours aligned); groups 32 apart, each a
|
|
167
180
|
`Fieldset label` (its `title-s` heading 16 above the fields, no frame); no dividers between groups.
|
|
@@ -315,6 +328,6 @@ export function RevenuePanel() {
|
|
|
315
328
|
}
|
|
316
329
|
```
|
|
317
330
|
|
|
318
|
-
For a plain KPI row use `Card.Root variant="stat-trend"` (Label · Value · Delta) in
|
|
319
|
-
see [patterns/dashboard.tsx](patterns/dashboard.tsx). When the trend over days matters, put a
|
|
331
|
+
For a plain KPI row use `Card.Root variant="stat-trend"` (Label · Value · Delta), each in a
|
|
332
|
+
`PageContent.Cell span={3}` of the page's `PageContent.Grid` — see [patterns/dashboard.tsx](patterns/dashboard.tsx). When the trend over days matters, put a
|
|
320
333
|
`Sparkline` in the `Card.Body` instead: it brings the value, the change and a line to scrub.
|
|
@@ -35,11 +35,13 @@ Things that belong together sit closer than things that do not.
|
|
|
35
35
|
| inline controls in a row (buttons, filters) | 8 | `--prime-space-2` |
|
|
36
36
|
| switch → switch in a settings list | 20 | `--prime-space-5` |
|
|
37
37
|
| group → group (fieldset, form section) | 32 | `--prime-space-8` |
|
|
38
|
-
|
|
|
39
|
-
|
|
|
38
|
+
| tile → tile, both axes (KPI cards, charts, panels) | 16 | `--prime-space-4` — `PageContent.Grid` already does it |
|
|
39
|
+
| block → block on a page (page header → body too) | 24 | `--prime-space-6` — `PageContent` already does it |
|
|
40
|
+
| section → section on a page (a new topic with an `h2`) | 40 | `PageContent.Section` already does it |
|
|
40
41
|
|
|
41
|
-
`PageContent.Body` already spaces its direct blocks
|
|
42
|
-
|
|
42
|
+
`PageContent.Body` already spaces its direct blocks 24 apart, a `PageContent.Section` 40 from its
|
|
43
|
+
neighbours, `PageContent.Grid` its tiles 16 on both axes, and `AppShell.Main` already carries the page
|
|
44
|
+
gutters — do not add your own outer padding.
|
|
43
45
|
|
|
44
46
|
## 3. Size axis
|
|
45
47
|
|
|
@@ -132,7 +134,7 @@ A chart color does one job, and the job picks the colors:
|
|
|
132
134
|
| `body-m` | 14/20 | default text |
|
|
133
135
|
| `body-l` | 16/24 | reading text |
|
|
134
136
|
| `title-s` | 14/20 600 | card title, group heading inside a card or form |
|
|
135
|
-
| `title-m` | 16/24 600 | page section heading
|
|
137
|
+
| `title-m` | 16/24 600 | page section heading (`PageContent.Section` draws it), modal title |
|
|
136
138
|
| `title-l` | 18/24 600 | large block title |
|
|
137
139
|
| `heading-s` | 20/28 600 | page sub-heading |
|
|
138
140
|
| `heading-m` | 24/32 600 | page title (`PageContent.Title`) |
|
|
@@ -163,7 +165,7 @@ level of air: a page with uniform 16px gaps everywhere reads as a list of unrela
|
|
|
163
165
|
- Flex children with text: `min-width: 0` + ellipsis or `overflow-wrap: anywhere`.
|
|
164
166
|
- Wide tables on phones: they scroll inside themselves with edge shadows; `stickyFirstColumn`, a column
|
|
165
167
|
chooser through `hiddenColumns`; never let the page scroll sideways.
|
|
166
|
-
- Card grids: `
|
|
168
|
+
- Card grids on a page: `PageContent.Grid` + `PageContent.Cell span` (folds by its own width); inside one block: `repeat(auto-fill, minmax(min(100%, …), 1fr))`.
|
|
167
169
|
- A page panel with sections, filter and search, view and the primary action is `PageToolbar`.
|
|
168
170
|
- Sticky parts come from the kit: `AppHeader` (with the `PageToolbar` in `AppHeader.Tools`), the
|
|
169
171
|
DataTable head and `stickyFirstColumn`, the Gantt header and first column — each sticks inside its
|
package/dist/skill/layouts.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Every screen sits in one frame: `AppShell.Root` with Nav / `AppHeader` / Main (the content panel is the page,
|
|
4
4
|
the nav rail one layer above it) → `PageContent` (header + body). `AppShell.Main` carries the page gutters
|
|
5
|
-
and `PageContent
|
|
6
|
-
margins**. Your CSS only lays out the inside of a block (grids, rows) with `gap` on `--prime-space-*`.
|
|
7
|
-
Main's top space under the header is part of the frame — 24, then
|
|
5
|
+
and `PageContent` spaces the page — blocks 24, a titled `PageContent.Section` 40, tiles in a
|
|
6
|
+
`PageContent.Grid` 16 on both axes — so your code adds **no outer padding and no margins**. Your CSS only lays out the inside of a block (grids, rows) with `gap` on `--prime-space-*`.
|
|
7
|
+
Main's top space under the header is part of the frame — 24, then 32 from 1024px: a page header starts
|
|
8
8
|
there; never cancel it with a negative margin or a padding override.
|
|
9
9
|
|
|
10
10
|
The page recipes are whole, working screens in [patterns/](patterns/) — start from the closest one
|
|
@@ -181,9 +181,9 @@ from what people do, not from how the backend is split. The full guide with the
|
|
|
181
181
|
|
|
182
182
|
## Page wrapper
|
|
183
183
|
|
|
184
|
-
- App pages with actions (dashboard, list, detail): `PageContent.
|
|
185
|
-
- Settings and long forms: `PageContent.Root maxWidth="wide"` with a
|
|
186
|
-
([patterns/settings-page.tsx](patterns/settings-page.tsx)), or `maxWidth="readable"` for a single
|
|
184
|
+
- App pages with actions (dashboard, list, detail): `PageContent.Page` — the full width of the panel.
|
|
185
|
+
- Settings and long forms: `PageContent.Root maxWidth="wide"` with a `PageContent.Section layout="aside"`
|
|
186
|
+
(heading column + panel) per section ([patterns/settings-page.tsx](patterns/settings-page.tsx)), or `maxWidth="readable"` for a single
|
|
187
187
|
narrow column of fields.
|
|
188
188
|
- Long reading text (terms, help): `PageContent.Root maxWidth="readable"`.
|
|
189
189
|
- A page as high as the panel (a log, a board, a chat): `AppShell.Root fillViewport` +
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
/* App shell: the frame (Sidebar / BottomNav / AppHeader) lays itself out; this file only places the
|
|
2
|
-
home page's blocks.
|
|
3
|
-
|
|
4
|
-
/* KPI row: as many ~14rem tiles as fit, one column on a phone. */
|
|
5
|
-
.kpis {
|
|
6
|
-
display: grid;
|
|
7
|
-
grid-template-columns: repeat(auto-fit, minmax(min(100%, calc(var(--prime-space-14) * 4)), 1fr));
|
|
8
|
-
gap: var(--prime-space-4);
|
|
9
|
-
}
|
|
2
|
+
home page's blocks. The rhythm comes from PageContent.Body, the tiles from PageContent.Grid. */
|
|
10
3
|
|
|
11
4
|
/* A list row: the text at the start, the meta (time, sum) at the end. */
|
|
12
5
|
.row {
|
|
@@ -213,7 +213,7 @@ export default function AppShellPattern() {
|
|
|
213
213
|
>
|
|
214
214
|
{/* Another section is another page: it cross-fades instead of flipping. */}
|
|
215
215
|
<Crossfade state={current}>
|
|
216
|
-
<PageContent.
|
|
216
|
+
<PageContent.Page aria-labelledby="page-title">
|
|
217
217
|
<PageContent.Header>
|
|
218
218
|
<PageContent.Title id="page-title">{section.label}</PageContent.Title>
|
|
219
219
|
<PageContent.Description>
|
|
@@ -225,15 +225,17 @@ export default function AppShellPattern() {
|
|
|
225
225
|
<PageContent.Body>
|
|
226
226
|
{current === "today" ? (
|
|
227
227
|
<>
|
|
228
|
-
<
|
|
228
|
+
<PageContent.Grid>
|
|
229
229
|
{KPIS.map((kpi) => (
|
|
230
|
-
<
|
|
231
|
-
<Card.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
230
|
+
<PageContent.Cell key={kpi.label} span={4}>
|
|
231
|
+
<Card.Root variant="stat-trend">
|
|
232
|
+
<Card.Label>{kpi.label}</Card.Label>
|
|
233
|
+
<Card.Value>{kpi.value}</Card.Value>
|
|
234
|
+
<Card.Delta tone={kpi.tone}>{kpi.delta}</Card.Delta>
|
|
235
|
+
</Card.Root>
|
|
236
|
+
</PageContent.Cell>
|
|
235
237
|
))}
|
|
236
|
-
</
|
|
238
|
+
</PageContent.Grid>
|
|
237
239
|
<Card.Root variant="list">
|
|
238
240
|
<Card.Header>
|
|
239
241
|
<Card.Title as="h2">Задачи на сегодня</Card.Title>
|
|
@@ -276,7 +278,7 @@ export default function AppShellPattern() {
|
|
|
276
278
|
</Card.Root>
|
|
277
279
|
)}
|
|
278
280
|
</PageContent.Body>
|
|
279
|
-
</PageContent.
|
|
281
|
+
</PageContent.Page>
|
|
280
282
|
</Crossfade>
|
|
281
283
|
</AppShell.Template>
|
|
282
284
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** A support messenger on `Chat`: dialogs beside the conversation (one pane at a time on a phone), history that loads above as you scroll up, dates and runs from data, every kind of message, search, pinned messages, replies and edits from the menu with a jump to the quoted message, read receipts and unread counts from what was seen, the typing signal both ways, and the Composer whose messages rise into the list while the client answers. */
|
|
1
|
+
/** A support messenger on `Chat`: dialogs with a search over them beside the conversation (one pane at a time on a phone), history that loads above as you scroll up, dates and runs from data, every kind of message, search, pinned messages, replies and edits from the menu with a jump to the quoted message, read receipts and unread counts from what was seen, the typing signal both ways, and the Composer whose messages rise into the list while the client answers. */
|
|
2
2
|
import {
|
|
3
3
|
Avatar,
|
|
4
4
|
Button,
|
|
@@ -550,6 +550,23 @@ export default function ChatPattern() {
|
|
|
550
550
|
};
|
|
551
551
|
}, []);
|
|
552
552
|
const dialog = DIALOGS.find((item) => item.id === dialogId) ?? DIALOGS[0];
|
|
553
|
+
/** The search over the dialogs: by name and by the last message a row shows. */
|
|
554
|
+
const [dialogQuery, setDialogQuery] = React.useState("");
|
|
555
|
+
// The open dialog shows its live last message; the others their stored one.
|
|
556
|
+
const lastOf = (item: Dialog) =>
|
|
557
|
+
item.id === dialogId ? messages[messages.length - 1] : undefined;
|
|
558
|
+
const previewOf = (item: Dialog) => {
|
|
559
|
+
const last = lastOf(item);
|
|
560
|
+
return last ? excerpt(last) : item.preview;
|
|
561
|
+
};
|
|
562
|
+
const needle = dialogQuery.trim().toLowerCase();
|
|
563
|
+
const foundDialogs = needle
|
|
564
|
+
? DIALOGS.filter(
|
|
565
|
+
(item) =>
|
|
566
|
+
item.name.toLowerCase().includes(needle) ||
|
|
567
|
+
previewOf(item).toLowerCase().includes(needle),
|
|
568
|
+
)
|
|
569
|
+
: DIALOGS;
|
|
553
570
|
/** The dialog whose history has arrived: opening another one loads it first. */
|
|
554
571
|
const [loadedId, setLoadedId] = React.useState<string | null>(null);
|
|
555
572
|
React.useEffect(() => {
|
|
@@ -765,14 +782,15 @@ export default function ChatPattern() {
|
|
|
765
782
|
<PageContent.List aria-label="Диалоги">
|
|
766
783
|
<Card.Root className={styles.paneCard}>
|
|
767
784
|
<ChatList.Root
|
|
768
|
-
items={
|
|
785
|
+
items={foundDialogs}
|
|
769
786
|
getKey={(item) => item.id}
|
|
770
787
|
value={dialogId}
|
|
771
788
|
onValueChange={setDialogId}
|
|
789
|
+
search={dialogQuery}
|
|
790
|
+
onSearchChange={setDialogQuery}
|
|
772
791
|
now={NOW}
|
|
773
792
|
renderItem={(item) => {
|
|
774
|
-
|
|
775
|
-
const last = item.id === dialogId ? messages[messages.length - 1] : undefined;
|
|
793
|
+
const last = lastOf(item);
|
|
776
794
|
return (
|
|
777
795
|
<ChatList.Item
|
|
778
796
|
time={last?.sentAt ?? item.at}
|
|
@@ -788,7 +806,7 @@ export default function ChatPattern() {
|
|
|
788
806
|
</ChatList.ItemIcon>
|
|
789
807
|
<ChatList.ItemText>{item.name}</ChatList.ItemText>
|
|
790
808
|
<ChatList.ItemDescription author={last?.from === "me" ? "Вы" : undefined}>
|
|
791
|
-
{
|
|
809
|
+
{previewOf(item)}
|
|
792
810
|
</ChatList.ItemDescription>
|
|
793
811
|
</ChatList.Item>
|
|
794
812
|
);
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
/* Dashboard:
|
|
2
|
-
|
|
3
|
-
/* KPI row: as many ~14rem tiles as fit, one column on a phone. */
|
|
4
|
-
.kpis {
|
|
5
|
-
display: grid;
|
|
6
|
-
grid-template-columns: repeat(auto-fit, minmax(min(100%, calc(var(--prime-space-14) * 4)), 1fr));
|
|
7
|
-
gap: var(--prime-space-4);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/* Panels: two columns from ~40rem of width; neighbours share one height. */
|
|
11
|
-
.panels {
|
|
12
|
-
display: grid;
|
|
13
|
-
grid-template-columns: repeat(auto-fit, minmax(min(100%, calc(var(--prime-space-20) * 4)), 1fr));
|
|
14
|
-
align-items: stretch;
|
|
15
|
-
gap: var(--prime-space-4);
|
|
16
|
-
}
|
|
1
|
+
/* Dashboard: the board of tiles is PageContent.Grid; here only the inside of the tiles. */
|
|
17
2
|
|
|
18
3
|
/* Bars inside a panel: 20 apart, like fields. */
|
|
19
4
|
.bars {
|
|
@@ -21,21 +6,6 @@
|
|
|
21
6
|
gap: var(--prime-space-5);
|
|
22
7
|
}
|
|
23
8
|
|
|
24
|
-
/* A section without a card: heading row 16 above the table. */
|
|
25
|
-
.recent {
|
|
26
|
-
display: grid;
|
|
27
|
-
gap: var(--prime-space-4);
|
|
28
|
-
min-width: 0;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.recentHeader {
|
|
32
|
-
display: flex;
|
|
33
|
-
flex-wrap: wrap;
|
|
34
|
-
align-items: baseline;
|
|
35
|
-
justify-content: space-between;
|
|
36
|
-
gap: var(--prime-space-2) var(--prime-space-4);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
9
|
.client {
|
|
40
10
|
display: inline-flex;
|
|
41
11
|
flex-wrap: wrap;
|