prime-ui-kit 0.17.0 → 0.19.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 (286) hide show
  1. package/README.md +2 -1
  2. package/dist/chunks/{chunk-PRP4B6OG.js → chunk-IVS3J7AS.js} +1 -1
  3. package/dist/chunks/{chunk-2ODXLPW3.js → chunk-QFHE5GKW.js} +1 -0
  4. package/dist/chunks/{chunk-7WWZYGRC.js → chunk-RZNGLELR.js} +4497 -3896
  5. package/dist/chunks/{chunk-A4WE3UNC.js → chunk-WYCTXD63.js} +18399 -17624
  6. package/dist/chunks/{chunk-SVFWPLNW.js → chunk-ZTZ2ML2B.js} +14 -3
  7. package/dist/color-picker.js +3 -3
  8. package/dist/components/badge/Badge.d.ts +17 -0
  9. package/dist/components/breadcrumb/Breadcrumb.d.ts +11 -2
  10. package/dist/components/button/Button.d.ts +1 -1
  11. package/dist/components/button-group/ButtonGroup.d.ts +1 -1
  12. package/dist/components/card/Card.d.ts +8 -4
  13. package/dist/components/chat/Chat.d.ts +3 -3
  14. package/dist/components/chat/MessageList.d.ts +5 -1
  15. package/dist/components/chat/groupMessages.d.ts +12 -4
  16. package/dist/components/chat-list/ChatList.d.ts +18 -9
  17. package/dist/components/chat-list/formatChatTime.d.ts +3 -3
  18. package/dist/components/checkbox/Checkbox.d.ts +6 -1
  19. package/dist/components/color-field/ColorField.d.ts +1 -1
  20. package/dist/components/color-picker/ColorPicker.d.ts +6 -1
  21. package/dist/components/color-picker/ColorPresets.d.ts +9 -1
  22. package/dist/components/crossfade/Crossfade.d.ts +1 -1
  23. package/dist/components/data-table/ColumnsMenu.d.ts +16 -20
  24. package/dist/components/data-table/DataTable.d.ts +16 -11
  25. package/dist/components/data-table/Head.d.ts +10 -1
  26. package/dist/components/data-table/Row.d.ts +7 -1
  27. package/dist/components/data-table/StateRows.d.ts +3 -1
  28. package/dist/components/data-table/types.d.ts +5 -0
  29. package/dist/components/digit-input/DigitInput.d.ts +1 -1
  30. package/dist/components/drawer/Drawer.d.ts +3 -2
  31. package/dist/components/field-list/FieldList.d.ts +24 -12
  32. package/dist/components/hint/Hint.d.ts +8 -2
  33. package/dist/components/index.d.ts +7 -4
  34. package/dist/components/input/Input.d.ts +27 -5
  35. package/dist/components/input/inputSuggestions.d.ts +39 -0
  36. package/dist/components/kanban/Kanban.d.ts +8 -9
  37. package/dist/components/list/List.d.ts +25 -6
  38. package/dist/components/message/Message.d.ts +15 -3
  39. package/dist/components/message/MessageSticker.d.ts +27 -0
  40. package/dist/components/message/formatMessageTime.d.ts +5 -2
  41. package/dist/components/modal/Modal.d.ts +3 -2
  42. package/dist/components/native-select/NativeSelect.d.ts +1 -1
  43. package/dist/components/radio/Radio.d.ts +11 -2
  44. package/dist/components/rich-text/RichText.d.ts +23 -0
  45. package/dist/components/scroll-container/ScrollContainer.d.ts +1 -1
  46. package/dist/components/scroll-container/scrollGlide.d.ts +26 -0
  47. package/dist/components/scroll-container/useScrollMotion.d.ts +25 -0
  48. package/dist/components/segmented-control/SegmentedControl.d.ts +1 -1
  49. package/dist/components/select/selectContext.d.ts +7 -0
  50. package/dist/components/slider/Slider.d.ts +1 -1
  51. package/dist/components/switch/Switch.d.ts +6 -1
  52. package/dist/components/tag-select/TagSelect.d.ts +1 -1
  53. package/dist/components/textarea/Textarea.d.ts +6 -1
  54. package/dist/components/timeline/Timeline.d.ts +1 -1
  55. package/dist/icon-set.js +2 -2
  56. package/dist/index.css +1 -1
  57. package/dist/index.js +5 -3
  58. package/dist/internal/ChoiceField.d.ts +3 -1
  59. package/dist/internal/FieldFrame.d.ts +8 -2
  60. package/dist/internal/fieldCell.d.ts +7 -1
  61. package/dist/internal/formatClock.d.ts +23 -2
  62. package/dist/internal/leaveGuard.d.ts +27 -0
  63. package/dist/internal/messageList.d.ts +2 -0
  64. package/dist/internal/overlay/DialogGuard.d.ts +3 -2
  65. package/dist/internal/overlay/DialogParts.d.ts +40 -6
  66. package/dist/internal/swatch.d.ts +5 -0
  67. package/dist/internal/tokenDefaults.d.ts +1 -0
  68. package/dist/layout/index.d.ts +1 -1
  69. package/dist/layout/page/Page.d.ts +48 -15
  70. package/dist/skill/anti-slop.md +28 -0
  71. package/dist/skill/api-contract.md +14 -7
  72. package/dist/skill/cheatsheet.md +28 -9
  73. package/dist/skill/checklist.md +9 -0
  74. package/dist/skill/choosing.md +25 -12
  75. package/dist/skill/components.md +1 -0
  76. package/dist/skill/composition.md +88 -16
  77. package/dist/skill/layouts.md +6 -4
  78. package/dist/skill/reference/components/badge/COMPONENT.md +14 -1
  79. package/dist/skill/reference/components/badge/examples/examples.module.css +7 -0
  80. package/dist/skill/reference/components/badge/examples/tag-row.tsx +34 -0
  81. package/dist/skill/reference/components/breadcrumb/COMPONENT.md +9 -4
  82. package/dist/skill/reference/components/breadcrumb/examples/router.tsx +17 -0
  83. package/dist/skill/reference/components/button/COMPONENT.md +1 -1
  84. package/dist/skill/reference/components/button-group/COMPONENT.md +1 -0
  85. package/dist/skill/reference/components/card/COMPONENT.md +7 -1
  86. package/dist/skill/reference/components/card/examples/states.tsx +59 -0
  87. package/dist/skill/reference/components/chat/COMPONENT.md +4 -1
  88. package/dist/skill/reference/components/chat/examples/time-zone.tsx +83 -0
  89. package/dist/skill/reference/components/chat-list/COMPONENT.md +12 -6
  90. package/dist/skill/reference/components/chat-list/examples/links.tsx +1 -1
  91. package/dist/skill/reference/components/chat-list/examples/router.tsx +63 -0
  92. package/dist/skill/reference/components/checkbox/COMPONENT.md +1 -0
  93. package/dist/skill/reference/components/color-field/COMPONENT.md +2 -1
  94. package/dist/skill/reference/components/color-picker/COMPONENT.md +14 -5
  95. package/dist/skill/reference/components/color-picker/examples/presets-custom.tsx +31 -0
  96. package/dist/skill/reference/components/color-swatches/COMPONENT.md +1 -0
  97. package/dist/skill/reference/components/confirm-dialog/COMPONENT.md +1 -1
  98. package/dist/skill/reference/components/crossfade/COMPONENT.md +1 -1
  99. package/dist/skill/reference/components/data-table/COMPONENT.md +46 -41
  100. package/dist/skill/reference/components/data-table/examples/actions-column.tsx +154 -0
  101. package/dist/skill/reference/components/data-table/examples/columns-visibility.tsx +3 -12
  102. package/dist/skill/reference/components/data-table/examples/controlled.tsx +1 -1
  103. package/dist/skill/reference/components/data-table/examples/narrow.tsx +4 -11
  104. package/dist/skill/reference/components/data-table/examples/numeric.tsx +9 -2
  105. package/dist/skill/reference/components/data-table/examples/row-link.tsx +18 -22
  106. package/dist/skill/reference/components/data-table/examples/screen.tsx +12 -1
  107. package/dist/skill/reference/components/data-table/examples/toolbar.tsx +26 -32
  108. package/dist/skill/reference/components/datepicker/COMPONENT.md +3 -1
  109. package/dist/skill/reference/components/datepicker/examples/deadline.tsx +33 -0
  110. package/dist/skill/reference/components/digit-input/COMPONENT.md +1 -0
  111. package/dist/skill/reference/components/dnd/COMPONENT.md +4 -3
  112. package/dist/skill/reference/components/dnd/examples/examples.module.css +4 -3
  113. package/dist/skill/reference/components/dnd/examples/handle.tsx +5 -5
  114. package/dist/skill/reference/components/dnd/examples/locked.tsx +1 -1
  115. package/dist/skill/reference/components/dnd/examples/table.tsx +6 -6
  116. package/dist/skill/reference/components/drawer/COMPONENT.md +38 -9
  117. package/dist/skill/reference/components/drawer/examples/client-card.tsx +32 -8
  118. package/dist/skill/reference/components/drawer/examples/header-actions.tsx +1 -1
  119. package/dist/skill/reference/components/drawer/examples/loading.tsx +58 -0
  120. package/dist/skill/reference/components/field-list/COMPONENT.md +40 -24
  121. package/dist/skill/reference/components/field-list/examples/base-row.tsx +2 -1
  122. package/dist/skill/reference/components/field-list/examples/locked.tsx +11 -11
  123. package/dist/skill/reference/components/field-list/examples/reorder.tsx +1 -1
  124. package/dist/skill/reference/components/field-list/examples/saving.tsx +83 -0
  125. package/dist/skill/reference/components/field-list/examples/screen.module.css +1 -1
  126. package/dist/skill/reference/components/field-list/examples/screen.tsx +168 -162
  127. package/dist/skill/reference/components/field-list/examples/totals.tsx +83 -0
  128. package/dist/skill/reference/components/file-upload/COMPONENT.md +1 -0
  129. package/dist/skill/reference/components/hint/COMPONENT.md +16 -2
  130. package/dist/skill/reference/components/hint/examples/variants.tsx +48 -0
  131. package/dist/skill/reference/components/input/COMPONENT.md +39 -9
  132. package/dist/skill/reference/components/input/examples/affixes.tsx +4 -2
  133. package/dist/skill/reference/components/input/examples/dismiss.tsx +29 -0
  134. package/dist/skill/reference/components/input/examples/suggestions.tsx +26 -0
  135. package/dist/skill/reference/components/input/examples/time.tsx +24 -0
  136. package/dist/skill/reference/components/input/examples/with-select.tsx +44 -0
  137. package/dist/skill/reference/components/kanban/COMPONENT.md +12 -8
  138. package/dist/skill/reference/components/kanban/examples/columns.tsx +4 -2
  139. package/dist/skill/reference/components/kanban/examples/states.tsx +7 -5
  140. package/dist/skill/reference/components/list/COMPONENT.md +39 -13
  141. package/dist/skill/reference/components/list/examples/reorder.tsx +42 -7
  142. package/dist/skill/reference/components/list/examples/row-controls.tsx +80 -0
  143. package/dist/skill/reference/components/list/examples/with-icon.tsx +23 -2
  144. package/dist/skill/reference/components/login-form/COMPONENT.md +8 -7
  145. package/dist/skill/reference/components/login-form/examples/states.tsx +12 -14
  146. package/dist/skill/reference/components/message/COMPONENT.md +30 -9
  147. package/dist/skill/reference/components/message/examples/preview.tsx +28 -0
  148. package/dist/skill/reference/components/message/examples/sticker.tsx +35 -0
  149. package/dist/skill/reference/components/modal/COMPONENT.md +35 -10
  150. package/dist/skill/reference/components/modal/examples/long-content.tsx +37 -18
  151. package/dist/skill/reference/components/native-select/COMPONENT.md +1 -0
  152. package/dist/skill/reference/components/notification/COMPONENT.md +1 -1
  153. package/dist/skill/reference/components/radio/COMPONENT.md +2 -0
  154. package/dist/skill/reference/components/rich-text/COMPONENT.md +93 -0
  155. package/dist/skill/reference/components/rich-text/examples/overview.tsx +19 -0
  156. package/dist/skill/reference/components/scroll-container/COMPONENT.md +8 -5
  157. package/dist/skill/reference/components/segmented-control/COMPONENT.md +4 -2
  158. package/dist/skill/reference/components/segmented-control/examples/in-form.tsx +1 -1
  159. package/dist/skill/reference/components/select/COMPONENT.md +7 -3
  160. package/dist/skill/reference/components/sheet/COMPONENT.md +18 -7
  161. package/dist/skill/reference/components/slider/COMPONENT.md +1 -0
  162. package/dist/skill/reference/components/slider/examples/controlled.tsx +1 -1
  163. package/dist/skill/reference/components/switch/COMPONENT.md +1 -0
  164. package/dist/skill/reference/components/tag-select/COMPONENT.md +1 -0
  165. package/dist/skill/reference/components/textarea/COMPONENT.md +1 -0
  166. package/dist/skill/reference/components/timeline/COMPONENT.md +3 -3
  167. package/dist/skill/reference/components/timeline/examples/structure.tsx +2 -1
  168. package/dist/skill/reference/layout/app-shell/COMPONENT.md +2 -2
  169. package/dist/skill/reference/layout/bottom-nav/COMPONENT.md +1 -0
  170. package/dist/skill/reference/layout/page/COMPONENT.md +42 -20
  171. package/dist/skill/reference/layout/page/examples/examples.module.css +7 -0
  172. package/dist/skill/reference/layout/page/examples/guard.tsx +87 -0
  173. package/dist/skill/reference/layout/page/examples/loading.tsx +57 -0
  174. package/dist/skill/reference/layout/page/examples/screen.module.css +1 -1
  175. package/dist/skill/reference/layout/page/examples/screen.tsx +93 -104
  176. package/dist/skill/reference/layout/page/examples/sections.tsx +2 -15
  177. package/dist/skill/reference/layout/sidebar/COMPONENT.md +1 -0
  178. package/dist/skill/responsive.md +19 -12
  179. package/dist/skill/ux.md +5 -3
  180. package/dist/tokens/semantic.d.ts +3 -1
  181. package/dist/tokens/themes/dark.d.ts +1 -1
  182. package/package.json +2 -2
  183. package/src/components/badge/COMPONENT.md +14 -1
  184. package/src/components/badge/examples/examples.module.css +7 -0
  185. package/src/components/badge/examples/tag-row.tsx +34 -0
  186. package/src/components/breadcrumb/COMPONENT.md +9 -4
  187. package/src/components/breadcrumb/examples/router.tsx +17 -0
  188. package/src/components/button/COMPONENT.md +1 -1
  189. package/src/components/button-group/COMPONENT.md +1 -0
  190. package/src/components/card/COMPONENT.md +7 -1
  191. package/src/components/card/examples/states.tsx +59 -0
  192. package/src/components/chat/COMPONENT.md +4 -1
  193. package/src/components/chat/examples/time-zone.tsx +83 -0
  194. package/src/components/chat-list/COMPONENT.md +12 -6
  195. package/src/components/chat-list/examples/links.tsx +1 -1
  196. package/src/components/chat-list/examples/router.tsx +63 -0
  197. package/src/components/checkbox/COMPONENT.md +1 -0
  198. package/src/components/color-field/COMPONENT.md +2 -1
  199. package/src/components/color-picker/COMPONENT.md +14 -5
  200. package/src/components/color-picker/examples/presets-custom.tsx +31 -0
  201. package/src/components/color-swatches/COMPONENT.md +1 -0
  202. package/src/components/confirm-dialog/COMPONENT.md +1 -1
  203. package/src/components/crossfade/COMPONENT.md +1 -1
  204. package/src/components/data-table/COMPONENT.md +46 -41
  205. package/src/components/data-table/examples/actions-column.tsx +154 -0
  206. package/src/components/data-table/examples/columns-visibility.tsx +3 -12
  207. package/src/components/data-table/examples/controlled.tsx +1 -1
  208. package/src/components/data-table/examples/narrow.tsx +4 -11
  209. package/src/components/data-table/examples/numeric.tsx +9 -2
  210. package/src/components/data-table/examples/row-link.tsx +18 -22
  211. package/src/components/data-table/examples/screen.tsx +12 -1
  212. package/src/components/data-table/examples/toolbar.tsx +26 -32
  213. package/src/components/datepicker/COMPONENT.md +3 -1
  214. package/src/components/datepicker/examples/deadline.tsx +33 -0
  215. package/src/components/digit-input/COMPONENT.md +1 -0
  216. package/src/components/dnd/COMPONENT.md +4 -3
  217. package/src/components/dnd/examples/examples.module.css +4 -3
  218. package/src/components/dnd/examples/handle.tsx +5 -5
  219. package/src/components/dnd/examples/locked.tsx +1 -1
  220. package/src/components/dnd/examples/table.tsx +6 -6
  221. package/src/components/drawer/COMPONENT.md +38 -9
  222. package/src/components/drawer/examples/client-card.tsx +32 -8
  223. package/src/components/drawer/examples/header-actions.tsx +1 -1
  224. package/src/components/drawer/examples/loading.tsx +58 -0
  225. package/src/components/field-list/COMPONENT.md +40 -24
  226. package/src/components/field-list/examples/base-row.tsx +2 -1
  227. package/src/components/field-list/examples/locked.tsx +11 -11
  228. package/src/components/field-list/examples/reorder.tsx +1 -1
  229. package/src/components/field-list/examples/saving.tsx +83 -0
  230. package/src/components/field-list/examples/screen.module.css +1 -1
  231. package/src/components/field-list/examples/screen.tsx +168 -162
  232. package/src/components/field-list/examples/totals.tsx +83 -0
  233. package/src/components/file-upload/COMPONENT.md +1 -0
  234. package/src/components/hint/COMPONENT.md +16 -2
  235. package/src/components/hint/examples/variants.tsx +48 -0
  236. package/src/components/input/COMPONENT.md +39 -9
  237. package/src/components/input/examples/affixes.tsx +4 -2
  238. package/src/components/input/examples/dismiss.tsx +29 -0
  239. package/src/components/input/examples/suggestions.tsx +26 -0
  240. package/src/components/input/examples/time.tsx +24 -0
  241. package/src/components/input/examples/with-select.tsx +44 -0
  242. package/src/components/kanban/COMPONENT.md +12 -8
  243. package/src/components/kanban/examples/columns.tsx +4 -2
  244. package/src/components/kanban/examples/states.tsx +7 -5
  245. package/src/components/list/COMPONENT.md +39 -13
  246. package/src/components/list/examples/reorder.tsx +42 -7
  247. package/src/components/list/examples/row-controls.tsx +80 -0
  248. package/src/components/list/examples/with-icon.tsx +23 -2
  249. package/src/components/login-form/COMPONENT.md +8 -7
  250. package/src/components/login-form/examples/states.tsx +12 -14
  251. package/src/components/message/COMPONENT.md +30 -9
  252. package/src/components/message/examples/preview.tsx +28 -0
  253. package/src/components/message/examples/sticker.tsx +35 -0
  254. package/src/components/modal/COMPONENT.md +35 -10
  255. package/src/components/modal/examples/long-content.tsx +37 -18
  256. package/src/components/native-select/COMPONENT.md +1 -0
  257. package/src/components/notification/COMPONENT.md +1 -1
  258. package/src/components/radio/COMPONENT.md +2 -0
  259. package/src/components/rich-text/COMPONENT.md +93 -0
  260. package/src/components/rich-text/examples/overview.tsx +19 -0
  261. package/src/components/scroll-container/COMPONENT.md +8 -5
  262. package/src/components/segmented-control/COMPONENT.md +4 -2
  263. package/src/components/segmented-control/examples/in-form.tsx +1 -1
  264. package/src/components/select/COMPONENT.md +7 -3
  265. package/src/components/sheet/COMPONENT.md +18 -7
  266. package/src/components/slider/COMPONENT.md +1 -0
  267. package/src/components/slider/examples/controlled.tsx +1 -1
  268. package/src/components/switch/COMPONENT.md +1 -0
  269. package/src/components/tag-select/COMPONENT.md +1 -0
  270. package/src/components/textarea/COMPONENT.md +1 -0
  271. package/src/components/timeline/COMPONENT.md +3 -3
  272. package/src/components/timeline/examples/structure.tsx +2 -1
  273. package/src/layout/app-shell/COMPONENT.md +2 -2
  274. package/src/layout/bottom-nav/COMPONENT.md +1 -0
  275. package/src/layout/page/COMPONENT.md +42 -20
  276. package/src/layout/page/examples/examples.module.css +7 -0
  277. package/src/layout/page/examples/guard.tsx +87 -0
  278. package/src/layout/page/examples/loading.tsx +57 -0
  279. package/src/layout/page/examples/screen.module.css +1 -1
  280. package/src/layout/page/examples/screen.tsx +93 -104
  281. package/src/layout/page/examples/sections.tsx +2 -15
  282. package/src/layout/sidebar/COMPONENT.md +1 -0
  283. package/src/styles/theme-dark.css +1 -1
  284. package/src/styles/theme-light.css +2 -1
  285. package/dist/components/data-table/context.d.ts +0 -14
  286. package/dist/components/scroll-container/useWheelToInline.d.ts +0 -10
