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.
Files changed (145) hide show
  1. package/README.md +1 -1
  2. package/dist/chunks/{chunk-OAXJWF3L.js → chunk-3PY52JP3.js} +1764 -1764
  3. package/dist/chunks/{chunk-X3JT5CLR.js → chunk-4KONMO7Z.js} +1 -1
  4. package/dist/chunks/{chunk-MB4CXSLL.js → chunk-W6EGTNG6.js} +3772 -2888
  5. package/dist/color-picker.js +2 -2
  6. package/dist/components/data-table/ColumnsMenu.d.ts +39 -17
  7. package/dist/components/data-table/DataTable.d.ts +41 -12
  8. package/dist/components/data-table/DataTableColumnsMenu.d.ts +45 -0
  9. package/dist/components/data-table/Head.d.ts +18 -5
  10. package/dist/components/data-table/Row.d.ts +10 -4
  11. package/dist/components/data-table/StateRows.d.ts +5 -3
  12. package/dist/components/data-table/columnOrder.d.ts +14 -0
  13. package/dist/components/data-table/reorder.d.ts +15 -5
  14. package/dist/components/data-table/types.d.ts +5 -0
  15. package/dist/components/data-table/useFrozenColumns.d.ts +6 -2
  16. package/dist/components/datepicker/DatepickerPanel.d.ts +1 -1
  17. package/dist/components/dnd/Dnd.d.ts +9 -5
  18. package/dist/components/dnd/inPlaceRow.d.ts +7 -0
  19. package/dist/components/dnd/useDragSource.d.ts +2 -0
  20. package/dist/components/dnd/useSortableList.d.ts +5 -0
  21. package/dist/components/field-list/FieldList.d.ts +37 -15
  22. package/dist/components/gantt/Gantt.d.ts +1 -1
  23. package/dist/components/index.d.ts +3 -1
  24. package/dist/components/kanban/Kanban.d.ts +4 -0
  25. package/dist/components/list/List.d.ts +9 -4
  26. package/dist/components/scroll-container/ScrollContainer.d.ts +1 -1
  27. package/dist/index.css +1 -1
  28. package/dist/index.js +4 -2
  29. package/dist/internal/ToolRow.d.ts +23 -0
  30. package/dist/internal/leaveGuard.d.ts +6 -0
  31. package/dist/internal/overlay/DialogGuard.d.ts +3 -2
  32. package/dist/internal/overlay/DialogParts.d.ts +14 -5
  33. package/dist/layout/index.d.ts +1 -1
  34. package/dist/layout/page/Page.d.ts +8 -14
  35. package/dist/skill/anti-slop.md +55 -1
  36. package/dist/skill/api-contract.md +2 -2
  37. package/dist/skill/cheatsheet.md +12 -4
  38. package/dist/skill/checklist.md +7 -0
  39. package/dist/skill/choosing.md +1 -0
  40. package/dist/skill/components.md +1 -1
  41. package/dist/skill/composition.md +196 -21
  42. package/dist/skill/layouts.md +1 -1
  43. package/dist/skill/reference/components/confirm-dialog/COMPONENT.md +1 -1
  44. package/dist/skill/reference/components/data-table/COMPONENT.md +110 -49
  45. package/dist/skill/reference/components/data-table/examples/actions-column.tsx +154 -0
  46. package/dist/skill/reference/components/data-table/examples/column-order.tsx +152 -0
  47. package/dist/skill/reference/components/data-table/examples/columns-visibility.tsx +18 -18
  48. package/dist/skill/reference/components/data-table/examples/controlled.tsx +32 -17
  49. package/dist/skill/reference/components/data-table/examples/examples.module.css +14 -36
  50. package/dist/skill/reference/components/data-table/examples/narrow.tsx +25 -19
  51. package/dist/skill/reference/components/data-table/examples/reorder.tsx +24 -2
  52. package/dist/skill/reference/components/data-table/examples/row-link.tsx +18 -22
  53. package/dist/skill/reference/components/data-table/examples/screen.tsx +12 -1
  54. package/dist/skill/reference/components/data-table/examples/selection.tsx +13 -18
  55. package/dist/skill/reference/components/data-table/examples/server.tsx +45 -31
  56. package/dist/skill/reference/components/data-table/examples/toolbar.tsx +22 -35
  57. package/dist/skill/reference/components/datepicker/COMPONENT.md +2 -2
  58. package/dist/skill/reference/components/dnd/COMPONENT.md +13 -7
  59. package/dist/skill/reference/components/dnd/examples/examples.module.css +4 -3
  60. package/dist/skill/reference/components/dnd/examples/handle.tsx +5 -5
  61. package/dist/skill/reference/components/dnd/examples/locked.tsx +1 -1
  62. package/dist/skill/reference/components/dnd/examples/table-columns.tsx +75 -0
  63. package/dist/skill/reference/components/dnd/examples/table.tsx +6 -6
  64. package/dist/skill/reference/components/drawer/COMPONENT.md +26 -10
  65. package/dist/skill/reference/components/drawer/examples/header-actions.tsx +1 -1
  66. package/dist/skill/reference/components/drawer/examples/loading.tsx +58 -0
  67. package/dist/skill/reference/components/field-list/COMPONENT.md +78 -47
  68. package/dist/skill/reference/components/field-list/examples/base-row.tsx +2 -1
  69. package/dist/skill/reference/components/field-list/examples/locked.tsx +11 -11
  70. package/dist/skill/reference/components/field-list/examples/narrow.tsx +108 -33
  71. package/dist/skill/reference/components/field-list/examples/reorder.tsx +1 -1
  72. package/dist/skill/reference/components/field-list/examples/row-menu.tsx +71 -0
  73. package/dist/skill/reference/components/field-list/examples/screen.module.css +1 -1
  74. package/dist/skill/reference/components/field-list/examples/screen.tsx +169 -162
  75. package/dist/skill/reference/components/field-list/examples/totals.tsx +9 -8
  76. package/dist/skill/reference/components/field-list/examples/variants.tsx +51 -0
  77. package/dist/skill/reference/components/gantt/COMPONENT.md +4 -4
  78. package/dist/skill/reference/components/kanban/COMPONENT.md +6 -2
  79. package/dist/skill/reference/components/kanban/examples/states.tsx +7 -5
  80. package/dist/skill/reference/components/list/COMPONENT.md +21 -11
  81. package/dist/skill/reference/components/list/examples/reorder.tsx +42 -7
  82. package/dist/skill/reference/components/modal/COMPONENT.md +23 -10
  83. package/dist/skill/reference/components/scroll-container/COMPONENT.md +1 -1
  84. package/dist/skill/reference/components/segmented-control/COMPONENT.md +3 -2
  85. package/dist/skill/reference/components/sheet/COMPONENT.md +18 -7
  86. package/dist/skill/reference/components/smart-filter/COMPONENT.md +2 -2
  87. package/dist/skill/reference/layout/page/COMPONENT.md +13 -17
  88. package/dist/skill/reference/layout/page/examples/screen.module.css +1 -1
  89. package/dist/skill/reference/layout/page/examples/screen.tsx +93 -104
  90. package/dist/skill/reference/layout/page/examples/sections.tsx +2 -15
  91. package/dist/skill/responsive.md +14 -10
  92. package/dist/tokens/semantic.d.ts +2 -2
  93. package/dist/tokens/themes/dark.d.ts +1 -1
  94. package/package.json +1 -1
  95. package/src/components/confirm-dialog/COMPONENT.md +1 -1
  96. package/src/components/data-table/COMPONENT.md +110 -49
  97. package/src/components/data-table/examples/actions-column.tsx +154 -0
  98. package/src/components/data-table/examples/column-order.tsx +152 -0
  99. package/src/components/data-table/examples/columns-visibility.tsx +18 -18
  100. package/src/components/data-table/examples/controlled.tsx +32 -17
  101. package/src/components/data-table/examples/examples.module.css +14 -36
  102. package/src/components/data-table/examples/narrow.tsx +25 -19
  103. package/src/components/data-table/examples/reorder.tsx +24 -2
  104. package/src/components/data-table/examples/row-link.tsx +18 -22
  105. package/src/components/data-table/examples/screen.tsx +12 -1
  106. package/src/components/data-table/examples/selection.tsx +13 -18
  107. package/src/components/data-table/examples/server.tsx +45 -31
  108. package/src/components/data-table/examples/toolbar.tsx +22 -35
  109. package/src/components/datepicker/COMPONENT.md +2 -2
  110. package/src/components/dnd/COMPONENT.md +13 -7
  111. package/src/components/dnd/examples/examples.module.css +4 -3
  112. package/src/components/dnd/examples/handle.tsx +5 -5
  113. package/src/components/dnd/examples/locked.tsx +1 -1
  114. package/src/components/dnd/examples/table-columns.tsx +75 -0
  115. package/src/components/dnd/examples/table.tsx +6 -6
  116. package/src/components/drawer/COMPONENT.md +26 -10
  117. package/src/components/drawer/examples/header-actions.tsx +1 -1
  118. package/src/components/drawer/examples/loading.tsx +58 -0
  119. package/src/components/field-list/COMPONENT.md +78 -47
  120. package/src/components/field-list/examples/base-row.tsx +2 -1
  121. package/src/components/field-list/examples/locked.tsx +11 -11
  122. package/src/components/field-list/examples/narrow.tsx +108 -33
  123. package/src/components/field-list/examples/reorder.tsx +1 -1
  124. package/src/components/field-list/examples/row-menu.tsx +71 -0
  125. package/src/components/field-list/examples/screen.module.css +1 -1
  126. package/src/components/field-list/examples/screen.tsx +169 -162
  127. package/src/components/field-list/examples/totals.tsx +9 -8
  128. package/src/components/field-list/examples/variants.tsx +51 -0
  129. package/src/components/gantt/COMPONENT.md +4 -4
  130. package/src/components/kanban/COMPONENT.md +6 -2
  131. package/src/components/kanban/examples/states.tsx +7 -5
  132. package/src/components/list/COMPONENT.md +21 -11
  133. package/src/components/list/examples/reorder.tsx +42 -7
  134. package/src/components/modal/COMPONENT.md +23 -10
  135. package/src/components/scroll-container/COMPONENT.md +1 -1
  136. package/src/components/segmented-control/COMPONENT.md +3 -2
  137. package/src/components/sheet/COMPONENT.md +18 -7
  138. package/src/components/smart-filter/COMPONENT.md +2 -2
  139. package/src/layout/page/COMPONENT.md +13 -17
  140. package/src/layout/page/examples/screen.module.css +1 -1
  141. package/src/layout/page/examples/screen.tsx +93 -104
  142. package/src/layout/page/examples/sections.tsx +2 -15
  143. package/src/styles/theme-dark.css +1 -1
  144. package/src/styles/theme-light.css +1 -1
  145. package/dist/components/data-table/context.d.ts +0 -14
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  Crossfade,
20
20
  DEFAULT_DATEPICKER_PRESETS,
