prime-ui-kit 0.21.2 → 0.21.4

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 +2 -2
  2. package/THIRD_PARTY_NOTICES +13 -0
  3. package/dist/chunks/{chunk-T7IEJSTH.js → chunk-HMLQCKRL.js} +36 -0
  4. package/dist/chunks/{chunk-7JYPFRZ3.js → chunk-PFASPQSJ.js} +393 -354
  5. package/dist/chunks/{chunk-SQGPQOUO.js → chunk-SUGCIPD7.js} +1 -1
  6. package/dist/chunks/{chunk-ME4VEAQ5.js → chunk-UNNQITFE.js} +2132 -1619
  7. package/dist/chunks/{chunk-VQ3UCVDV.js → chunk-WGXXPJDR.js} +5 -2
  8. package/dist/color-picker.js +3 -3
  9. package/dist/components/chat-list/ChatList.d.ts +4 -3
  10. package/dist/components/command-menu/CommandMenu.d.ts +1 -4
  11. package/dist/components/composer/Composer.d.ts +1 -1
  12. package/dist/components/data-table/DataTable.d.ts +9 -7
  13. package/dist/components/data-table/Footer.d.ts +7 -3
  14. package/dist/components/datepicker/DatepickerPanel.d.ts +2 -2
  15. package/dist/components/dnd/labels.d.ts +2 -0
  16. package/dist/components/field-list/FieldList.d.ts +2 -4
  17. package/dist/components/file-upload/FileUpload.d.ts +1 -9
  18. package/dist/components/index.d.ts +2 -2
  19. package/dist/components/input/Input.d.ts +1 -1
  20. package/dist/components/kanban/Kanban.d.ts +0 -2
  21. package/dist/components/list/List.d.ts +7 -1
  22. package/dist/components/login-form/LoginForm.d.ts +5 -1
  23. package/dist/components/message/Message.d.ts +5 -1
  24. package/dist/components/message/MessageParts.d.ts +7 -2
  25. package/dist/components/rich-text/RichText.d.ts +12 -4
  26. package/dist/components/select/selectContext.d.ts +0 -2
  27. package/dist/components/smart-filter/SmartFilter.d.ts +1 -5
  28. package/dist/components/sparkline/Sparkline.d.ts +50 -8
  29. package/dist/components/tag-select/TagSelect.d.ts +0 -2
  30. package/dist/components/textarea/Textarea.d.ts +2 -2
  31. package/dist/icon-set.d.ts +5 -1
  32. package/dist/icon-set.js +8 -2
  33. package/dist/icons/glyph.d.ts +12 -1
  34. package/dist/icons/glyphs/telegram.d.ts +2 -0
  35. package/dist/icons/glyphs/vk.d.ts +2 -0
  36. package/dist/icons/glyphs/whatsapp.d.ts +2 -0
  37. package/dist/icons/registry.d.ts +8 -1
  38. package/dist/index.css +1 -1
  39. package/dist/index.js +7 -3
  40. package/dist/internal/ToolRow.d.ts +3 -1
  41. package/dist/internal/autofill.d.ts +30 -0
  42. package/dist/internal/barPlan.d.ts +7 -1
  43. package/dist/internal/blockStack.d.ts +7 -0
  44. package/dist/internal/overlay/DialogParts.d.ts +8 -6
  45. package/dist/internal/richText.d.ts +32 -1
  46. package/dist/internal/textFormatting.d.ts +2 -2
  47. package/dist/layout/page/Page.d.ts +2 -1
  48. package/dist/skill/SKILL.md +3 -2
  49. package/dist/skill/api-contract.md +2 -1
  50. package/dist/skill/cheatsheet.md +1 -1
  51. package/dist/skill/checklist.md +8 -1
  52. package/dist/skill/choosing.md +76 -5
  53. package/dist/skill/components.md +2 -2
  54. package/dist/skill/composition.md +108 -20
  55. package/dist/skill/foundations.md +1 -1
  56. package/dist/skill/layouts.md +4 -4
  57. package/dist/skill/reference/components/button/examples/in-form.tsx +0 -1
  58. package/dist/skill/reference/components/card/COMPONENT.md +1 -1
  59. package/dist/skill/reference/components/card/examples/screen.tsx +9 -11
  60. package/dist/skill/reference/components/chat-list/COMPONENT.md +6 -5
  61. package/dist/skill/reference/components/chat-list/examples/structure.tsx +4 -4
  62. package/dist/skill/reference/components/command-menu/COMPONENT.md +1 -2
  63. package/dist/skill/reference/components/command-menu/examples/states.tsx +1 -1
  64. package/dist/skill/reference/components/composer/COMPONENT.md +5 -4
  65. package/dist/skill/reference/components/composer/examples/formatting.tsx +1 -1
  66. package/dist/skill/reference/components/data-table/COMPONENT.md +17 -16
  67. package/dist/skill/reference/components/data-table/examples/server.tsx +2 -1
  68. package/dist/skill/reference/components/datepicker/COMPONENT.md +2 -2
  69. package/dist/skill/reference/components/dnd/COMPONENT.md +2 -1
  70. package/dist/skill/reference/components/drawer/COMPONENT.md +9 -6
  71. package/dist/skill/reference/components/drawer/examples/screen.tsx +1 -5
  72. package/dist/skill/reference/components/drawer/examples/tab-switcher.tsx +158 -0
  73. package/dist/skill/reference/components/field-list/COMPONENT.md +7 -7
  74. package/dist/skill/reference/components/field-list/examples/limits.tsx +2 -2
  75. package/dist/skill/reference/components/file-upload/COMPONENT.md +8 -12
  76. package/dist/skill/reference/components/file-upload/examples/gallery.tsx +2 -2
  77. package/dist/skill/reference/components/file-upload/examples/in-form.tsx +2 -4
  78. package/dist/skill/reference/components/file-upload/examples/narrow.tsx +1 -0
  79. package/dist/skill/reference/components/file-upload/examples/overview.tsx +1 -5
  80. package/dist/skill/reference/components/file-upload/examples/sizes.tsx +1 -1
  81. package/dist/skill/reference/components/file-upload/examples/states.tsx +4 -5
  82. package/dist/skill/reference/components/file-upload/examples/validation.tsx +2 -7
  83. package/dist/skill/reference/components/input/COMPONENT.md +3 -0
  84. package/dist/skill/reference/components/input/examples/in-form.tsx +0 -1
  85. package/dist/skill/reference/components/input/examples/validation.tsx +1 -1
  86. package/dist/skill/reference/components/kanban/COMPONENT.md +1 -2
  87. package/dist/skill/reference/components/list/COMPONENT.md +11 -6
  88. package/dist/skill/reference/components/list/examples/reorder.tsx +30 -3
  89. package/dist/skill/reference/components/login-form/COMPONENT.md +2 -2
  90. package/dist/skill/reference/components/login-form/examples/sizes.tsx +6 -2
  91. package/dist/skill/reference/components/message/COMPONENT.md +12 -7
  92. package/dist/skill/reference/components/message/examples/status.tsx +2 -1
  93. package/dist/skill/reference/components/message/examples/text-formatting.tsx +13 -3
  94. package/dist/skill/reference/components/modal/COMPONENT.md +5 -4
  95. package/dist/skill/reference/components/rich-text/COMPONENT.md +45 -7
  96. package/dist/skill/reference/components/rich-text/examples/entities.tsx +20 -0
  97. package/dist/skill/reference/components/select/COMPONENT.md +1 -2
  98. package/dist/skill/reference/components/sheet/COMPONENT.md +5 -4
  99. package/dist/skill/reference/components/smart-filter/COMPONENT.md +3 -5
  100. package/dist/skill/reference/components/sparkline/COMPONENT.md +40 -12
  101. package/dist/skill/reference/components/sparkline/examples/lower-is-better.tsx +46 -0
  102. package/dist/skill/reference/components/sparkline/examples/own-value.tsx +41 -0
  103. package/dist/skill/reference/components/sparkline/examples/reference.tsx +2 -1
  104. package/dist/skill/reference/components/tabs/COMPONENT.md +1 -1
  105. package/dist/skill/reference/components/tabs/examples/orientation.tsx +1 -1
  106. package/dist/skill/reference/components/tag-select/COMPONENT.md +0 -2
  107. package/dist/skill/reference/components/tag-select/examples/overview.tsx +0 -1
  108. package/dist/skill/reference/components/tag-select/examples/sizes.tsx +0 -1
  109. package/dist/skill/reference/components/textarea/COMPONENT.md +6 -3
  110. package/dist/skill/reference/icon-set.ts +5 -1
  111. package/dist/skill/reference/layout/page/COMPONENT.md +13 -12
  112. package/dist/skill/reference/layout/page/examples/collapse.tsx +45 -31
  113. package/dist/skill/reference/layout/page/examples/router.tsx +47 -14
  114. package/dist/skill/ux.md +98 -4
  115. package/package.json +1 -1
  116. package/src/components/button/examples/in-form.tsx +0 -1
  117. package/src/components/card/COMPONENT.md +1 -1
  118. package/src/components/card/examples/screen.tsx +9 -11
  119. package/src/components/chat-list/COMPONENT.md +6 -5
  120. package/src/components/chat-list/examples/structure.tsx +4 -4
  121. package/src/components/command-menu/COMPONENT.md +1 -2
  122. package/src/components/command-menu/examples/states.tsx +1 -1
  123. package/src/components/composer/COMPONENT.md +5 -4
  124. package/src/components/composer/examples/formatting.tsx +1 -1
  125. package/src/components/data-table/COMPONENT.md +17 -16
  126. package/src/components/data-table/examples/server.tsx +2 -1
  127. package/src/components/datepicker/COMPONENT.md +2 -2
  128. package/src/components/dnd/COMPONENT.md +2 -1
  129. package/src/components/drawer/COMPONENT.md +9 -6
  130. package/src/components/drawer/examples/screen.tsx +1 -5
  131. package/src/components/drawer/examples/tab-switcher.tsx +158 -0
  132. package/src/components/field-list/COMPONENT.md +7 -7
  133. package/src/components/field-list/examples/limits.tsx +2 -2
  134. package/src/components/file-upload/COMPONENT.md +8 -12
  135. package/src/components/file-upload/examples/gallery.tsx +2 -2
  136. package/src/components/file-upload/examples/in-form.tsx +2 -4
  137. package/src/components/file-upload/examples/narrow.tsx +1 -0
  138. package/src/components/file-upload/examples/overview.tsx +1 -5
  139. package/src/components/file-upload/examples/sizes.tsx +1 -1
  140. package/src/components/file-upload/examples/states.tsx +4 -5
  141. package/src/components/file-upload/examples/validation.tsx +2 -7
  142. package/src/components/input/COMPONENT.md +3 -0
  143. package/src/components/input/examples/in-form.tsx +0 -1
  144. package/src/components/input/examples/validation.tsx +1 -1
  145. package/src/components/kanban/COMPONENT.md +1 -2
  146. package/src/components/list/COMPONENT.md +11 -6
  147. package/src/components/list/examples/reorder.tsx +30 -3
  148. package/src/components/login-form/COMPONENT.md +2 -2
  149. package/src/components/login-form/examples/sizes.tsx +6 -2
  150. package/src/components/message/COMPONENT.md +12 -7
  151. package/src/components/message/examples/status.tsx +2 -1
  152. package/src/components/message/examples/text-formatting.tsx +13 -3
  153. package/src/components/modal/COMPONENT.md +5 -4
  154. package/src/components/rich-text/COMPONENT.md +45 -7
  155. package/src/components/rich-text/examples/entities.tsx +20 -0
  156. package/src/components/select/COMPONENT.md +1 -2
  157. package/src/components/sheet/COMPONENT.md +5 -4
  158. package/src/components/smart-filter/COMPONENT.md +3 -5
  159. package/src/components/sparkline/COMPONENT.md +40 -12
  160. package/src/components/sparkline/examples/lower-is-better.tsx +46 -0
  161. package/src/components/sparkline/examples/own-value.tsx +41 -0
  162. package/src/components/sparkline/examples/reference.tsx +2 -1
  163. package/src/components/tabs/COMPONENT.md +1 -1
  164. package/src/components/tabs/examples/orientation.tsx +1 -1
  165. package/src/components/tag-select/COMPONENT.md +0 -2
  166. package/src/components/tag-select/examples/overview.tsx +0 -1
  167. package/src/components/tag-select/examples/sizes.tsx +0 -1
  168. package/src/components/textarea/COMPONENT.md +6 -3
  169. package/src/layout/page/COMPONENT.md +13 -12
  170. package/src/layout/page/examples/collapse.tsx +45 -31
  171. package/src/layout/page/examples/router.tsx +47 -14
