prime-ui-kit 0.14.0 → 0.15.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 (490) hide show
  1. package/README.md +13 -14
  2. package/dist/chunks/{chunk-P6ZO37JM.js → chunk-2IKH55MI.js} +1 -1
  3. package/dist/chunks/{chunk-SYEX7OVT.js → chunk-THFZIHKU.js} +276 -263
  4. package/dist/chunks/{chunk-WXKRUVSE.js → chunk-XJAVS6FH.js} +49 -48
  5. package/dist/color-picker.js +2 -2
  6. package/dist/components/dnd/Dnd.d.ts +1 -1
  7. package/dist/components/login-form/LoginForm.d.ts +5 -1
  8. package/dist/index.css +1 -1
  9. package/dist/index.js +2 -2
  10. package/dist/internal/swatch.d.ts +6 -2
  11. package/dist/layout/app-header/AppHeader.d.ts +9 -28
  12. package/dist/layout/index.d.ts +1 -1
  13. package/dist/skill/SKILL.md +9 -7
  14. package/dist/skill/api-contract.md +2 -2
  15. package/dist/skill/cheatsheet.md +9 -2
  16. package/dist/skill/checklist.md +9 -5
  17. package/dist/skill/choosing.md +7 -9
  18. package/dist/skill/components.md +5 -12
  19. package/dist/skill/composition.md +281 -54
  20. package/dist/skill/foundations.md +1 -1
  21. package/dist/skill/layouts.md +70 -37
  22. package/dist/skill/reference/components/avatar/examples/states.tsx +4 -4
  23. package/dist/skill/reference/components/badge/examples/applied-filters.tsx +13 -19
  24. package/dist/skill/reference/components/badge/examples/examples.module.css +1 -8
  25. package/dist/skill/reference/components/badge/examples/server-tags.tsx +2 -6
  26. package/dist/skill/reference/components/banner/examples/examples.module.css +1 -5
  27. package/dist/skill/reference/components/banner/examples/page-strip.tsx +16 -22
  28. package/dist/skill/reference/components/breadcrumb/COMPONENT.md +4 -3
  29. package/dist/skill/reference/components/breadcrumb/examples/ellipsis.tsx +2 -2
  30. package/dist/skill/reference/components/button-group/COMPONENT.md +2 -2
  31. package/dist/skill/reference/components/button-group/examples/in-form.tsx +22 -2
  32. package/dist/skill/reference/components/button-group/examples/states.tsx +10 -1
  33. package/dist/skill/reference/components/card/COMPONENT.md +2 -1
  34. package/dist/skill/reference/components/card/examples/screen.tsx +362 -0
  35. package/dist/skill/reference/components/card/examples/variants.tsx +15 -1
  36. package/dist/skill/reference/components/chart-frame/COMPONENT.md +2 -1
  37. package/dist/skill/reference/components/chart-frame/examples/fill-height.tsx +119 -0
  38. package/dist/skill/reference/components/chart-frame/examples/states.tsx +3 -1
  39. package/dist/skill/reference/components/chat/COMPONENT.md +3 -1
  40. package/dist/skill/reference/components/chat/examples/group-chat.tsx +15 -2
  41. package/dist/skill/reference/components/chat/examples/reactions.tsx +121 -0
  42. package/dist/skill/reference/components/chat/examples/screen.module.css +9 -0
  43. package/dist/skill/{patterns/chat.tsx → reference/components/chat/examples/screen.tsx} +170 -351
  44. package/dist/skill/reference/components/chat-list/COMPONENT.md +1 -0
  45. package/dist/skill/reference/components/chat-list/examples/links.tsx +72 -0
  46. package/dist/skill/reference/components/checkbox/COMPONENT.md +1 -1
  47. package/dist/skill/reference/components/checkbox/examples/examples.module.css +5 -1
  48. package/dist/skill/reference/components/checkbox/examples/indicator.tsx +13 -7
  49. package/dist/skill/reference/components/checkbox/examples/states.tsx +6 -1
  50. package/dist/skill/reference/components/color-field/COMPONENT.md +1 -1
  51. package/dist/skill/reference/components/color-picker/COMPONENT.md +4 -3
  52. package/dist/skill/reference/components/color-picker/examples/controlled-open.tsx +37 -0
  53. package/dist/skill/reference/components/color-picker/examples/presets.tsx +2 -2
  54. package/dist/skill/reference/components/color-picker/examples/states.tsx +23 -14
  55. package/dist/skill/reference/components/composer/COMPONENT.md +2 -1
  56. package/dist/skill/reference/components/composer/examples/attach-menu.tsx +78 -0
  57. package/dist/skill/reference/components/composer/examples/attachments.tsx +2 -2
  58. package/dist/skill/reference/components/crossfade/COMPONENT.md +1 -0
  59. package/dist/skill/reference/components/crossfade/examples/screen.tsx +197 -0
  60. package/dist/skill/reference/components/data-table/COMPONENT.md +3 -2
  61. package/dist/skill/reference/components/data-table/examples/detail-panel.tsx +36 -37
  62. package/dist/skill/reference/components/data-table/examples/examples.module.css +0 -12
  63. package/dist/skill/reference/components/data-table/examples/numeric.tsx +36 -4
  64. package/dist/skill/reference/components/data-table/examples/overview.tsx +1 -0
  65. package/dist/skill/reference/components/data-table/examples/row-open.tsx +7 -36
  66. package/dist/skill/{patterns/list-page.module.css → reference/components/data-table/examples/screen.module.css} +1 -7
  67. package/dist/skill/{patterns/list-page.tsx → reference/components/data-table/examples/screen.tsx} +84 -64
  68. package/dist/skill/reference/components/datepicker/COMPONENT.md +5 -4
  69. package/dist/skill/reference/components/datepicker/examples/examples.module.css +2 -6
  70. package/dist/skill/reference/components/datepicker/examples/in-form.tsx +47 -55
  71. package/dist/skill/reference/components/datepicker/examples/locale.tsx +35 -0
  72. package/dist/skill/reference/components/datepicker/examples/range-presets.tsx +11 -2
  73. package/dist/skill/reference/components/datepicker/examples/states.tsx +9 -1
  74. package/dist/skill/reference/components/description-list/COMPONENT.md +1 -0
  75. package/dist/skill/{patterns/detail-page.module.css → reference/components/description-list/examples/screen.module.css} +2 -9
  76. package/dist/skill/reference/components/description-list/examples/screen.tsx +214 -0
  77. package/dist/skill/reference/components/description-list/examples/structure.tsx +1 -1
  78. package/dist/skill/reference/components/digit-input/COMPONENT.md +3 -1
  79. package/dist/skill/reference/components/digit-input/examples/states.tsx +3 -1
  80. package/dist/skill/reference/components/digit-input/examples/without-focus-ring.tsx +13 -0
  81. package/dist/skill/reference/components/divider/examples/with-icon.tsx +2 -2
  82. package/dist/skill/reference/components/dnd/COMPONENT.md +4 -2
  83. package/dist/skill/reference/components/dnd/examples/drop-zones.tsx +82 -49
  84. package/dist/skill/reference/components/dnd/examples/examples.module.css +50 -0
  85. package/dist/skill/reference/components/dnd/examples/states.tsx +54 -0
  86. package/dist/skill/reference/components/dnd/examples/table.tsx +113 -0
  87. package/dist/skill/reference/components/drawer/COMPONENT.md +1 -0
  88. package/dist/skill/reference/components/drawer/examples/dismiss.tsx +5 -3
  89. package/dist/skill/reference/components/drawer/examples/examples.module.css +0 -13
  90. package/dist/skill/reference/components/drawer/examples/header-actions.tsx +5 -9
  91. package/dist/skill/reference/components/drawer/examples/overview.tsx +6 -18
  92. package/dist/skill/reference/components/drawer/examples/screen.tsx +298 -0
  93. package/dist/skill/reference/components/dropdown/examples/examples.module.css +1 -5
  94. package/dist/skill/reference/components/empty-page/COMPONENT.md +1 -1
  95. package/dist/skill/reference/components/empty-page/examples/region-error.tsx +8 -2
  96. package/dist/skill/reference/components/example-frame/COMPONENT.md +3 -2
  97. package/dist/skill/reference/components/example-frame/examples/initial-view.tsx +44 -0
  98. package/dist/skill/reference/components/field-list/COMPONENT.md +2 -1
  99. package/dist/skill/reference/components/field-list/examples/errors.tsx +5 -2
  100. package/dist/skill/{patterns/record-editor.tsx → reference/components/field-list/examples/screen.tsx} +3 -43
  101. package/dist/skill/reference/components/file-upload/COMPONENT.md +7 -6
  102. package/dist/skill/reference/components/file-upload/examples/custom-body.tsx +2 -1
  103. package/dist/skill/reference/components/file-upload/examples/examples.module.css +1 -7
  104. package/dist/skill/reference/components/file-upload/examples/file-cards.tsx +10 -1
  105. package/dist/skill/reference/components/file-upload/examples/gallery.tsx +2 -2
  106. package/dist/skill/reference/components/file-upload/examples/in-form.tsx +4 -9
  107. package/dist/skill/reference/components/file-upload/examples/overview.tsx +5 -1
  108. package/dist/skill/reference/components/file-upload/examples/row-parts.tsx +24 -1
  109. package/dist/skill/{patterns/media-library.module.css → reference/components/file-upload/examples/screen.module.css} +2 -1
  110. package/dist/skill/reference/components/file-upload/examples/screen.tsx +646 -0
  111. package/dist/skill/reference/components/file-upload/examples/states.tsx +6 -2
  112. package/dist/skill/reference/components/file-upload/examples/validation.tsx +11 -2
  113. package/dist/skill/reference/components/file-upload/examples/variants.tsx +3 -20
  114. package/dist/skill/reference/components/gantt/COMPONENT.md +2 -1
  115. package/dist/skill/reference/components/gantt/examples/screen.module.css +14 -0
  116. package/dist/skill/{patterns/schedule.tsx → reference/components/gantt/examples/screen.tsx} +174 -170
  117. package/dist/skill/reference/components/gantt/examples/states.tsx +6 -2
  118. package/dist/skill/reference/components/input/COMPONENT.md +2 -2
  119. package/dist/skill/reference/components/input/examples/examples.module.css +0 -6
  120. package/dist/skill/reference/components/input/examples/in-form.tsx +1 -9
  121. package/dist/skill/reference/components/input/examples/password-strength.tsx +34 -8
  122. package/dist/skill/reference/components/input/examples/states.tsx +6 -1
  123. package/dist/skill/reference/components/kanban/COMPONENT.md +3 -2
  124. package/dist/skill/reference/components/kanban/examples/columns.tsx +8 -5
  125. package/dist/skill/reference/components/kanban/examples/screen.module.css +17 -0
  126. package/dist/skill/{patterns/kanban-board.tsx → reference/components/kanban/examples/screen.tsx} +130 -172
  127. package/dist/skill/reference/components/kanban/examples/states.tsx +17 -6
  128. package/dist/skill/reference/components/login-form/COMPONENT.md +6 -5
  129. package/dist/skill/reference/components/login-form/examples/examples.module.css +2 -21
  130. package/dist/skill/reference/components/login-form/examples/forgot-password.tsx +28 -32
  131. package/dist/skill/reference/components/login-form/examples/narrow.tsx +1 -1
  132. package/dist/skill/reference/components/login-form/examples/overview.tsx +45 -47
  133. package/dist/skill/reference/components/login-form/examples/register.tsx +64 -68
  134. package/dist/skill/reference/components/login-form/examples/reset-password.tsx +50 -47
  135. package/dist/skill/reference/components/login-form/examples/sizes.tsx +4 -6
  136. package/dist/skill/reference/components/login-form/examples/states.tsx +53 -57
  137. package/dist/skill/reference/components/login-form/examples/structure.tsx +18 -22
  138. package/dist/skill/reference/components/login-form/examples/verification-code.tsx +41 -45
  139. package/dist/skill/reference/components/media-viewer/COMPONENT.md +1 -1
  140. package/dist/skill/reference/components/media-viewer/examples/dismiss.tsx +5 -1
  141. package/dist/skill/reference/components/message/COMPONENT.md +4 -3
  142. package/dist/skill/reference/components/message/examples/file.tsx +5 -1
  143. package/dist/skill/reference/components/message/examples/media-open.tsx +48 -0
  144. package/dist/skill/reference/components/message/examples/status.tsx +21 -3
  145. package/dist/skill/reference/components/message/examples/text-formatting.tsx +7 -1
  146. package/dist/skill/reference/components/modal/COMPONENT.md +2 -1
  147. package/dist/skill/reference/components/modal/examples/custom-container.tsx +20 -20
  148. package/dist/skill/reference/components/modal/examples/dismiss.tsx +5 -3
  149. package/dist/skill/reference/components/modal/examples/enter-key.tsx +116 -0
  150. package/dist/skill/reference/components/modal/examples/examples.module.css +7 -13
  151. package/dist/skill/reference/components/modal/examples/structure.tsx +5 -2
  152. package/dist/skill/reference/components/native-select/COMPONENT.md +1 -1
  153. package/dist/skill/reference/components/native-select/examples/states.tsx +5 -1
  154. package/dist/skill/reference/components/notification/COMPONENT.md +1 -1
  155. package/dist/skill/reference/components/notification/examples/dismiss.tsx +2 -2
  156. package/dist/skill/reference/components/page-content/COMPONENT.md +1 -0
  157. package/dist/skill/reference/components/page-content/examples/list-detail.tsx +0 -3
  158. package/dist/skill/{patterns/settings-page.tsx → reference/components/page-content/examples/screen.tsx} +3 -3
  159. package/dist/skill/reference/components/page-content/examples/variants.tsx +4 -11
  160. package/dist/skill/reference/components/page-toolbar/COMPONENT.md +17 -15
  161. package/dist/skill/reference/components/page-toolbar/examples/narrow.tsx +6 -3
  162. package/dist/skill/reference/components/pagination/examples/controlled.tsx +29 -29
  163. package/dist/skill/reference/components/pagination/examples/examples.module.css +0 -2
  164. package/dist/skill/reference/components/progress-bar/COMPONENT.md +1 -1
  165. package/dist/skill/reference/components/progress-bar/examples/segments.tsx +2 -2
  166. package/dist/skill/reference/components/progress-circle/COMPONENT.md +1 -1
  167. package/dist/skill/reference/components/progress-circle/examples/segments.tsx +8 -3
  168. package/dist/skill/reference/components/radio/COMPONENT.md +1 -0
  169. package/dist/skill/reference/components/radio/examples/examples.module.css +14 -0
  170. package/dist/skill/reference/components/radio/examples/in-form.tsx +45 -0
  171. package/dist/skill/reference/components/scroll-container/COMPONENT.md +1 -0
  172. package/dist/skill/reference/components/scroll-container/examples/auto-scroll.tsx +3 -3
  173. package/dist/skill/reference/components/scroll-container/examples/chat.tsx +3 -3
  174. package/dist/skill/reference/components/scroll-container/examples/edges.tsx +6 -3
  175. package/dist/skill/reference/components/scroll-container/examples/examples.module.css +4 -17
  176. package/dist/skill/reference/components/scroll-container/examples/load-more.tsx +51 -0
  177. package/dist/skill/reference/components/scroll-container/examples/lock.tsx +3 -5
  178. package/dist/skill/reference/components/scroll-container/examples/overscroll.tsx +3 -3
  179. package/dist/skill/reference/components/scroll-container/examples/overview.tsx +3 -3
  180. package/dist/skill/reference/components/scroll-container/examples/scrollbar.tsx +13 -7
  181. package/dist/skill/reference/components/scroll-container/examples/variants.tsx +13 -7
  182. package/dist/skill/reference/components/segmented-control/COMPONENT.md +2 -2
  183. package/dist/skill/reference/components/segmented-control/examples/in-form.tsx +5 -1
  184. package/dist/skill/reference/components/segmented-control/examples/states.tsx +10 -1
  185. package/dist/skill/reference/components/select/COMPONENT.md +1 -1
  186. package/dist/skill/reference/components/select/examples/examples.module.css +2 -7
  187. package/dist/skill/reference/components/select/examples/states.tsx +13 -1
  188. package/dist/skill/reference/components/sheet/COMPONENT.md +2 -0
  189. package/dist/skill/reference/components/sheet/examples/guard.tsx +72 -0
  190. package/dist/skill/reference/components/sheet/examples/header-actions.tsx +56 -0
  191. package/dist/skill/reference/components/slider/COMPONENT.md +2 -1
  192. package/dist/skill/reference/components/slider/examples/examples.module.css +8 -0
  193. package/dist/skill/reference/components/slider/examples/in-form.tsx +47 -0
  194. package/dist/skill/reference/components/slider/examples/states.tsx +4 -1
  195. package/dist/skill/reference/components/smart-filter/COMPONENT.md +1 -1
  196. package/dist/skill/reference/components/smart-filter/examples/controlled.tsx +3 -3
  197. package/dist/skill/reference/components/smart-filter/examples/ranges.tsx +44 -10
  198. package/dist/skill/reference/components/sparkline/COMPONENT.md +1 -1
  199. package/dist/skill/reference/components/sparkline/examples/reference.tsx +2 -1
  200. package/dist/skill/reference/components/tabs/COMPONENT.md +1 -1
  201. package/dist/skill/reference/components/tabs/examples/sizes.tsx +1 -1
  202. package/dist/skill/reference/components/tag-select/COMPONENT.md +1 -1
  203. package/dist/skill/reference/components/tag-select/examples/states.tsx +8 -1
  204. package/dist/skill/reference/components/textarea/COMPONENT.md +1 -1
  205. package/dist/skill/reference/components/textarea/examples/examples.module.css +0 -6
  206. package/dist/skill/reference/components/textarea/examples/in-form.tsx +1 -9
  207. package/dist/skill/reference/components/textarea/examples/states.tsx +2 -1
  208. package/dist/skill/reference/components/thumbnail/COMPONENT.md +2 -1
  209. package/dist/skill/reference/components/thumbnail/examples/examples.module.css +1 -1
  210. package/dist/skill/reference/components/thumbnail/examples/fit.tsx +24 -0
  211. package/dist/skill/reference/components/thumbnail/examples/video.tsx +20 -1
  212. package/dist/skill/reference/components/timeline/COMPONENT.md +1 -1
  213. package/dist/skill/reference/components/timeline/examples/links.tsx +55 -14
  214. package/dist/skill/reference/components/tooltip/COMPONENT.md +1 -1
  215. package/dist/skill/reference/components/tooltip/examples/toolbar.tsx +2 -2
  216. package/dist/skill/reference/components/typography/examples/weights.tsx +3 -0
  217. package/dist/skill/reference/components/video-note/COMPONENT.md +2 -2
  218. package/dist/skill/reference/components/video-note/examples/controlled.tsx +19 -2
  219. package/dist/skill/reference/components/video-note/examples/in-message.tsx +5 -2
  220. package/dist/skill/reference/components/visually-hidden/COMPONENT.md +2 -1
  221. package/dist/skill/reference/components/visually-hidden/examples/assertive.tsx +39 -0
  222. package/dist/skill/reference/components/voice-player/COMPONENT.md +2 -2
  223. package/dist/skill/reference/components/voice-player/examples/call-recording.tsx +19 -9
  224. package/dist/skill/reference/components/voice-player/examples/controlled.tsx +20 -3
  225. package/dist/skill/reference/layout/app-header/COMPONENT.md +46 -63
  226. package/dist/skill/reference/layout/app-header/examples/breadcrumbs.tsx +10 -9
  227. package/dist/skill/reference/layout/app-header/examples/examples.module.css +1 -1
  228. package/dist/skill/reference/layout/app-header/examples/in-app-shell.tsx +0 -1
  229. package/dist/skill/reference/layout/app-header/examples/narrow.tsx +4 -5
  230. package/dist/skill/reference/layout/app-header/examples/overview.tsx +8 -10
  231. package/dist/skill/reference/layout/app-header/examples/page-title.tsx +3 -10
  232. package/dist/skill/reference/layout/app-shell/COMPONENT.md +4 -3
  233. package/dist/skill/reference/layout/app-shell/examples/overview.tsx +15 -30
  234. package/dist/skill/reference/layout/app-shell/examples/screen.tsx +262 -0
  235. package/dist/skill/reference/layout/app-shell/examples/template.tsx +29 -9
  236. package/dist/skill/reference/layout/sidebar/COMPONENT.md +7 -4
  237. package/dist/skill/reference/layout/sidebar/examples/account.tsx +2 -2
  238. package/dist/skill/reference/layout/sidebar/examples/collapsible-groups.tsx +12 -4
  239. package/dist/skill/reference/layout/sidebar/examples/custom-part.tsx +53 -0
  240. package/dist/skill/reference/layout/sidebar/examples/examples.module.css +5 -0
  241. package/dist/skill/reference/layout/sidebar/examples/narrow.tsx +53 -0
  242. package/dist/skill/reference/layout/sidebar/examples/persist.tsx +57 -0
  243. package/dist/skill/reference/layout/sidebar/examples/router.tsx +14 -2
  244. package/dist/skill/reference/layout/sidebar/examples/structure.tsx +7 -2
  245. package/dist/skill/responsive.md +2 -2
  246. package/package.json +1 -1
  247. package/src/components/avatar/examples/states.tsx +4 -4
  248. package/src/components/badge/examples/applied-filters.tsx +13 -19
  249. package/src/components/badge/examples/examples.module.css +1 -8
  250. package/src/components/badge/examples/server-tags.tsx +2 -6
  251. package/src/components/banner/examples/examples.module.css +1 -5
  252. package/src/components/banner/examples/page-strip.tsx +16 -22
  253. package/src/components/breadcrumb/COMPONENT.md +4 -3
  254. package/src/components/breadcrumb/examples/ellipsis.tsx +2 -2
  255. package/src/components/button-group/COMPONENT.md +2 -2
  256. package/src/components/button-group/examples/in-form.tsx +22 -2
  257. package/src/components/button-group/examples/states.tsx +10 -1
  258. package/src/components/card/COMPONENT.md +2 -1
  259. package/src/components/card/examples/screen.module.css +14 -0
  260. package/src/components/card/examples/screen.tsx +362 -0
  261. package/src/components/card/examples/variants.tsx +15 -1
  262. package/src/components/chart-frame/COMPONENT.md +2 -1
  263. package/src/components/chart-frame/examples/fill-height.tsx +119 -0
  264. package/src/components/chart-frame/examples/states.tsx +3 -1
  265. package/src/components/chat/COMPONENT.md +3 -1
  266. package/src/components/chat/examples/group-chat.tsx +15 -2
  267. package/src/components/chat/examples/reactions.tsx +121 -0
  268. package/src/components/chat/examples/screen.module.css +9 -0
  269. package/src/components/chat/examples/screen.tsx +718 -0
  270. package/src/components/chat-list/COMPONENT.md +1 -0
  271. package/src/components/chat-list/examples/links.tsx +72 -0
  272. package/src/components/checkbox/COMPONENT.md +1 -1
  273. package/src/components/checkbox/examples/examples.module.css +5 -1
  274. package/src/components/checkbox/examples/indicator.tsx +13 -7
  275. package/src/components/checkbox/examples/states.tsx +6 -1
  276. package/src/components/color-field/COMPONENT.md +1 -1
  277. package/src/components/color-picker/COMPONENT.md +4 -3
  278. package/src/components/color-picker/examples/controlled-open.tsx +37 -0
  279. package/src/components/color-picker/examples/presets.tsx +2 -2
  280. package/src/components/color-picker/examples/states.tsx +23 -14
  281. package/src/components/composer/COMPONENT.md +2 -1
  282. package/src/components/composer/examples/attach-menu.tsx +78 -0
  283. package/src/components/composer/examples/attachments.tsx +2 -2
  284. package/src/components/crossfade/COMPONENT.md +1 -0
  285. package/src/components/crossfade/examples/screen.module.css +40 -0
  286. package/src/components/crossfade/examples/screen.tsx +197 -0
  287. package/src/components/data-table/COMPONENT.md +3 -2
  288. package/src/components/data-table/examples/detail-panel.tsx +36 -37
  289. package/src/components/data-table/examples/examples.module.css +0 -12
  290. package/src/components/data-table/examples/numeric.tsx +36 -4
  291. package/src/components/data-table/examples/overview.tsx +1 -0
  292. package/src/components/data-table/examples/row-open.tsx +7 -36
  293. package/src/components/data-table/examples/screen.module.css +13 -0
  294. package/src/components/data-table/examples/screen.tsx +354 -0
  295. package/src/components/datepicker/COMPONENT.md +5 -4
  296. package/src/components/datepicker/examples/examples.module.css +2 -6
  297. package/src/components/datepicker/examples/in-form.tsx +47 -55
  298. package/src/components/datepicker/examples/locale.tsx +35 -0
  299. package/src/components/datepicker/examples/range-presets.tsx +11 -2
  300. package/src/components/datepicker/examples/states.tsx +9 -1
  301. package/src/components/description-list/COMPONENT.md +1 -0
  302. package/src/components/description-list/examples/screen.module.css +64 -0
  303. package/src/components/description-list/examples/screen.tsx +214 -0
  304. package/src/components/description-list/examples/structure.tsx +1 -1
  305. package/src/components/digit-input/COMPONENT.md +3 -1
  306. package/src/components/digit-input/examples/states.tsx +3 -1
  307. package/src/components/digit-input/examples/without-focus-ring.tsx +13 -0
  308. package/src/components/divider/examples/with-icon.tsx +2 -2
  309. package/src/components/dnd/COMPONENT.md +4 -2
  310. package/src/components/dnd/examples/drop-zones.tsx +82 -49
  311. package/src/components/dnd/examples/examples.module.css +50 -0
  312. package/src/components/dnd/examples/states.tsx +54 -0
  313. package/src/components/dnd/examples/table.tsx +113 -0
  314. package/src/components/drawer/COMPONENT.md +1 -0
  315. package/src/components/drawer/examples/dismiss.tsx +5 -3
  316. package/src/components/drawer/examples/examples.module.css +0 -13
  317. package/src/components/drawer/examples/header-actions.tsx +5 -9
  318. package/src/components/drawer/examples/overview.tsx +6 -18
  319. package/src/components/drawer/examples/screen.module.css +14 -0
  320. package/src/components/drawer/examples/screen.tsx +298 -0
  321. package/src/components/dropdown/examples/examples.module.css +1 -5
  322. package/src/components/empty-page/COMPONENT.md +1 -1
  323. package/src/components/empty-page/examples/region-error.tsx +8 -2
  324. package/src/components/example-frame/COMPONENT.md +3 -2
  325. package/src/components/example-frame/examples/initial-view.tsx +44 -0
  326. package/src/components/field-list/COMPONENT.md +2 -1
  327. package/src/components/field-list/examples/errors.tsx +5 -2
  328. package/src/components/field-list/examples/screen.module.css +18 -0
  329. package/src/components/field-list/examples/screen.tsx +316 -0
  330. package/src/components/file-upload/COMPONENT.md +7 -6
  331. package/src/components/file-upload/examples/custom-body.tsx +2 -1
  332. package/src/components/file-upload/examples/examples.module.css +1 -7
  333. package/src/components/file-upload/examples/file-cards.tsx +10 -1
  334. package/src/components/file-upload/examples/gallery.tsx +2 -2
  335. package/src/components/file-upload/examples/in-form.tsx +4 -9
  336. package/src/components/file-upload/examples/overview.tsx +5 -1
  337. package/src/components/file-upload/examples/row-parts.tsx +24 -1
  338. package/src/components/file-upload/examples/screen.module.css +45 -0
  339. package/src/components/file-upload/examples/screen.tsx +646 -0
  340. package/src/components/file-upload/examples/states.tsx +6 -2
  341. package/src/components/file-upload/examples/validation.tsx +11 -2
  342. package/src/components/file-upload/examples/variants.tsx +3 -20
  343. package/src/components/gantt/COMPONENT.md +2 -1
  344. package/src/components/gantt/examples/screen.module.css +14 -0
  345. package/src/components/gantt/examples/screen.tsx +726 -0
  346. package/src/components/gantt/examples/states.tsx +6 -2
  347. package/src/components/input/COMPONENT.md +2 -2
  348. package/src/components/input/examples/examples.module.css +0 -6
  349. package/src/components/input/examples/in-form.tsx +1 -9
  350. package/src/components/input/examples/password-strength.tsx +34 -8
  351. package/src/components/input/examples/states.tsx +6 -1
  352. package/src/components/kanban/COMPONENT.md +3 -2
  353. package/src/components/kanban/examples/columns.tsx +8 -5
  354. package/src/components/kanban/examples/screen.module.css +17 -0
  355. package/src/components/kanban/examples/screen.tsx +572 -0
  356. package/src/components/kanban/examples/states.tsx +17 -6
  357. package/src/components/login-form/COMPONENT.md +6 -5
  358. package/src/components/login-form/examples/examples.module.css +2 -21
  359. package/src/components/login-form/examples/forgot-password.tsx +28 -32
  360. package/src/components/login-form/examples/narrow.tsx +1 -1
  361. package/src/components/login-form/examples/overview.tsx +45 -47
  362. package/src/components/login-form/examples/register.tsx +64 -68
  363. package/src/components/login-form/examples/reset-password.tsx +50 -47
  364. package/src/components/login-form/examples/sizes.tsx +4 -6
  365. package/src/components/login-form/examples/states.tsx +53 -57
  366. package/src/components/login-form/examples/structure.tsx +18 -22
  367. package/src/components/login-form/examples/verification-code.tsx +41 -45
  368. package/src/components/media-viewer/COMPONENT.md +1 -1
  369. package/src/components/media-viewer/examples/dismiss.tsx +5 -1
  370. package/src/components/message/COMPONENT.md +4 -3
  371. package/src/components/message/examples/file.tsx +5 -1
  372. package/src/components/message/examples/media-open.tsx +48 -0
  373. package/src/components/message/examples/status.tsx +21 -3
  374. package/src/components/message/examples/text-formatting.tsx +7 -1
  375. package/src/components/modal/COMPONENT.md +2 -1
  376. package/src/components/modal/examples/custom-container.tsx +20 -20
  377. package/src/components/modal/examples/dismiss.tsx +5 -3
  378. package/src/components/modal/examples/enter-key.tsx +116 -0
  379. package/src/components/modal/examples/examples.module.css +7 -13
  380. package/src/components/modal/examples/structure.tsx +5 -2
  381. package/src/components/native-select/COMPONENT.md +1 -1
  382. package/src/components/native-select/examples/states.tsx +5 -1
  383. package/src/components/notification/COMPONENT.md +1 -1
  384. package/src/components/notification/examples/dismiss.tsx +2 -2
  385. package/src/components/page-content/COMPONENT.md +1 -0
  386. package/src/components/page-content/examples/list-detail.tsx +0 -3
  387. package/src/components/page-content/examples/screen.module.css +27 -0
  388. package/src/components/page-content/examples/screen.tsx +196 -0
  389. package/src/components/page-content/examples/variants.tsx +4 -11
  390. package/src/components/page-toolbar/COMPONENT.md +17 -15
  391. package/src/components/page-toolbar/examples/narrow.tsx +6 -3
  392. package/src/components/pagination/examples/controlled.tsx +29 -29
  393. package/src/components/pagination/examples/examples.module.css +0 -2
  394. package/src/components/progress-bar/COMPONENT.md +1 -1
  395. package/src/components/progress-bar/examples/segments.tsx +2 -2
  396. package/src/components/progress-circle/COMPONENT.md +1 -1
  397. package/src/components/progress-circle/examples/segments.tsx +8 -3
  398. package/src/components/radio/COMPONENT.md +1 -0
  399. package/src/components/radio/examples/examples.module.css +14 -0
  400. package/src/components/radio/examples/in-form.tsx +45 -0
  401. package/src/components/scroll-container/COMPONENT.md +1 -0
  402. package/src/components/scroll-container/examples/auto-scroll.tsx +3 -3
  403. package/src/components/scroll-container/examples/chat.tsx +3 -3
  404. package/src/components/scroll-container/examples/edges.tsx +6 -3
  405. package/src/components/scroll-container/examples/examples.module.css +4 -17
  406. package/src/components/scroll-container/examples/load-more.tsx +51 -0
  407. package/src/components/scroll-container/examples/lock.tsx +3 -5
  408. package/src/components/scroll-container/examples/overscroll.tsx +3 -3
  409. package/src/components/scroll-container/examples/overview.tsx +3 -3
  410. package/src/components/scroll-container/examples/scrollbar.tsx +13 -7
  411. package/src/components/scroll-container/examples/variants.tsx +13 -7
  412. package/src/components/segmented-control/COMPONENT.md +2 -2
  413. package/src/components/segmented-control/examples/in-form.tsx +5 -1
  414. package/src/components/segmented-control/examples/states.tsx +10 -1
  415. package/src/components/select/COMPONENT.md +1 -1
  416. package/src/components/select/examples/examples.module.css +2 -7
  417. package/src/components/select/examples/states.tsx +13 -1
  418. package/src/components/sheet/COMPONENT.md +2 -0
  419. package/src/components/sheet/examples/guard.tsx +72 -0
  420. package/src/components/sheet/examples/header-actions.tsx +56 -0
  421. package/src/components/slider/COMPONENT.md +2 -1
  422. package/src/components/slider/examples/examples.module.css +8 -0
  423. package/src/components/slider/examples/in-form.tsx +47 -0
  424. package/src/components/slider/examples/states.tsx +4 -1
  425. package/src/components/smart-filter/COMPONENT.md +1 -1
  426. package/src/components/smart-filter/examples/controlled.tsx +3 -3
  427. package/src/components/smart-filter/examples/ranges.tsx +44 -10
  428. package/src/components/sparkline/COMPONENT.md +1 -1
  429. package/src/components/sparkline/examples/reference.tsx +2 -1
  430. package/src/components/tabs/COMPONENT.md +1 -1
  431. package/src/components/tabs/examples/sizes.tsx +1 -1
  432. package/src/components/tag-select/COMPONENT.md +1 -1
  433. package/src/components/tag-select/examples/states.tsx +8 -1
  434. package/src/components/textarea/COMPONENT.md +1 -1
  435. package/src/components/textarea/examples/examples.module.css +0 -6
  436. package/src/components/textarea/examples/in-form.tsx +1 -9
  437. package/src/components/textarea/examples/states.tsx +2 -1
  438. package/src/components/thumbnail/COMPONENT.md +2 -1
  439. package/src/components/thumbnail/examples/examples.module.css +1 -1
  440. package/src/components/thumbnail/examples/fit.tsx +24 -0
  441. package/src/components/thumbnail/examples/video.tsx +20 -1
  442. package/src/components/timeline/COMPONENT.md +1 -1
  443. package/src/components/timeline/examples/links.tsx +55 -14
  444. package/src/components/tooltip/COMPONENT.md +1 -1
  445. package/src/components/tooltip/examples/toolbar.tsx +2 -2
  446. package/src/components/typography/examples/weights.tsx +3 -0
  447. package/src/components/video-note/COMPONENT.md +2 -2
  448. package/src/components/video-note/examples/controlled.tsx +19 -2
  449. package/src/components/video-note/examples/in-message.tsx +5 -2
  450. package/src/components/visually-hidden/COMPONENT.md +2 -1
  451. package/src/components/visually-hidden/examples/assertive.tsx +39 -0
  452. package/src/components/voice-player/COMPONENT.md +2 -2
  453. package/src/components/voice-player/examples/call-recording.tsx +19 -9
  454. package/src/components/voice-player/examples/controlled.tsx +20 -3
  455. package/src/layout/app-header/COMPONENT.md +46 -63
  456. package/src/layout/app-header/examples/breadcrumbs.tsx +10 -9
  457. package/src/layout/app-header/examples/examples.module.css +1 -1
  458. package/src/layout/app-header/examples/in-app-shell.tsx +0 -1
  459. package/src/layout/app-header/examples/narrow.tsx +4 -5
  460. package/src/layout/app-header/examples/overview.tsx +8 -10
  461. package/src/layout/app-header/examples/page-title.tsx +3 -10
  462. package/src/layout/app-shell/COMPONENT.md +4 -3
  463. package/src/layout/app-shell/examples/overview.tsx +15 -30
  464. package/src/layout/app-shell/examples/screen.module.css +11 -0
  465. package/src/layout/app-shell/examples/screen.tsx +262 -0
  466. package/src/layout/app-shell/examples/template.tsx +29 -9
  467. package/src/layout/sidebar/COMPONENT.md +7 -4
  468. package/src/layout/sidebar/examples/account.tsx +2 -2
  469. package/src/layout/sidebar/examples/collapsible-groups.tsx +12 -4
  470. package/src/layout/sidebar/examples/custom-part.tsx +53 -0
  471. package/src/layout/sidebar/examples/examples.module.css +5 -0
  472. package/src/layout/sidebar/examples/narrow.tsx +53 -0
  473. package/src/layout/sidebar/examples/persist.tsx +57 -0
  474. package/src/layout/sidebar/examples/router.tsx +14 -2
  475. package/src/layout/sidebar/examples/structure.tsx +7 -2
  476. package/dist/skill/patterns/app-shell.tsx +0 -303
  477. package/dist/skill/patterns/chat.module.css +0 -8
  478. package/dist/skill/patterns/dashboard.tsx +0 -340
  479. package/dist/skill/patterns/detail-page.tsx +0 -206
  480. package/dist/skill/patterns/form-drawer.tsx +0 -240
  481. package/dist/skill/patterns/kanban-board.module.css +0 -28
  482. package/dist/skill/patterns/media-library.tsx +0 -637
  483. package/dist/skill/patterns/schedule.module.css +0 -22
  484. package/dist/skill/patterns/screen-states.tsx +0 -187
  485. /package/dist/skill/{patterns/dashboard.module.css → reference/components/card/examples/screen.module.css} +0 -0
  486. /package/dist/skill/{patterns/screen-states.module.css → reference/components/crossfade/examples/screen.module.css} +0 -0
  487. /package/dist/skill/{patterns/form-drawer.module.css → reference/components/drawer/examples/screen.module.css} +0 -0
  488. /package/dist/skill/{patterns/record-editor.module.css → reference/components/field-list/examples/screen.module.css} +0 -0
  489. /package/dist/skill/{patterns/settings-page.module.css → reference/components/page-content/examples/screen.module.css} +0 -0
  490. /package/dist/skill/{patterns/app-shell.module.css → reference/layout/app-shell/examples/screen.module.css} +0 -0
