prime-ui-kit 0.11.1 → 0.13.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 (171) hide show
  1. package/README.md +4 -1
  2. package/dist/chunks/{chunk-WDTSHOTD.js → chunk-E6SQBSDF.js} +140 -30
  3. package/dist/chunks/{chunk-CUH6DJ57.js → chunk-Z3WKKAS6.js} +6572 -5024
  4. package/dist/chunks/{chunk-GKX3QAUC.js → chunk-ZSJMNA46.js} +14 -5
  5. package/dist/color-picker.js +2 -2
  6. package/dist/components/chat/Chat.d.ts +11 -12
  7. package/dist/components/chat/chatContext.d.ts +2 -0
  8. package/dist/components/chat-list/ChatList.d.ts +191 -0
  9. package/dist/components/chat-list/formatChatTime.d.ts +6 -0
  10. package/dist/components/command-menu/CommandMenu.d.ts +10 -5
  11. package/dist/components/composer/Composer.d.ts +3 -10
  12. package/dist/components/data-table/DataTable.d.ts +16 -2
  13. package/dist/components/data-table/Row.d.ts +23 -7
  14. package/dist/components/data-table/StateRows.d.ts +5 -1
  15. package/dist/components/data-table/reorder.d.ts +3 -1
  16. package/dist/components/data-table/rows.d.ts +20 -4
  17. package/dist/components/file-upload/FileUpload.d.ts +7 -0
  18. package/dist/components/gantt/Gantt.d.ts +19 -1
  19. package/dist/components/gantt/GanttBar.d.ts +4 -1
  20. package/dist/components/gantt/GanttRow.d.ts +8 -1
  21. package/dist/components/index.d.ts +11 -7
  22. package/dist/components/kanban/Kanban.d.ts +17 -2
  23. package/dist/components/list/List.d.ts +120 -0
  24. package/dist/components/message/Message.d.ts +13 -0
  25. package/dist/components/message/MessageParts.d.ts +2 -0
  26. package/dist/components/modal/DialogParts.d.ts +5 -1
  27. package/dist/components/select/Select.d.ts +11 -5
  28. package/dist/components/select/SelectItem.d.ts +19 -0
  29. package/dist/components/select/selectContext.d.ts +4 -0
  30. package/dist/components/sparkline/Sparkline.d.ts +14 -3
  31. package/dist/components/textarea/Textarea.d.ts +11 -3
  32. package/dist/hooks/useUploadQueue.d.ts +5 -1
  33. package/dist/icons/registry.d.ts +2 -0
  34. package/dist/index.css +1 -1
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.js +10 -2
  37. package/dist/internal/VisuallyHidden.d.ts +6 -1
  38. package/dist/internal/attachmentTile.d.ts +5 -1
  39. package/dist/internal/richText.d.ts +7 -2
  40. package/dist/internal/textFormatting.d.ts +52 -0
  41. package/dist/internal/textFormattingToolbar.d.ts +26 -0
  42. package/dist/skill/api-contract.md +1 -1
  43. package/dist/skill/cheatsheet.md +13 -1
  44. package/dist/skill/choosing.md +9 -4
  45. package/dist/skill/components.md +7 -5
  46. package/dist/skill/composition.md +9 -1
  47. package/dist/skill/layouts.md +4 -1
  48. package/dist/skill/patterns/app-shell.module.css +18 -0
  49. package/dist/skill/patterns/app-shell.tsx +301 -0
  50. package/dist/skill/patterns/chat.module.css +0 -25
  51. package/dist/skill/patterns/chat.tsx +72 -29
  52. package/dist/skill/patterns/kanban-board.module.css +28 -0
  53. package/dist/skill/patterns/kanban-board.tsx +612 -0
  54. package/dist/skill/patterns/media-library.module.css +66 -0
  55. package/dist/skill/patterns/media-library.tsx +638 -0
  56. package/dist/skill/patterns/schedule.module.css +22 -0
  57. package/dist/skill/patterns/schedule.tsx +722 -0
  58. package/dist/skill/reference/components/chat/COMPONENT.md +13 -4
  59. package/dist/skill/reference/components/chat/examples/states.tsx +100 -0
  60. package/dist/skill/reference/components/chat-list/COMPONENT.md +256 -0
  61. package/dist/skill/reference/components/chat-list/examples/controlled.tsx +103 -0
  62. package/dist/skill/reference/components/chat-list/examples/examples.module.css +66 -0
  63. package/dist/skill/reference/components/chat-list/examples/live.tsx +133 -0
  64. package/dist/skill/reference/components/chat-list/examples/long-list.tsx +87 -0
  65. package/dist/skill/reference/components/chat-list/examples/narrow.tsx +88 -0
  66. package/dist/skill/reference/components/chat-list/examples/overview.tsx +194 -0
  67. package/dist/skill/reference/components/chat-list/examples/search.tsx +71 -0
  68. package/dist/skill/reference/components/chat-list/examples/sizes.tsx +61 -0
  69. package/dist/skill/reference/components/chat-list/examples/states.tsx +107 -0
  70. package/dist/skill/reference/components/chat-list/examples/structure.tsx +135 -0
  71. package/dist/skill/reference/components/command-menu/COMPONENT.md +15 -11
  72. package/dist/skill/reference/components/command-menu/examples/server-search.tsx +11 -9
  73. package/dist/skill/reference/components/composer/COMPONENT.md +8 -7
  74. package/dist/skill/reference/components/composer/examples/formatting.tsx +1 -1
  75. package/dist/skill/reference/components/confirm-dialog/COMPONENT.md +1 -0
  76. package/dist/skill/reference/components/data-table/COMPONENT.md +21 -6
  77. package/dist/skill/reference/components/data-table/examples/groups.tsx +2 -1
  78. package/dist/skill/reference/components/data-table/examples/trend.tsx +85 -0
  79. package/dist/skill/reference/components/drawer/COMPONENT.md +7 -6
  80. package/dist/skill/reference/components/drawer/examples/client-card.tsx +50 -0
  81. package/dist/skill/reference/components/fieldset/COMPONENT.md +1 -0
  82. package/dist/skill/reference/components/file-upload/COMPONENT.md +12 -8
  83. package/dist/skill/reference/components/file-upload/examples/overview.tsx +14 -3
  84. package/dist/skill/reference/components/gantt/COMPONENT.md +15 -5
  85. package/dist/skill/reference/components/gantt/examples/row-link.tsx +96 -0
  86. package/dist/skill/reference/components/kanban/COMPONENT.md +12 -4
  87. package/dist/skill/reference/components/kanban/examples/item-link.tsx +92 -0
  88. package/dist/skill/reference/components/list/COMPONENT.md +195 -0
  89. package/dist/skill/reference/components/list/examples/as-link.tsx +37 -0
  90. package/dist/skill/reference/components/list/examples/examples.module.css +18 -0
  91. package/dist/skill/reference/components/list/examples/overflow.tsx +46 -0
  92. package/dist/skill/reference/components/list/examples/overview.tsx +63 -0
  93. package/dist/skill/reference/components/list/examples/reorder.tsx +48 -0
  94. package/dist/skill/reference/components/list/examples/sizes.tsx +35 -0
  95. package/dist/skill/reference/components/list/examples/states.tsx +27 -0
  96. package/dist/skill/reference/components/list/examples/with-icon.tsx +60 -0
  97. package/dist/skill/reference/components/message/COMPONENT.md +18 -2
  98. package/dist/skill/reference/components/message/examples/bot-actions.tsx +51 -0
  99. package/dist/skill/reference/components/modal/COMPONENT.md +6 -4
  100. package/dist/skill/reference/components/page-content/COMPONENT.md +2 -2
  101. package/dist/skill/reference/components/page-content/examples/examples.module.css +5 -12
  102. package/dist/skill/reference/components/page-content/examples/list-detail.tsx +13 -18
  103. package/dist/skill/reference/components/select/COMPONENT.md +38 -7
  104. package/dist/skill/reference/components/select/examples/create-action.tsx +107 -0
  105. package/dist/skill/reference/components/select/examples/saving.tsx +47 -0
  106. package/dist/skill/reference/components/sheet/COMPONENT.md +4 -4
  107. package/dist/skill/reference/components/sparkline/COMPONENT.md +16 -2
  108. package/dist/skill/reference/components/sparkline/examples/compact.tsx +41 -0
  109. package/dist/skill/reference/components/sparkline/examples/examples.module.css +12 -0
  110. package/dist/skill/reference/components/textarea/COMPONENT.md +22 -2
  111. package/dist/skill/reference/components/textarea/examples/formatting.tsx +20 -0
  112. package/dist/skill/reference/components/visually-hidden/COMPONENT.md +6 -3
  113. package/dist/skill/reference/components/visually-hidden/examples/overview.tsx +2 -2
  114. package/package.json +2 -2
  115. package/src/components/chat/COMPONENT.md +13 -4
  116. package/src/components/chat/examples/states.tsx +100 -0
  117. package/src/components/chat-list/COMPONENT.md +256 -0
  118. package/src/components/chat-list/examples/controlled.tsx +103 -0
  119. package/src/components/chat-list/examples/examples.module.css +66 -0
  120. package/src/components/chat-list/examples/live.tsx +133 -0
  121. package/src/components/chat-list/examples/long-list.tsx +87 -0
  122. package/src/components/chat-list/examples/narrow.tsx +88 -0
  123. package/src/components/chat-list/examples/overview.tsx +194 -0
  124. package/src/components/chat-list/examples/search.tsx +71 -0
  125. package/src/components/chat-list/examples/sizes.tsx +61 -0
  126. package/src/components/chat-list/examples/states.tsx +107 -0
  127. package/src/components/chat-list/examples/structure.tsx +135 -0
  128. package/src/components/command-menu/COMPONENT.md +15 -11
  129. package/src/components/command-menu/examples/server-search.tsx +11 -9
  130. package/src/components/composer/COMPONENT.md +8 -7
  131. package/src/components/composer/examples/formatting.tsx +1 -1
  132. package/src/components/confirm-dialog/COMPONENT.md +1 -0
  133. package/src/components/data-table/COMPONENT.md +21 -6
  134. package/src/components/data-table/examples/groups.tsx +2 -1
  135. package/src/components/data-table/examples/trend.tsx +85 -0
  136. package/src/components/drawer/COMPONENT.md +7 -6
  137. package/src/components/drawer/examples/client-card.tsx +50 -0
  138. package/src/components/fieldset/COMPONENT.md +1 -0
  139. package/src/components/file-upload/COMPONENT.md +12 -8
  140. package/src/components/file-upload/examples/overview.tsx +14 -3
  141. package/src/components/gantt/COMPONENT.md +15 -5
  142. package/src/components/gantt/examples/row-link.tsx +96 -0
  143. package/src/components/kanban/COMPONENT.md +12 -4
  144. package/src/components/kanban/examples/item-link.tsx +92 -0
  145. package/src/components/list/COMPONENT.md +195 -0
  146. package/src/components/list/examples/as-link.tsx +37 -0
  147. package/src/components/list/examples/examples.module.css +18 -0
  148. package/src/components/list/examples/overflow.tsx +46 -0
  149. package/src/components/list/examples/overview.tsx +63 -0
  150. package/src/components/list/examples/reorder.tsx +48 -0
  151. package/src/components/list/examples/sizes.tsx +35 -0
  152. package/src/components/list/examples/states.tsx +27 -0
  153. package/src/components/list/examples/with-icon.tsx +60 -0
  154. package/src/components/message/COMPONENT.md +18 -2
  155. package/src/components/message/examples/bot-actions.tsx +51 -0
  156. package/src/components/modal/COMPONENT.md +6 -4
  157. package/src/components/page-content/COMPONENT.md +2 -2
  158. package/src/components/page-content/examples/examples.module.css +5 -12
  159. package/src/components/page-content/examples/list-detail.tsx +13 -18
  160. package/src/components/select/COMPONENT.md +38 -7
  161. package/src/components/select/examples/create-action.tsx +107 -0
  162. package/src/components/select/examples/saving.tsx +47 -0
  163. package/src/components/sheet/COMPONENT.md +4 -4
  164. package/src/components/sparkline/COMPONENT.md +16 -2
  165. package/src/components/sparkline/examples/compact.tsx +41 -0
  166. package/src/components/sparkline/examples/examples.module.css +12 -0
  167. package/src/components/textarea/COMPONENT.md +22 -2
  168. package/src/components/textarea/examples/formatting.tsx +20 -0
  169. package/src/components/visually-hidden/COMPONENT.md +6 -3
  170. package/src/components/visually-hidden/examples/overview.tsx +2 -2
  171. package/dist/components/composer/format.d.ts +0 -39
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from "./components/index";
2
2
  export * from "./hooks/useTokenValue";