package/dist/index.js CHANGED
@@ -75,6 +75,8 @@ import {
75
75
  formatFileSize,
76
76
  formatMessageDate,
77
77
  formatMessageTime,
78
+ getRichTextSource,
79
+ getTextEntities,
78
80
  getWaveform,
79
81
  groupMessages,
80
82
  hueOf,
@@ -88,7 +90,7 @@ import {
88
90
  useSidebar,
89
91
  useTokenValue,
90
92
  useUploadQueue
91
- } from "./chunks/chunk-ME4VEAQ5.js";
93
+ } from "./chunks/chunk-UNNQITFE.js";
92
94
  import {
93
95
  Avatar,
94
96
  Badge,
@@ -123,11 +125,11 @@ import {
123
125
  tierMappings,
124
126
  tones,
125
127
  variants
126
- } from "./chunks/chunk-7JYPFRZ3.js";
128
+ } from "./chunks/chunk-PFASPQSJ.js";
127
129
  import {
128
130
  ControlSizeProvider,
129
131
  useOptionalControlSize
130
- } from "./chunks/chunk-T7IEJSTH.js";
132
+ } from "./chunks/chunk-HMLQCKRL.js";
131
133
  export {
132
134
  Accordion,
133
135
  AppShell,
@@ -231,6 +233,8 @@ export {
231
233
  formatMessageDate,
232
234
  formatMessageTime,
233
235
  getPasswordStrength,
236
+ getRichTextSource,
237
+ getTextEntities,
234
238
  getWaveform,
235
239
  groupMessages,
236
240
  hueOf,
@@ -17,12 +17,14 @@ export declare function ToolCell({ className, ...rest }: ToolCellProps): import(
17
17
  type ToolRowProps = DivProps & {
18
18
  /** 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. */
19
19
  fill?: boolean;
20
+ /** The edge a filled row gathers its cells at: `end` — tools of the content; `start` — a section's sub-pages under its title. */
21
+ align?: "start" | "end";
20
22
  };
21
23
  /**
22
24
  * A row of tools as written, each in a cell of its kind: a search grows, switchers and pickers
23
25
  * keep their content width beside it and fill a short line, buttons keep their width at the end.
24
26
  */
25
- export declare function ToolRow({ fill, className, children, ...rest }: ToolRowProps): import("react/jsx-runtime").JSX.Element;
27
+ export declare function ToolRow({ fill, align, className, children, ...rest }: ToolRowProps): import("react/jsx-runtime").JSX.Element;
26
28
  type ToolChipsProps = DivProps;
27
29
  /** The applied filters (`SmartFilter.Chips`) on a line of their own under a tool row; gone while empty. */
28
30
  export declare function ToolChips({ className, ...rest }: ToolChipsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Browser autofill and password managers stay off a kit text field: a product screen is full of
4
+ * searches, filters and record fields that a saved login or address has no business filling.
5
+ * `autocomplete="off"` turns off the browser's own suggestions where it is honoured (Chrome and
6
+ * Firefox history and address autofill on desktop); browsers still read login-looking fields on
7
+ * their own, and the password-manager extensions read only their own opt-outs: 1Password
8
+ * `data-1p-ignore`, LastPass `data-lpignore`, Bitwarden `data-bwignore`, Dashlane
9
+ * `data-form-type="other"`.
10
+ */
11
+ export declare const AUTOFILL_OFF: {
12
+ readonly autoComplete: "off";
13
+ readonly "data-1p-ignore": "true";
14
+ readonly "data-lpignore": "true";
15
+ readonly "data-bwignore": "true";
16
+ readonly "data-form-type": "other";
17
+ };
18
+ type AutofillProps = {
19
+ autoComplete?: string;
20
+ } & Partial<Omit<typeof AUTOFILL_OFF, "autoComplete">>;
21
+ export declare function AutofillScope({ children }: {
22
+ children: React.ReactNode;
23
+ }): import("react/jsx-runtime").JSX.Element;
24
+ /**
25
+ * The autofill attributes of a text field. A consumer's `autoComplete` token other than `"off"`
26
+ * opts in: the token goes through and the opt-outs are dropped. Without a token the field is off,
27
+ * except inside `AutofillScope`, where the browser and the password managers decide.
28
+ */
29
+ export declare function useAutofill(autoComplete: string | undefined): AutofillProps;
30
+ export {};
@@ -4,7 +4,7 @@
4
4
  * widths it was chosen from (no re-measure loop).
5
5
  *
6
6
  * The steps, in the order the row gives way:
7
- * - `one` — heading · tools · actions on one line;
7
+ * - `one` — heading · tools · actions on one line (never for a section's sub-pages, see `nav`);
8
8
  * - `below` — the tools move to a line of their own under the heading and the actions;
9
9
  * - `icons-secondary` — the secondary buttons show only their icons;
10
10
  * - `icons` — the primary button too;
@@ -30,6 +30,12 @@ export type BarWidths = {
30
30
  path: boolean;
31
31
  /** Natural width of the tools on one line; 0 — none. */
32
32
  tools: number;
33
+ /**
34
+ * The tools hold a navigation — a section's sub-pages as route links. They keep a line of their
35
+ * own from the start: on the first line they would stand where the heading and the actions of
36
+ * each sub-page leave room, and those differ from one sub-page to the next.
37
+ */
38
+ nav: boolean;
33
39
  actions: BarAction[];
34
40
  /** Width of the «⋯» button. */
35
41
  more: number;
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Whether the blocks placed here stand in a host's stack of sections: a dialog body says so, and a
4
+ * `Tabs.Panel` inside it lays out its own blocks the section step apart, the rhythm of the body's
5
+ * sections — so a tab's blocks need no spacing wrapper of the consumer's.
6
+ */
7
+ export declare const BlockStackContext: React.Context<boolean>;
@@ -201,11 +201,13 @@ export type DialogActionsProps = React.HTMLAttributes<HTMLDivElement> & {
201
201
  ref?: React.Ref<HTMLDivElement>;
202
202
  };
203
203
  /**
204
- * The buttons at the end of the header, before the close button: ghost icon buttons (each named,
205
- * with a Tooltip) and a ⋯ Dropdown — never a status, which leads the `Description`. They take tier
206
- * `s`, the close button's, and sit on the title's line. In the footer
207
- * it is the start group — an action apart from the main pair («Удалить», «Выйти») — and its
208
- * buttons keep the footer's tier.
204
+ * The controls at the end of the header, before the close button: first one switcher that applies
205
+ * to the whole open tab (a `SegmentedControl`, a `Datepicker` period), then the buttons — ghost icon
206
+ * buttons (each named, with a Tooltip) and a ⋯ Dropdown — never a status, which leads the
207
+ * `Description`. They take tier `s`, the close button's, and sit on the title's line; each stands
208
+ * in a cell that enters and closes up, so a switcher shown only on some tabs never jolts the row.
209
+ * In the footer it is the start group — an action apart from the main pair («Удалить», «Выйти») —
210
+ * and its buttons keep the footer's tier.
209
211
  */
210
212
  export declare function DialogActions({ className, children, ...rest }: DialogActionsProps): import("react/jsx-runtime").JSX.Element;
211
213
  export declare namespace DialogActions {
@@ -215,7 +217,7 @@ export type DialogBodyProps = React.HTMLAttributes<HTMLDivElement> & {
215
217
  ref?: React.Ref<HTMLDivElement>;
216
218
  };
217
219
  /** The only scrolling zone; header and footer stay put. An open layer: its end never scrolls the page. */
218
- export declare function DialogBody({ className, ...rest }: DialogBodyProps): import("react/jsx-runtime").JSX.Element;
220
+ export declare function DialogBody({ className, children, ...rest }: DialogBodyProps): import("react/jsx-runtime").JSX.Element;
219
221
  export declare namespace DialogBody {
220
222
  var displayName: string;
221
223
  }
@@ -1,10 +1,41 @@
1
1
  import * as React from "react";
2
+ /** What a range of a message text is: its format, and the address of a `link`. */
3
+ export type TextEntityType = "bold" | "italic" | "underline" | "strikethrough" | "spoiler" | "code" | "pre" | "link" | "quote";
4
+ /** One formatted range of a plain text: `offset` and `length` in UTF-16 code units. */
5
+ export type TextEntity = {
6
+ type: TextEntityType;
7
+ offset: number;
8
+ length: number;
9
+ /** The address of a `link`; without it the range itself is the address. */
10
+ url?: string;
11
+ /** The language of a `pre` block. */
12
+ language?: string;
13
+ };
14
+ /** A plain text with its formatted ranges: what a messenger sends and takes. */
15
+ export type FormattedText = {
16
+ text: string;
17
+ entities: TextEntity[];
18
+ };
19
+ /**
20
+ * The plain text and entities of a Markdown source — what a messenger takes: Composer writes
21
+ * Markdown, the messenger's API wants `{ text, entities }`.
22
+ */
23
+ export declare function getTextEntities(source: string): FormattedText;
24
+ /**
25
+ * The Markdown source of a plain text and its entities — to edit a received or stored text in
26
+ * Composer or a formatting Textarea. Text that would read as markup is escaped (`\*`); a format
27
+ * the subset cannot hold (`_italic_` inside a word) is dropped, its text kept.
28
+ */
29
+ export declare function getRichTextSource({ text, entities }: FormattedText): string;
2
30
  type RichTextLabels = {
3
31
  /** Accessible name of a masked spoiler: its text is not read until it is revealed. */
4
32
  spoiler: string;
5
33
  };
6
34
  type RichTextProps = {
35
+ /** The Markdown source, or the plain text that `entities` format. */
7
36
  source: string;
37
+ /** Formatted ranges of `source`: it is then plain text, never read as Markdown. */
38
+ entities?: readonly TextEntity[];
8
39
  /** Rendered at the very end of the last line (a chat bubble's meta reserve). */
9
40
  end?: React.ReactNode;
10
41
  /** Marks every case-insensitive match of this text (a search through a chat). */
@@ -13,5 +44,5 @@ type RichTextProps = {
13
44
  labels: RichTextLabels;
14
45
  };
15
46
  /** A message text with its Markdown subset rendered safely; `end` closes the last text line. */
16
- export declare function RichText({ source, end, highlight, labels }: RichTextProps): import("react/jsx-runtime").JSX.Element;
47
+ export declare function RichText({ source, entities, end, highlight, labels }: RichTextProps): import("react/jsx-runtime").JSX.Element;
17
48
  export {};
@@ -4,7 +4,7 @@
4
4
  * the result to its textarea. `internal/richText` renders the same subset.
5
5
  */
6
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";
7
+ export type TextFormat = "bold" | "italic" | "underline" | "strikethrough" | "code" | "spoiler" | "link" | "quote";
8
8
  /** Names of the formatting toolbar and of its tools, one per format. */
9
9
  export type TextFormattingLabels = Record<TextFormat, string> & {
10
10
  /** Accessible name of the formatting toolbar. */
@@ -31,7 +31,7 @@ export declare function diffRange(before: string, after: string): {
31
31
  insert: string;
32
32
  };
33
33
  /**
34
- * The format a key press asks for: Mod+B · Mod+I · Mod+Shift+X · Mod+E · Mod+Shift+H · Mod+K ·
34
+ * The format a key press asks for: Mod+B · Mod+I · Mod+U · Mod+Shift+X · Mod+E · Mod+Shift+H · Mod+K ·
35
35
  * Mod+Shift+9. The spoiler is not Telegram's Mod+Shift+P: Firefox and Edge reserve it for a private
36
36
  * window and never hand it to the page.
37
37
  */
@@ -60,7 +60,8 @@ export type PageTopBarProps = React.HTMLAttributes<HTMLElement> & {
60
60
  * The page's strip at its top edge, across the panel. Up to four groups in one grid —
61
61
  * `Page.Heading`, `Page.Tools`, `Page.Actions` on the first line, `Page.Chips` under them — laid
62
62
  * out by one plan chosen from their natural widths (`planBar`): one line while everything fits;
63
- * then the tools take a line of their own; then the buttons give way to their icons, secondary
63
+ * then the tools take a line of their own (a section's sub-pages — tools holding a navigation —
64
+ * always, at the start, so the switch stands still from one sub-page to the next); then the buttons give way to their icons, secondary
64
65
  * first, and fold into «⋯»; only then does a heading with a path stack (path over title), and a
65
66
  * heading without one truncate. Sticky inside the scroller of
66
67
  * the page; while the shell's Sidebar is off-canvas it starts with the menu button. With
@@ -123,8 +123,9 @@ Take a semantic `<Icon name="…" />` first, else a glyph from the full animated
123
123
  7. Labels above fields, always — once per column for records in rows (`FieldList`), never numbered
124
124
  («Пункт 1»), never repeating their heading. Placeholder is an example value, never the label.
125
125
  8. Text through `Typography` roles (`title-m` section, `title-s` group, `body-m` text, `caption` meta;
126
- the page title is `Page.Title`, one per page: in the `Page.Heading` of `Page.TopBar` on a work screen, in the flow
127
- on a document page or an overview). No custom font sizes.
126
+ the page title is `Page.Title`, one per page: in the `Page.Heading` of `Page.TopBar` on every screen
127
+ of data — lists, boards, records, dashboards and analytics alike; in the flow only on a document page
128
+ with no page tools). No custom font sizes.
128
129
  9. Overlays (Modal, ConfirmDialog, Drawer, Sheet, Popover, Dropdown, Tooltip, CommandMenu, Select,
129
130
  MediaViewer) come from the kit — never hand-made — and stay mounted: drive them with `open`,
130
131
  never `{open && …}`.
@@ -385,7 +385,8 @@ crumb of your own.
385
385
  - `status.*`: `danger`, `emailSent`, `failed`, `favorite`, `info`, `locked`, `muted`, `sound`, `offline`, `pending`, `read`, `sent`, `success`, `trendUp`, `warning`;
386
386
  - `theme.*`: `dark`, `light`;
387
387
  - `view.*`: `code`, `columns`, `preview`;
388
- - `viewport.*`: `desktop`, `mobile`, `tablet`.
388
+ - `viewport.*`: `desktop`, `mobile`, `tablet`;
389
+ - `brand.*`: `telegram`, `vk`, `whatsapp`.
389
390
  <!-- icons:end -->
390
391
 
391
392
  Every glyph comes from the kit, in this order:
@@ -95,7 +95,7 @@ reference is each component's `COMPONENT.md` ([components.md](components.md)), t
95
95
  - A filtered page: `<SmartFilter.Root>` wraps `<Page.Root>` — it draws nothing; not `SmartFilter.Root` inside the bar.
96
96
  - No size knob on the title: the bar sizes the heading and gives way by width itself ([the five steps](responsive.md#the-pages-top-bar--pagetopbar)).
97
97
  - Sign-in, sign-up, an invite and a password reset are modes of one `/login` screen, one `LoginForm` with the action's icon in `LoginForm.Logo` and the default `align` — not a page per mode, not a centred brand mark ([auth](layouts.md#auth-sign-in-code)).
98
- - A document page (settings, a long form, help) and an overview (a dashboard) put `<Page.Header>` with its `<Page.Heading>` in the flow — the large title — with an optional `<Page.TopBar collapse>` above it that repeats the title once it scrolls away. One `<Page.Title>` per page — never two titles, never no `<h1>`.
98
+ - A dashboard, analytics or statistics page titles itself in `<Page.TopBar>` like a list — not a bar of tools over a large title in the flow. Only a document page (settings, a long form, help) puts `<Page.Header>` with its `<Page.Heading>` in the flow, with an optional `<Page.TopBar collapse>` above it whose heading holds the path. One `<Page.Title>` per page — never two titles, never no `<h1>`.
99
99
 
100
100
  ## Cards and surfaces
101
101
 
@@ -5,7 +5,7 @@ checks.
5
5
 
6
6
  ## Composition
7
7
  - [ ] The screen starts from the closest working screen and keeps its skeleton ([screens](composition.md#screens)).
8
- - [ ] One `Page.Title` per page, always an `<h1>`: a work screen titles itself in the `Page.Heading` of its `Page.TopBar`, a document page or an overview in a `Page.Header` in the flow ([page skeleton](composition.md#1-page-skeleton)).
8
+ - [ ] One `Page.Title` per page, always an `<h1>`: every screen of data — dashboards and analytics too — titles itself in the `Page.Heading` of its `Page.TopBar`, only a document page in a `Page.Header` in the flow; no `collapse` bar without a heading ([page skeleton](composition.md#1-page-skeleton)).
9
9
  - [ ] The top bar holds only this page's groups — `Page.Heading`, `Page.Tools`, `Page.Actions`, `Page.Chips` — no app-wide header or global search ([the top bar](composition.md#the-top-bar)).
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)).
@@ -14,6 +14,7 @@ checks.
14
14
  - [ ] A control stands with what it changes: a table's search, filter, chips and chooser are in its `toolbar` / `toolbarChips` on every page; the bar keeps the title, sections, the view, the shared period and the buttons ([where the filters go](composition.md#where-the-filters-go)).
15
15
  - [ ] A table section has an `options` filter field for each categorical column and a `date-range` field per date column, its value in the URL ([where the filters go](composition.md#where-the-filters-go)).
16
16
  - [ ] Actions on selected rows are `renderSelectionActions` — one «Действия · N» menu added to the tools row, which stays ([controls of a table](composition.md#controls-of-a-table)).
17
+ - [ ] Creating one of several kinds whose forms differ is a button with a `Dropdown` of kinds (grouped by family), a pick opens that kind's form; no choosing step and «Далее» inside a dialog ([a kind that leads to its own form](choosing.md#a-kind-that-leads-to-its-own-form)).
17
18
  - [ ] A Card only for a standalone titled block — never around a table, a single field or the page ([surfaces](composition.md#4-surfaces)).
18
19
  - [ ] 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)).
19
20
  - [ ] 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)).
@@ -24,6 +25,9 @@ checks.
24
25
  - [ ] 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)).
25
26
  - [ ] A FieldList row shows its «×» in sight whatever its columns; `rowActions="menu"` only on a reorderable touch-first list; `variant` by the rule ([forms](composition.md#7-forms)).
26
27
  - [ ] A section named after a set of records shows the records, with an action per row ([data shape](ux.md#2-the-shape-of-the-data-decides-the-layout)).
28
+ - [ ] Records of one kind are one list in their real order; rows the system fixes are `locked` in it, never a second section ([data shape](ux.md#2-the-shape-of-the-data-decides-the-layout)).
29
+ - [ ] Every heading names what nothing else names: no section over a block with its own title, no first block repeating its tab ([label economy](ux.md#3-label-economy)).
30
+ - [ ] Every hint, limit, counter and range line serves the task; none is on because the prop exists, and a `max` worth knowing is in `hint` ([label economy](ux.md#3-label-economy)).
27
31
  - [ ] Labels on a form of rows ≤ columns + the list label; none repeated, numbered or repeating its heading ([label economy](ux.md#3-label-economy)).
28
32
  - [ ] No text explains the layout; every hint is two to five words the label and the control leave out, never the obvious outcome ([label economy](ux.md#3-label-economy)).
29
33
  - [ ] Every setting sits with what it governs, as a field beside its neighbours; a stored choice is a `Select`, no section holds one setting ([where a setting lives](ux.md#10-where-a-setting-lives)).
@@ -35,6 +39,8 @@ checks.
35
39
  - [ ] A request a value starts — a simulated one too — is cancelled when the value changes again ([nothing to guess](ux.md#6-nothing-to-guess)).
36
40
  - [ ] Tabs, filters, search, page and the open record are in the URL ([nothing to guess](ux.md#6-nothing-to-guess)).
37
41
  - [ ] Defaults prefill what the system knows; each field has the control and `inputMode` of its data ([ask less](ux.md#7-ask-less)).
42
+ - [ ] `autoComplete` tokens only on sign-in and sign-up fields (`LoginForm.Form`, a password change); no token on a search or a record field ([ask less](ux.md#7-ask-less)).
43
+ - [ ] No search, filter or record field (other than a record's e-mail) says «username», «login», «email», «логин», «почта» in its placeholder, label, `name` or `id` — Safari offers saved logins there ([ask less](ux.md#7-ask-less)).
38
44
  - [ ] I looked at the screen in a browser, filled badly ([the visual check](ux.md#9-look-at-it-before-handing-it-over)).
39
45
 
40
46
  ## Grid and spacing
@@ -74,6 +80,7 @@ checks.
74
80
  - [ ] One secondary action is an icon button with a Tooltip; a «⋯» menu holds two or more ([a menu from two actions](composition.md#a-menu-from-two-actions)).
75
81
  - [ ] An entity has one drawer opened from everywhere; permissions lock fields and hide tabs and actions; header actions act on the record, never lead elsewhere ([a record in a drawer](composition.md#a-record-in-a-drawer)).
76
82
  - [ ] A record drawer is one form with one «Сохранить» in its footer while dirty and `Drawer.Guard`; no save per section, no unsaved-status text ([a record in a drawer](composition.md#a-record-in-a-drawer)).
83
+ - [ ] A control that changes the whole open tab (a language, a period) is one control in the header's `Drawer.Actions` before the buttons, only on the tabs it applies to ([a record in a drawer](composition.md#a-record-in-a-drawer)).
77
84
  - [ ] A drawer loads its tabs' data itself, the history has its own tab, and returning to a tab never shows a skeleton again ([a record in a drawer](composition.md#a-record-in-a-drawer)).
78
85
  - [ ] An on/off permission or setting is a `Switch`, a level per entity a `FieldList` of `Select`s, a set of values in a table cell `Badge.Group overflow="count"` ([a flag, a level, a set](choosing.md#a-flag-a-level-a-set)).
79
86
  - [ ] The same action has the same label and variant everywhere on the screen ([secondary actions](api-contract.md#secondary-actions)).
@@ -42,7 +42,7 @@ under them → its `renderTotals`.
42
42
  |---|---|
43
43
  | a capability or a setting that is on or off, in a form or applied at once | Switch ([a flag, a level, a set](#a-flag-a-level-a-set)) |
44
44
  | items picked out of a set, a consent, an option of the action being sent | Checkbox |
45
- | exactly one of 2–5 visible options in a form | Radio (`Radio.Group` with `label`) |
45
+ | exactly one of 2–5 visible options, a value of the one form whose other fields stay the same | Radio (`Radio.Group` with `label`); a choice that leads to a different form is a menu, not a field ([a kind that leads to its own form](#a-kind-that-leads-to-its-own-form)) |
46
46
  | exactly one of 2–5 modes that change the view right now — never a stored setting | SegmentedControl |
47
47
  | one of many (> 5) or a long list; icons, descriptions, search, groups | Select (`Select.Content searchable`) |
48
48
  | one record out of thousands for a form field (a client, a contract), searched on the server | Select with `search` / `onSearchChange`, `filter={false}`, `loading`, `hasMore` + `onLoadMore` — never a read-only field that opens a CommandMenu |
@@ -77,7 +77,7 @@ Records with a start and an end compared across time — who is busy when, a pro
77
77
  phases and milestones → Gantt (never a DataTable of dates or hand-drawn bars); moved and resized by
78
78
  hand through `onBarChange`.
79
79
  Chronological events → Timeline (two facts under an event → two `Timeline.ItemMeta`, they stack); a conversation between people (support, a team chat) → Message —
80
- never a Timeline or a list of Cards styled as bubbles. Code → CodeBlock. A number with its trend over days or weeks (revenue, orders, sign-ups) → Sparkline: the latest value, its change and a line you can scrub; values with a sign by period → Sparkline `variant="bars"`; against a plan → `reference`; missing days → `value: null`; a chart with axes or several series → ChartFrame around a chart library or your own SVG (the panel, legend, tooltip, states and the screen-reader table come from the kit, the colors from `series` / `chartColors` — never status tones); values to read, sort or compare row by row → DataTable, not a chart.
80
+ never a Timeline or a list of Cards styled as bubbles. Code → CodeBlock. A number with its trend over days or weeks (revenue, orders, sign-ups) → Sparkline: the headline value and a line you can scrub; its change badge against the previous period → `compareTo` (`change` for a point-to-point badge while scrubbing, off by default); a metric whose growth is bad (response time, costs, refusals) → `better="lower"`; a share or a conversion in percent → `changeUnit="points"` («+0,5 п. п.», never a percent of a percent); a period figure that is neither the sum nor the mean of the points (a median, unique clients) → `value`; values with a sign by period → Sparkline `variant="bars"`; against a plan → `reference`; missing days → `value: null` in `data`; a chart with axes or several series → ChartFrame around a chart library or your own SVG (the panel, legend, tooltip, states and the screen-reader table come from the kit, the colors from `series` / `chartColors` — never status tones); values to read, sort or compare row by row → DataTable, not a chart.
81
81
 
82
82
  **Messages.** A whole conversation → `Chat`: `Chat.Root` with `Chat.Header` (avatar, `Chat.Title`,
83
83
  `Chat.Description`, `Chat.Actions` with `Chat.SearchButton`), `Chat.Search`, `Chat.Pinned`,
@@ -137,8 +137,8 @@ the rail has no brand header. On phones the off-canvas Sidebar opens from the me
137
137
  page's `Page.TopBar` draws by itself; a trigger of your own reads `useAppShellNav()`. There is no
138
138
  app-wide header: the bar at the top of the panel is the page's `Page.TopBar` — on a work screen the
139
139
  title group (`Page.Heading`), the page's tools (`Page.Tools`), its buttons (`Page.Actions`) and the
140
- active filters (`Page.Chips`) — [the top bar](composition.md#the-top-bar). A document
141
- page (settings, a long form, help) or an overview has its large title in the flow (`Page.Header`
140
+ active filters (`Page.Chips`) — [the top bar](composition.md#the-top-bar), on a dashboard as on a
141
+ list. Only a document page (settings, a long form, help) has its large title in the flow (`Page.Header`
142
142
  outside the bar), with an optional `Page.TopBar collapse` that repeats it once it scrolls away. A record opened from a table: its own page → DataTable `getRowHref`; in place →
143
143
  `onRowClick` + Drawer; a list worked through record by record → `Page.Split` with a `List` of
144
144
  the records (tickets, letters, invoices; `current` on the open one; a row's own Switch or menu in
@@ -221,6 +221,77 @@ column, and shows only the ones that have a level.
221
221
  </>
222
222
  ```
223
223
 
224
+ ### A kind that leads to its own form
225
+
226
+ Creating or connecting one of several kinds of record whose forms differ (a Telegram bot or a
227
+ Telegram account, a VK community or a VK account; an invoice or an act) is a fork before the form,
228
+ not a field of it. Ask of the choice: do the fields after it stay the same?
229
+
230
+ | The choice | Shape |
231
+ |---|---|
232
+ | the fields stay the same; the choice is one more value of the record (a client type, a currency) | a field of the form: `Radio.Group` for 2–5, `Select` for more |
233
+ | each option has its own fields, its own steps or its own service to connect | the action button opens a `Dropdown`: one `Dropdown.Item` per kind (`Dropdown.ItemIcon` with its glyph), in a `Dropdown.Group label` per family when the kinds group (by messenger, by document type); a pick opens that kind's form straight away, titled by the kind («Подключить Telegram-бота»); in ⌘K, one `CommandMenu.Item` per kind |
234
+
235
+ Never a dialog whose first step is the choice and a «Далее»: the person already said what they
236
+ want by pressing the item, and a dialog that changes its fields under a choice cannot say what it
237
+ will create. One kind only → the button opens its form directly, no menu.
238
+
239
+ ```tsx partial
240
+ // bad — the kind is a first step inside the dialog: two choices and «Далее» before any field
241
+ <Modal.Body>
242
+ <SegmentedControl.Root label="Мессенджер" defaultValue="telegram">…</SegmentedControl.Root>
243
+ <Radio.Group label="Что подключить" defaultValue="bot">…</Radio.Group>
244
+ </Modal.Body>
245
+ <Modal.Footer>
246
+ <Button.Root>Далее</Button.Root>
247
+ </Modal.Footer>
248
+ ```
249
+
250
+ ```tsx good
251
+ // good — the button is a menu of kinds grouped by messenger; a pick opens that kind's form
252
+ // declare: const openConnect: (kind: "telegram-bot" | "telegram-account" | "vk-group" | "vk-account") => void
253
+ <Dropdown.Root>
254
+ <Dropdown.Trigger>
255
+ <Button.Root>
256
+ <Button.Icon>
257
+ <Icon name="action.add" />
258
+ </Button.Icon>
259
+ Подключить
260
+ </Button.Root>
261
+ </Dropdown.Trigger>
262
+ <Dropdown.Content>
263
+ <Dropdown.Group label="Telegram">
264
+ <Dropdown.Item onSelect={() => openConnect("telegram-bot")}>
265
+ <Dropdown.ItemIcon>
266
+ <Icon name="brand.telegram" />
267
+ </Dropdown.ItemIcon>
268
+ Бот
269
+ </Dropdown.Item>
270
+ <Dropdown.Item onSelect={() => openConnect("telegram-account")}>
271
+ <Dropdown.ItemIcon>
272
+ <Icon name="brand.telegram" />
273
+ </Dropdown.ItemIcon>
274
+ Аккаунт
275
+ </Dropdown.Item>
276
+ </Dropdown.Group>
277
+ <Dropdown.Group label="ВКонтакте">
278
+ <Dropdown.Item onSelect={() => openConnect("vk-group")}>
279
+ <Dropdown.ItemIcon>
280
+ <Icon name="brand.vk" />
281
+ </Dropdown.ItemIcon>
282
+ Сообщество
283
+ </Dropdown.Item>
284
+ <Dropdown.Item onSelect={() => openConnect("vk-account")}>
285
+ <Dropdown.ItemIcon>
286
+ <Icon name="brand.vk" />
287
+ </Dropdown.ItemIcon>
288
+ Аккаунт
289
+ </Dropdown.Item>
290
+ </Dropdown.Group>
291
+ </Dropdown.Content>
292
+ </Dropdown.Root>
293
+ ```
294
+
224
295
  ## 3. Pairs that are easy to confuse
225
296
 
226
297
  | Pair | Rule |
@@ -238,7 +309,7 @@ column, and shows only the ones that have a level.
238
309
  | Button / LinkButton | Button does something (submit, open, delete). LinkButton goes somewhere (a URL, «Подробнее», «Все заказы»). |
239
310
  | Checkbox / Switch / Radio | Switch: a capability or a setting that is on or off — a permission, a privacy toggle — whether it saves with a form or at once. Checkbox: items picked out of a set, a consent, a one-off option of the action being sent. Radio: one of several, submitted later ([a flag, a level, a set](#a-flag-a-level-a-set)). |
240
311
  | Gallery / MediaViewer / Thumbnail | Thumbnail: one picture or video of an object in a list, card or picker (selectable with `checked`). Gallery: an album of 1–10 photos and videos laid out by their ratios; it opens MediaViewer itself. MediaViewer: the full-screen viewer on its own, opened from your own thumbnails or a button (`getOrigin` for the zoom out of the tile). A grid of many images to browse → a CSS grid of Thumbnails, not a Gallery. |
241
- | Sparkline / ChartFrame | Sparkline: one series as a headline number with its change and a small line or bars you can scrub (`variant="bars"` for signed values). ChartFrame: a panel around a real chart (axes, several series, legend, tooltip, states) drawn by a library or your SVG. Rows to compare → DataTable. |
312
+ | Sparkline / ChartFrame | Sparkline: one series as a headline number and a small line or bars you can scrub, its change badge on request (`variant="bars"` for signed values). ChartFrame: a panel around a real chart (axes, several series, legend, tooltip, states) drawn by a library or your SVG. Rows to compare → DataTable. |
242
313
  | VoicePlayer / VideoNote | The same player props (`src`, `duration`, `playing`, transcript, `transfer`). VoicePlayer: audio, a waveform seek slider and a playback rate. VideoNote: a round face-to-camera video with a `poster`; alone in `Message.Root` it is the message. A video with controls → `Thumbnail.Video` / `Message.Media`. |
243
314
  | Card / plain section / Grid | Card for a bounded block of related content that stands out (metric, settings panel, side facts). A page section that is the only thing in its region needs no card: `Page.Section label` and the content. Several tiles of one screen → one `Page.Grid` with a `Page.Cell span` each; a grid inside one block (thumbnails, fields) is the block's own CSS. Never wrap a single field, a DataTable or a whole page in a Card. |
244
315
  | EmptyPage / empty DataTable | Empty table (no rows yet or nothing matches the filter): the DataTable `empty` prop — headers and toolbar stay. Whole page or region with no data at all (first run): EmptyPage with one action. |
@@ -78,9 +78,9 @@ Show data: labels, people, objects, cards, tables, boards, feeds, code, disclose
78
78
  | Gantt | A resource or project schedule: rows of people, rooms, vehicles or tasks with bars on a day / week / month axis, edited by pointer and keyboard. | [COMPONENT.md](reference/components/gantt/COMPONENT.md) · [examples](reference/components/gantt/examples/) |
79
79
  | Timeline | An event feed: dots on a thin line, event title and date, an optional amount on the right, grouped under labels. | [COMPONENT.md](reference/components/timeline/COMPONENT.md) · [examples](reference/components/timeline/examples/) |
80
80
  | CodeBlock | A static TypeScript / TSX snippet with syntax highlighting, on a filled panel or bare inside a host. | [COMPONENT.md](reference/components/code-block/COMPONENT.md) · [examples](reference/components/code-block/examples/) |
81
- | RichText | Text in the chat's Markdown subset outside a message — a bot's welcome text, a template, a comment — rendered as elements, never as HTML. | [COMPONENT.md](reference/components/rich-text/COMPONENT.md) · [examples](reference/components/rich-text/examples/) |
81
+ | RichText | Text in the chat's Markdown subset — or a messenger's plain text with its entities — outside a message (a bot's welcome text, a template, a comment), rendered as elements, never as HTML. | [COMPONENT.md](reference/components/rich-text/COMPONENT.md) · [examples](reference/components/rich-text/examples/) |
82
82
  | Accordion | Collapsible sections: FAQ, settings groups, checkout steps. | [COMPONENT.md](reference/components/accordion/COMPONENT.md) · [examples](reference/components/accordion/examples/) |
83
- | Sparkline | A small chart — a smooth line over a fading area, a bare line or bars — with its headline: the value at rest, its change and its date; any point can be scrubbed. | [COMPONENT.md](reference/components/sparkline/COMPONENT.md) · [examples](reference/components/sparkline/examples/) |
83
+ | Sparkline | A small chart — a smooth line over a fading area, a bare line or bars — with its headline: the value at rest, its date and an optional change; any point can be scrubbed. | [COMPONENT.md](reference/components/sparkline/COMPONENT.md) · [examples](reference/components/sparkline/examples/) |
84
84
  | ChartFrame | A dashboard panel around any chart — Recharts, ECharts, visx, Chart.js or a hand-drawn SVG — with the title, a headline value, a measured plot area, a series legend, a tooltip body and a data table for screen readers. | [COMPONENT.md](reference/components/chart-frame/COMPONENT.md) · [examples](reference/components/chart-frame/examples/) |
85
85
  | DescriptionList | Read-only properties of a record: «label — value» pairs on a `<dl>` that stack by themselves where the room is narrow, with a muted dash for an empty value. | [COMPONENT.md](reference/components/description-list/COMPONENT.md) · [examples](reference/components/description-list/examples/) |
86
86