@@ -1,7 +1,7 @@
1
- /** A support messenger on `Chat`: dialogs with a search over them beside the conversation (one pane at a time on a phone), history that loads above as you scroll up, dates and runs from data, every kind of message, search, pinned messages, replies and edits from the menu with a jump to the quoted message, read receipts and unread counts from what was seen, the typing signal both ways, and the Composer whose messages rise into the list while the client answers. */
1
+ /** A support messenger as a work screen: the title and the unread count in the app header, the dialogs with a search over them beside the conversation (one pane at a time on a phone), history that loads above as you scroll up, the unread band, reply and edit from the menu, the typing signal both ways, unread counts from what was seen, and the Composer whose messages rise into the list while the client answers — `items`, `renderMessage`, `hasOlder`, `onLoadOlder`, `typing`. */
2
2
  import {
3
+ AppHeader,
3
4
  Avatar,
4
- Button,
5
5
  Card,
6
6
  Chat,
7
7
  ChatList,
@@ -15,29 +15,21 @@ import {
15
15
  hueOf,
16
16
  Icon,
17
17
  Message,
18
- type MessageReaction,
19
18
  type MessageStatus,
20
- type MessageUser,
21
19
  PageContent,
22
20
  type UploadContext,
23
21
  useUploadQueue,
24
- VideoNote,
25
22
  VoicePlayer,
26
23
  } from "prime-ui-kit";
27
24
  import * as React from "react";
28
25
 
29
- import styles from "./chat.module.css";
26
+ import styles from "./screen.module.css";
30
27
 
31
28
  type Content =
32
29
  | { kind: "text"; text: string }
33
30
  | { kind: "media"; items: GalleryItem[]; caption?: string }
34
- | { kind: "voice"; src: string; waveform: number[]; duration: number; transcribable?: boolean }
35
- | { kind: "videoNote"; src: string; duration: number; transcript?: string }
36
- | { kind: "file"; name: string; size: number; href: string }
37
- | { kind: "location"; title: string; address: string }
38
- | { kind: "contact"; name: string; phone: string }
39
- | { kind: "link"; text: string; href: string; site: string; title: string; description: string }
40
- | { kind: "service"; text: string };
31
+ | { kind: "voice"; src: string; waveform: number[]; duration: number }
32
+ | { kind: "file"; name: string; size: number; href: string };
41
33
 
42
34
  type ChatMessage = Content & {
43
35
  id: string;
@@ -45,14 +37,15 @@ type ChatMessage = Content & {
45
37
  sentAt: Date;
46
38
  status?: MessageStatus;
47
39
  edited?: boolean;
48
- forwardedFrom?: string;
49
40
  replyTo?: string;
50
- reactions?: MessageReaction[];
51
41
  };
52
42
 
53
43
  type Dialog = {
54
44
  id: string;
55
45
  name: string;
46
+ contact: string;
47
+ /** The contact in the dative, for «Ответ Ирине Беловой». */
48
+ contactTo: string;
56
49
  initials: string;
57
50
  unread: number;
58
51
  preview: string;
@@ -62,40 +55,12 @@ type Dialog = {
62
55
  const NOW = new Date(2026, 9, 8, 15, 0);
63
56
  const at = (day: number, hour: number, minute: number) => new Date(2026, 9, day, hour, minute);
64
57
 
65
- const AUDIO = "https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3";
66
- const VIDEO = "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4";
67
- const MAP = "https://tile.openstreetmap.org/15/19808/10244.png";
68
- const WAVE = [
69
- 0.2, 0.4, 0.7, 0.5, 0.9, 0.6, 0.3, 0.5, 0.8, 0.6, 0.35, 0.55, 0.75, 0.4, 0.25, 0.6, 0.85, 0.5,
70
- 0.3, 0.45, 0.65, 0.4, 0.2,
71
- ];
72
-
73
- /** The reader (the support agent) and the client's contact, as reactors of a chip. */
74
- const ME: MessageUser = {
75
- id: "me",
76
- name: "Вы",
77
- src: "https://images.unsplash.com/photo-1599566150163-29194dcaad36?w=128&h=128&fit=crop",
78
- };
79
- const CLIENT: MessageUser = {
80
- id: "irina",
81
- name: "Ирина Белова",
82
- src: "https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=128&h=128&fit=crop",
83
- };
84
-
85
- /** The client's reaction to the reply. */
86
- const LIKE: MessageReaction = { emoji: "👍", count: 1, users: [CLIENT] };
87
-
88
- const photo = (seed: string, width: number, height: number, alt: string): GalleryItem => ({
89
- src: `https://picsum.photos/seed/${seed}/${width}/${height}`,
90
- width,
91
- height,
92
- alt,
93
- });
94
-
95
58
  const DIALOGS: Dialog[] = [
96
59
  {
97
60
  id: "vector",
98
61
  name: "ООО «Вектор»",
62
+ contact: "Ирина Белова",
63
+ contactTo: "Ирине Беловой",
99
64
  initials: "ВК",
100
65
  unread: 2,
101
66
  preview: "Ок, жду акт",
@@ -104,6 +69,8 @@ const DIALOGS: Dialog[] = [
104
69
  {
105
70
  id: "granit",
106
71
  name: "ООО «Гранит»",
72
+ contact: "Олег Савин",
73
+ contactTo: "Олегу Савину",
107
74
  initials: "ГР",
108
75
  unread: 0,
109
76
  preview: "Акт сверки подписан",
@@ -112,6 +79,8 @@ const DIALOGS: Dialog[] = [
112
79
  {
113
80
  id: "kozlov",
114
81
  name: "ИП Козлов",
82
+ contact: "Андрей Козлов",
83
+ contactTo: "Андрею Козлову",
115
84
  initials: "ИК",
116
85
  unread: 1,
117
86
  preview: "Подключите ЭДО",
@@ -145,14 +114,20 @@ const HISTORY: ChatMessage[] = [
145
114
  sentAt: at(7, 10, 12),
146
115
  kind: "text",
147
116
  text: "Не можем выгрузить счёт **INV-2048** в 1С, ошибка после «Экспорт».",
148
- reactions: [{ emoji: "👀", count: 1, selected: true, users: [ME] }],
149
117
  },
150
118
  {
151
119
  id: "m2",
152
120
  from: "client",
153
121
  sentAt: at(7, 10, 13),
154
122
  kind: "media",
155
- items: [photo("export-error", 800, 520, "Окно с ошибкой экспорта")],
123
+ items: [
124
+ {
125
+ src: "https://picsum.photos/seed/export-error/800/520",
126
+ width: 800,
127
+ height: 520,
128
+ alt: "Окно с ошибкой экспорта",
129
+ },
130
+ ],
156
131
  caption: "Вот так выглядит",
157
132
  },
158
133
  {
@@ -161,69 +136,26 @@ const HISTORY: ChatMessage[] = [
161
136
  sentAt: at(7, 10, 20),
162
137
  status: "read",
163
138
  kind: "text",
164
- text: "Похоже, в карточке не заполнен ИНН. Инструкция: https://prime.app/wiki/requisites",
139
+ text: "Похоже, в карточке контрагента не заполнен ИНН. Инструкция: https://prime.app/wiki/requisites",
165
140
  },
166
141
  {
167
142
  id: "m4",
168
- from: "me",
169
- sentAt: at(7, 10, 21),
170
- status: "read",
171
- kind: "link",
172
- text: "И статья о выгрузке:",
173
- href: "https://prime.app/blog/export-1c",
174
- site: "Блог Prime",
175
- title: "Выгрузка счетов в 1С за пять минут",
176
- description: "Настройка обмена, реквизиты контрагента и типичные ошибки экспорта.",
177
- },
178
- {
179
- id: "s1",
180
- from: "me",
181
- sentAt: at(8, 9, 0),
182
- kind: "service",
183
- text: "Ирина Белова добавила Дмитрия Орлова",
184
- },
185
- {
186
- id: "m5",
187
143
  from: "client",
188
144
  sentAt: at(8, 9, 40),
189
- kind: "media",
190
- items: [
191
- photo("dock-1", 600, 800, "Рампа 1"),
192
- photo("dock-2", 800, 600, "Рампа 2"),
193
- photo("dock-3", 800, 600, "Рампа 3"),
194
- ],
195
- },
196
- {
197
- id: "m6",
198
- from: "client",
199
- sentAt: at(8, 9, 41),
200
- kind: "videoNote",
201
- src: VIDEO,
202
- duration: 6,
203
- transcript: "Вот рампа номер два, машина встанет сюда задним ходом.",
204
- },
205
- {
206
- id: "m7",
207
- from: "client",
208
- sentAt: at(8, 9, 43),
209
- kind: "voice",
210
- src: AUDIO,
211
- waveform: WAVE,
212
- duration: 18,
213
- transcribable: true,
145
+ kind: "text",
146
+ text: "Заполнили, счёт выгрузился. Пришлите, пожалуйста, договор на второй склад.",
214
147
  },
215
148
  {
216
- id: "m8",
149
+ id: "m5",
217
150
  from: "me",
218
151
  sentAt: at(8, 10, 5),
219
152
  status: "read",
220
- replyTo: "m7",
153
+ replyTo: "m4",
221
154
  kind: "text",
222
- text: "Понял про вторую рампу. Высылаю договор и адрес склада.",
223
- reactions: [LIKE],
155
+ text: "Высылаю договор, акт подготовлю до пятницы.",
224
156
  },
225
157
  {
226
- id: "m9",
158
+ id: "m6",
227
159
  from: "me",
228
160
  sentAt: at(8, 10, 6),
229
161
  status: "read",
@@ -233,28 +165,16 @@ const HISTORY: ChatMessage[] = [
233
165
  href: "https://prime.app/files/contract-418.pdf",
234
166
  },
235
167
  {
236
- id: "m10",
237
- from: "me",
238
- sentAt: at(8, 10, 6),
239
- status: "read",
240
- kind: "location",
241
- title: "Склад «Центральный»",
242
- address: "Москва, ул. Складочная, 1, стр. 6",
243
- },
244
- {
245
- id: "m11",
168
+ id: "m7",
246
169
  from: "client",
247
170
  sentAt: at(8, 14, 30),
248
- forwardedFrom: "Дмитрий Орлов",
249
- kind: "contact",
250
- name: "Дмитрий Орлов",
251
- phone: "+7 916 204-18-77",
171
+ kind: "text",
172
+ text: "Подписали, скан у бухгалтерии.",
252
173
  },
253
- { id: "m12", from: "client", sentAt: at(8, 14, 31), kind: "text", text: "Ок, жду акт" },
174
+ { id: "m8", from: "client", sentAt: at(8, 14, 31), kind: "text", text: "Ок, жду акт" },
254
175
  ];
255
176
 
256
- const FIRST_UNREAD = "m11";
257
- const PINNED = ["m9"];
177
+ const FIRST_UNREAD = "m7";
258
178
 
259
179
  /** What the client answers, one after another. */
260
180
  const CLIENT_REPLIES = [
@@ -268,7 +188,6 @@ const MESSAGE_READ_MS = 1500;
268
188
  const CLIENT_TYPING_MS = 1800;
269
189
  const HISTORY_MS = 900;
270
190
  const DIALOG_MS = 800;
271
- const TRANSCRIBE_MS = 1400;
272
191
 
273
192
  /** A stand-in for the file server: sends an attachment in a few steps. */
274
193
  function sendFile(file: File, { signal, onProgress }: UploadContext): Promise<string> {
@@ -290,29 +209,26 @@ function sendFile(file: File, { signal, onProgress }: UploadContext): Promise<st
290
209
 
291
210
  const wait = (ms: number) => new Promise<void>((resolve) => window.setTimeout(resolve, ms));
292
211
 
293
- /** A stand-in for the speech service: the voice message's text, asked for on its first open. */
294
- const transcribeVoice = () =>
295
- wait(TRANSCRIBE_MS).then(
296
- () => "Машина будет в девять, водитель позвонит за полчаса. Пропуск заказан на рампу два.",
297
- );
212
+ /** «1 непрочитанный», «3 непрочитанных». */
213
+ const unreadLabel = (count: number) => {
214
+ const tens = count % 100;
215
+ const ones = count % 10;
216
+ const word = ones === 1 && tens !== 11 ? "непрочитанный" : "непрочитанных";
217
+ return count === 0 ? "Всё прочитано" : `${count} ${word}`;
218
+ };
298
219
 
299
220
  const excerpt = (message: ChatMessage) => {
300
221
  switch (message.kind) {
301
222
  case "text":
302
- case "link":
303
223
  return message.text;
304
224
  case "voice":
305
225
  return "Голосовое сообщение";
306
- case "videoNote":
307
- return "Видеосообщение";
308
226
  case "media":
309
227
  return (
310
228
  message.caption ?? (message.items.every((item) => item.type === "video") ? "Видео" : "Фото")
311
229
  );
312
230
  case "file":
313
231
  return message.name;
314
- default:
315
- return "Сообщение";
316
232
  }
317
233
  };
318
234
 
@@ -327,44 +243,15 @@ const albumsOf = (media: readonly ComposerMediaItem[]) =>
327
243
  const localSourcesOf = (message: Content): string[] =>
328
244
  (message.kind === "media"
329
245
  ? message.items.map((item) => item.src)
330
- : message.kind === "voice" || message.kind === "videoNote"
246
+ : message.kind === "voice"
331
247
  ? [message.src]
332
248
  : []
333
249
  ).filter((src) => src.startsWith("blob:"));
334
250
 
335
- /** Puts the reader's reaction or takes it back, with the reader among its `users`. */
336
- const toggleReactions = (reactions: MessageReaction[], emoji: string): MessageReaction[] =>
337
- reactions.some((reaction) => reaction.emoji === emoji)
338
- ? reactions
339
- .map((reaction) => {
340
- if (reaction.emoji !== emoji) return reaction;
341
- const others = (reaction.users ?? []).filter((user) => user.id !== ME.id);
342
- return {
343
- ...reaction,
344
- selected: !reaction.selected,
345
- count: reaction.count + (reaction.selected ? -1 : 1),
346
- users: reaction.selected ? others : [...others, ME],
347
- };
348
- })
349
- .filter((reaction) => reaction.count > 0)
350
- : [...reactions, { emoji, count: 1, selected: true, users: [ME] }];
351
-
352
251
  const renderContent = (message: ChatMessage) => {
353
252
  switch (message.kind) {
354
253
  case "text":
355
254
  return <Message.Text>{message.text}</Message.Text>;
356
- case "link":
357
- return (
358
- <>
359
- <Message.Text>{message.text}</Message.Text>
360
- <Message.LinkPreview
361
- href={message.href}
362
- site={message.site}
363
- title={message.title}
364
- description={message.description}
365
- />
366
- </>
367
- );
368
255
  case "media":
369
256
  return (
370
257
  <>
@@ -374,43 +261,16 @@ const renderContent = (message: ChatMessage) => {
374
261
  );
375
262
  case "voice":
376
263
  return (
377
- <VoicePlayer
378
- src={message.src}
379
- waveform={message.waveform}
380
- duration={message.duration}
381
- onTranscribe={message.transcribable ? transcribeVoice : undefined}
382
- />
383
- );
384
- case "videoNote":
385
- return (
386
- <VideoNote src={message.src} duration={message.duration} transcript={message.transcript} />
264
+ <VoicePlayer src={message.src} waveform={message.waveform} duration={message.duration} />
387
265
  );
388
266
  case "file":
389
267
  return <Message.File name={message.name} fileSize={message.size} href={message.href} />;
390
- case "location":
391
- return (
392
- <Message.Location
393
- src={MAP}
394
- href="https://www.openstreetmap.org/#map=16/55.7512/37.6184"
395
- title={message.title}
396
- address={message.address}
397
- />
398
- );
399
- case "contact":
400
- return (
401
- <Message.Contact name={message.name} phone={message.phone} color="teal">
402
- <Button.Root variant="soft" tone="neutral" size="s">
403
- Написать
404
- </Button.Root>
405
- </Message.Contact>
406
- );
407
- default:
408
- return null;
409
268
  }
410
269
  };
411
270
 
412
271
  type ChatComposerProps = {
413
272
  replyTo: ChatMessage | null;
273
+ replyAuthor: string;
414
274
  editing: ChatMessage | null;
415
275
  onReplyDismiss: () => void;
416
276
  onEditDismiss: () => void;
@@ -424,6 +284,7 @@ type ChatComposerProps = {
424
284
  /** The Composer with its upload queue: an upload tick re-renders the composer, not the thread. */
425
285
  const ChatComposer = React.memo(function ChatComposer({
426
286
  replyTo,
287
+ replyAuthor,
427
288
  editing,
428
289
  onReplyDismiss,
429
290
  onEditDismiss,
@@ -444,9 +305,8 @@ const ChatComposer = React.memo(function ChatComposer({
444
305
 
445
306
  // Optimistic: every message is in the list at once with `sending`, so the send returns at once
446
307
  // and the field is free for the next one. The bar's `value` says what it answers or edits.
447
- // Photos and videos go as albums in the tray's order with the text as their caption, other
448
- // files as file messages. The tray's previews are revoked after the send, so an album plays from
449
- // its own URLs of the files (the thread lets them go with the message).
308
+ // Photos and videos go as albums with the text as their caption, other files as file messages.
309
+ // The tray's previews are revoked after the send, so an album plays from its own URLs.
450
310
  const send = ({ text, media, files, replyTo: reply, editOf }: ComposerMessage) => {
451
311
  if (editOf) {
452
312
  onSave(editOf, text);
@@ -496,7 +356,7 @@ const ChatComposer = React.memo(function ChatComposer({
496
356
  <Composer.Attach />
497
357
  {replyTo ? (
498
358
  <Composer.Context value={replyTo.id} onDismiss={onReplyDismiss}>
499
- <Composer.Title>Ответ {replyTo.from === "me" ? "себе" : "Ирине Беловой"}</Composer.Title>
359
+ <Composer.Title>Ответ {replyTo.from === "me" ? "себе" : replyAuthor}</Composer.Title>
500
360
  <Composer.Description>{excerpt(replyTo)}</Composer.Description>
501
361
  </Composer.Context>
502
362
  ) : null}
@@ -517,14 +377,12 @@ const ChatComposer = React.memo(function ChatComposer({
517
377
  );
518
378
  });
519
379
 
520
- export default function ChatPattern() {
380
+ export default function ChatScreenExample() {
521
381
  const [dialogId, setDialogId] = React.useState<string | null>("vector");
522
382
  const [messages, setMessages] = React.useState<ChatMessage[]>(HISTORY);
523
383
  const [hasOlder, setHasOlder] = React.useState(true);
524
384
  const [replyTo, setReplyTo] = React.useState<ChatMessage | null>(null);
525
385
  const [editing, setEditing] = React.useState<ChatMessage | null>(null);
526
- const [selected, setSelected] = React.useState<readonly string[] | null>(null);
527
- const [pinned, setPinned] = React.useState<readonly string[]>(PINNED);
528
386
  const [clientTyping, setClientTyping] = React.useState(false);
529
387
  const [unread, setUnread] = React.useState<Record<string, number>>(() =>
530
388
  Object.fromEntries(DIALOGS.map((item) => [item.id, item.unread])),
@@ -550,6 +408,7 @@ export default function ChatPattern() {
550
408
  };
551
409
  }, []);
552
410
  const dialog = DIALOGS.find((item) => item.id === dialogId) ?? DIALOGS[0];
411
+ const unreadTotal = Object.values(unread).reduce((sum, count) => sum + count, 0);
553
412
  /** The search over the dialogs: by name and by the last message a row shows. */
554
413
  const [dialogQuery, setDialogQuery] = React.useState("");
555
414
  // The open dialog shows its live last message; the others their stored one.
@@ -660,25 +519,11 @@ export default function ChatPattern() {
660
519
  setReplyTo(message);
661
520
  }, []);
662
521
 
663
- const togglePin = React.useCallback(
664
- (id: string) =>
665
- setPinned((current) =>
666
- current.includes(id) ? current.filter((item) => item !== id) : [...current, id],
667
- ),
668
- [],
669
- );
670
-
671
522
  const reportActivity = React.useCallback((activity: ComposerActivity | null) => {
672
523
  // A real chat sends it over its socket; here the simulated client reads it.
673
524
  myActivity.current = activity;
674
525
  }, []);
675
526
 
676
- const toggleReaction = React.useCallback(
677
- (id: string, emoji: string) =>
678
- patch(id, (message) => ({ reactions: toggleReactions(message.reactions ?? [], emoji) })),
679
- [patch],
680
- );
681
-
682
527
  const remove = React.useCallback(
683
528
  (id: string) => setMessages((current) => current.filter((item) => item.id !== id)),
684
529
  [],
@@ -687,21 +532,16 @@ export default function ChatPattern() {
687
532
  const dismissReply = React.useCallback(() => setReplyTo(null), []);
688
533
  const dismissEdit = React.useCallback(() => setEditing(null), []);
689
534
 
690
- const pinnedItems = pinned.flatMap((id) => {
691
- const message = messages.find((item) => item.id === id);
692
- return message ? [{ value: id, text: excerpt(message) }] : [];
693
- });
694
-
695
535
  // The newest messages for what a row looks up (a reply's original), without making every row
696
536
  // render again when one message changes.
697
537
  const messagesRef = React.useRef(messages);
698
538
  messagesRef.current = messages;
539
+ const contact = dialog.contact;
699
540
 
700
- // A message renders again only when its data, its place or this function change: a reaction, a
701
- // status or an upload tick re-renders the one message it touches.
541
+ // A message renders again only when its data, its place or this function change: a status or an
542
+ // upload tick re-renders the one message it touches.
702
543
  const renderMessage = React.useCallback(
703
544
  (message: ChatMessage) => {
704
- if (message.kind === "service") return <Chat.Service>{message.text}</Chat.Service>;
705
545
  const own = message.from === "me";
706
546
  const original = message.replyTo
707
547
  ? messagesRef.current.find((item) => item.id === message.replyTo)
@@ -712,19 +552,13 @@ export default function ChatPattern() {
712
552
  time={message.sentAt}
713
553
  status={message.status}
714
554
  edited={message.edited}
715
- onReactionToggle={(emoji) => toggleReaction(message.id, emoji)}
716
555
  >
717
- {message.forwardedFrom ? <Message.Forwarded from={message.forwardedFrom} /> : null}
718
556
  {original ? (
719
- <Message.Reply
720
- author={original.from === "me" ? "Вы" : "Ирина Белова"}
721
- target={original.id}
722
- >
557
+ <Message.Reply author={original.from === "me" ? "Вы" : contact} target={original.id}>
723
558
  {excerpt(original)}
724
559
  </Message.Reply>
725
560
  ) : null}
726
561
  {renderContent(message)}
727
- {message.reactions?.length ? <Message.Reactions items={message.reactions} /> : null}
728
562
  <Message.Menu>
729
563
  <Dropdown.Item onSelect={() => startReply(message)}>
730
564
  <Dropdown.ItemIcon>
@@ -746,12 +580,6 @@ export default function ChatPattern() {
746
580
  </Dropdown.ItemIcon>
747
581
  Скопировать
748
582
  </Dropdown.Item>
749
- <Dropdown.Item onSelect={() => togglePin(message.id)}>
750
- <Dropdown.ItemIcon>
751
- <Icon name="action.pin" />
752
- </Dropdown.ItemIcon>
753
- {pinned.includes(message.id) ? "Открепить" : "Закрепить"}
754
- </Dropdown.Item>
755
583
  {own ? (
756
584
  <Dropdown.Item tone="danger" onSelect={() => remove(message.id)}>
757
585
  <Dropdown.ItemIcon>
@@ -764,136 +592,127 @@ export default function ChatPattern() {
764
592
  </Message.Root>
765
593
  );
766
594
  },
767
- [pinned, toggleReaction, startReply, startEdit, togglePin, remove],
595
+ [contact, startReply, startEdit, remove],
768
596
  );
769
597
 
770
598
  return (
771
- <PageContent.Root fill>
772
- <PageContent.Header>
773
- <PageContent.Title>Поддержка</PageContent.Title>
774
- </PageContent.Header>
775
- <PageContent.Body>
776
- <PageContent.Split
777
- open={dialogId !== null}
778
- onOpenChange={(next) => {
779
- if (!next) setDialogId(null);
780
- }}
781
- >
782
- <PageContent.List aria-label="Диалоги">
783
- <Card.Root className={styles.paneCard}>
784
- <ChatList.Root
785
- items={foundDialogs}
786
- getKey={(item) => item.id}
787
- value={dialogId}
788
- onValueChange={setDialogId}
789
- search={dialogQuery}
790
- onSearchChange={setDialogQuery}
791
- now={NOW}
792
- renderItem={(item) => {
793
- const last = lastOf(item);
794
- return (
795
- <ChatList.Item
796
- time={last?.sentAt ?? item.at}
797
- status={last?.from === "me" ? last.status : undefined}
798
- unread={unread[item.id] ?? 0}
799
- typing={item.id === dialogId && clientTyping}
800
- >
801
- <ChatList.ItemIcon>
802
- <Avatar.Root color={hueOf(item.id)}>
803
- <Avatar.Fallback>{item.initials}</Avatar.Fallback>
804
- {item.id === "vector" ? <Avatar.Status status="online" /> : null}
805
- </Avatar.Root>
806
- </ChatList.ItemIcon>
807
- <ChatList.ItemText>{item.name}</ChatList.ItemText>
808
- <ChatList.ItemDescription author={last?.from === "me" ? "Вы" : undefined}>
809
- {previewOf(item)}
810
- </ChatList.ItemDescription>
811
- </ChatList.Item>
812
- );
813
- }}
814
- />
815
- </Card.Root>
816
- </PageContent.List>
817
-
818
- <PageContent.Detail aria-label="Переписка">
819
- <Card.Root className={styles.paneCard}>
820
- <Chat.Root typing={clientTyping} selected={selected} onSelectedChange={setSelected}>
821
- <Chat.Header>
822
- <PageContent.BackButton />
823
- <Avatar.Root color={hueOf(dialog.id)}>
824
- <Avatar.Fallback>{dialog.initials}</Avatar.Fallback>
825
- <Avatar.Status status="online" />
826
- </Avatar.Root>
827
- <Chat.Title>{dialog.name}</Chat.Title>
828
- <Chat.Description>Ирина Белова · в сети</Chat.Description>
829
- <Chat.Actions>
830
- <Chat.SearchButton />
831
- </Chat.Actions>
832
- </Chat.Header>
833
- <Chat.Search />
834
- <Chat.Pinned items={pinnedItems} onRemove={togglePin} />
835
- <Chat.Messages
836
- loading={loadedId !== dialogId}
837
- reader={ME}
838
- items={messages}
839
- getKey={(message) => message.id}
840
- getAuthor={(message) => message.from}
841
- getDate={(message) => message.sentAt}
842
- isService={(message) => message.kind === "service"}
843
- renderMessage={renderMessage}
844
- unread={FIRST_UNREAD}
599
+ <>
600
+ {/* A work screen: in an app this header fills the shell's `header` slot and carries the page —
601
+ the only h1 and one line of data; main has no PageContent.Header. */}
602
+ <AppHeader.Root>
603
+ <AppHeader.Start>
604
+ <AppHeader.Title>
605
+ Поддержка
606
+ <AppHeader.Description>{unreadLabel(unreadTotal)}</AppHeader.Description>
607
+ </AppHeader.Title>
608
+ </AppHeader.Start>
609
+ </AppHeader.Root>
610
+
611
+ <PageContent.Root fill>
612
+ <PageContent.Body>
613
+ <PageContent.Split
614
+ open={dialogId !== null}
615
+ onOpenChange={(next) => {
616
+ if (!next) setDialogId(null);
617
+ }}
618
+ >
619
+ <PageContent.List aria-label="Диалоги">
620
+ <Card.Root className={styles.paneCard}>
621
+ <ChatList.Root
622
+ items={foundDialogs}
623
+ getKey={(item) => item.id}
624
+ value={dialogId}
625
+ onValueChange={setDialogId}
626
+ search={dialogQuery}
627
+ onSearchChange={setDialogQuery}
845
628
  now={NOW}
846
- hasOlder={hasOlder}
847
- onLoadOlder={loadOlder}
848
- onNewMessage={(_, { direction }) => {
849
- if (direction !== "incoming" || !dialogId) return;
850
- setUnread((current) => ({
851
- ...current,
852
- [dialogId]: (current[dialogId] ?? 0) + 1,
853
- }));
854
- }}
855
- onRead={(values) => {
856
- if (!dialogId) return;
857
- setUnread((current) => ({
858
- ...current,
859
- [dialogId]: Math.max(0, (current[dialogId] ?? 0) - values.length),
860
- }));
629
+ renderItem={(item) => {
630
+ const last = lastOf(item);
631
+ return (
632
+ <ChatList.Item
633
+ time={last?.sentAt ?? item.at}
634
+ status={last?.from === "me" ? last.status : undefined}
635
+ unread={unread[item.id] ?? 0}
636
+ typing={item.id === dialogId && clientTyping}
637
+ >
638
+ <ChatList.ItemIcon>
639
+ <Avatar.Root color={hueOf(item.id)}>
640
+ <Avatar.Fallback>{item.initials}</Avatar.Fallback>
641
+ {item.id === "vector" ? <Avatar.Status status="online" /> : null}
642
+ </Avatar.Root>
643
+ </ChatList.ItemIcon>
644
+ <ChatList.ItemText>{item.name}</ChatList.ItemText>
645
+ <ChatList.ItemDescription author={last?.from === "me" ? "Вы" : undefined}>
646
+ {previewOf(item)}
647
+ </ChatList.ItemDescription>
648
+ </ChatList.Item>
649
+ );
861
650
  }}
862
- >
863
- <Chat.SelectionBar>
864
- <Button.Root
865
- variant="soft"
866
- tone="danger"
867
- size="s"
868
- disabled={!selected?.length}
869
- onClick={() => {
870
- setMessages((current) =>
871
- current.filter((item) => !selected?.includes(item.id)),
872
- );
873
- setSelected(null);
874
- }}
875
- >
876
- Удалить
877
- </Button.Root>
878
- </Chat.SelectionBar>
879
- </Chat.Messages>
880
- <Chat.Footer>
881
- <ChatComposer
882
- replyTo={replyTo}
883
- editing={editing}
884
- onReplyDismiss={dismissReply}
885
- onEditDismiss={dismissEdit}
886
- onEditLast={editLast}
887
- onPost={post}
888
- onSave={save}
889
- onActivityChange={reportActivity}
651
+ />
652
+ </Card.Root>
653
+ </PageContent.List>
654
+
655
+ <PageContent.Detail aria-label="Переписка">
656
+ <Card.Root className={styles.paneCard}>
657
+ <Chat.Root typing={clientTyping}>
658
+ <Chat.Header>
659
+ <PageContent.BackButton />
660
+ <Avatar.Root color={hueOf(dialog.id)}>
661
+ <Avatar.Fallback>{dialog.initials}</Avatar.Fallback>
662
+ {dialog.id === "vector" ? <Avatar.Status status="online" /> : null}
663
+ </Avatar.Root>
664
+ <Chat.Title>{dialog.name}</Chat.Title>
665
+ <Chat.Description>{dialog.contact}</Chat.Description>
666
+ <Chat.Actions>
667
+ <Chat.SearchButton />
668
+ </Chat.Actions>
669
+ </Chat.Header>
670
+ <Chat.Search />
671
+ <Chat.Messages
672
+ loading={loadedId !== dialogId}
673
+ items={messages}
674
+ getKey={(message) => message.id}
675
+ getAuthor={(message) => message.from}
676
+ getDate={(message) => message.sentAt}
677
+ renderMessage={renderMessage}
678
+ unread={FIRST_UNREAD}
679
+ now={NOW}
680
+ hasOlder={hasOlder}
681
+ onLoadOlder={loadOlder}
682
+ onNewMessage={(_, { direction }) => {
683
+ if (direction !== "incoming" || !dialogId) return;
684
+ setUnread((current) => ({
685
+ ...current,
686
+ [dialogId]: (current[dialogId] ?? 0) + 1,
687
+ }));
688
+ }}
689
+ onRead={(values) => {
690
+ if (!dialogId) return;
691
+ setUnread((current) => ({
692
+ ...current,
693
+ [dialogId]: Math.max(0, (current[dialogId] ?? 0) - values.length),
694
+ }));
695
+ }}
890
696
  />
891
- </Chat.Footer>
892
- </Chat.Root>
893
- </Card.Root>
894
- </PageContent.Detail>
895
- </PageContent.Split>
896
- </PageContent.Body>
897
- </PageContent.Root>
697
+ <Chat.Footer>
698
+ <ChatComposer
699
+ replyTo={replyTo}
700
+ replyAuthor={dialog.contactTo}
701
+ editing={editing}
702
+ onReplyDismiss={dismissReply}
703
+ onEditDismiss={dismissEdit}
704
+ onEditLast={editLast}
705
+ onPost={post}
706
+ onSave={save}
707
+ onActivityChange={reportActivity}
708
+ />
709
+ </Chat.Footer>
710
+ </Chat.Root>
711
+ </Card.Root>
712
+ </PageContent.Detail>
713
+ </PageContent.Split>
714
+ </PageContent.Body>
715
+ </PageContent.Root>
716
+ </>
898
717
  );
899
718
  }