3
3
  export * from "./hooks/useUploadQueue";
4
4
  export * from "./icons/index";
5
+ export { formatFileSize } from "./internal/fileSize";
5
6
  export { hueOf } from "./internal/palette";
6
7
  export * from "./internal/states";
7
8
  export type { TransferDirection, TransferHandler, TransferState } from "./internal/transfer";
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  Card,
12
12
  ChartFrame,
13
13
  Chat,
14
+ ChatList,
14
15
  CodeBlock,
15
16
  ColorPresets,
16
17
  CommandMenu,
@@ -33,6 +34,7 @@ import {
33
34
  Kanban,
34
35
  Kbd,
35
36
  LinkButton,
37
+ List,
36
38
  LoginForm,
37
39
  MediaViewer,
38
40
  Message,
@@ -66,7 +68,9 @@ import {
66
68
  celebrate,
67
69
  chartColors,
68
70
  datepickerPresets,
71
+ formatChatTime,
69
72
  formatDatepickerValue,
73
+ formatFileSize,
70
74
  formatMessageDate,
71
75
  formatMessageTime,
72
76
  getWaveform,
@@ -81,7 +85,7 @@ import {
81
85
  useSidebar,
82
86
  useTokenValue,
83
87
  useUploadQueue
84
- } from "./chunks/chunk-CUH6DJ57.js";
88
+ } from "./chunks/chunk-Z3WKKAS6.js";
85
89
  import {
86
90
  Avatar,
87
91
  Badge,
@@ -113,7 +117,7 @@ import {
113
117
  textTones,
114
118
  tones,
115
119
  variants
116
- } from "./chunks/chunk-WDTSHOTD.js";
120
+ } from "./chunks/chunk-E6SQBSDF.js";
117
121
  import {
118
122
  ControlSizeProvider,
119
123
  useOptionalControlSize
@@ -133,6 +137,7 @@ export {
133
137
  Card,
134
138
  ChartFrame,
135
139
  Chat,
140
+ ChatList,
136
141
  Checkbox,
137
142
  CodeBlock,
138
143
  ColorPresets,
@@ -164,6 +169,7 @@ export {
164
169
  Kbd,
165
170
  Label,
166
171
  LinkButton,
172
+ List,
167
173
  LoginForm,
168
174
  MediaViewer,
169
175
  Message,
@@ -209,7 +215,9 @@ export {
209
215
  createIcon,
210
216
  dataStates,
211
217
  datepickerPresets,
218
+ formatChatTime,
212
219
  formatDatepickerValue,
220
+ formatFileSize,
213
221
  formatMessageDate,
214
222
  formatMessageTime,
215
223
  getPasswordStrength,
@@ -1,5 +1,10 @@
1
1
  import type * as React from "react";
2
2
  export type VisuallyHiddenProps = React.HTMLAttributes<HTMLSpanElement> & {
3
+ /**
4
+ * An announcement region: `polite` — `role="status"`, read after what is being said; `assertive`
5
+ * — `role="alert"`, read at once. Both are atomic: the whole text is read on every change.
6
+ */
7
+ live?: "polite" | "assertive";
3
8
  ref?: React.Ref<HTMLSpanElement>;
4
9
  };
5
10
  /** The sr-only recipe as a class, for an element that must stay itself (a focusable file input). */
@@ -9,7 +14,7 @@ export declare const visuallyHiddenClass: string;
9
14
  * Public as `VisuallyHidden` (docs in `src/components/visually-hidden/`); inside the kit it replaces
10
15
  * the per-component `.srOnly` / `.visuallyHidden` CSS copies.
11
16
  */
12
- export declare function VisuallyHidden({ className, ...rest }: VisuallyHiddenProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function VisuallyHidden({ className, live, ...rest }: VisuallyHiddenProps): import("react/jsx-runtime").JSX.Element;
13
18
  export declare namespace VisuallyHidden {
14
19
  var displayName: string;
15
20
  }
@@ -16,6 +16,10 @@ export type AttachmentTileProps = Omit<React.HTMLAttributes<HTMLElement>, "child
16
16
  nameTitle?: string;
17
17
  /** The size, at the end of the bottom band; a video without it shows its duration. */
18
18
  meta?: string;
19
+ /** The file's address: the tile's middle becomes a link that opens it in a new tab. */
20
+ href?: string;
21
+ /** Name of the `href` link («Открыть договор.pdf»). */
22
+ openLabel?: string;
19
23
  /**
20
24
  * The pill at the top start — the format (JPG, PDF) or a word such as «Обложка»; decorative, the
21
25
  * name says it. On a picture or a video it stands on the media shade; a file passes a kit Badge.
@@ -56,7 +60,7 @@ export type AttachmentTileProps = Omit<React.HTMLAttributes<HTMLElement>, "child
56
60
  * picture, a video's first frame (lazy) with its play mark, or the sheet of a file; the upload disk
57
61
  * with its progress ring stands over the middle. Shared by FileUpload tiles and the Composer tray.
58
62
  */
59
- export declare function AttachmentTile({ id, kind, src, name, nameTitle, meta, badge, upload, uploadState, cancelLabel, retryLabel, onRetry, onRemove, removeLabel, pending, arrived, leaving, className, ref, ...rest }: AttachmentTileProps): import("react/jsx-runtime").JSX.Element;
63
+ export declare function AttachmentTile({ id, kind, src, name, nameTitle, meta, href, openLabel, badge, upload, uploadState, cancelLabel, retryLabel, onRetry, onRemove, removeLabel, pending, arrived, leaving, className, ref, ...rest }: AttachmentTileProps): import("react/jsx-runtime").JSX.Element;
60
64
  /** A row of tiles scrolls sideways — its hidden edges fade, a scroll rests on a tile, a vertical
61
65
  * wheel moves it — with the bar hidden. */
62
66
  export declare function AttachmentStripScroller({ children }: {
@@ -3,7 +3,7 @@ export type RichInline = {
3
3
  type: "text";
4
4
  text: string;
5
5
  } | {
6
- type: "bold" | "italic" | "strike";
6
+ type: "bold" | "italic" | "strike" | "spoiler";
7
7
  children: RichInline[];
8
8
  } | {
9
9
  type: "code";
@@ -29,12 +29,17 @@ export declare function safeHref(raw: string): string | null;
29
29
  export declare function parseInline(source: string): RichInline[];
30
30
  /** Blocks of a message: runs of text, quotes (`> ` lines) and fenced code. */
31
31
  export declare function parseRichText(source: string): RichBlock[];
32
+ export type RichTextLabels = {
33
+ /** Accessible name of a masked spoiler: its text is not read until it is revealed. */
34
+ spoiler: string;
35
+ };
32
36
  export type RichTextProps = {
33
37
  source: string;
34
38
  /** Rendered at the very end of the last line (a chat bubble's meta reserve). */
35
39
  end?: React.ReactNode;
36
40
  /** Marks every case-insensitive match of this text (a search through a chat). */
37
41
  highlight?: string;
42
+ labels?: Partial<RichTextLabels>;
38
43
  };
39
44
  /** A message text with its Markdown subset rendered safely; `end` closes the last text line. */
40
- export declare function RichText({ source, end, highlight }: RichTextProps): import("react/jsx-runtime").JSX.Element;
45
+ export declare function RichText({ source, end, highlight, labels: labelsProp }: RichTextProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Markdown formatting of a plain text (the formatting toolbar and hotkeys of Composer and Textarea,
3
+ * `useTextFormatting`): pure functions over the text and its selection, so a component only applies
4
+ * the result to its textarea. `internal/richText` renders the same subset.
5
+ */
6
+ /** A formatting command: what the toolbar button or the hotkey applies to the selection. */
7
+ export type TextFormat = "bold" | "italic" | "strikethrough" | "code" | "spoiler" | "link" | "quote";
8
+ /** Names of the formatting toolbar and of its tools, one per format. */
9
+ export type TextFormattingLabels = Record<TextFormat, string> & {
10
+ /** Accessible name of the formatting toolbar. */
11
+ formatting: string;
12
+ };
13
+ export declare const TEXT_FORMATTING_LABELS: TextFormattingLabels;
14
+ /** Every format in toolbar order. */
15
+ export declare const TEXT_FORMATS: readonly TextFormat[];
16
+ /** Text with its selection, `start <= end` (a caret when equal). */
17
+ export type TextSelection = {
18
+ text: string;
19
+ start: number;
20
+ end: number;
21
+ };
22
+ /** The text and selection after `format` is applied to the selection. */
23
+ export declare function applyFormat(selection: TextSelection, format: TextFormat): TextSelection;
24
+ /**
25
+ * The one span that differs between two texts (common prefix and suffix removed), so the change can
26
+ * be typed into the textarea as one edit and stay on its undo stack.
27
+ */
28
+ export declare function diffRange(before: string, after: string): {
29
+ start: number;
30
+ end: number;
31
+ insert: string;
32
+ };
33
+ /**
34
+ * The format a key press asks for: Mod+B · Mod+I · Mod+Shift+X · Mod+E · Mod+Shift+H · Mod+K ·
35
+ * Mod+Shift+9. The spoiler is not Telegram's Mod+Shift+P: Firefox and Edge reserve it for a private
36
+ * window and never hand it to the page.
37
+ */
38
+ export declare function formatForKey(event: {
39
+ key: string;
40
+ code: string;
41
+ metaKey: boolean;
42
+ ctrlKey: boolean;
43
+ shiftKey: boolean;
44
+ altKey: boolean;
45
+ }): TextFormat | null;
46
+ /** Shortcut of every format, by modifier names: rendered as `⌘B` on Apple systems, `Ctrl+B` elsewhere. */
47
+ export declare const FORMAT_SHORTCUTS: Record<TextFormat, {
48
+ shift: boolean;
49
+ key: string;
50
+ }>;
51
+ /** `⌘⇧X` on Apple systems, `Ctrl+Shift+X` elsewhere. */
52
+ export declare function shortcutText(format: TextFormat, apple: boolean): string;
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+ import { type ControlSize } from "./states";
3
+ import { type TextFormattingLabels } from "./textFormatting";
4
+ export type TextFormattingOptions = {
5
+ textareaRef: React.RefObject<HTMLTextAreaElement | null>;
6
+ /** The hotkeys and the toolbar. */
7
+ enabled: boolean;
8
+ /** Whether the toolbar may show now (not while the field is disabled, read-only or covered). */
9
+ toolbar: boolean;
10
+ labels: TextFormattingLabels;
11
+ /** Tier of the field; the tools stand one tier smaller. */
12
+ size: ControlSize;
13
+ /** Id of the textarea (`aria-controls` of the toolbar). */
14
+ controlsId?: string;
15
+ };
16
+ export type TextFormatting = {
17
+ /** On the textarea's `onSelect`. */
18
+ onSelect: () => void;
19
+ /** Call first in the textarea's `onKeyDown`; `true` when the key was a formatting one and is used. */
20
+ onKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => boolean;
21
+ /** On the `position: relative` box around the field and the toolbar. */
22
+ anchorProps: Pick<React.HTMLAttributes<HTMLElement>, "onFocus" | "onBlur">;
23
+ /** The toolbar while it shows or leaves. */
24
+ toolbar: React.ReactNode;
25
+ };
26
+ export declare function useTextFormatting({ textareaRef, enabled, toolbar: toolbarAllowed, labels, size, controlsId, }: TextFormattingOptions): TextFormatting;
@@ -235,7 +235,7 @@ it saves).
235
235
  | `Icon`, `createIcon` | kit icon set and domain glyphs — see Icons below |
236
236
  | `chartColors` | the chart palette as CSS values for a chart library: `series` 1…8 in order, `other`, `grid`, `axis`, `scale` — follows the theme |
237
237
  | `useTokenValue("--prime-space-7")` | a length token in px for geometry CSS cannot take (SVG coordinates, canvas, virtual rows); follows a theme switch |
238
- | `VisuallyHidden` | text for screen readers only (an icon-only part's name, a spoken count); `role="status"` on it for a polite announcement |
238
+ | `VisuallyHidden` | text for screen readers only (an icon-only part's name, a spoken count); `live="polite"` on it for an announcement (`"assertive"` only for an error that blocks) |
239
239
 
240
240
  ## Icons
241
241
 
@@ -34,8 +34,10 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
34
34
  - Show `error` after blur or on submit; `<form noValidate onSubmit>` + `<Button.Root type="submit" loading>` — not validation on every keystroke.
35
35
  - Groups of fields: `<Fieldset label="Контакты" disabled={saving}>` with the submit `<Button.Root loading>` outside it — not a hand-reset `<fieldset>` with a `Typography` heading, not `disabled` on every field while saving. One choice with a heading (`Radio.Group label`) needs no Fieldset.
36
36
  - Uploads: `const queue = useUploadQueue({ upload })` → `<FileUpload.Root queue={queue} />` (`FileUpload.Queue` when the picker lives elsewhere) — progress, «В очереди», the reason of a failure, retry and remove come with it (three at once by default, `concurrency`); not a hand-kept list of files with own progress bars. A photo appears at once from its local preview.
37
+ - After-upload work (attach the file to a record) runs inside `upload` after the request resolves — `context.id` is the item; a file the user can open is `FileUploadFile.href`, not your own download button beside the row.
37
38
  - A choice in a form (SegmentedControl, ButtonGroup) is a field too: `label`, `hint`, `error` on its root — not a `Label` wired by hand.
38
39
  - A record card that saves each field: `<Input.Root onCommit={save}>` (save returns a Promise) — not hand-written blur / Enter / Escape handlers or a field disabled while saving.
40
+ - Markdown text outside a chat (an announcement, a task description) is `<Textarea.Root formatting>`: the Composer's toolbar, hotkeys and subset (`**`, `_`, `~~`, `` ` ``, `||spoiler||`, links, `> `) — not a hand-made toolbar over a textarea. Turn it on only where the text is shown with its Markdown.
39
41
  - Copy or call next to a value: `Input.CopyButton`, `Input.Action` inside `Input.Wrapper` — not a Button beside the field; a password: `Input.RevealButton`.
40
42
 
41
43
  ## State
@@ -44,6 +46,9 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
44
46
  - Controlled triads, same names everywhere: `value` / `onValueChange`, `checked` / `onCheckedChange`, `open` / `onOpenChange` — not `onChange(event)` (except the native `Input.Field` / Textarea `onChange`), `isOpen`, `onToggle`.
45
47
  - Selection count is `multiple` (`Select.Root multiple`) — not `type="multiple"`.
46
48
  - A slow or risky action is an async event: return the request's Promise from `onConfirm`, `onCommit`, `onReorder`, `onBarChange`, `onSend`, a Switch's `onCheckedChange`, a Slider's `onCommit`, a DigitInput's `onComplete`, a Kanban or FileUpload change, a Badge `onRemove`, TagSelect `onCreate`… — the component shows pending, keeps the change on resolve and rolls it back on reject; not `true` / `false`, not your own pending flag, no `onBeforeX`.
49
+ - A Select that saves on pick: return the save's Promise from `onValueChange` — the value shows at once with a Spinner on the trigger and a rejection rolls it back (`labels.changeFailed`); never reset the value by hand. A «Новый клиент» row in the list is `Select.Action` (`onSelect`), never a fake `Select.Item` with a sentinel value or a button under the field.
50
+ - An option reads its parts by element type: put `Avatar.Root`, `Thumbnail.Root`, `Badge.Root` and `Select.Item*` straight in the item and call your helper as a function (`{clientAvatar(c)}`), not as `<ClientAvatar />`; anything else trailing goes in `Select.ItemMeta`.
51
+ - Create from a search: one `persistent` `CommandMenu.Item` at the end («Создать клиента «{query}»», while the query is not empty) — it stays when a namesake matches; `CommandMenu.Empty` holds only the text.
47
52
  - A value that settles at the end of a gesture saves in its own event: Slider `onCommit` — not a request on every `onValueChange` step.
48
53
 
49
54
  ## Text: labels vs children
@@ -87,12 +92,18 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
87
92
  - A chart with axes or several series: `<ChartFrame.Root series state>` around the library (or your SVG) with `ChartFrame.Plot renderPlot`, `Legend`, `Table` — not a hand-made Card with a grey box while loading and a legend of colored dots.
88
93
  - Amounts, counts and dates are `numeric` columns; statuses are `Badge.Root color` from one status → color map.
89
94
  - 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.
95
+ - 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`.
96
+ - 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.
97
+ - A trend in a table row is `<Sparkline layout="compact">` in the column's `cell` (a column `width`, series built once, not inline); the number itself goes in its own column.
90
98
  - Properties of one record: `<DescriptionList.Root>` of `DescriptionList.Item` — not a hand-made grid of label / value text, not a one-row DataTable.
91
99
  - Who is busy when, a plan with dates: `Gantt` with `onBarChange` / `onBarCreate` — not bars drawn by hand or a DataTable of start and end dates. Statuses moved by hand: `Kanban`.
100
+ - Open a board card with `Kanban.Root onItemClick` (click, Enter, Space, never after a drag) or `getItemHref`; a Gantt row with `onRowClick` / `getRowHref` (the DataTable contract), a bar with `onSelect`; more facts in a bar tooltip with `renderBarTooltip` (it adds lines under the dates). A legend of hues is a row of `Badge.Root color` + `Badge.Dot` in the host's `toolbar`, the category also in text. Hour slots (bookings, appointments) are not Gantt — its unit is a calendar day.
92
101
 
93
102
  ## Chat
94
103
 
95
104
  - A conversation is `Chat`: `<Chat.Root typing selected search>` with `Chat.Header` (avatar, `Chat.Title`, `Chat.Description`, `Chat.Actions` + `Chat.SearchButton`), `Chat.Search`, `Chat.Pinned`, `<Chat.Messages items getKey getAuthor getDate renderMessage>` and `<Chat.Footer><Composer.Root /></Chat.Footer>` — not a header, a ScrollContainer and a list of your own, not Cards or a Timeline styled as bubbles, not a Textarea with a send button. A list without the rest is `Chat.Root` with `Chat.Messages` alone; one message on its own is `Message.Root`.
105
+ - The list of conversations is `ChatList` — not a `List` or Cards with your own preview, unread counts and «печатает…». Back button and avatar in the chat header are plain children of `Chat.Header`: it lays them out side by side.
106
+ - A dialog loading is `<Chat.Messages loading>` (bubble skeletons that cross-fade into the list) — not a Spinner or grey boxes. A bot's buttons under a message are `Message.Actions` rows of `Button.Root variant="soft" tone="neutral"` (`asChild` for links), never buttons inside `Message.Text`.
96
107
  - `renderMessage` returns a `Message.Root` (its `value` and `position` are set for you) or a `Chat.Service`; keep it stable with `useCallback` (other messages it looks up through a ref) — an item renders again only when it, its place or the function changes. Dates, runs and the unread band (`unread={key}`) come from the list.
97
108
  - A group chat is `<Chat.Messages type="group">`: put `Message.Author` (`color={hueOf(user.id)}`, `tag`, `Message.AuthorIcon` for a crown) and `Message.Avatar` on every message — the list shows the header on the first of a run and the avatar on the last; a direct chat shows neither.
98
109
  - A voice message is `VoicePlayer` inside `Message.Root`; a round video is `VideoNote` inside `Message.Root`; photos, files, places and contacts are `Message` parts — not FileUpload rows or Cards. The same album outside a chat is `Gallery`. The two players share one API: learn one, use both.
@@ -111,6 +122,7 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
111
122
  ## Overlays
112
123
 
113
124
  - Blocking decision or ≤ 4 fields → `Modal` (`<Modal.Content size="s">` for a confirm); long form, filters, details → `Drawer`; a short task that ends by itself (a date, an action, a note) → `Sheet`; small panel at a control → `Popover`; list of actions → `Dropdown`; a field value → `Select`; a name for an icon button → `Tooltip`; ⌘K → `CommandMenu`; a photo, a scan or a video full screen from its thumbnail → `MediaViewer` (an album in a grid is `Gallery`, which opens it for you; a standalone viewer keeps `open` and `index` in two states) — not a Modal with an `<img>`.
125
+ - A record's drawer or modal shows its person or object as `Drawer.Icon` / `Modal.Icon` around `Avatar.Root` / `Thumbnail.Root`. A confirm that asks for input (a reason) is a `Modal size="s"` with the field, `Modal.Confirm` and `Modal.Guard busy` — `ConfirmDialog` is a question only.
114
126
  - Never a hand-made `div` backdrop, dialog, menu or tooltip.
115
127
  - Every Modal and Drawer has all three zones: `Header` (title, ×) · `Body` (content, scrolls) · `Footer` (actions). Details: `<Drawer.Footer><Drawer.Close><Button.Root variant="outline" tone="neutral">Закрыть</Button.Root></Drawer.Close></Drawer.Footer>`, plus the main action last; a form: «Отмена» + submit. Not a drawer that ends with the body, not Save at the bottom of the body. A `Sheet` (from the bottom edge) is the opposite: no footer by default — a pick or a swipe ends it — and its header is optional: keep it when the content needs a title, drop it (× and hairline go too) when the content speaks for itself, with `aria-label` on `Sheet.Content`. Never `Drawer.Content side="bottom"`.
116
128
  - Content inside a Drawer, a Sheet or a Card is the host's own: a calendar is `Datepicker.Panel` straight in the body, across its width — no card, no padded box around it.
@@ -163,7 +175,7 @@ Full rules: [responsive.md](responsive.md).
163
175
  - Breakpoints 640 · 768 · 1024 · 1280 only for the app frame; inside a page — intrinsic grids (`auto-fill, minmax`) and container queries.
164
176
  - A wide table scrolls inside itself; offer `DataTable.ColumnsMenu` in its `toolbar` — never drop columns by breakpoint, never hand-build the chooser.
165
177
  - 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.
166
- - A list + detail screen is `PageContent.Split` in a `PageContent.Root fill` page; a full-height table is `scrollHeight="fill"` — never a height computed from `100vh`.
178
+ - A list + detail screen is `PageContent.Split` in a `PageContent.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`.
167
179
  - 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.
168
180
  - Your hover styles inside `@media (hover: hover)`; nothing hover-only. Do not wrap kit fields to raise their text for iOS — they keep 16px on touch already.
169
181
  - A long form saves through the panel action with a count («Сохранить 3 изменения») — not a sticky bar at the bottom of a phone.
@@ -11,9 +11,9 @@ confuse. Links: [components.md](components.md). Whole screens: [composition.md](
11
11
  | type a value (text, number, code, file, photos), group the fields of a form | Inputs — Input, Textarea, DigitInput, FileUpload, Label, Hint, Fieldset |
12
12
  | pick from options, toggle, set a range, filter, a date or a color | Selection — Checkbox, Radio, Switch, SegmentedControl, Slider, Select, NativeSelect, TagSelect, SmartFilter, Datepicker, ColorField, ColorPicker, ColorSwatches |
13
13
  | see data: status labels, people, objects, numbers and their trend, properties of a record, rows, a board, a schedule, events, code, disclosed sections | Data display — Badge, Avatar, Thumbnail, Kbd, Card, DescriptionList, DataTable, Kanban, Gantt, Timeline, CodeBlock, Accordion, Sparkline, ChartFrame |
14
- | hold a conversation: read and send chat messages of every kind | Messages — Chat, Message, Gallery, VoicePlayer, VideoNote, Composer |
14
+ | hold a conversation: pick a dialog, read and send chat messages of every kind | Messages — Chat, ChatList, Message, Gallery, VoicePlayer, VideoNote, Composer |
15
15
  | learn what happened, how far along it is, or watch a region load and change state; say it to a screen reader | Status and loading — Banner, Notification, ProgressBar, ProgressCircle, Spinner, Skeleton, Crossfade, EmptyPage, VisuallyHidden |
16
- | move between places: app sections, panels of a screen, the path, pages, steps | Navigation — Tabs, Breadcrumb, Pagination, Stepper, Sidebar, BottomNav |
16
+ | move between places: app sections, panels of a screen, the records of a list, the path, pages, steps | Navigation — Tabs, Breadcrumb, Pagination, List, Stepper, Sidebar, BottomNav |
17
17
  | see something on top of the page, or a photo or video full screen | Overlays — Tooltip, Popover, Dropdown, Modal, ConfirmDialog, Drawer, Sheet, CommandMenu, MediaViewer |
18
18
  | get the app frame and the structure of a page | Page — AppShell, AppHeader, PageContent, PageToolbar, Divider, ScrollContainer |
19
19
  | drag things by hand | Interaction — Dnd (a status board: Kanban in Data display) |
@@ -74,7 +74,10 @@ never a Timeline or a list of Cards styled as bubbles. Code → CodeBlock. A num
74
74
  `Chat.Messages` (from `items` with `renderMessage`, or `Message.Root` children) and `Chat.Footer`
75
75
  with the Composer — never a hand-built header, ScrollContainer and list. A list of messages without
76
76
  the rest (comments as a chat, a read-only history) → `Chat.Root` with `Chat.Messages` alone; one
77
- message on its own → `Message.Root`. A group chat → `Chat.Messages type="group"`: `Message.Author`
77
+ message on its own → `Message.Root`. The list of conversations beside the chat (the dialogs pane of a
78
+ messenger or an inbox) → `ChatList` (`items`, `renderItem` with `ChatList.Item`, `value`): unread
79
+ counts, «печатает…», drafts, ticks, a row menu and search come with it — never `List` with a hand-made
80
+ count and preview; `List` stays for records that are not conversations (tickets, letters, invoices). A group chat → `Chat.Messages type="group"`: `Message.Author`
78
81
  (`tag`, `Message.AuthorIcon`) and `Message.Avatar` on every message, the list shows them on the
79
82
  first and the last of a run. A voice message → `VoicePlayer` inside `Message.Root`; a call recording
80
83
  outside a chat → `VoicePlayer` alone. A round video message → `VideoNote` inside `Message.Root`
@@ -125,7 +128,8 @@ filter above a single table goes into the DataTable `toolbar` instead. A page th
125
128
  height to the work (a board, a chat) may let `AppHeader` carry the page instead: `AppHeader.Title
126
129
  as="h1"` and the PageToolbar in `AppHeader.Tools` — then main has no `PageContent.Header`. A record
127
130
  opened from a table: its own page → DataTable `getRowHref`; in place → `onRowClick` + Drawer; a list
128
- worked through record by record (mail, chat, tickets) → `PageContent.Split`. Sections of a long rail → `Sidebar.Group
131
+ worked through record by record (mail, chat, tickets) → `PageContent.Split` with a `List` of the records
132
+ (`current` on the open one). Sections of a long rail → `Sidebar.Group
129
133
  collapsible`; a section with sub-pages → `Sidebar.Sub`; the signed-in user → `Sidebar.Account` in a
130
134
  `Dropdown.Trigger` at the bottom of `Sidebar.Footer`.
131
135
 
@@ -133,6 +137,7 @@ collapsible`; a section with sub-pages → `Sidebar.Sub`; the signed-in user →
133
137
 
134
138
  | Pair | Rule |
135
139
  |---|---|
140
+ | List / Sidebar / Tabs / Card list / DataTable | List: the records of a list + detail screen (dialogs, tickets, mail), one open at a time (`current`), each row a button or a link with a text line, a preview, meta and a count. Sidebar: the sections of the **app**. Tabs: panels of **one screen**. `Card.List` (`variant="list"`): static facts in a card, nothing opens. DataTable: many records compared by columns, sorted, filtered, selected in bulk. |
136
141
  | Tabs / SegmentedControl | Tabs switch **panels of content** (navigation inside a screen). SegmentedControl switches a **value or mode** of the same content (period, view mode, filter). |
137
142
  | Modal / Drawer / Sheet / Popover / Tooltip | Modal: blocking decision or a short form (≤ 4 fields) that needs full attention. Drawer: long form, filters, record details beside the page, while keeping page context. Sheet: a short task that ends by itself — a pick, an action, a short note — raised from the bottom edge. Popover: small non-modal panel anchored to a control (quick edit, explanation with a link). Tooltip: a few words naming or explaining a control, no interactive content. |
138
143
  | Select / NativeSelect | Select: the kit's floating list — icons, descriptions, groups, search, `multiple`, `clearable`. NativeSelect: a plain text list where the OS picker is better (phones) or the value must post with a form by `name`. |
@@ -24,7 +24,7 @@ Type a value, and the field anatomy: label, hint, error.
24
24
  | Component | Purpose | Docs |
25
25
  |---|---|---|
26
26
  | Input | Single-line text field with label, hint, error and slots for icons, affixes, a clear button and a counter. | [COMPONENT.md](reference/components/input/COMPONENT.md) · [examples](reference/components/input/examples/) |
27
- | Textarea | Multi-line text field with label, hint, error and a character counter; grows with its content from `rows` lines up to `maxRows`, then scrolls inside. | [COMPONENT.md](reference/components/textarea/COMPONENT.md) · [examples](reference/components/textarea/examples/) |
27
+ | Textarea | Multi-line text field with label, hint, error and a character counter; grows with its content from `rows` lines up to `maxRows`, then scrolls inside; `formatting` adds the Composer's Markdown toolbar and hotkeys. | [COMPONENT.md](reference/components/textarea/COMPONENT.md) · [examples](reference/components/textarea/examples/) |
28
28
  | DigitInput | A row of square single-digit cells for a fixed-length code (OTP from SMS, PIN, pickup code), with the field label, hint and error. | [COMPONENT.md](reference/components/digit-input/COMPONENT.md) · [examples](reference/components/digit-input/examples/) |
29
29
  | FileUpload | Files and the image gallery in one component: a document field (rows), file cards, an image gallery with a cover (`trigger="tile"` `layout="grid"` `cover`) or an attachment tray (`trigger="button"` + `FileUpload.Queue layout="strip"`); uploads through a queue (`useUploadQueue`), reorder and server saves. | [COMPONENT.md](reference/components/file-upload/COMPONENT.md) · [examples](reference/components/file-upload/examples/) |
30
30
  | Label | Field label (native `<label>`) with required and optional markers. | [COMPONENT.md](reference/components/label/COMPONENT.md) · [examples](reference/components/label/examples/) |
@@ -60,7 +60,7 @@ Show data: labels, people, objects, cards, tables, boards, feeds, code, disclose
60
60
  | Badge | The kit's one chip: a static label for a status, category or count, a removable value or applied filter, and a pressable toggle with a hover action — in a palette color. | [COMPONENT.md](reference/components/badge/COMPONENT.md) · [examples](reference/components/badge/examples/) |
61
61
  | Avatar | A round photo of a person or organization with an initials or icon fallback, a presence dot and overlapping groups. | [COMPONENT.md](reference/components/avatar/COMPONENT.md) · [examples](reference/components/avatar/examples/) |
62
62
  | Thumbnail | A preview of an object — product, vehicle, file, cover — at a fixed aspect ratio (`ratio`), an image or a video (`Thumbnail.Video`, first frame and length), a colored icon fallback, and a selectable tile (`checked`). | [COMPONENT.md](reference/components/thumbnail/COMPONENT.md) · [examples](reference/components/thumbnail/examples/) |
63
- | Sparkline | A small line or bar chart with its headline — the value at rest, its change against the point before and its date — where any point can be scrubbed. | [COMPONENT.md](reference/components/sparkline/COMPONENT.md) · [examples](reference/components/sparkline/examples/) |
63
+ | Sparkline | A small line or bar chart with its headline — the value at rest, its change against the point before and its date — where any point can be scrubbed; `layout="compact"` — just the plot, for a table cell. | [COMPONENT.md](reference/components/sparkline/COMPONENT.md) · [examples](reference/components/sparkline/examples/) |
64
64
  | ChartFrame | A dashboard panel around any chart (Recharts, ECharts, visx, Chart.js or a hand-drawn SVG): title, headline value, a measured plot area, a series legend that hides series (`hiddenSeries`), loading / empty / error states (`state`, `onRetry`) that cross-fade, a tooltip body and a data table for screen readers. | [COMPONENT.md](reference/components/chart-frame/COMPONENT.md) · [examples](reference/components/chart-frame/examples/) |
65
65
  | Kbd | A key cap for a keyboard key or a shortcut, rendered as a native `<kbd>`. | [COMPONENT.md](reference/components/kbd/COMPONENT.md) · [examples](reference/components/kbd/examples/) |
66
66
  | Card | A filled surface block with structural templates for metrics, charts, lists, calls to action and covers. | [COMPONENT.md](reference/components/card/COMPONENT.md) · [examples](reference/components/card/examples/) |
@@ -78,12 +78,13 @@ A conversation: message bubbles of every kind and the composer.
78
78
 
79
79
  | Component | Purpose | Docs |
80
80
  |---|---|---|
81
- | Chat | A conversation assembled: `Chat.Header` (avatar, title, status that turns into «печатает…», actions, `Chat.SearchButton`), `Chat.Search` (marks, counts and steps through matches), `Chat.Pinned`, `Chat.Messages` — the one message list: from `items` + `renderMessage` (dates, runs, unread band added) or `Message.Root` children, virtualized, new messages that enter by themselves, the «к последним» button, read receipts, jumps, selection with `Chat.SelectionBar`, history paging, `type="group"` author headers and avatars — and `Chat.Footer` for the Composer. | [COMPONENT.md](reference/components/chat/COMPONENT.md) · [examples](reference/components/chat/examples/) |
82
- | Message | One chat message in its shell (bubble, picture, circle): incoming / outgoing, runs of one author (the tail ends a run), time and delivery ticks in the corner, a group chat's author header (`Message.Author` with `tag` and `Message.AuthorIcon`), reply, forwarded, reactions with the reaction bar, the context menu, and every content kind (text with Markdown, `Message.Media` albums, round video, voice, file, location, contact, link preview); `Message.Typing`. | [COMPONENT.md](reference/components/message/COMPONENT.md) · [examples](reference/components/message/examples/) |
81
+ | Chat | A conversation assembled: `Chat.Header` (avatar, title, status that turns into «печатает…», actions, `Chat.SearchButton`), `Chat.Search` (marks, counts and steps through matches), `Chat.Pinned`, `Chat.Messages` — the one message list: from `items` + `renderMessage` (dates, runs, unread band added) or `Message.Root` children, virtualized, new messages that enter by themselves, the «к последним» button, read receipts, jumps, selection with `Chat.SelectionBar`, history paging, `type="group"` author headers and avatars — and `Chat.Footer` for the Composer; a first-load state of bubble skeletons (`Chat.Messages loading`). | [COMPONENT.md](reference/components/chat/COMPONENT.md) · [examples](reference/components/chat/examples/) |
82
+ | Message | One chat message in its shell (bubble, picture, circle): incoming / outgoing, runs of one author (the tail ends a run), time and delivery ticks in the corner, a group chat's author header (`Message.Author` with `tag` and `Message.AuthorIcon`), reply, forwarded, reactions with the reaction bar, the context menu, and every content kind (text with Markdown, `Message.Media` albums, round video, voice, file, location, contact, link preview); `Message.Typing`; a bot's inline keyboard (`Message.Actions`); spoilers in the Markdown. | [COMPONENT.md](reference/components/message/COMPONENT.md) · [examples](reference/components/message/examples/) |
83
83
  | Gallery | Photos and videos as a messenger album: one, or up to ten laid out from their aspect ratios with nothing measured; video tiles with a first frame and length, the shared transfer disk per tile, tiles that open the album in MediaViewer (`viewer={false}` to open your own from `onOpenChange` / `onIndexChange`). `Message.Media` is a Gallery in a message. | [COMPONENT.md](reference/components/gallery/COMPONENT.md) · [examples](reference/components/gallery/examples/) |
84
84
  | VoicePlayer | A voice message player: a round play disk, a waveform that is the seek slider, and the time with the playback speed — in a chat bubble or standalone; one sound at a time on the page; `getWaveform` for audio without levels. | [COMPONENT.md](reference/components/voice-player/COMPONENT.md) · [examples](reference/components/voice-player/examples/) |
85
85
  | VideoNote | A round video message: a still first frame with the play disk, played with sound on a press; the same player API as VoicePlayer (transcript, transfer either way, one sound at a time); alone in `Message.Root` it is the message. | [COMPONENT.md](reference/components/video-note/COMPONENT.md) · [examples](reference/components/video-note/examples/) |
86
86
  | Composer | The message input of a chat: a field that grows from one line, Enter to send, attachments through an upload queue, a reply / edit bar whose id goes with the send, Markdown formatting of a selection, voice and round video messages with pause, and the «печатает…» signal (`onActivityChange`). | [COMPONENT.md](reference/components/composer/COMPONENT.md) · [examples](reference/components/composer/examples/) |
87
+ | ChatList | The conversation list of a messenger, built from List: dialogs from `items` + `renderItem` (`ChatList.Item` with the avatar, name, time, preview), the open one `value`, a search field that marks matches, unread / muted / «@» counts, pin, delivery ticks, «печатает…» and drafts that cross-fade, a row menu at the pointer (`ChatList.ItemMenu`), rows that glide to a new order, virtualized rows, loading placeholders and `hasMore` / `onLoadMore`. | [COMPONENT.md](reference/components/chat-list/COMPONENT.md) · [examples](reference/components/chat-list/examples/) |
87
88
 
88
89
  ## Status and loading (`status`)
89
90
 
@@ -103,13 +104,14 @@ Say what happens: system messages, progress, loading placeholders, state swaps,
103
104
 
104
105
  ## Navigation (`navigation`)
105
106
 
106
- Move between places: app navigation (side rail, bottom bar on phones), tabs, the path, pages, steps.
107
+ Move between places: app navigation (side rail, bottom bar on phones), tabs, the records of a list, the path, pages, steps.
107
108
 
108
109
  | Component | Purpose | Docs |
109
110
  |---|---|---|
110
111
  | Tabs | Tabs for navigating between content panels of one screen. | [COMPONENT.md](reference/components/tabs/COMPONENT.md) · [examples](reference/components/tabs/examples/) |
111
112
  | Breadcrumb | Breadcrumbs: the path to the current page. | [COMPONENT.md](reference/components/breadcrumb/COMPONENT.md) · [examples](reference/components/breadcrumb/examples/) |
112
113
  | Pagination | Page-by-page navigation: arrows, page numbers with ellipsis and a compact «3 / 12» view. | [COMPONENT.md](reference/components/pagination/COMPONENT.md) · [examples](reference/components/pagination/examples/) |
114
+ | List | A selectable list of rows — the list half of a list + detail screen: conversations, tickets, mail, records; the open one is `current`. | [COMPONENT.md](reference/components/list/COMPONENT.md) · [examples](reference/components/list/examples/) |
113
115
  | Stepper | Steps of a multi-step process with pending, active, completed and danger statuses. | [COMPONENT.md](reference/components/stepper/COMPONENT.md) · [examples](reference/components/stepper/examples/) |
114
116
  | Sidebar | App side navigation in three modes — expanded, compact, hidden — and an off-canvas panel on narrow screens. | [COMPONENT.md](reference/layout/sidebar/COMPONENT.md) · [examples](reference/layout/sidebar/examples/) |
115
117
  | BottomNav | Phone navigation: 2–6 sections at the bottom, flat or a floating glass capsule, icons with or without labels, an optional round action. | [COMPONENT.md](reference/layout/bottom-nav/COMPONENT.md) · [examples](reference/layout/bottom-nav/examples/) |
@@ -17,7 +17,11 @@ is, and is shown live with its code in the playground (category «Компози
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
18
  | 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
19
  | 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
- | Messenger | [chat.tsx](patterns/chat.tsx) | PageContent.Split of dialogs 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` |
20
+ | 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
+ | 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
+ | 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
+ | 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` |
21
25
 
22
26
  Pick the closest one, rename the domain, keep the skeleton. Change structure only when the task needs
23
27
  it, and then by the rules below.
@@ -176,6 +180,10 @@ Hierarchy comes from space and a handful of type roles, not from size jumps, col
176
180
 
177
181
  - A list page is `DataTable` with the filters in its `toolbar` — `SmartFilter.Toolbar` and
178
182
  `SmartFilter.Chips` inside `SmartFilter.Root` (or one SegmentedControl / Select for a single field).
183
+ - Where the filters go: tools of one table live in its `toolbar`; when the page itself carries
184
+ sections with counts, a view switch (board / list, grid / list) or filters that drive more than one
185
+ table — a board, a schedule, a media grid — the page gets `PageToolbar` and the table keeps only its
186
+ own tools (`DataTable.ColumnsMenu`).
179
187
  - Paging is built in: `paging="pages"` (default, Pagination and the «Показано 1–6 из 10» range in the
180
188
  footer), `paging="infinite"` for feeds, `paging="none"` for short lists on dashboards and details.
181
189
  - When the server holds more than you load (a CRM list, a register): `source="server"` — `rows` is the
@@ -4,6 +4,8 @@ Every screen sits in one frame: `AppShell.Root` with Nav / `AppHeader` / Main (t
4
4
  the nav rail one layer above it) → `PageContent` (header + body). `AppShell.Main` carries the page gutters
5
5
  and `PageContent.Body` spaces its blocks 40 apart, so your code adds **no outer padding and no
6
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 40 from 1024px: a page header starts
8
+ there; never cancel it with a negative margin or a padding override.
7
9
 
8
10
  The page recipes are whole, working screens in [patterns/](patterns/) — start from the closest one
9
11
  ([the list](composition.md#patterns)) and keep its skeleton. The rules behind them are in [composition.md](composition.md).
@@ -191,7 +193,8 @@ from what people do, not from how the backend is split. The full guide with the
191
193
  `PageContent.Detail` in a filling page. Side by side from 48rem of the split's own width, one pane
192
194
  at a time below it with `PageContent.BackButton` first in the detail's header; `open` is «a record
193
195
  is chosen». Each pane gives its last child the free height: a Card with its own `ScrollContainer`
194
- (`anchor="end"` for a thread). Details that open over a table and close again are a Drawer instead.
196
+ (`anchor="end"` for a thread). The records in the list pane are a `List` (`current` on the open
197
+ one). Details that open over a table and close again are a Drawer instead.
195
198
 
196
199
  ## Which pattern to start from
197
200
 
@@ -0,0 +1,18 @@
1
+ /* App shell: the frame (Sidebar / BottomNav / AppHeader) lays itself out; this file only places the
2
+ home page's blocks. Blocks 40 apart come from PageContent.Body. */
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
+ }
10
+
11
+ /* A list row: the text at the start, the meta (time, sum) at the end. */
12
+ .row {
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: space-between;
16
+ gap: var(--prime-space-4);
17
+ min-width: 0;
18
+ }