21
21
  DataTable,
22
+ DataTableColumnsMenu,
22
23
  Datepicker,
23
24
  DescriptionList,
24
25
  DigitInput,
@@ -87,7 +88,7 @@ import {
87
88
  useSidebar,
88
89
  useTokenValue,
89
90
  useUploadQueue
90
- } from "./chunks/chunk-MB4CXSLL.js";
91
+ } from "./chunks/chunk-W6EGTNG6.js";
91
92
  import {
92
93
  Avatar,
93
94
  Badge,
@@ -122,7 +123,7 @@ import {
122
123
  tierMappings,
123
124
  tones,
124
125
  variants
125
- } from "./chunks/chunk-OAXJWF3L.js";
126
+ } from "./chunks/chunk-3PY52JP3.js";
126
127
  import {
127
128
  ControlSizeProvider,
128
129
  useOptionalControlSize
@@ -153,6 +154,7 @@ export {
153
154
  Crossfade,
154
155
  DEFAULT_DATEPICKER_PRESETS,
155
156
  DataTable,
157
+ DataTableColumnsMenu,
156
158
  Datepicker,
157
159
  DescriptionList,
158
160
  DigitInput,
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ type DivProps = React.HTMLAttributes<HTMLDivElement> & {
3
+ ref?: React.Ref<HTMLDivElement>;
4
+ };
5
+ type ToolGroupProps = DivProps;
6
+ /**
7
+ * A host's own items kept together in one cell that takes the free room (the Gantt's legend or
8
+ * title before its zoom): they wrap inside it.
9
+ */
10
+ export declare function ToolGroup({ className, ...rest }: ToolGroupProps): import("react/jsx-runtime").JSX.Element;
11
+ type ToolRowProps = DivProps & {
12
+ /** The row has a line of its own: it wraps and its cells fill it by kind. Off — one line, every cell at its natural width. */
13
+ fill?: boolean;
14
+ };
15
+ /**
16
+ * A row of tools as written, each in a cell of its kind: a search grows, switchers and pickers
17
+ * keep their content width beside it and fill a short line, buttons keep their width at the end.
18
+ */
19
+ export declare function ToolRow({ fill, className, children, ...rest }: ToolRowProps): import("react/jsx-runtime").JSX.Element;
20
+ type ToolChipsProps = DivProps;
21
+ /** The applied filters (`SmartFilter.Chips`) on a line of their own under a tool row; gone while empty. */
22
+ export declare function ToolChips({ className, ...rest }: ToolChipsProps): import("react/jsx-runtime").JSX.Element;
23
+ export {};
@@ -18,4 +18,10 @@ export declare function leaveGuardCapture(onClickCapture?: React.MouseEventHandl
18
18
  * it is `busy` or `dirty` the browser asks before the tab closes or reloads (`beforeunload`).
19
19
  */
20
20
  export declare function useLeaveGuard({ busy, dirty }: LeaveGuardState, ask: (leave: () => void) => void): void;
21
+ /**
22
+ * While `hold` is true the browser asks before the tab closes or reloads (`beforeunload`): a page
23
+ * guard, and a dialog guard (`Modal.Guard`, `Drawer.Guard`, `Sheet.Guard`) whose form is not saved.
24
+ * `released` lets a leave the person already agreed to go without a second prompt.
25
+ */
26
+ export declare function useUnloadGuard(hold: boolean, released?: () => boolean): void;
21
27
  export {};
@@ -10,8 +10,9 @@ export type DialogGuardProps = {
10
10
  };
11
11
  /**
12
12
  * Holds a Modal, Drawer or Sheet from inside its content, where the form state lives: put it anywhere in
13
- * `Content`. Renders nothing but the «discard changes?» confirm when it is asked. Closing from code
14
- * (`open={false}`) is never held.
13
+ * `Content`. Renders nothing but the «discard changes?» confirm when it is asked. While `busy` or
14
+ * `dirty` the browser also asks before the tab closes or reloads, as `Page.Guard` does. Closing from
15
+ * code (`open={false}`) is never held.
15
16
  */
16
17
  export declare function DialogGuard({ busy, dirty }: DialogGuardProps): import("react/jsx-runtime").JSX.Element;
17
18
  export declare namespace DialogGuard {
@@ -148,9 +148,17 @@ export declare namespace DialogTitle {
148
148
  var displayName: string;
149
149
  }
150
150
  export type DialogDescriptionProps = Omit<React.HTMLAttributes<HTMLParagraphElement>, "id"> & {
151
+ /**
152
+ * Its data loads: a `Skeleton` holds the line's place and `aria-busy` is set, so the header never moves; the data fades in.
153
+ */
154
+ loading?: boolean;
151
155
  ref?: React.Ref<HTMLParagraphElement>;
152
156
  };
153
- export declare function DialogDescription({ className, ...rest }: DialogDescriptionProps): import("react/jsx-runtime").JSX.Element;
157
+ /**
158
+ * The line under the title. While its data loads (`loading`) it keeps its place with a Skeleton, so
159
+ * the header never moves when the data arrives; the data then fades in under the same element.
160
+ */
161
+ export declare function DialogDescription({ loading, className, children, ...rest }: DialogDescriptionProps): import("react/jsx-runtime").JSX.Element;
154
162
  export declare namespace DialogDescription {
155
163
  var displayName: string;
156
164
  }
@@ -163,9 +171,10 @@ export type DialogHeaderProps = React.HTMLAttributes<HTMLElement> & {
163
171
  };
164
172
  /**
165
173
  * `[Icon] [Title + Description] [Actions] [close]` in one row. Layout is a CSS grid, so child
166
- * order does not matter; text children stack in the text column.
174
+ * order does not matter; text children stack in the text column. When the row is too narrow for
175
+ * the title beside the actions (a phone), the actions move under the text.
167
176
  */
168
- export declare function DialogHeader({ showClose, className, children, ...rest }: DialogHeaderProps): import("react/jsx-runtime").JSX.Element;
177
+ export declare function DialogHeader({ showClose, className, children, ref, ...rest }: DialogHeaderProps): import("react/jsx-runtime").JSX.Element;
169
178
  export declare namespace DialogHeader {
170
179
  var displayName: string;
171
180
  }
@@ -205,13 +214,13 @@ export type DialogSectionProps = Omit<React.HTMLAttributes<HTMLElement>, "title"
205
214
  * at the end of the heading row, then the content 16 below. Sections stand further apart than the
206
215
  * plain blocks of the body.
207
216
  */
208
- export declare function DialogSection({ label, description, className, children, ...rest }: DialogSectionProps): import("react/jsx-runtime").JSX.Element;
217
+ export declare function DialogSection({ label, description, className, children, ref, ...rest }: DialogSectionProps): import("react/jsx-runtime").JSX.Element;
209
218
  export declare namespace DialogSection {
210
219
  var displayName: string;
211
220
  }
212
221
  export type DialogFooterProps = React.HTMLAttributes<HTMLElement> & {
213
222
  /**
214
- * `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.
223
+ * `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.
215
224
  */
216
225
  layout?: DialogFooterLayout;
217
226
  ref?: React.Ref<HTMLElement>;
@@ -2,7 +2,7 @@ export type { AppShellFooterProps, AppShellMainProps, AppShellNavProps, AppShell
2
2
  export { AppShell, useAppShellNav } from "./app-shell/AppShell";
3
3
  export type { BottomNavActionProps, BottomNavItemCountProps, BottomNavItemIconProps, BottomNavItemProps, BottomNavLabels, BottomNavRootProps, } from "./bottom-nav/BottomNav";
4
4
  export { BottomNav } from "./bottom-nav/BottomNav";
5
- export type { PageActionsProps, PageBackButtonProps, PageBodyProps, PageCellProps, PageCellRows, PageCellSpan, PageChipsProps, PageDescriptionProps, PageDetailProps, PageGridProps, PageGuardProps, PageHeaderProps, PageHeadingProps, PageLabels, PageListProps, PageMaxWidth, PageRootProps, PageSectionLayout, PageSectionProps, PageSplitProps, PageTitleProps, PageToolsProps, PageTopBarProps, } from "./page/Page";
5
+ export type { PageActionsProps, PageBackButtonProps, PageBodyProps, PageCellProps, PageCellRows, PageCellSpan, PageChipsProps, PageDescriptionProps, PageDetailProps, PageGridProps, PageGuardProps, PageHeaderProps, PageHeadingProps, PageLabels, PageListProps, PageMaxWidth, PageRootProps, PageSectionProps, PageSplitProps, PageTitleProps, PageToolsProps, PageTopBarProps, } from "./page/Page";
6
6
  export { Page } from "./page/Page";
7
7
  export type { SidebarAccountProps, SidebarBrandLogoProps, SidebarBrandProps, SidebarContentProps, SidebarFooterProps, SidebarGroupProps, SidebarHeaderProps, SidebarItemActionProps, SidebarItemCountProps, SidebarItemIconProps, SidebarItemProps, SidebarItemShortcutProps, SidebarLabels, SidebarMode, SidebarRootProps, SidebarSubContentProps, SidebarSubProps, SidebarSubTriggerProps, SidebarToggleProps, } from "./sidebar/Sidebar";
8
8
  export { Sidebar, useSidebar } from "./sidebar/Sidebar";
@@ -128,14 +128,14 @@ declare namespace PageActions {
128
128
  }
129
129
  export type PageToolsProps = DivProps;
130
130
  /**
131
- * The page's tools — sections, search and filter, view — as kit controls in the order written.
132
- * On the bar's first line each keeps its natural width; on a line of their own they fill it:
133
- * a search (`SmartFilter.Toolbar`, `Input.Root`) takes the room first, switchers and fields share
134
- * the rest (a `SegmentedControl.Root` and a `Datepicker.Root` are `fullWidth` unless set; a Select
135
- * trigger fills by itself), and they wrap when one line is not enough. Named (`aria-label`), it is
136
- * a `group`.
131
+ * The page's tools — sections, search and filter, view — as kit controls in the order written, laid
132
+ * out by the one tool row (`ToolRow`, as the DataTable `toolbar`). On the bar's first line each keeps
133
+ * its natural width; on a line of their own a search takes the free room, switchers and pickers
134
+ * keep their width at the end (a `SegmentedControl.Root` and a `Datepicker.Root` are `fullWidth`
135
+ * unless set, so they fill a short line), and they wrap when one line is not enough. Named
136
+ * (`aria-label`), it is a `group`.
137
137
  */
138
- declare function PageTools({ className, children, ref, ...rest }: PageToolsProps): import("react/jsx-runtime").JSX.Element;
138
+ declare function PageTools({ className, ...rest }: PageToolsProps): import("react/jsx-runtime").JSX.Element;
139
139
  declare namespace PageTools {
140
140
  var displayName: string;
141
141
  }
@@ -151,8 +151,6 @@ declare function PageBody({ className, ...rest }: PageBodyProps): import("react/
151
151
  declare namespace PageBody {
152
152
  var displayName: string;
153
153
  }
154
- /** Where the heading of a section stands: over its content or in a column beside it. */
155
- export type PageSectionLayout = "stack" | "aside";
156
154
  export type PageSectionProps = Omit<React.HTMLAttributes<HTMLElement>, "title"> & {
157
155
  /**
158
156
  * The visible heading (`h2`); it also names the section.
@@ -162,10 +160,6 @@ export type PageSectionProps = Omit<React.HTMLAttributes<HTMLElement>, "title">
162
160
  * One line of context under the heading, in secondary body-s.
163
161
  */
164
162
  description?: React.ReactNode;
165
- /**
166
- * `stack` — the heading over the content; `aside` — a ~16rem heading column beside it (a settings page), above it once the content would get narrower than 30rem.
167
- */
168
- layout?: PageSectionLayout;
169
163
  ref?: React.Ref<HTMLElement>;
170
164
  };
171
165
  /**
@@ -173,7 +167,7 @@ export type PageSectionProps = Omit<React.HTMLAttributes<HTMLElement>, "title">
173
167
  * at its end) over its content. Sections stand further apart than the plain blocks of the body,
174
168
  * so the air shows where a new topic starts.
175
169
  */
176
- declare function PageSection({ label, description, layout, className, children, ...rest }: PageSectionProps): import("react/jsx-runtime").JSX.Element;
170
+ declare function PageSection({ label, description, className, children, ...rest }: PageSectionProps): import("react/jsx-runtime").JSX.Element;
177
171
  declare namespace PageSection {
178
172
  var displayName: string;
179
173
  }
@@ -242,7 +242,8 @@ The same for one value in several units — one heading, the fields in a row, th
242
242
  | `<Button.Root onClick={() => location.assign(url)}>` | `<Button.Root asChild><a href={url}>…</a></Button.Root>` or `LinkButton` |
243
243
  | a hand-built spinner, shimmer, grey placeholder box or «Loading…» text | `loading` on Button / Select / DataTable; a region: `Skeleton` of its content inside `Crossfade`; no shape to hold: `Spinner` |
244
244
  | `{loading ? <Spinner /> : <List />}` — a state that flips | `<Crossfade state={status}>` around the region, `Skeleton` while loading |
245
- | a search Input + a row of filter Selects above a table | `SmartFilter` in the page's top bar: its toolbar in `Page.Tools`, its chips in `Page.Chips` ([where the filters go](composition.md#where-the-filters-go)) |
245
+ | a search Input + a row of filter Selects above a table | `SmartFilter` next to the table it filters: its toolbar and chips in the table's `toolbar` / `toolbarChips` beside KPI cards, in `Page.Tools` / `Page.Chips` when the table is the page ([where the filters go](composition.md#where-the-filters-go)) |
246
+ | the table's search, filter or «Колонки» in the page's bar above KPI cards | the bar keeps the title, the period that drives every block and the primary action; the table's tools go in its own `toolbar` ([where the filters go](composition.md#where-the-filters-go)) |
246
247
  | a grid of «label: value» text pairs for one record | `DescriptionList.Root` + `DescriptionList.Item` |
247
248
  | a Modal hand-built as «Удалить?» with your own pending flag | `ConfirmDialog.Root` with an async `onConfirm` |
248
249
  | a `<fieldset>` reset by hand, or `disabled` on every field while saving | `<Fieldset label disabled={saving}>` |
@@ -292,6 +293,59 @@ The same for one value in several units — one heading, the fields in a row, th
292
293
  />
293
294
  ```
294
295
 
296
+ A bar above the table for a single button — the column chooser, an export — wastes a row and splits
297
+ the page's actions ([controls of a table](composition.md#controls-of-a-table)):
298
+
299
+ ```tsx partial
300
+ // bad — a toolbar that holds one control
301
+ <DataTable columns={columns} rows={orders} toolbar={<Button.Root>Колонки</Button.Root>} />
302
+ ```
303
+
304
+ A chooser cell in the header of a table that has no row actions is a column of nothing — an empty
305
+ cell in every row:
306
+
307
+ ```tsx partial
308
+ // bad — columnsMenu with no row-actions column to stand over
309
+ <DataTable columns={columns} rows={orders} columnsMenu />
310
+ ```
311
+
312
+ ```tsx good
313
+ // good — a page that is the table, no row actions: the chooser is in the bar, on the state the table reads
314
+ // declare: const orders: { id: string; client: string; total: number }[]
315
+ // declare: const hidden: string[]
316
+ // declare: const setHidden: (hidden: string[]) => void
317
+ <Page.Root>
318
+ <Page.TopBar>
319
+ <Page.Heading>
320
+ <Page.Title>Заказы</Page.Title>
321
+ </Page.Heading>
322
+ <Page.Tools>
323
+ <DataTableColumnsMenu
324
+ columns={[
325
+ { id: "id", header: "Заказ", accessor: "id", hideable: false },
326
+ { id: "client", header: "Клиент", accessor: "client" },
327
+ { id: "total", header: "Сумма", accessor: "total", numeric: true },
328
+ ]}
329
+ hiddenColumns={hidden}
330
+ onHiddenColumnsChange={setHidden}
331
+ />
332
+ </Page.Tools>
333
+ </Page.TopBar>
334
+ <Page.Body>
335
+ <DataTable
336
+ columns={[
337
+ { id: "id", header: "Заказ", accessor: "id", hideable: false },
338
+ { id: "client", header: "Клиент", accessor: "client" },
339
+ { id: "total", header: "Сумма", accessor: "total", numeric: true },
340
+ ]}
341
+ hiddenColumns={hidden}
342
+ rows={orders}
343
+ getRowKey={(row) => row.id}
344
+ />
345
+ </Page.Body>
346
+ </Page.Root>
347
+ ```
348
+
295
349
  ## 7b. A top bar out of its groups
296
350
 
297
351
  The bar lays itself out only from its four groups ([the top bar](composition.md#the-top-bar)); a
@@ -23,7 +23,7 @@ import type { ControlSize, PaletteColor, TextTone, Tone, Variant } from "prime-u
23
23
  | Validation | `invalid`, `hint`, `error` | Props, never parts. A non-empty `error` implies `invalid`; sets `aria-invalid`, `data-invalid`. |
24
24
  | Value | `value` / `defaultValue` / `onValueChange(value)` | Accordion, Chat.Pinned, ChatList, ColorField, ColorPicker, ColorPresets, ColorSwatches, Composer, Datepicker, Datepicker.Panel, DigitInput, FieldList, FileUpload, FileUpload.Queue, Kanban, NativeSelect, Pagination, Radio.Group, SegmentedControl, Select, Slider, SmartFilter, Stepper, Tabs, TagSelect. |
25
25
  | Text value | native `value` / `onChange` + `onValueChange(string)` | Input (`Input.Field`), Textarea. |
26
- | Other state | `x` / `defaultX` / `onXChange(x)` | Badge `pressed`; Button `pressed`; ChartFrame `hiddenSeries`; Chat `selected`, `search`; ChatList `search`; ColorPicker `format`; CommandMenu `search`; Composer `recordMode`; DataTable `hiddenColumns`, `page`, `sort`, `selected`, `expanded`, `collapsedGroups`; ExampleFrame `viewport`, `colorScheme`; Gallery `index`; Gantt `scale`; Kanban `columnOrder`; MediaViewer `index`; Message.Media `index`; Select `search`; SmartFilter `search`; VideoNote `playing`, `transcriptExpanded`; VoicePlayer `rate`, `playing`, `transcriptExpanded`; Sidebar `mode`. |
26
+ | Other state | `x` / `defaultX` / `onXChange(x)` | Badge `pressed`; Button `pressed`; ChartFrame `hiddenSeries`; Chat `selected`, `search`; ChatList `search`; ColorPicker `format`; CommandMenu `search`; Composer `recordMode`; DataTable `hiddenColumns`, `columnOrder`, `page`, `sort`, `selected`, `expanded`, `collapsedGroups`; ExampleFrame `viewport`, `colorScheme`; Gallery `index`; Gantt `scale`; Kanban `columnOrder`; MediaViewer `index`; Message.Media `index`; Select `search`; SmartFilter `search`; VideoNote `playing`, `transcriptExpanded`; VoicePlayer `rate`, `playing`, `transcriptExpanded`; Sidebar `mode`. |
27
27
  | Checked | `checked` / `defaultChecked` / `onCheckedChange(checked)` | Checkbox, Dropdown.CheckboxItem, Switch, Thumbnail. |
28
28
  | Open | `open` / `defaultOpen` / `onOpenChange(open)` | ColorPresets, CommandMenu, ConfirmDialog, Datepicker, Drawer, Dropdown, Gallery, MediaViewer, Message.Media, Modal, Popover, Select, Sheet, TagSelect, Tooltip, Page.Split, Sidebar, Sidebar.Group, Sidebar.Sub. |
29
29
  | Dismiss | `closeOnOutsideClick` (default `true`), `closeOnEscape` (default `true`) | Both on every overlay that opens a layer: the dialogs, the floating panels, the panels of Select, TagSelect, Datepicker and ColorPresets, the `suggestions` list of Input, the off-canvas Sidebar and the reaction bar of a message. `ConfirmDialog` takes both with `closeOnOutsideClick` default `false` (Escape is held while `onConfirm` runs). Tooltip and Notification take neither. A form holds closing with `Modal.Guard` / `Drawer.Guard` / `Sheet.Guard`. |
@@ -295,7 +295,7 @@ export function DeleteProject({ onDelete }: { onDelete: () => Promise<void> }) {
295
295
 
296
296
  Leaving a form with unsaved changes is the same contract: `Modal.Guard` / `Drawer.Guard` inside the
297
297
  content with `dirty` (any dismiss asks «Не сохранять изменения?») and `busy` (nothing closes it while
298
- it saves). A form that is the whole page takes `Page.Guard` (`dirty`, `busy`) inside the page: it
298
+ it saves); while either holds, the browser also asks before the tab closes or reloads. A form that is the whole page takes `Page.Guard` (`dirty`, `busy`) inside the page: it
299
299
  holds a press on `Sidebar.Item`, `BottomNav.Item` and `Breadcrumb.Item` links — router links
300
300
  included — and the browser's own close or reload; never a `beforeunload` or a ConfirmDialog on a
301
301
  crumb of your own.
@@ -44,6 +44,7 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
44
44
  - Copy or call next to a value: `Input.CopyButton`, `Input.Action` inside `Input.Wrapper` — not a Button beside the field; a password: `Input.RevealButton`.
45
45
  - Records of one shape a person adds and removes (invoice lines, contacts, specs, prices by period): `<FieldList columns={[{ label: "Услуга" }, { label: "Цена", width: "m" }]} value onValueChange getId createItem addLabel renderItem={(item, row) => <>…fields without label…</>} />` — the headings stand once, every field takes its column heading as its label; not a `.map()` of fields with a `label` in every row, not «Позиция 1», «Позиция 2».
46
46
  - Small attributes in a row of fields: an icon-only `Select` (`<Select.Trigger><Select.TriggerIcon>…</Select.TriggerIcon></Select.Trigger>`, no `Select.Value`) and a `ColorPresets` swatch in `width: "compact"` columns — not a full-width Select and a ColorField on a line of their own.
47
+ - FieldList row actions: `rowActions="menu"` (one «⋯») for dense rows or touch, the default «×» + handle for short lists reordered often; `variant="surface"` when the list sits among other fields, `plain` when it is the card's content — not a hand-made «⋯» or a Card around the list.
47
48
  - A base value with overrides (a price and its changes by month): one `FieldList`, the base as the first row pinned by `getLocked` — not a separate field above a list in another geometry.
48
49
  - Rows that add up: `FieldList renderTotals={(items) => <>…one node per column…</>}` draws «Итого» under the rows on the same tracks — not a line of text typed under the list, not a DataTable of fields.
49
50
  - One value in several units (a deposit in ₽, $, ฿): the fields side by side in one row under one heading, each unit in `Input.InlineAffix` — not a label per field, not the unit in the label and in the field.
@@ -84,9 +85,9 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
84
85
 
85
86
  - The app is navigation and the page: everything at the top belongs to the page's `<Page.Root>` — not an app-wide header, a global search or notifications above every page.
86
87
  - A work screen (a list, a board, a chat, a record) titles itself in `<Page.TopBar>`: `<Page.Heading>` with `<Page.Title>` + `<Page.Description>` carrying a line of data («128 в работе · 6 ждут оплаты») — not a sentence about the page.
87
- - The bar holds only its four groups: `<Page.Heading>`, `<Page.Tools aria-label>` (sections, `<SmartFilter.Toolbar />`, the view — in reading order), `<Page.Actions>`, `<Page.Chips>` (`<SmartFilter.Chips />`) — not a hand-made flex row of title, tabs, search and buttons ([the top bar](composition.md#the-top-bar)).
88
+ - The bar holds only its four groups: `<Page.Heading>`, `<Page.Tools aria-label>` (sections, the view, a shared period; `<SmartFilter.Toolbar />` of a one-list page — in reading order), `<Page.Actions>`, `<Page.Chips>` (`<SmartFilter.Chips />`) — not a hand-made flex row of title, tabs, search and buttons ([the top bar](composition.md#the-top-bar)).
88
89
  - A nested record: `<Breadcrumb.Root>` first in `<Page.Heading>`, then `<Page.Title>` with the record's name — the path ends in the title on one line; not a title row under the path, not a breadcrumb in the body. With a router each crumb is `<Breadcrumb.Item asChild>` around its `Link`.
89
- - A line of data that loads after the page opens: `<Page.Description loading={!summary}>` from the first render — not a Description mounted when the data arrives, which moves the title.
90
+ - A line of data that loads after the page opens: `<Page.Description loading={!summary}>` from the first render (`Drawer.Description` / `Modal.Description` alike) — not a Description mounted when the data arrives, which moves the title, nor a `Skeleton` placed in it by hand.
90
91
  - A page that edits a record: `<Page.Guard dirty={changed} busy={saving} />` inside `Page.Root` — the Sidebar, BottomNav and path links ask before the changes are lost; not your own `beforeunload` or a confirm wired to each link.
91
92
  - Every button of the page goes into one `<Page.Actions>`, secondary first, the primary last, each with a `<Button.Icon>` beside its label — not buttons split between two places, not a button with only text.
92
93
  - A filtered page: `<SmartFilter.Root>` wraps `<Page.Root>` — it draws nothing; not `SmartFilter.Root` inside the bar.
@@ -97,6 +98,7 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
97
98
 
98
99
  - `Card.Root` only for a bounded block with its own title: a KPI tile (`variant="stat-trend"`), a settings panel (`variant="panel"`), side facts — not around a whole section, a form field or a page.
99
100
  - Never a `DataTable` inside a Card — the table is already a filled block; a card in a card only sparingly ([the rule](foundations.md#card-in-a-card)).
101
+ - A titled panel (a settings section, a group of a long form) is `<Card.Root>` with `<Card.Header>` (`<Card.Title as="h2">`, `<Card.Description>`) stacked in the flow — never a heading column beside its panel ([a titled block](composition.md#4-surfaces)).
100
102
  - Section without a card: `<Page.Section label="…">` + the content (a link or button beside the heading in `Page.Actions`) — not a hand-made `<section>` + `Typography as="h2"` + own gap.
101
103
  - The tiles of a screen (KPI cards, charts, panels): one `<Page.Grid>` with a `<Page.Cell span>` per tile — not a grid per row (the rows would stand 24 apart instead of 16). A chart beside a panel is an 8 and a 4 written next to each other — the grid pairs them at mid widths; not 6 + 6 to dodge a hole.
102
104
  - A KPI tile whose figure loads: `<Card.Root variant="stat-trend" loading>` with its parts mounted — not Skeletons placed in `Card.Value` by hand.
@@ -108,6 +110,8 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
108
110
  ## Tables
109
111
 
110
112
  - A list page is a `<DataTable columns rows getRowKey>` under the page's `<SmartFilter.Root fields value onValueChange>` with `<SmartFilter.Toolbar />` in its `Page.Tools` and `<SmartFilter.Chips />` in its `Page.Chips` ([where the filters go](composition.md#where-the-filters-go)); filter rows with `matchesSmartFilter` — not a search Input and a row of Selects above the table.
113
+ - A table beside KPI cards or a chart: `<SmartFilter.Root>` around the `<DataTable toolbar={<><SmartFilter.Toolbar /><DataTableColumnsMenu … /></>} toolbarChips={<SmartFilter.Chips />}>` — not its search, filter and «Колонки» in the page's bar above blocks they do not filter ([where the filters go](composition.md#where-the-filters-go)).
114
+ - The column chooser is `<DataTable columnsMenu>` over the row actions, else `<DataTableColumnsMenu>` beside the table's search on the same state; `toolbar` takes two or more controls of the table as they are — no wrapper ([controls of a table](composition.md#controls-of-a-table)) — never a toolbar for one button.
111
115
  - Search over a table or a list page is always SmartFilter, even with no filter fields yet (`fields={[]}` is a plain search that grows into filters). A search `Input` only outside a filtered list: inside a picker, a settings section, a panel.
112
116
  - Paging is built in: `paging="pages"` (default) with `pageSize`, `paging="infinite"` for feeds, `paging="none"` for short lists — not a separate `<Pagination>` under a DataTable.
113
117
  - Long lists are virtualized inside the kit: pass the whole array to `DataTable` (with `scrollHeight` or `paging="infinite"`; over 100 rows — grouped, `onReorder` and `renderExpanded` bodies render every row, keep those short or paged), `TagSelect options`, `Chat.Messages items`, `Gantt rows` — never slice it by hand, never wrap a kit list in react-window / react-virtual or your own windowing. Big remote data is still paged or loaded on scroll by the server (`source="server"`, `onLoadMore`).
@@ -119,7 +123,11 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
119
123
  - A column header that needs a word more («Маржа»): the column's `description` (an info glyph with a tooltip) — not the explanation squeezed into the header, not your own icon and Tooltip in `header`.
120
124
  - A list row with controls of its own (a Switch, a ⋯ menu): `List.ItemActions` inside `List.Item`, a stage colour as `Badge.Dot color` in `List.ItemIcon` — not a control inside the row's button, not a row rebuilt as a `div`.
121
125
  - Rows people order by hand: `onReorder(id, beforeId)` (apply it with `moveBefore`, return the save's Promise) — not a Dnd list rebuilt next to the table.
126
+ - A DataTable's grip stays at the end of the row (`reorderHandle` default); `start` or `row` only by the rule ([choosing](choosing.md#3-pairs-that-are-easy-to-confuse)) — never a grip column of your own.
127
+ - Columns people arrange: `columnOrder` + `onColumnOrderChange` next to `hiddenColumns` and the column chooser, both saved by the app — not up / down buttons, a settings dialog or a header drag of your own.
122
128
  - A short list of records people order by hand (stages, priorities) is `<List.Root onReorder>` with `List.Item value` — not up / down buttons or a hand-made `Dnd.Sortable`.
129
+ - A toggle in a reorderable row leads it (`List.ItemActions side="start"`) and a plain settings row ends with it ([list rows](composition.md#list-rows)) — never a Switch between the label and the grip.
130
+ - The grip ends its row: a `Dnd.Handle` is the last child of its item, after the row's own controls, and a locked row's lock takes the same place (List, FieldList and Kanban place theirs there) — not before the row's text.
123
131
  - Groups that fold: pass `defaultCollapsedGroups={[]}` (or the controlled `collapsedGroups` + `onCollapsedGroupsChange`) — not your own button in `renderGroupHeader` or filtering `rows`. One heatmap over several columns: the same `scale: { min, max }` on each.
124
132
  - A trend in a table row is `<Sparkline layout="compact">` in the column's `cell` (a column `width`, series built once, not inline, the same `min` / `max` on every row when rows compare by size); the number itself goes in its own column. A missing day is `value: null`, never `0`.
125
133
  - Properties of one record: `<DescriptionList.Root>` of `DescriptionList.Item` — not a hand-made grid of label / value text, not a one-row DataTable.
@@ -160,7 +168,7 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
160
168
  - A destructive confirm is `<ConfirmDialog.Root title description confirmLabel onConfirm={remove}>` with a `ConfirmDialog.Trigger` — not a Modal assembled as a confirm with your own pending state; a reversible delete offers «Отменить» in the notification instead.
161
169
  - Overlays close smoothly only if they stay mounted: render the `Root` always and drive visibility with `open` alone — never `{open && <Drawer.Root>}`, never a changing `key` on the Root. The kit keeps the last content on screen while the exit plays.
162
170
  - Placement of a menu, popover, select or picker: leave the defaults — `side="bottom"`, `align="auto"`: it opens below the trigger, lines up with the trigger edge that has the room (a «⋯» at the end of a row grows toward the start) and flips to the top by itself. Never write `side` or `align` to restate that; set `align` only when the panel must line up with one edge of a block whatever room there is. A Popover under a wide anchor that must never be narrower than it: `<Popover.Content width="min-trigger">`.
163
- - While a request runs: `<Modal.Guard busy={saving} />` inside the content (`Drawer.Guard`, `Sheet.Guard` alike) — Escape, a press outside, × and `Close` are held, × and `Close` disabled; add `dirty` to ask before unsaved changes are lost. Not `closeOnEscape={false}` or `showClose={false}` by hand.
171
+ - While a request runs: `<Modal.Guard busy={saving} />` inside the content (`Drawer.Guard`, `Sheet.Guard` alike) — Escape, a press outside, × and `Close` are held, × and `Close` disabled; add `dirty` to ask before unsaved changes are lost; both also hold the browser's tab close. Not `closeOnEscape={false}` or `showClose={false}` by hand, not your own `beforeunload`.
164
172
 
165
173
  ## Motion — it comes with the components
166
174
 
@@ -204,7 +212,7 @@ Full rules: [responsive.md](responsive.md).
204
212
 
205
213
  - Rearrange, never hide: every desktop function is on the phone, in another place or a menu — not `display: none` below a breakpoint.
206
214
  - Breakpoints only in [their scope](responsive.md#breakpoints); inside a page — [intrinsic grids](responsive.md#intrinsic-grids) and container queries.
207
- - A wide table scrolls inside itself; offer `DataTable.ColumnsMenu` in its `toolbar` — never drop columns by breakpoint, never hand-build the chooser.
215
+ - A wide table scrolls inside itself; offer the column chooser (`columnsMenu` or `DataTableColumnsMenu`) — never drop columns by breakpoint, never hand-build the chooser.
208
216
  - A row that opens a record page is `getRowHref` (a real link: ⌘-click, new tab), a row that opens a drawer is `onRowClick` — never `navigate()` in `onRowClick`, never your own `event.target` checks for buttons in the row.
209
217
  - A list + detail screen is `Page.Split` (`Page.List`, `Page.Detail`) in a `Page.Root fill` page, its records a `List` (`current` on the open one) — never ghost Buttons with a soft current; a full-height table is `scrollHeight="fill"` — never a height computed from `100vh`.
210
218
  - App sections on phones: `BottomNav` in `AppShell.Footer` — not in Main, not `position: fixed`, no own media query: the shell hides the footer while the Sidebar rail is on screen; a short task from the bottom edge: `Sheet` — Select, Dropdown, Popover and Modal become sheets below 640 by themselves.
@@ -10,11 +10,16 @@ checks.
10
10
  - [ ] A nested record's path ends in its title on one line inside `Page.Heading`; no title row under the path ([the top bar](composition.md#the-top-bar)).
11
11
  - [ ] Every button of the page is in one `Page.Actions`, the primary last, each with a `Button.Icon` ([the top bar](composition.md#the-top-bar)).
12
12
  - [ ] `SmartFilter.Root` wraps `Page.Root`; its toolbar is in `Page.Tools`, its chips in `Page.Chips` ([the top bar](composition.md#the-top-bar)).
13
+ - [ ] A filter stands next to what it filters: beside KPI cards or a chart, the table's search, filter, chips and chooser are in its `toolbar` / `toolbarChips`; the bar keeps the title, the shared period and the primary action ([where the filters go](composition.md#where-the-filters-go)).
13
14
  - [ ] A Card only for a standalone titled block — never around a table, a single field or the page ([surfaces](composition.md#4-surfaces)).
15
+ - [ ] A titled panel carries its heading in its `Card.Header` and stacks in the flow; no heading column beside a panel ([surfaces](composition.md#4-surfaces)).
16
+ - [ ] A DataTable's row grip and column order follow the rule: grip at the end, columns by `columnOrder` saved with `hiddenColumns` ([choosing](choosing.md#3-pairs-that-are-easy-to-confuse)).
17
+ - [ ] The column chooser is `columnsMenu` only over a row-actions column, else `DataTableColumnsMenu` beside the table's search; a DataTable `toolbar` holds two or more table controls, unwrapped, or does not exist ([controls of a table](composition.md#controls-of-a-table)).
14
18
 
15
19
  ## UX ([ux.md](ux.md))
16
20
  - [ ] I wrote the five-line brief before the JSX ([brief](ux.md#1-brief-before-code)).
17
21
  - [ ] Every entity is laid out by its shape: a record is one row or one block ([data shape](ux.md#2-the-shape-of-the-data-decides-the-layout)).
22
+ - [ ] A FieldList picks its `rowActions` (buttons or menu) and its `variant` (plain or surface) by the rules ([forms](composition.md#7-forms)).
18
23
  - [ ] Labels on a form of rows ≤ columns + the list label; none repeated, numbered or repeating its heading ([label economy](ux.md#3-label-economy)).
19
24
  - [ ] No text explains the layout; every hint says what the person cannot see ([label economy](ux.md#3-label-economy)).
20
25
  - [ ] Buttons say the verb and the object, errors say how to fix it, empty states offer the next step ([words](ux.md#4-words)).
@@ -35,6 +40,7 @@ checks.
35
40
  ## Sizes and alignment
36
41
  - [ ] Default size `m`; a tier changes for a whole region, never for one control ([the host tier](api-contract.md#size-and-the-host-tier)).
37
42
  - [ ] Every row of controls uses one size and its heights line up ([density](composition.md#5-density-and-size)).
43
+ - [ ] A reorderable row's toggle leads it and the grip ends it; a plain settings row ends with its toggle ([list rows](composition.md#list-rows)).
38
44
  - [ ] Controls in table cells are one tier below the table; no CSS sets row or cell heights ([size axis](foundations.md#3-size-axis)).
39
45
  - [ ] Icon-only buttons are square (round only for media and transfers) and named ([icons](api-contract.md#icons)).
40
46
 
@@ -59,6 +65,7 @@ checks.
59
65
 
60
66
  ## Hierarchy
61
67
  - [ ] One primary action per area, last in its row; the rest `soft` / `ghost` / `outline` neutral ([actions](composition.md#6-actions)).
68
+ - [ ] A dialog footer holds Cancel and the primary only; deleting the edited record is the last item of the header ⋯ menu ([actions](composition.md#6-actions)).
62
69
  - [ ] The same action has the same label and variant everywhere on the screen ([secondary actions](api-contract.md#secondary-actions)).
63
70
 
64
71
  ## States
@@ -171,6 +171,7 @@ the records (tickets, letters, invoices; `current` on the open one; a row's own
171
171
  | Accordion / Tabs | Accordion when the user may need several sections open or reads them in order (FAQ, settings groups). Tabs when sections are alternatives viewed one at a time. |
172
172
  | Divider / gap | Divider inside a block between rows or a form and its footer. Between blocks and groups use air (`gap`), not lines. |
173
173
  | Breadcrumb / back button | Breadcrumb on pages below a top-level section (Счета → № 2026-0418) and deeper; not on top-level pages. A single “Back” without hierarchy → `LinkButton` or `Button.Root variant="ghost"`. |
174
+ | DataTable grip `end` / `start` / `row`, column order | Rows whose order is data (stages, a price list): `onReorder` with the grip at the end of the row (`reorderHandle="end"`, the default — after the row actions); `start` only when the order is the list's main point and nothing is selected; `row` (the whole row carries) only for short rows with nothing to select, copy or follow as a link. Sorted by a column, rows do not reorder — say so, never hide the grip. Columns people arrange for themselves: `columnOrder` with `hiddenColumns` and the column chooser — a header drag (the header moves alone), Alt + ← / → and the chooser's grips; the key column `reorderable: false` (or pinned), the actions column `hideHeader`; the app saves both lists. |
174
175
  | Pagination / infinite scroll | Pages for tables people return to or reference: DataTable `paging="pages"` (default, Pagination in the footer). `paging="infinite"` for feeds. Standalone Pagination only for lists that are not a DataTable. |
175
176
  | Stepper / Tabs | Stepper for a sequential process with completion status; Tabs for non-sequential views. |
176
177
  | Gantt / Timeline / Kanban / DataTable | Gantt: records with a start and an end compared across time (who is busy when, a plan with phases and milestones), moved, stretched and drawn by hand (`onBarChange`, `onBarCreate`). Timeline: a history of events at points in time, read top to bottom. Kanban: work items moved between statuses, time does not matter. DataTable: many records compared by many attributes, sorted, filtered and selected — dates are only columns. |
@@ -40,7 +40,7 @@ Type a value, and the field anatomy: label, hint, error.
40
40
  | Label | Field label (native `<label>`) with required and optional markers. | [COMPONENT.md](reference/components/label/COMPONENT.md) · [examples](reference/components/label/examples/) |
41
41
  | Hint | Help text or a validation error under a field. | [COMPONENT.md](reference/components/hint/COMPONENT.md) · [examples](reference/components/hint/examples/) |
42
42
  | Fieldset | A frameless group of related form fields: a native `<fieldset>` with a `title-s` legend, the form rhythm between its fields and one `disabled` for all of them. | [COMPONENT.md](reference/components/fieldset/COMPONENT.md) · [examples](reference/components/fieldset/examples/) |
43
- | FieldList | An editable list of records of one shape — invoice lines, specs, contacts, prices by period: one row per record, the column headings once above the rows, a remove button per row and an add button under them. | [COMPONENT.md](reference/components/field-list/COMPONENT.md) · [examples](reference/components/field-list/examples/) |
43
+ | FieldList | An editable list of records of one shape — invoice lines, specs, contacts, prices by period: one row per record on one grid, the column headings once above the rows, the row's actions at its end and an add button under them. | [COMPONENT.md](reference/components/field-list/COMPONENT.md) · [examples](reference/components/field-list/examples/) |
44
44
 
45
45
  ## Selection (`selection`)
46
46