prime-ui-kit 0.18.0 → 0.20.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 +1 -1
- package/dist/chunks/{chunk-OAXJWF3L.js → chunk-3PY52JP3.js} +1764 -1764
- package/dist/chunks/{chunk-X3JT5CLR.js → chunk-4KONMO7Z.js} +1 -1
- package/dist/chunks/{chunk-MB4CXSLL.js → chunk-W6EGTNG6.js} +3772 -2888
- package/dist/color-picker.js +2 -2
- package/dist/components/data-table/ColumnsMenu.d.ts +39 -17
- package/dist/components/data-table/DataTable.d.ts +41 -12
- package/dist/components/data-table/DataTableColumnsMenu.d.ts +45 -0
- package/dist/components/data-table/Head.d.ts +18 -5
- package/dist/components/data-table/Row.d.ts +10 -4
- package/dist/components/data-table/StateRows.d.ts +5 -3
- package/dist/components/data-table/columnOrder.d.ts +14 -0
- package/dist/components/data-table/reorder.d.ts +15 -5
- package/dist/components/data-table/types.d.ts +5 -0
- package/dist/components/data-table/useFrozenColumns.d.ts +6 -2
- package/dist/components/datepicker/DatepickerPanel.d.ts +1 -1
- package/dist/components/dnd/Dnd.d.ts +9 -5
- package/dist/components/dnd/inPlaceRow.d.ts +7 -0
- package/dist/components/dnd/useDragSource.d.ts +2 -0
- package/dist/components/dnd/useSortableList.d.ts +5 -0
- package/dist/components/field-list/FieldList.d.ts +37 -15
- package/dist/components/gantt/Gantt.d.ts +1 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/kanban/Kanban.d.ts +4 -0
- package/dist/components/list/List.d.ts +9 -4
- package/dist/components/scroll-container/ScrollContainer.d.ts +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +4 -2
- package/dist/internal/ToolRow.d.ts +23 -0
- package/dist/internal/leaveGuard.d.ts +6 -0
- package/dist/internal/overlay/DialogGuard.d.ts +3 -2
- package/dist/internal/overlay/DialogParts.d.ts +14 -5
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/page/Page.d.ts +8 -14
- package/dist/skill/anti-slop.md +55 -1
- package/dist/skill/api-contract.md +2 -2
- package/dist/skill/cheatsheet.md +12 -4
- package/dist/skill/checklist.md +7 -0
- package/dist/skill/choosing.md +1 -0
- package/dist/skill/components.md +1 -1
- package/dist/skill/composition.md +196 -21
- package/dist/skill/layouts.md +1 -1
- package/dist/skill/reference/components/confirm-dialog/COMPONENT.md +1 -1
- package/dist/skill/reference/components/data-table/COMPONENT.md +110 -49
- package/dist/skill/reference/components/data-table/examples/actions-column.tsx +154 -0
- package/dist/skill/reference/components/data-table/examples/column-order.tsx +152 -0
- package/dist/skill/reference/components/data-table/examples/columns-visibility.tsx +18 -18
- package/dist/skill/reference/components/data-table/examples/controlled.tsx +32 -17
- package/dist/skill/reference/components/data-table/examples/examples.module.css +14 -36
- package/dist/skill/reference/components/data-table/examples/narrow.tsx +25 -19
- package/dist/skill/reference/components/data-table/examples/reorder.tsx +24 -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/selection.tsx +13 -18
- package/dist/skill/reference/components/data-table/examples/server.tsx +45 -31
- package/dist/skill/reference/components/data-table/examples/toolbar.tsx +22 -35
- package/dist/skill/reference/components/datepicker/COMPONENT.md +2 -2
- package/dist/skill/reference/components/dnd/COMPONENT.md +13 -7
- 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-columns.tsx +75 -0
- package/dist/skill/reference/components/dnd/examples/table.tsx +6 -6
- package/dist/skill/reference/components/drawer/COMPONENT.md +26 -10
- 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 +78 -47
- 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/narrow.tsx +108 -33
- package/dist/skill/reference/components/field-list/examples/reorder.tsx +1 -1
- package/dist/skill/reference/components/field-list/examples/row-menu.tsx +71 -0
- package/dist/skill/reference/components/field-list/examples/screen.module.css +1 -1
- package/dist/skill/reference/components/field-list/examples/screen.tsx +169 -162
- package/dist/skill/reference/components/field-list/examples/totals.tsx +9 -8
- package/dist/skill/reference/components/field-list/examples/variants.tsx +51 -0
- package/dist/skill/reference/components/gantt/COMPONENT.md +4 -4
- package/dist/skill/reference/components/kanban/COMPONENT.md +6 -2
- package/dist/skill/reference/components/kanban/examples/states.tsx +7 -5
- package/dist/skill/reference/components/list/COMPONENT.md +21 -11
- package/dist/skill/reference/components/list/examples/reorder.tsx +42 -7
- package/dist/skill/reference/components/modal/COMPONENT.md +23 -10
- package/dist/skill/reference/components/scroll-container/COMPONENT.md +1 -1
- package/dist/skill/reference/components/segmented-control/COMPONENT.md +3 -2
- package/dist/skill/reference/components/sheet/COMPONENT.md +18 -7
- package/dist/skill/reference/components/smart-filter/COMPONENT.md +2 -2
- package/dist/skill/reference/layout/page/COMPONENT.md +13 -17
- 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/responsive.md +14 -10
- package/dist/tokens/semantic.d.ts +2 -2
- package/dist/tokens/themes/dark.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/confirm-dialog/COMPONENT.md +1 -1
- package/src/components/data-table/COMPONENT.md +110 -49
- package/src/components/data-table/examples/actions-column.tsx +154 -0
- package/src/components/data-table/examples/column-order.tsx +152 -0
- package/src/components/data-table/examples/columns-visibility.tsx +18 -18
- package/src/components/data-table/examples/controlled.tsx +32 -17
- package/src/components/data-table/examples/examples.module.css +14 -36
- package/src/components/data-table/examples/narrow.tsx +25 -19
- package/src/components/data-table/examples/reorder.tsx +24 -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/selection.tsx +13 -18
- package/src/components/data-table/examples/server.tsx +45 -31
- package/src/components/data-table/examples/toolbar.tsx +22 -35
- package/src/components/datepicker/COMPONENT.md +2 -2
- package/src/components/dnd/COMPONENT.md +13 -7
- 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-columns.tsx +75 -0
- package/src/components/dnd/examples/table.tsx +6 -6
- package/src/components/drawer/COMPONENT.md +26 -10
- 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 +78 -47
- 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/narrow.tsx +108 -33
- package/src/components/field-list/examples/reorder.tsx +1 -1
- package/src/components/field-list/examples/row-menu.tsx +71 -0
- package/src/components/field-list/examples/screen.module.css +1 -1
- package/src/components/field-list/examples/screen.tsx +169 -162
- package/src/components/field-list/examples/totals.tsx +9 -8
- package/src/components/field-list/examples/variants.tsx +51 -0
- package/src/components/gantt/COMPONENT.md +4 -4
- package/src/components/kanban/COMPONENT.md +6 -2
- package/src/components/kanban/examples/states.tsx +7 -5
- package/src/components/list/COMPONENT.md +21 -11
- package/src/components/list/examples/reorder.tsx +42 -7
- package/src/components/modal/COMPONENT.md +23 -10
- package/src/components/scroll-container/COMPONENT.md +1 -1
- package/src/components/segmented-control/COMPONENT.md +3 -2
- package/src/components/sheet/COMPONENT.md +18 -7
- package/src/components/smart-filter/COMPONENT.md +2 -2
- package/src/layout/page/COMPONENT.md +13 -17
- 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/styles/theme-dark.css +1 -1
- package/src/styles/theme-light.css +1 -1
- package/dist/components/data-table/context.d.ts +0 -14
|
@@ -15,9 +15,9 @@ playground page. What each screen teaches is in [Screen rules](#screen-rules) at
|
|
|
15
15
|
|---|---|---|---|
|
|
16
16
|
| [List page](#list-page) | data-table | [screen.tsx](reference/components/data-table/examples/screen.tsx) | the title, a line of figures, SmartFilter search and filters, export and one primary action in the page's top bar; statuses, row menu, pages, filtered empty state |
|
|
17
17
|
| [Detail page](#detail-page) | description-list | [screen.tsx](reference/components/description-list/examples/screen.tsx) | a nested record: the path ending in the title, status and due date and the record's actions by weight in the top bar; main column + side column of Cards, Timeline |
|
|
18
|
-
| [Settings page](#settings-page) | page | [screen.tsx](reference/layout/page/examples/screen.tsx) | the path in a collapsing top bar, the large title in the flow,
|
|
18
|
+
| [Settings page](#settings-page) | page | [screen.tsx](reference/layout/page/examples/screen.tsx) | the path in a collapsing top bar, the large title in the flow, a card with its own header per section, form with its own save, Switches that apply at once, danger zone with a confirm |
|
|
19
19
|
| [Form in a Drawer](#form-in-a-drawer) | drawer | [screen.tsx](reference/components/drawer/examples/screen.tsx) | the title, counts, search and the Drawer trigger in the top bar; Drawer form: groups, errors on submit, focus on the first invalid field, loading, toast |
|
|
20
|
-
| [Record editor](#record-editor) | field-list | [screen.tsx](reference/components/field-list/examples/screen.tsx) | a document page (a long form): the title and a line of description in `Page.Heading`, «Сохранить тариф» in `Page.Actions`, both in a `Page.Header` in the flow; a plan card edited in
|
|
20
|
+
| [Record editor](#record-editor) | field-list | [screen.tsx](reference/components/field-list/examples/screen.tsx) | a document page (a long form): the title and a line of description in `Page.Heading`, «Сохранить тариф» in `Page.Actions`, both in a `Page.Header` in the flow; a plan card edited in card sections: name and description, specs in a `FieldList` (icon-only `Select` and `ColorPresets` in compact columns, pinned rows, reorder), 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 |
|
|
21
21
|
| [Dashboard](#dashboard) | card | [screen.tsx](reference/components/card/examples/screen.tsx) | the large title + a line of data in the flow, period switch and report in a collapsing `Page.TopBar`; 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 |
|
|
22
22
|
| [Screen states](#screen-states) | crossfade | [screen.tsx](reference/components/crossfade/examples/screen.tsx) | title + the statement date in `Page.TopBar`, page error in a Banner with retry, region loading with a Skeleton, Crossfade between states, table error in place, first-run EmptyPage |
|
|
23
23
|
| [Messenger](#messenger) | chat | [screen.tsx](reference/components/chat/examples/screen.tsx) | title + the unread count in `Page.TopBar`; a full-height Page.Split of a `ChatList` with its search (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.Messages` from `items` + `renderMessage` (dates, runs and the unread band from the data, history loaded above with `hasOlder` / `onLoadOlder`, a first load in skeletons), text, a photo, a file and sent voice, reply and edit from the menu through `Composer.Context messageId` (`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`, the Composer with attachments in `Chat.Footer` |
|
|
@@ -66,7 +66,7 @@ AppShell.Root once per app (layouts.md#app-frame-once-per-a
|
|
|
66
66
|
│ │ ├─ Breadcrumb.Root only on a nested record: the levels above this page
|
|
67
67
|
│ │ ├─ Page.Title the one h1, the path's last level
|
|
68
68
|
│ │ └─ Page.Description a line of data
|
|
69
|
-
│ ├─ Page.Tools sections,
|
|
69
|
+
│ ├─ Page.Tools what acts on the whole page — sections, a period, view; a one-list page's search
|
|
70
70
|
│ ├─ Page.Actions every button of the page, each with an icon; the primary last
|
|
71
71
|
│ └─ Page.Chips SmartFilter.Chips: a line under everything, gone while empty
|
|
72
72
|
└─ Page.Body blocks 24 apart, the first 24 under the bar's controls
|
|
@@ -98,9 +98,11 @@ order is Heading, Tools, Actions, Chips whatever the JSX order.
|
|
|
98
98
|
then `Page.Title` with its name, on one line (the heading draws the chevron between them), and
|
|
99
99
|
`Page.Description` under the whole line. The title is never a row of its own under the path, and
|
|
100
100
|
the path never repeats the title as its last item.
|
|
101
|
-
- **`Page.Tools`** — the page
|
|
102
|
-
(a `SegmentedControl` with counts),
|
|
103
|
-
|
|
101
|
+
- **`Page.Tools`** — what acts on the whole page, as kit controls in the order a person reads them:
|
|
102
|
+
sections (a `SegmentedControl` with counts), the view, a period that drives several blocks; the
|
|
103
|
+
search and filter (`SmartFilter.Toolbar`) only when the page is one list
|
|
104
|
+
([where the filters go](#where-the-filters-go)). Named with `aria-label`; no buttons and no
|
|
105
|
+
wrappers inside.
|
|
104
106
|
- **`Page.Actions`** — the one place for the page's buttons: secondary ones (`soft` + `neutral`)
|
|
105
107
|
first, the primary (`solid`) last. Every Button carries a `Button.Icon` beside its label: the bar
|
|
106
108
|
shows it as the icon alone when the line runs short.
|
|
@@ -234,6 +236,39 @@ Hierarchy comes from space and a handful of type roles, not from size jumps, col
|
|
|
234
236
|
- **Card when** the block stands alone and has its own title: a KPI tile, a settings panel, facts in a
|
|
235
237
|
side column, a chart. **No card when** the block is the only thing in its region: a section heading
|
|
236
238
|
and the content are enough.
|
|
239
|
+
- **A titled block stands in the flow, its heading inside it.** A panel with a title and a line of
|
|
240
|
+
description (a settings section, a group of a long form) is a `Card.Root` whose `Card.Header`
|
|
241
|
+
holds `Card.Title as="h2"` and `Card.Description`; the cards stack 24 apart. Never a heading column
|
|
242
|
+
beside its panel: it takes a third of the width from the fields and breaks the reading line.
|
|
243
|
+
|
|
244
|
+
```tsx partial
|
|
245
|
+
// bad — the heading in a column on the left, the panel on the right
|
|
246
|
+
<div className={styles.headingColumnAndPanel}>
|
|
247
|
+
<div>
|
|
248
|
+
<Typography as="h2" variant="title-m">Компания</Typography>
|
|
249
|
+
<Typography tone="secondary">Попадают в счета и акты.</Typography>
|
|
250
|
+
</div>
|
|
251
|
+
<Card.Root variant="panel">…</Card.Root>
|
|
252
|
+
</div>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```tsx good
|
|
256
|
+
// good — the card carries its own heading, stacked in the flow
|
|
257
|
+
<Card.Root variant="panel">
|
|
258
|
+
<Card.Header>
|
|
259
|
+
<Card.Title as="h2">Компания</Card.Title>
|
|
260
|
+
<Card.Description>Попадают в счета и акты.</Card.Description>
|
|
261
|
+
</Card.Header>
|
|
262
|
+
<Card.Body>
|
|
263
|
+
<Input.Root label="Название">
|
|
264
|
+
<Input.Wrapper>
|
|
265
|
+
<Input.Field name="name" />
|
|
266
|
+
</Input.Wrapper>
|
|
267
|
+
</Input.Root>
|
|
268
|
+
</Card.Body>
|
|
269
|
+
</Card.Root>
|
|
270
|
+
```
|
|
271
|
+
|
|
237
272
|
- `DataTable` is already a filled block — never inside a Card; a card in a card only as
|
|
238
273
|
[the next layer, sparingly](foundations.md#card-in-a-card). No borders around blocks; `Divider`
|
|
239
274
|
only inside a block (list rows, a form and its footer).
|
|
@@ -257,6 +292,9 @@ Hierarchy comes from space and a handful of type roles, not from size jumps, col
|
|
|
257
292
|
table cell.
|
|
258
293
|
- Placement: every button of the page in one `Page.Actions` — of `Page.TopBar` on a work screen, of the flow `Page.Header` on a document page — the primary last; card actions in `Card.Footer`; dialog actions in
|
|
259
294
|
`Modal.Footer` / `Drawer.Footer`; row actions in the last column; bulk actions in the table toolbar.
|
|
295
|
+
- A dialog footer is the dialog's answer: Cancel and the primary. Deleting the record the dialog
|
|
296
|
+
edits is not a third footer button: it is the last `tone="danger"` item of a ⋯ `Dropdown` in
|
|
297
|
+
`Modal.Actions` / `Drawer.Actions`, confirmed by a `ConfirmDialog`.
|
|
260
298
|
- An empty state's action replaces the header's primary, it does not duplicate it.
|
|
261
299
|
- Destructive: `tone="danger"`; a standalone trigger is `outline`, in a menu it is the last item, the
|
|
262
300
|
confirm is a `ConfirmDialog` (its `onConfirm` returns the request's Promise).
|
|
@@ -268,13 +306,24 @@ Hierarchy comes from space and a handful of type roles, not from size jumps, col
|
|
|
268
306
|
- Labels above every field (`label`), placeholder is an example value, help in `hint`, problems in
|
|
269
307
|
`error` — after blur or on submit. Mark the minority (`required` or `optional`).
|
|
270
308
|
- Records of one shape that a person adds, edits and removes are a `FieldList`: one row per record,
|
|
271
|
-
the column headings once above the rows, small attributes (an icon,
|
|
309
|
+
the column headings once above the rows, small attributes (an icon, then its color) in `compact`
|
|
272
310
|
columns. Never a label in every row, never numbered labels, never one record split over lines.
|
|
311
|
+
FieldList draws every row's actions itself — the remove «×» and the handle after it, a pinned
|
|
312
|
+
row's remove off with its reason (`labels.lockedReason`) — so never add your own.
|
|
313
|
+
- The row's actions (`rowActions`): `"buttons"` (default) for short lists edited often on a desktop,
|
|
314
|
+
where moving rows is a main task — remove and handle in sight on every row; `"menu"` — one «⋯» with
|
|
315
|
+
«Выше», «Ниже», «Удалить» — for dense rows (three or more columns), touch-heavy screens, or a row
|
|
316
|
+
that would carry more than remove and move.
|
|
317
|
+
- The look (`variant`): `"plain"` (default) when the list is the content of its card or section;
|
|
318
|
+
`"surface"` when the list stands among other fields of a form or card, so its rows read as one
|
|
319
|
+
block apart from the fields around it.
|
|
273
320
|
- One value in several units is one row of fields under one heading, each unit in its field's
|
|
274
321
|
`Input.InlineAffix`; a base value with overrides is one `FieldList` with the base pinned first.
|
|
275
322
|
The shape of the data decides the layout: [ux.md](ux.md#2-the-shape-of-the-data-decides-the-layout).
|
|
276
323
|
- Rows that add up (shares of a bonus, hours, amounts) carry their sums under them in the same list:
|
|
277
|
-
`FieldList renderTotals`, a short number in a `width: "xs"` column.
|
|
324
|
+
`FieldList renderTotals`, a short number in a `width: "xs"` column. A number with its unit
|
|
325
|
+
(`inputMode="decimal"` and an end `Input.InlineAffix`) ends at the field's end, and its column's
|
|
326
|
+
total follows it there by itself. Return the save's promise from
|
|
278
327
|
its `onValueChange` when each edit saves at once.
|
|
279
328
|
- Fields 20 apart; related short fields side by side in an
|
|
280
329
|
[intrinsic grid](responsive.md#intrinsic-grids) with `align-items: start`
|
|
@@ -296,13 +345,130 @@ Hierarchy comes from space and a handful of type roles, not from size jumps, col
|
|
|
296
345
|
|
|
297
346
|
### Where the filters go
|
|
298
347
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
348
|
+
A filter stands next to what it filters.
|
|
349
|
+
|
|
350
|
+
- `Page.TopBar` holds only what acts on the whole page: the heading with its line of data, a period
|
|
351
|
+
or a date range that drives several blocks (the KPI cards and the table), the page's sections or
|
|
352
|
+
view, the primary action.
|
|
353
|
+
- A page that holds more than its table — KPI cards, a chart, another block: the table's own
|
|
354
|
+
controls (search and filter, the chips, the column chooser, a table / cards view, bulk actions)
|
|
355
|
+
stand in the table's tools row. `SmartFilter.Root` wraps the `DataTable`; `SmartFilter.Toolbar`
|
|
356
|
+
and `DataTableColumnsMenu` go in its `toolbar`, `SmartFilter.Chips` in its `toolbarChips`
|
|
357
|
+
([server.tsx](reference/components/data-table/examples/server.tsx)).
|
|
358
|
+
- A page that is one list — the table, the board or the schedule is the whole page: «next to the
|
|
359
|
+
list» and «on the page» are one place, so its search and filter stay in the bar. `SmartFilter.Root`
|
|
360
|
+
wraps `Page.Root`, `SmartFilter.Toolbar` in `Page.Tools`, `SmartFilter.Chips` in `Page.Chips`
|
|
361
|
+
([the list page](#list-page)). A search alone is the same SmartFilter with `fields={[]}`.
|
|
362
|
+
- What picks the place: does the control change everything under the bar? A period that recomputes
|
|
363
|
+
the cards and the table does — the bar. A search that narrows only the rows does not — the
|
|
364
|
+
table's row.
|
|
365
|
+
- Both rows are one tool row of the kit: written in reading order, a search takes the free room,
|
|
366
|
+
switchers and buttons keep their width at the end, and a short row wraps by itself — pass the
|
|
367
|
+
controls as they are, never inside a flex wrapper of your own.
|
|
368
|
+
|
|
369
|
+
```tsx partial
|
|
370
|
+
// bad — the table's search, filter, chips and chooser in the bar, above KPI cards they do not filter
|
|
371
|
+
<SmartFilter.Root fields={FIELDS}>
|
|
372
|
+
<Page.Root>
|
|
373
|
+
<Page.TopBar>
|
|
374
|
+
<Page.Heading>
|
|
375
|
+
<Page.Title>Модели</Page.Title>
|
|
376
|
+
</Page.Heading>
|
|
377
|
+
<Page.Tools aria-label="Модели">
|
|
378
|
+
<SmartFilter.Toolbar />
|
|
379
|
+
<DataTableColumnsMenu columns={COLUMNS} hiddenColumns={hidden} onHiddenColumnsChange={setHidden} />
|
|
380
|
+
</Page.Tools>
|
|
381
|
+
<Page.Chips>
|
|
382
|
+
<SmartFilter.Chips />
|
|
383
|
+
</Page.Chips>
|
|
384
|
+
</Page.TopBar>
|
|
385
|
+
<Page.Body>
|
|
386
|
+
<Page.Grid>{/* four KPI cards */}</Page.Grid>
|
|
387
|
+
<DataTable columns={COLUMNS} rows={models} hiddenColumns={hidden} />
|
|
388
|
+
</Page.Body>
|
|
389
|
+
</Page.Root>
|
|
390
|
+
</SmartFilter.Root>
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
```tsx good
|
|
394
|
+
// good — the bar keeps the title, the period that drives the cards and the table, the primary
|
|
395
|
+
// action; the table's search, filter, chips and chooser stand in the table's own tools row
|
|
396
|
+
// declare: const FIELDS: Parameters<typeof SmartFilter.Root>[0]["fields"]
|
|
397
|
+
// declare: const models: { id: string; name: string; rides: number }[]
|
|
398
|
+
// declare: const kpis: { label: string; value: string }[]
|
|
399
|
+
// declare: const hidden: string[]
|
|
400
|
+
// declare: const setHidden: (hidden: string[]) => void
|
|
401
|
+
<Page.Root>
|
|
402
|
+
<Page.TopBar>
|
|
403
|
+
<Page.Heading>
|
|
404
|
+
<Page.Title>Модели</Page.Title>
|
|
405
|
+
</Page.Heading>
|
|
406
|
+
<Page.Tools aria-label="Модели">
|
|
407
|
+
<Datepicker.Root mode="range" aria-label="Период" />
|
|
408
|
+
</Page.Tools>
|
|
409
|
+
<Page.Actions>
|
|
410
|
+
<Button.Root>
|
|
411
|
+
<Button.Icon>
|
|
412
|
+
<Icon name="action.add" />
|
|
413
|
+
</Button.Icon>
|
|
414
|
+
Добавить модель
|
|
415
|
+
</Button.Root>
|
|
416
|
+
</Page.Actions>
|
|
417
|
+
</Page.TopBar>
|
|
418
|
+
<Page.Body>
|
|
419
|
+
<Page.Grid>
|
|
420
|
+
{kpis.map((kpi) => (
|
|
421
|
+
<Page.Cell key={kpi.label} span={3}>
|
|
422
|
+
<Card.Root variant="stat-trend">
|
|
423
|
+
<Card.Label>{kpi.label}</Card.Label>
|
|
424
|
+
<Card.Value>{kpi.value}</Card.Value>
|
|
425
|
+
</Card.Root>
|
|
426
|
+
</Page.Cell>
|
|
427
|
+
))}
|
|
428
|
+
</Page.Grid>
|
|
429
|
+
<SmartFilter.Root fields={FIELDS}>
|
|
430
|
+
<DataTable
|
|
431
|
+
columns={[
|
|
432
|
+
{ id: "name", header: "Модель", accessor: "name", hideable: false },
|
|
433
|
+
{ id: "rides", header: "Поездки", accessor: "rides", numeric: true },
|
|
434
|
+
]}
|
|
435
|
+
rows={models}
|
|
436
|
+
getRowKey={(row) => row.id}
|
|
437
|
+
hiddenColumns={hidden}
|
|
438
|
+
toolbar={
|
|
439
|
+
<>
|
|
440
|
+
<SmartFilter.Toolbar />
|
|
441
|
+
<DataTableColumnsMenu
|
|
442
|
+
columns={[
|
|
443
|
+
{ id: "name", header: "Модель", accessor: "name", hideable: false },
|
|
444
|
+
{ id: "rides", header: "Поездки", accessor: "rides", numeric: true },
|
|
445
|
+
]}
|
|
446
|
+
hiddenColumns={hidden}
|
|
447
|
+
onHiddenColumnsChange={setHidden}
|
|
448
|
+
/>
|
|
449
|
+
</>
|
|
450
|
+
}
|
|
451
|
+
toolbarChips={<SmartFilter.Chips />}
|
|
452
|
+
/>
|
|
453
|
+
</SmartFilter.Root>
|
|
454
|
+
</Page.Body>
|
|
455
|
+
</Page.Root>
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Controls of a table
|
|
459
|
+
|
|
460
|
+
- The column chooser has one place per table. Over the row actions: `columnsMenu` puts the icon
|
|
461
|
+
trigger in the head cell of the row-actions column (the last column, `hideHeader`), and it sticks
|
|
462
|
+
with `stickyLastColumn`. A table without row actions gets no chooser cell in its header: the
|
|
463
|
+
chooser is `<DataTableColumnsMenu>` beside the table's search ([where the filters go](#where-the-filters-go)),
|
|
464
|
+
bound to the same `hiddenColumns` / `columnOrder` state the table reads. Give the key column and
|
|
465
|
+
the actions column `hideable: false`.
|
|
466
|
+
- The DataTable `toolbar` holds two or more controls of the table itself — its search and filter,
|
|
467
|
+
the chooser, a view, a bulk bar of the selected rows — as they are, in reading order; its chips go
|
|
468
|
+
to `toolbarChips`.
|
|
469
|
+
- One control never gets a row of its own. A lone «Экспорт» or «Создать» is the page's
|
|
470
|
+
(`Page.Actions`); a lone chooser joins the table's search or the page's tools; a count or a total
|
|
471
|
+
of the list is data in `Page.Description`.
|
|
306
472
|
|
|
307
473
|
### Tables
|
|
308
474
|
|
|
@@ -321,6 +487,15 @@ Hierarchy comes from space and a handful of type roles, not from size jumps, col
|
|
|
321
487
|
- Phones: the table scrolls inside itself, `stickyFirstColumn` keeps the key column; never let the page
|
|
322
488
|
scroll sideways. Short lists of objects can be `Card variant="list"` instead of a table.
|
|
323
489
|
|
|
490
|
+
### List rows
|
|
491
|
+
|
|
492
|
+
- A row's own controls stand beside its element in `List.ItemActions`, one tier below the row.
|
|
493
|
+
- A plain settings row reads label first and ends with its toggle (the platform convention).
|
|
494
|
+
- A row people reorder leads with its toggle — `<List.ItemActions side="start">`, or the Switch as the
|
|
495
|
+
first child of a `Dnd.SortableItem` — so the on / off state reads first, like a checkbox, and the
|
|
496
|
+
grip keeps the end edge ([reorder.tsx](reference/components/list/examples/reorder.tsx)).
|
|
497
|
+
- Never a toggle between the label and the grip: «Почта … [Switch] [grip]» reads as two handles.
|
|
498
|
+
|
|
324
499
|
## 9. Feedback
|
|
325
500
|
|
|
326
501
|
| Situation | Component |
|
|
@@ -455,10 +630,10 @@ What each screen in [the index](#screens) demonstrates — keep these when you s
|
|
|
455
630
|
[data-table/examples/screen.tsx](reference/components/data-table/examples/screen.tsx)
|
|
456
631
|
|
|
457
632
|
- A work screen titles itself in its `Page.TopBar`: `Page.Title` is the only heading, its `Page.Description` is one line of figures («10 счетов · просрочено 2 на …»), not a sentence about the page; there is no second header in the flow.
|
|
458
|
-
- `SmartFilter.Root` wraps the page
|
|
633
|
+
- The table is the whole page, so its filters are the page's: `SmartFilter.Root` wraps the page, `SmartFilter.Toolbar` in `Page.Tools`, `SmartFilter.Chips` in `Page.Chips`; export (`soft` + `neutral`, an icon beside its label) and the one primary action (`solid`, last) in `Page.Actions`. The table itself has no `toolbar`; add a KPI row above it and the search, filter and chips move into the table's `toolbar` / `toolbarChips`.
|
|
459
634
|
- A status is a `Badge` with one `tone` per meaning across the product (`STATUS` is one map); the text carries the meaning, the tone repeats it.
|
|
460
635
|
- Numbers and dates are `numeric` columns with `headerAlign="end"` (tabular figures, values and heading on the end edge). On a phone the table scrolls inside itself and `stickyFirstColumn` keeps the number in place — the page never scrolls sideways.
|
|
461
|
-
- Row actions are a `Dropdown` behind an `action.more` icon of size `s
|
|
636
|
+
- Row actions are a `Dropdown` behind an `action.more` icon of size `s` in the last column (`hideHeader`), pinned to the end edge with `stickyLastColumn` so they stay in reach while the columns scroll: controls in cells are one tier below the table. Delete is `tone="danger"` at the end of the menu.
|
|
462
637
|
- Empty after a filter is the table's `empty` with `EmptyPage size="s"` and a «Сбросить фильтры» action; the header and the filters stay in place.
|
|
463
638
|
|
|
464
639
|
### Detail page
|
|
@@ -478,7 +653,7 @@ What each screen in [the index](#screens) demonstrates — keep these when you s
|
|
|
478
653
|
[page/examples/screen.tsx](reference/layout/page/examples/screen.tsx)
|
|
479
654
|
|
|
480
655
|
- A document page: `Page.Root maxWidth="wide"`, the large `Page.Title` with a sentence of `Page.Description` in the `Page.Heading` of a `Page.Header` in the flow, and a `Page.TopBar collapse` whose `Page.Heading` holds only the path and repeats the title once it scrolls away.
|
|
481
|
-
- A section is `
|
|
656
|
+
- A section is a `Card variant="panel"` with its own `Card.Header` (`Card.Title as="h2"` + a line of `Card.Description`), the cards stacked in the flow ([a titled block](#4-surfaces)).
|
|
482
657
|
- The form wraps the whole `Card.Root` in `<form noValidate>`; the buttons are in `Card.Footer`: «Отменить изменения» `ghost` + `neutral`, then the primary «Сохранить». While the request runs — `loading` on the button, and one `Fieldset disabled` turns the fields off; the form stays on screen, so success is the button text «Сохранено» for two seconds, without a notification.
|
|
483
658
|
- Fields are 20 px apart; two short related fields side by side while there is width, with `align-items: start` and `reserveSupportRow`, so an error does not shift the neighbour.
|
|
484
659
|
- An error comes through the field's `error` after submit, not while typing; a format hint goes in `hint`.
|
|
@@ -502,10 +677,10 @@ What each screen in [the index](#screens) demonstrates — keep these when you s
|
|
|
502
677
|
|
|
503
678
|
[field-list/examples/screen.tsx](reference/components/field-list/examples/screen.tsx)
|
|
504
679
|
|
|
505
|
-
- A record editor is a document page — a long form read top to bottom in
|
|
680
|
+
- A record editor is a document page — a long form read top to bottom in card sections, like Settings — so its title stands in the flow: `Page.Header` with the title and one line of description in `Page.Heading` and the primary action in `Page.Actions`; no second title anywhere.
|
|
506
681
|
- The shape of the data decides the layout: records a person adds and removes are a `FieldList`, one row per record; the column headings stand once above the rows, and the fields in the cells take them as their labels.
|
|
507
682
|
- No label in every row and no numbered one («Преимущество 1»): a one-column list has no headings at all — its section names it ([reorder.tsx](reference/components/field-list/examples/reorder.tsx)).
|
|
508
|
-
- Small attributes of a record go in compact columns of its row: the icon is a `Select` with only `Select.TriggerIcon`, the color `ColorPresets`; the first specs are pinned (`getLocked`) — the lock
|
|
683
|
+
- Small attributes of a record go in compact columns of its row, the color right after the icon it paints: the icon is a `Select` with only `Select.TriggerIcon`, the color `ColorPresets`; the first specs are pinned (`getLocked`) — the lock and the remove that is off say so themselves, without explanatory text.
|
|
509
684
|
- The base price and its changes are one list: the base row is pinned first and shows when it applies, the changes are rows with a month and a price; the ruble sign is in the field (`Input.InlineAffix`), not in every heading.
|
|
510
685
|
- One value in three currencies is three fields in one row under one group heading, each with its own name for screen readers and the currency inside the field.
|
|
511
686
|
- The lists live in the page state, the name and description fields in the `<form>` of the first section; the primary «Сохранить тариф» in `Page.Actions` is tied to it through `form={formId}` and saves everything. While the request runs the fields and the lists are off (`Fieldset disabled`, `disabled`), afterwards «Сохранено» on the button.
|
|
@@ -566,7 +741,7 @@ What each screen in [the index](#screens) demonstrates — keep these when you s
|
|
|
566
741
|
- The placement of deals is `value` as `{ [stage]: id[] }`; `onValueChange` returns the request's promise and applies `value` only after the answer; a server refusal returns the card to its place.
|
|
567
742
|
- Stage totals are in `renderColumnMeta` («2 сделки · 3 575 000 ₽»), the stage color is the column's `color`.
|
|
568
743
|
- A deal opens through `onItemOpen` of `Kanban.Root` (click, Enter, Space; never after a drag) in a `Drawer` with a `DescriptionList`; no `onClick` / `onKeyDown` of your own on the card; a deal with its own page — `getItemHref`. The stage change is available in the panel too — dragging is not the only way; `Drawer.Guard busy` holds the panel while saving.
|
|
569
|
-
- The first load is `loading` on `Kanban` and `DataTable`: skeletons in the geometry of cards and rows.
|
|
744
|
+
- The first load is `loading` on `Kanban` and `DataTable`: skeletons in the geometry of cards and rows. Stages that come from the server too: `loading` with empty `columns` draws `loadingColumns` placeholder columns — never a board skeleton of your own beside the `Kanban`.
|
|
570
745
|
|
|
571
746
|
### App frame
|
|
572
747
|
|
package/dist/skill/layouts.md
CHANGED
|
@@ -155,7 +155,7 @@ shows only icons); none on the children of a sub-list.
|
|
|
155
155
|
page's title and tools are in its `Page.TopBar`, its blocks go into `Page.Body`
|
|
156
156
|
([the page skeleton](composition.md#1-page-skeleton)).
|
|
157
157
|
- Document pages — settings and long forms: `Page.Root maxWidth="wide"` with `Page.Header` in the
|
|
158
|
-
flow (the page's h1) and a `
|
|
158
|
+
flow (the page's h1) and a `Card.Root` with its own `Card.Header` per section
|
|
159
159
|
([the settings page](reference/layout/page/examples/screen.tsx)), or `maxWidth="readable"` for a single
|
|
160
160
|
narrow column of fields.
|
|
161
161
|
- Long reading text (terms, help): `Page.Root maxWidth="readable"` with `Page.Header` in the flow.
|
|
@@ -134,5 +134,5 @@ The dialog is a Modal `s` with the floating surface, a header and a footer; ther
|
|
|
134
134
|
- `tone="accent"` for a delete → destructive is always `danger`.
|
|
135
135
|
|
|
136
136
|
## Related
|
|
137
|
-
- **Built from:** the dialog Modal stands on (`internal/overlay/ModalDialog` at `s`, the header, body and footer of `internal/overlay/DialogParts`), [ScrollContainer](../scroll-container/COMPONENT.md) (the body), [Avatar](../avatar/COMPONENT.md) and [Thumbnail](../thumbnail/COMPONENT.md) (pictures the shared icon slot recognises), [Button](../button/COMPONENT.md) (Cancel, the confirm with `loading`), [Banner](../banner/COMPONENT.md) (the failure message), `Icon` (`status.danger` in the Banner)
|
|
137
|
+
- **Built from:** the dialog Modal stands on (`internal/overlay/ModalDialog` at `s`, the header, body and footer of `internal/overlay/DialogParts`), [ScrollContainer](../scroll-container/COMPONENT.md) (the body), [Avatar](../avatar/COMPONENT.md) and [Thumbnail](../thumbnail/COMPONENT.md) (pictures the shared icon slot recognises), [Skeleton](../skeleton/COMPONENT.md) (the shared description's loading line), [Button](../button/COMPONENT.md) (Cancel, the confirm with `loading`), [Banner](../banner/COMPONENT.md) (the failure message), `Icon` (`status.danger` in the Banner)
|
|
138
138
|
- **See also:** [Modal](../modal/COMPONENT.md), [Drawer](../drawer/COMPONENT.md), [Notification](../notification/COMPONENT.md)
|