@@ -1,4 +1,4 @@
1
- /** A record editor as a document page, a long form whose title, description and save stand in the content header: a plan's name, its specs and prices in one form; repeating records as FieldList rows with the headings once, a base price pinned above its changes, one value in three currencies in one row — `getLocked`, `compact`. */
1
+ /** A record editor as a document page, a long form whose title, description and save stand in the content header and whose blocks are titled cards one under another: a plan's name, its specs and prices; repeating records as FieldList rows with the headings once, an icon and its color side by side, a base price pinned above its changes, one value in three currencies in one row — `getLocked`, `compact`. */
2
2
  import {
3
3
  Button,
4
4
  Card,
@@ -29,9 +29,9 @@ type Period = { id: string; from: string; price: string; base?: boolean };
29
29
 
30
30
  const SPEC_COLUMNS: FieldListColumn[] = [
31
31
  { label: "Иконка", width: "compact" },
32
+ { label: "Цвет", width: "compact" },
32
33
  { label: "Параметр" },
33
34
  { label: "Значение" },
34
- { label: "Цвет", width: "compact" },
35
35
  ];
36
36
 
37
37
  const SPEC_ICONS: { name: IconName; label: string }[] = [
@@ -97,6 +97,7 @@ const SAVED_MS = 2000;
97
97
 
98
98
  export default function FieldListScreenExample() {
99
99
  const formId = React.useId();
100
+ const specsTitleId = React.useId();
100
101
  const [specs, setSpecs] = React.useState(SPECS);
101
102
  const [periods, setPeriods] = React.useState(PERIODS);
102
103
  const [nameError, setNameError] = React.useState<string>();
@@ -134,182 +135,187 @@ export default function FieldListScreenExample() {
134
135
  </Page.Actions>
135
136
  </Page.Header>
136
137
  <Page.Body>
137
- <Page.Section layout="aside" label="Основное">
138
- <form id={formId} noValidate onSubmit={save}>
139
- <Card.Root variant="panel">
140
- <Card.Body>
141
- <Fieldset disabled={saving} aria-label="Основное">
142
- <Input.Root label="Название" error={nameError}>
138
+ <form id={formId} noValidate onSubmit={save}>
139
+ <Card.Root variant="panel">
140
+ <Card.Header>
141
+ <Card.Title as="h2">Основное</Card.Title>
142
+ </Card.Header>
143
+ <Card.Body>
144
+ <Fieldset disabled={saving} aria-label="Основное">
145
+ <Input.Root label="Название" error={nameError}>
146
+ <Input.Wrapper>
147
+ <Input.Field name="name" defaultValue="Команда" />
148
+ </Input.Wrapper>
149
+ </Input.Root>
150
+ <Textarea.Root
151
+ name="summary"
152
+ label="Описание"
153
+ optional
154
+ rows={2}
155
+ defaultValue="Для отделов до 50 человек: задачи, документы и отчёты в одном месте."
156
+ />
157
+ </Fieldset>
158
+ </Card.Body>
159
+ </Card.Root>
160
+ </form>
161
+ <Card.Root variant="panel">
162
+ <Card.Header>
163
+ <Card.Title as="h2" id={specsTitleId}>
164
+ Характеристики
165
+ </Card.Title>
166
+ <Card.Description>Первые три закреплены: их видно в карточке тарифа.</Card.Description>
167
+ </Card.Header>
168
+ <Card.Body>
169
+ <FieldList
170
+ aria-labelledby={specsTitleId}
171
+ disabled={saving}
172
+ columns={SPEC_COLUMNS}
173
+ reorderable
174
+ value={specs}
175
+ onValueChange={setSpecs}
176
+ getId={(spec) => spec.id}
177
+ getLabel={(spec) => spec.name}
178
+ getLocked={(spec) => spec.pinned ?? false}
179
+ createItem={(): Spec => ({
180
+ id: crypto.randomUUID(),
181
+ icon: "object.globe",
182
+ name: "",
183
+ value: "",
184
+ color: "#14b8a6",
185
+ })}
186
+ addLabel="Добавить характеристику"
187
+ renderItem={(spec, row) => (
188
+ <>
189
+ <Select.Root
190
+ value={spec.icon}
191
+ onValueChange={(icon) => row.update({ icon: icon as IconName })}
192
+ >
193
+ <Select.Trigger>
194
+ <Select.TriggerIcon>
195
+ <Icon name={spec.icon} />
196
+ </Select.TriggerIcon>
197
+ </Select.Trigger>
198
+ <Select.Content>
199
+ {SPEC_ICONS.map((icon) => (
200
+ <Select.Item key={icon.name} value={icon.name}>
201
+ <Select.ItemIcon>
202
+ <Icon name={icon.name} />
203
+ </Select.ItemIcon>
204
+ {icon.label}
205
+ </Select.Item>
206
+ ))}
207
+ </Select.Content>
208
+ </Select.Root>
209
+ <ColorPresets.Root
210
+ value={spec.color}
211
+ onValueChange={(color) => row.update({ color: color ?? spec.color })}
212
+ >
213
+ <ColorPresets.Trigger>
214
+ <ColorPresets.SwatchButton />
215
+ </ColorPresets.Trigger>
216
+ <ColorPresets.Content />
217
+ </ColorPresets.Root>
218
+ <Input.Root>
143
219
  <Input.Wrapper>
144
- <Input.Field name="name" defaultValue="Команда" />
220
+ <Input.Field
221
+ value={spec.name}
222
+ onChange={(event) => row.update({ name: event.target.value })}
223
+ />
145
224
  </Input.Wrapper>
146
225
  </Input.Root>
147
- <Textarea.Root
148
- name="summary"
149
- label="Описание"
150
- optional
151
- rows={2}
152
- defaultValue="Для отделов до 50 человек: задачи, документы и отчёты в одном месте."
153
- />
154
- </Fieldset>
155
- </Card.Body>
156
- </Card.Root>
157
- </form>
158
- </Page.Section>
159
-
160
- <Page.Section layout="aside" label="Характеристики">
161
- <Card.Root variant="panel">
162
- <Card.Body>
226
+ <Input.Root>
227
+ <Input.Wrapper>
228
+ <Input.Field
229
+ value={spec.value}
230
+ onChange={(event) => row.update({ value: event.target.value })}
231
+ />
232
+ </Input.Wrapper>
233
+ </Input.Root>
234
+ </>
235
+ )}
236
+ />
237
+ </Card.Body>
238
+ </Card.Root>
239
+ <Card.Root variant="panel">
240
+ <Card.Header>
241
+ <Card.Title as="h2">Стоимость</Card.Title>
242
+ </Card.Header>
243
+ <Card.Body className={styles.groups}>
244
+ <Fieldset label="За месяц" disabled={saving}>
163
245
  <FieldList
164
- aria-label="Характеристики"
165
- disabled={saving}
166
- columns={SPEC_COLUMNS}
167
- reorderable
168
- value={specs}
169
- onValueChange={setSpecs}
170
- getId={(spec) => spec.id}
171
- getLabel={(spec) => spec.name}
172
- getLocked={(spec) => spec.pinned ?? false}
173
- createItem={(): Spec => ({
174
- id: crypto.randomUUID(),
175
- icon: "object.globe",
176
- name: "",
177
- value: "",
178
- color: "#14b8a6",
179
- })}
180
- addLabel="Добавить характеристику"
181
- renderItem={(spec, row) => (
246
+ columns={PRICE_COLUMNS}
247
+ value={periods}
248
+ onValueChange={setPeriods}
249
+ getId={(period) => period.id}
250
+ getLocked={(period) => period.base ?? false}
251
+ labels={{ lockedReason: "Базовую цену нельзя удалить" }}
252
+ createItem={() => ({ id: crypto.randomUUID(), from: "", price: "" })}
253
+ addLabel="Добавить изменение цены"
254
+ renderItem={(period, row) => (
182
255
  <>
183
- <Select.Root
184
- value={spec.icon}
185
- onValueChange={(icon) => row.update({ icon: icon as IconName })}
186
- >
187
- <Select.Trigger>
188
- <Select.TriggerIcon>
189
- <Icon name={spec.icon} />
190
- </Select.TriggerIcon>
191
- </Select.Trigger>
192
- <Select.Content>
193
- {SPEC_ICONS.map((icon) => (
194
- <Select.Item key={icon.name} value={icon.name}>
195
- <Select.ItemIcon>
196
- <Icon name={icon.name} />
197
- </Select.ItemIcon>
198
- {icon.label}
199
- </Select.Item>
200
- ))}
201
- </Select.Content>
202
- </Select.Root>
203
- <Input.Root>
204
- <Input.Wrapper>
205
- <Input.Field
206
- value={spec.name}
207
- onChange={(event) => row.update({ name: event.target.value })}
208
- />
209
- </Input.Wrapper>
210
- </Input.Root>
211
- <Input.Root>
212
- <Input.Wrapper>
213
- <Input.Field
214
- value={spec.value}
215
- onChange={(event) => row.update({ value: event.target.value })}
216
- />
217
- </Input.Wrapper>
218
- </Input.Root>
219
- <ColorPresets.Root
220
- value={spec.color}
221
- onValueChange={(color) => row.update({ color: color ?? spec.color })}
222
- >
223
- <ColorPresets.Trigger>
224
- <ColorPresets.SwatchButton />
225
- </ColorPresets.Trigger>
226
- <ColorPresets.Content />
227
- </ColorPresets.Root>
228
- </>
229
- )}
230
- />
231
- </Card.Body>
232
- </Card.Root>
233
- </Page.Section>
234
-
235
- <Page.Section layout="aside" label="Стоимость">
236
- <Card.Root variant="panel">
237
- <Card.Body className={styles.groups}>
238
- <Fieldset label="За месяц" disabled={saving}>
239
- <FieldList
240
- columns={PRICE_COLUMNS}
241
- value={periods}
242
- onValueChange={setPeriods}
243
- getId={(period) => period.id}
244
- getLocked={(period) => period.base ?? false}
245
- createItem={() => ({ id: crypto.randomUUID(), from: "", price: "" })}
246
- addLabel="Добавить изменение цены"
247
- renderItem={(period, row) => (
248
- <>
249
- {period.base ? (
250
- <Input.Root>
251
- <Input.Wrapper>
252
- <Input.Field value="Всегда, если нет изменений" readOnly />
253
- </Input.Wrapper>
254
- </Input.Root>
255
- ) : (
256
- <Select.Root
257
- value={period.from}
258
- onValueChange={(from) => row.update({ from })}
259
- placeholder="Выберите месяц"
260
- >
261
- <Select.Trigger>
262
- <Select.Value />
263
- </Select.Trigger>
264
- <Select.Content>
265
- {MONTHS.map((month) => (
266
- <Select.Item key={month.value} value={month.value}>
267
- {month.label}
268
- </Select.Item>
269
- ))}
270
- </Select.Content>
271
- </Select.Root>
272
- )}
256
+ {period.base ? (
273
257
  <Input.Root>
274
258
  <Input.Wrapper>
275
- <Input.Field
276
- value={period.price}
277
- inputMode="decimal"
278
- placeholder="0"
279
- onChange={(event) => row.update({ price: event.target.value })}
280
- />
281
- <Input.InlineAffix side="end">₽</Input.InlineAffix>
259
+ <Input.Field value="Всегда, если нет изменений" readOnly />
282
260
  </Input.Wrapper>
283
261
  </Input.Root>
284
- </>
285
- )}
286
- />
287
- </Fieldset>
288
- <Fieldset
289
- label="Подключение"
290
- description="Разовый платёж; пустая валюта не выставляется."
291
- disabled={saving}
292
- >
293
- <div className={styles.currencies}>
294
- {SETUP_FEE.map((fee) => (
295
- <Input.Root key={fee.name}>
262
+ ) : (
263
+ <Select.Root
264
+ value={period.from}
265
+ onValueChange={(from) => row.update({ from })}
266
+ placeholder="Выберите месяц"
267
+ >
268
+ <Select.Trigger>
269
+ <Select.Value />
270
+ </Select.Trigger>
271
+ <Select.Content>
272
+ {MONTHS.map((month) => (
273
+ <Select.Item key={month.value} value={month.value}>
274
+ {month.label}
275
+ </Select.Item>
276
+ ))}
277
+ </Select.Content>
278
+ </Select.Root>
279
+ )}
280
+ <Input.Root>
296
281
  <Input.Wrapper>
297
282
  <Input.Field
298
- name={fee.name}
299
- aria-label={fee.label}
283
+ value={period.price}
300
284
  inputMode="decimal"
301
285
  placeholder="0"
302
- defaultValue={fee.value}
286
+ onChange={(event) => row.update({ price: event.target.value })}
303
287
  />
304
- <Input.InlineAffix side="end">{fee.currency}</Input.InlineAffix>
288
+ <Input.InlineAffix side="end">₽</Input.InlineAffix>
305
289
  </Input.Wrapper>
306
290
  </Input.Root>
307
- ))}
308
- </div>
309
- </Fieldset>
310
- </Card.Body>
311
- </Card.Root>
312
- </Page.Section>
291
+ </>
292
+ )}
293
+ />
294
+ </Fieldset>
295
+ <Fieldset
296
+ label="Подключение"
297
+ description="Разовый платёж; пустая валюта не выставляется."
298
+ disabled={saving}
299
+ >
300
+ <div className={styles.currencies}>
301
+ {SETUP_FEE.map((fee) => (
302
+ <Input.Root key={fee.name}>
303
+ <Input.Wrapper>
304
+ <Input.Field
305
+ name={fee.name}
306
+ aria-label={fee.label}
307
+ inputMode="decimal"
308
+ placeholder="0"
309
+ defaultValue={fee.value}
310
+ />
311
+ <Input.InlineAffix side="end">{fee.currency}</Input.InlineAffix>
312
+ </Input.Wrapper>
313
+ </Input.Root>
314
+ ))}
315
+ </div>
316
+ </Fieldset>
317
+ </Card.Body>
318
+ </Card.Root>
313
319
  </Page.Body>
314
320
  </Page.Root>
315
321
  );
@@ -0,0 +1,83 @@
1
+ /** A deal bonus split between the team: the share takes a narrow column, and the totals row under the lines sums the shares and the amounts, each ending where its fields end their numbers — `renderTotals`, `width`. */
2
+ import { FieldList, type FieldListColumn, Input } from "prime-ui-kit";
3
+ import * as React from "react";
4
+
5
+ type Share = { id: string; member: string; percent: string };
6
+
7
+ const BONUS = 240_000;
8
+
9
+ const COLUMNS: FieldListColumn[] = [
10
+ { label: "Участник" },
11
+ { label: "Доля", width: "xs" },
12
+ { label: "Сумма", width: "m" },
13
+ ];
14
+
15
+ const SHARES: Share[] = [
16
+ { id: "sokolova", member: "Анна Соколова", percent: "50" },
17
+ { id: "demin", member: "Олег Демин", percent: "30" },
18
+ { id: "kotova", member: "Ирина Котова", percent: "20" },
19
+ ];
20
+
21
+ const AMOUNT = new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 0 });
22
+
23
+ const percentOf = (share: Share) => Number(share.percent.replace(",", ".")) || 0;
24
+
25
+ export default function FieldListTotalsExample() {
26
+ const [shares, setShares] = React.useState(SHARES);
27
+
28
+ return (
29
+ <FieldList
30
+ label="Бонус по сделке"
31
+ hint="Доли в сумме дают 100 %."
32
+ columns={COLUMNS}
33
+ value={shares}
34
+ onValueChange={setShares}
35
+ getId={(share) => share.id}
36
+ getLabel={(share) => share.member}
37
+ createItem={() => ({ id: crypto.randomUUID(), member: "", percent: "" })}
38
+ addLabel="Добавить участника"
39
+ renderItem={(share, row) => (
40
+ <>
41
+ <Input.Root>
42
+ <Input.Wrapper>
43
+ <Input.Field
44
+ value={share.member}
45
+ onChange={(event) => row.update({ member: event.target.value })}
46
+ />
47
+ </Input.Wrapper>
48
+ </Input.Root>
49
+ <Input.Root>
50
+ <Input.Wrapper>
51
+ <Input.Field
52
+ value={share.percent}
53
+ inputMode="decimal"
54
+ onChange={(event) => row.update({ percent: event.target.value })}
55
+ />
56
+ <Input.InlineAffix side="end">%</Input.InlineAffix>
57
+ </Input.Wrapper>
58
+ </Input.Root>
59
+ <Input.Root>
60
+ <Input.Wrapper>
61
+ <Input.Field
62
+ value={AMOUNT.format((BONUS * percentOf(share)) / 100)}
63
+ inputMode="decimal"
64
+ readOnly
65
+ />
66
+ <Input.InlineAffix side="end">₽</Input.InlineAffix>
67
+ </Input.Wrapper>
68
+ </Input.Root>
69
+ </>
70
+ )}
71
+ renderTotals={(items) => {
72
+ const percent = items.reduce((sum, share) => sum + percentOf(share), 0);
73
+ return (
74
+ <>
75
+ {null}
76
+ {`${percent} %`}
77
+ {`${AMOUNT.format((BONUS * percent) / 100)} ₽`}
78
+ </>
79
+ );
80
+ }}
81
+ />
82
+ );
83
+ }
@@ -120,6 +120,7 @@ A rejection of `upload` fails the file with its message as the reason; retry que
120
120
  | `required` | `boolean` | — | Red `*` after the label and native `required` on the input. |
121
121
  | `optional` | `boolean` | — | Muted marker right after the label text (`labels.optional`). |
122
122
  | `hint` | `ReactNode` | — | Help text under the field. Hidden while `error` is shown. |
123
+ | `hintTone` | `"neutral" \| "success" \| "warning" \| "danger"` | `"neutral"` | Status color of `hint` («Осталось 12 дней» in `warning`). The words carry the status, the color repeats it; an `error` replaces the hint and its tone. A validation error is `error`, never a `danger` hint. |
123
124
  | `error` | `ReactNode` | — | Error message under the field (a rejected file, too few files); implies `invalid`. |
124
125
  | `invalid` | `boolean` | — | Danger line and icon of the zone, `aria-invalid` on the input. A non-empty `error` implies it. |
125
126
  | `focusRing` | `boolean` | `true` | `false` hides only the visual focus ring of the drop zone and the add tile (`data-focus-ring="false"`); focus, keyboard and ARIA stay. The picker Button keeps its ring. |
@@ -8,6 +8,7 @@
8
8
  ## When to use
9
9
  - Under a control without `hint` / `error` props (an embedded `Datepicker.Panel`, a custom control), where you wire the ids yourself: format rules, limits, where the value comes from.
10
10
  - As the error message of such a control (`invalid`), in the same slot as the hint.
11
+ - A status the field's value has (a deadline that nears, a check that passed): `tone`, with the status in the words.
11
12
 
12
13
  ## When not to use
13
14
  - Input, Textarea, Select, Checkbox, Slider, SegmentedControl and other fields with `hint` / `error` props → pass the props; they render Hint and link the ids ([Input](../input/COMPONENT.md), [Textarea](../textarea/COMPONENT.md), [Checkbox](../checkbox/COMPONENT.md), [Slider](../slider/COMPONENT.md)).
@@ -36,6 +37,7 @@ Hint.Root <p> with the text
36
37
  | Prop | Type | Default | Description |
37
38
  |---|---|---|---|
38
39
  | `size` | `"xs" \| "s" \| "m" \| "l" \| "xl"` | host tier, else `"m"` | Tier of the paired field: 12/16 for xs–m, 13/20 for l and xl. Without it the tier of its host, else `m`. |
40
+ | `tone` | `"neutral" \| "success" \| "warning" \| "danger"` | `"neutral"` | Status color of the text (a deadline that nears, a check that passed). The words carry the status, the color repeats it; `invalid` and `disabled` take over the color. |
39
41
  | `invalid` | `boolean` | `false` | Error styling: danger text that drops in (fade + 4 px from above). |
40
42
  | `disabled` | `boolean` | `false` | Disabled text color, next to a disabled control. A disabled `Fieldset` around the hint sets it too. |
41
43
  | `children` | `ReactNode` | — | Text, optionally with `Hint.Icon` first. |
@@ -49,7 +51,15 @@ Hint.Root <p> with the text
49
51
  | `…rest` | `HTMLAttributes<HTMLSpanElement>` | — | `children` (the glyph), `className` and the other span attributes. |
50
52
 
51
53
  ## Variants
52
- Hint has no `variant` or `tone`; its states are flags.
54
+ ### tone
55
+ | Value | Looks like | Use when | Default |
56
+ |---|---|---|---|
57
+ | `neutral` | Muted text | Format rules, limits, where the value comes from | yes |
58
+ | `success` | Success text | The value passed a check («ИНН проверен») | |
59
+ | `warning` | Warning text | The value needs attention soon («Осталось 12 дней») | |
60
+ | `danger` | Danger text, no `aria-invalid` | A status of the data, not a validation error («Срок истёк») | |
61
+
62
+ The words carry the status; the color only repeats it, and a `Hint.Icon` with the status glyph adds a shape. A validation error is `invalid` (or a field's `error`), never `tone="danger"`. `invalid` and `disabled` take over the tone's color.
53
63
 
54
64
  ### size
55
65
  | Value | Looks like | Use when | Default |
@@ -68,11 +78,12 @@ The hint is always smaller than the field text; use the field's size.
68
78
  | `invalid` | Danger text, drops in | Validation error | `false` |
69
79
  | `disabled` | Disabled text | Under a disabled control | `false` |
70
80
 
71
- **Combinations** — one element switches between the hint text and the error text with `invalid` (no layout jump); a standalone hint that changes after an action stays mounted with `aria-live="polite"` (inside a field the frame does it). `invalid` and `disabled` together make no sense. There is no success state: use neutral text, or a [Banner](../banner/COMPONENT.md) for a result.
81
+ **Combinations** — one element switches between the hint text and the error text with `invalid` (no layout jump); a standalone hint that changes after an action stays mounted with `aria-live="polite"` (inside a field the frame does it). `invalid` and `disabled` together make no sense. A result of the whole form goes to a [Banner](../banner/COMPONENT.md), not a toned hint.
72
82
 
73
83
  ## States
74
84
  | State | Driven by | DOM |
75
85
  |---|---|---|
86
+ | status | `tone` | `data-tone` with the shared text tone (none for `neutral`, and none while `invalid` or `disabled`); the color eases between tones |
76
87
  | error | `invalid` | `data-invalid="true"`; the message drops in over `fast` (fade + `--prime-space-1` from above, no shake, no layout shift) and the color eases from the hint color |
77
88
  | disabled | `disabled`, or a disabled `Fieldset` around it | `data-disabled="true"` |
78
89
  | size | `size`, else the host tier | `data-size` |
@@ -106,6 +117,7 @@ No `labels`.
106
117
  | Example | Shows |
107
118
  |---|---|
108
119
  | [overview.tsx](examples/overview.tsx) | Help text under a calendar that has no hint of its own, linked to it through `id` and `aria-describedby` — `id`. |
120
+ | [variants.tsx](examples/variants.tsx) | Status hints whose words carry the status and the color repeats it — `tone`. |
109
121
  | [sizes.tsx](examples/sizes.tsx) | Every size tier, one per field tier: 12/16 for xs–m, 13/20 for l and xl — `size`. |
110
122
  | [states.tsx](examples/states.tsx) | Default help text next to an error and a hint under a disabled control — `invalid`, `disabled`. |
111
123
  | [with-icon.tsx](examples/with-icon.tsx) | A leading icon centred on the first line, in the default and the error state — `Hint.Icon`. |
@@ -116,6 +128,8 @@ No `labels`.
116
128
  - A separate hint and error element at the same time → one element; the error replaces the hint.
117
129
  - Hint without `id` + `aria-describedby` → the screen reader does not connect it to the field.
118
130
  - Hint size different from the field size → use the same `size`, or none inside a sized host.
131
+ - `tone="danger"` for a validation error → `invalid` (or the field's `error`): only it sets `aria-invalid`.
132
+ - A status by color alone («12 д.» in orange) → the words say it («Осталось 12 дней»), the tone repeats it.
119
133
  - `role="alert"` on a field error → none; the field frame announces it, a standalone hint takes `aria-live="polite"`.
120
134
 
121
135
  ## Related
@@ -0,0 +1,48 @@
1
+ /** Status hints whose words carry the status and the color repeats it — `tone`. */
2
+ import { Hint, Icon, Typography } from "prime-ui-kit";
3
+
4
+ export default function HintVariantsExample() {
5
+ return (
6
+ <div>
7
+ <div>
8
+ <Hint.Root>Действует до 30 ноября.</Hint.Root>
9
+ <Typography as="span" variant="caption" tone="muted">
10
+ neutral
11
+ </Typography>
12
+ </div>
13
+ <div>
14
+ <Hint.Root tone="success">
15
+ <Hint.Icon>
16
+ <Icon name="status.success" />
17
+ </Hint.Icon>
18
+ ИНН проверен в ФНС.
19
+ </Hint.Root>
20
+ <Typography as="span" variant="caption" tone="muted">
21
+ success
22
+ </Typography>
23
+ </div>
24
+ <div>
25
+ <Hint.Root tone="warning">
26
+ <Hint.Icon>
27
+ <Icon name="status.warning" />
28
+ </Hint.Icon>
29
+ Осталось 12 дней.
30
+ </Hint.Root>
31
+ <Typography as="span" variant="caption" tone="muted">
32
+ warning
33
+ </Typography>
34
+ </div>
35
+ <div>
36
+ <Hint.Root tone="danger">
37
+ <Hint.Icon>
38
+ <Icon name="status.danger" />
39
+ </Hint.Icon>
40
+ Срок истёк 3 дня назад.
41
+ </Hint.Root>
42
+ <Typography as="span" variant="caption" tone="muted">
43
+ danger
44
+ </Typography>
45
+ </div>
46
+ </div>
47
+ );
48
+ }