@archytechtor/ui 0.0.0-stage → 0.2.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 (479) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -2
  3. package/dist/components/accordion/accordion.d.ts +25 -0
  4. package/dist/components/accordion/accordion.js +88 -0
  5. package/dist/components/accordion/index.d.ts +3 -0
  6. package/dist/components/accordion/index.js +1 -0
  7. package/dist/components/accordion/types.d.ts +39 -0
  8. package/dist/components/alert/alert.d.ts +25 -0
  9. package/dist/components/alert/alert.js +59 -0
  10. package/dist/components/alert/index.d.ts +3 -0
  11. package/dist/components/alert/index.js +3 -0
  12. package/dist/components/alert/types.d.ts +32 -0
  13. package/dist/components/alert/types.js +19 -0
  14. package/dist/components/alert-dialog/alert-dialog.d.ts +73 -0
  15. package/dist/components/alert-dialog/alert-dialog.js +170 -0
  16. package/dist/components/alert-dialog/index.d.ts +3 -0
  17. package/dist/components/alert-dialog/index.js +1 -0
  18. package/dist/components/alert-dialog/types.d.ts +80 -0
  19. package/dist/components/aspect-ratio/aspect-ratio.d.ts +8 -0
  20. package/dist/components/aspect-ratio/aspect-ratio.js +20 -0
  21. package/dist/components/aspect-ratio/index.d.ts +3 -0
  22. package/dist/components/aspect-ratio/index.js +1 -0
  23. package/dist/components/aspect-ratio/types.d.ts +8 -0
  24. package/dist/components/attachment/attachment.d.ts +57 -0
  25. package/dist/components/attachment/attachment.js +139 -0
  26. package/dist/components/attachment/index.d.ts +3 -0
  27. package/dist/components/attachment/index.js +3 -0
  28. package/dist/components/attachment/types.d.ts +79 -0
  29. package/dist/components/attachment/types.js +43 -0
  30. package/dist/components/avatar/avatar.d.ts +39 -0
  31. package/dist/components/avatar/avatar.js +88 -0
  32. package/dist/components/avatar/index.d.ts +3 -0
  33. package/dist/components/avatar/index.js +3 -0
  34. package/dist/components/avatar/types.d.ts +54 -0
  35. package/dist/components/avatar/types.js +21 -0
  36. package/dist/components/badge/badge.d.ts +7 -0
  37. package/dist/components/badge/badge.js +23 -0
  38. package/dist/components/badge/index.d.ts +3 -0
  39. package/dist/components/badge/index.js +3 -0
  40. package/dist/components/badge/types.d.ts +21 -0
  41. package/dist/components/badge/types.js +26 -0
  42. package/dist/components/bottom-nav/bottom-nav.d.ts +19 -0
  43. package/dist/components/bottom-nav/bottom-nav.js +59 -0
  44. package/dist/components/bottom-nav/index.d.ts +3 -0
  45. package/dist/components/bottom-nav/index.js +3 -0
  46. package/dist/components/bottom-nav/types.d.ts +45 -0
  47. package/dist/components/bottom-nav/types.js +44 -0
  48. package/dist/components/box/box.d.ts +7 -0
  49. package/dist/components/box/box.js +26 -0
  50. package/dist/components/box/index.d.ts +3 -0
  51. package/dist/components/box/index.js +3 -0
  52. package/dist/components/box/types.d.ts +23 -0
  53. package/dist/components/box/types.js +27 -0
  54. package/dist/components/breadcrumb/breadcrumb.d.ts +43 -0
  55. package/dist/components/breadcrumb/breadcrumb.js +115 -0
  56. package/dist/components/breadcrumb/index.d.ts +3 -0
  57. package/dist/components/breadcrumb/index.js +1 -0
  58. package/dist/components/breadcrumb/types.d.ts +36 -0
  59. package/dist/components/bubble/bubble.d.ts +25 -0
  60. package/dist/components/bubble/bubble.js +69 -0
  61. package/dist/components/bubble/index.d.ts +3 -0
  62. package/dist/components/bubble/index.js +3 -0
  63. package/dist/components/bubble/types.d.ts +51 -0
  64. package/dist/components/bubble/types.js +60 -0
  65. package/dist/components/button/button.d.ts +7 -0
  66. package/dist/components/button/button.js +155 -0
  67. package/dist/components/button/index.d.ts +4 -0
  68. package/dist/components/button/index.js +4 -0
  69. package/dist/components/button/types.d.ts +80 -0
  70. package/dist/components/button/types.js +99 -0
  71. package/dist/components/button/use-hold-to-confirm.d.ts +25 -0
  72. package/dist/components/button/use-hold-to-confirm.js +161 -0
  73. package/dist/components/button-group/button-group.d.ts +20 -0
  74. package/dist/components/button-group/button-group.js +62 -0
  75. package/dist/components/button-group/index.d.ts +3 -0
  76. package/dist/components/button-group/index.js +3 -0
  77. package/dist/components/button-group/types.d.ts +30 -0
  78. package/dist/components/button-group/types.js +19 -0
  79. package/dist/components/calendar/calendar.d.ts +15 -0
  80. package/dist/components/calendar/calendar.js +157 -0
  81. package/dist/components/calendar/index.d.ts +3 -0
  82. package/dist/components/calendar/index.js +1 -0
  83. package/dist/components/calendar/types.d.ts +21 -0
  84. package/dist/components/card/card.d.ts +43 -0
  85. package/dist/components/card/card.js +97 -0
  86. package/dist/components/card/index.d.ts +3 -0
  87. package/dist/components/card/index.js +3 -0
  88. package/dist/components/card/types.d.ts +44 -0
  89. package/dist/components/card/types.js +19 -0
  90. package/dist/components/carousel/carousel.d.ts +36 -0
  91. package/dist/components/carousel/carousel.js +190 -0
  92. package/dist/components/carousel/index.d.ts +3 -0
  93. package/dist/components/carousel/index.js +1 -0
  94. package/dist/components/carousel/types.d.ts +79 -0
  95. package/dist/components/chart/chart.d.ts +35 -0
  96. package/dist/components/chart/chart.js +212 -0
  97. package/dist/components/chart/index.d.ts +3 -0
  98. package/dist/components/chart/index.js +1 -0
  99. package/dist/components/chart/types.d.ts +89 -0
  100. package/dist/components/chart/types.js +7 -0
  101. package/dist/components/checkbox/checkbox.d.ts +7 -0
  102. package/dist/components/checkbox/checkbox.js +31 -0
  103. package/dist/components/checkbox/index.d.ts +3 -0
  104. package/dist/components/checkbox/index.js +1 -0
  105. package/dist/components/checkbox/types.d.ts +25 -0
  106. package/dist/components/code-block/code-block.d.ts +49 -0
  107. package/dist/components/code-block/code-block.js +188 -0
  108. package/dist/components/code-block/highlighter.d.ts +15 -0
  109. package/dist/components/code-block/highlighter.js +123 -0
  110. package/dist/components/code-block/index.d.ts +5 -0
  111. package/dist/components/code-block/index.js +5 -0
  112. package/dist/components/code-block/types.d.ts +94 -0
  113. package/dist/components/code-block/types.js +38 -0
  114. package/dist/components/code-block/use-highlighted-code.d.ts +9 -0
  115. package/dist/components/code-block/use-highlighted-code.js +67 -0
  116. package/dist/components/collapsible/collapsible.d.ts +19 -0
  117. package/dist/components/collapsible/collapsible.js +44 -0
  118. package/dist/components/collapsible/index.d.ts +3 -0
  119. package/dist/components/collapsible/index.js +1 -0
  120. package/dist/components/collapsible/types.d.ts +26 -0
  121. package/dist/components/combobox/combobox.d.ts +104 -0
  122. package/dist/components/combobox/combobox.js +365 -0
  123. package/dist/components/combobox/index.d.ts +3 -0
  124. package/dist/components/combobox/index.js +1 -0
  125. package/dist/components/combobox/types.d.ts +229 -0
  126. package/dist/components/command/command.d.ts +55 -0
  127. package/dist/components/command/command.js +179 -0
  128. package/dist/components/command/index.d.ts +3 -0
  129. package/dist/components/command/index.js +1 -0
  130. package/dist/components/command/types.d.ts +69 -0
  131. package/dist/components/context-menu/context-menu.d.ts +92 -0
  132. package/dist/components/context-menu/context-menu.js +270 -0
  133. package/dist/components/context-menu/index.d.ts +3 -0
  134. package/dist/components/context-menu/index.js +1 -0
  135. package/dist/components/context-menu/types.d.ts +138 -0
  136. package/dist/components/data-table/data-table-features.d.ts +21 -0
  137. package/dist/components/data-table/data-table-features.js +22 -0
  138. package/dist/components/data-table/data-table.d.ts +69 -0
  139. package/dist/components/data-table/data-table.js +283 -0
  140. package/dist/components/data-table/index.d.ts +4 -0
  141. package/dist/components/data-table/index.js +3 -0
  142. package/dist/components/data-table/types.d.ts +63 -0
  143. package/dist/components/date-input/date-input.d.ts +7 -0
  144. package/dist/components/date-input/date-input.js +147 -0
  145. package/dist/components/date-input/index.d.ts +3 -0
  146. package/dist/components/date-input/index.js +1 -0
  147. package/dist/components/date-input/types.d.ts +27 -0
  148. package/dist/components/date-picker/date-picker.d.ts +7 -0
  149. package/dist/components/date-picker/date-picker.js +74 -0
  150. package/dist/components/date-picker/index.d.ts +3 -0
  151. package/dist/components/date-picker/index.js +1 -0
  152. package/dist/components/date-picker/types.d.ts +38 -0
  153. package/dist/components/date-range-picker/date-range-picker.d.ts +7 -0
  154. package/dist/components/date-range-picker/date-range-picker.js +89 -0
  155. package/dist/components/date-range-picker/index.d.ts +3 -0
  156. package/dist/components/date-range-picker/index.js +1 -0
  157. package/dist/components/date-range-picker/types.d.ts +43 -0
  158. package/dist/components/dialog/dialog.d.ts +61 -0
  159. package/dist/components/dialog/dialog.js +160 -0
  160. package/dist/components/dialog/index.d.ts +3 -0
  161. package/dist/components/dialog/index.js +1 -0
  162. package/dist/components/dialog/types.d.ts +68 -0
  163. package/dist/components/drawer/drawer.d.ts +69 -0
  164. package/dist/components/drawer/drawer.js +245 -0
  165. package/dist/components/drawer/index.d.ts +3 -0
  166. package/dist/components/drawer/index.js +1 -0
  167. package/dist/components/drawer/types.d.ts +93 -0
  168. package/dist/components/dropdown-menu/dropdown-menu.d.ts +92 -0
  169. package/dist/components/dropdown-menu/dropdown-menu.js +268 -0
  170. package/dist/components/dropdown-menu/index.d.ts +3 -0
  171. package/dist/components/dropdown-menu/index.js +1 -0
  172. package/dist/components/dropdown-menu/types.d.ts +143 -0
  173. package/dist/components/empty/empty.d.ts +37 -0
  174. package/dist/components/empty/empty.js +84 -0
  175. package/dist/components/empty/index.d.ts +3 -0
  176. package/dist/components/empty/index.js +3 -0
  177. package/dist/components/empty/types.d.ts +39 -0
  178. package/dist/components/empty/types.js +13 -0
  179. package/dist/components/expanded-card/expanded-card.d.ts +43 -0
  180. package/dist/components/expanded-card/expanded-card.js +352 -0
  181. package/dist/components/expanded-card/index.d.ts +3 -0
  182. package/dist/components/expanded-card/index.js +1 -0
  183. package/dist/components/expanded-card/types.d.ts +52 -0
  184. package/dist/components/field/field.d.ts +62 -0
  185. package/dist/components/field/field.js +173 -0
  186. package/dist/components/field/index.d.ts +3 -0
  187. package/dist/components/field/index.js +3 -0
  188. package/dist/components/field/types.d.ts +81 -0
  189. package/dist/components/field/types.js +21 -0
  190. package/dist/components/form/form.d.ts +49 -0
  191. package/dist/components/form/form.js +136 -0
  192. package/dist/components/form/index.d.ts +3 -0
  193. package/dist/components/form/index.js +1 -0
  194. package/dist/components/form/types.d.ts +59 -0
  195. package/dist/components/grid/grid.d.ts +7 -0
  196. package/dist/components/grid/grid.js +36 -0
  197. package/dist/components/grid/index.d.ts +3 -0
  198. package/dist/components/grid/index.js +3 -0
  199. package/dist/components/grid/types.d.ts +35 -0
  200. package/dist/components/grid/types.js +102 -0
  201. package/dist/components/hover-card/hover-card.d.ts +19 -0
  202. package/dist/components/hover-card/hover-card.js +63 -0
  203. package/dist/components/hover-card/index.d.ts +3 -0
  204. package/dist/components/hover-card/index.js +1 -0
  205. package/dist/components/hover-card/types.d.ts +27 -0
  206. package/dist/components/image-gallery/image-gallery.d.ts +8 -0
  207. package/dist/components/image-gallery/image-gallery.js +294 -0
  208. package/dist/components/image-gallery/index.d.ts +4 -0
  209. package/dist/components/image-gallery/index.js +3 -0
  210. package/dist/components/image-gallery/masonry.d.ts +11 -0
  211. package/dist/components/image-gallery/masonry.js +62 -0
  212. package/dist/components/image-gallery/types.d.ts +80 -0
  213. package/dist/components/input/index.d.ts +3 -0
  214. package/dist/components/input/index.js +1 -0
  215. package/dist/components/input/input.d.ts +7 -0
  216. package/dist/components/input/input.js +18 -0
  217. package/dist/components/input/types.d.ts +28 -0
  218. package/dist/components/input-group/index.d.ts +3 -0
  219. package/dist/components/input-group/index.js +3 -0
  220. package/dist/components/input-group/input-group.d.ts +37 -0
  221. package/dist/components/input-group/input-group.js +103 -0
  222. package/dist/components/input-group/types.d.ts +64 -0
  223. package/dist/components/input-group/types.js +31 -0
  224. package/dist/components/input-otp/index.d.ts +3 -0
  225. package/dist/components/input-otp/index.js +1 -0
  226. package/dist/components/input-otp/input-otp.d.ts +25 -0
  227. package/dist/components/input-otp/input-otp.js +76 -0
  228. package/dist/components/input-otp/types.d.ts +43 -0
  229. package/dist/components/item/index.d.ts +3 -0
  230. package/dist/components/item/index.js +3 -0
  231. package/dist/components/item/item.d.ts +61 -0
  232. package/dist/components/item/item.js +149 -0
  233. package/dist/components/item/types.d.ts +75 -0
  234. package/dist/components/item/types.js +48 -0
  235. package/dist/components/kbd/index.d.ts +3 -0
  236. package/dist/components/kbd/index.js +1 -0
  237. package/dist/components/kbd/kbd.d.ts +13 -0
  238. package/dist/components/kbd/kbd.js +30 -0
  239. package/dist/components/kbd/types.d.ts +9 -0
  240. package/dist/components/label/index.d.ts +3 -0
  241. package/dist/components/label/index.js +1 -0
  242. package/dist/components/label/label.d.ts +7 -0
  243. package/dist/components/label/label.js +17 -0
  244. package/dist/components/label/types.d.ts +8 -0
  245. package/dist/components/loader/index.d.ts +3 -0
  246. package/dist/components/loader/index.js +3 -0
  247. package/dist/components/loader/loader.d.ts +8 -0
  248. package/dist/components/loader/loader.js +386 -0
  249. package/dist/components/loader/types.d.ts +27 -0
  250. package/dist/components/loader/types.js +58 -0
  251. package/dist/components/marker/index.d.ts +3 -0
  252. package/dist/components/marker/index.js +3 -0
  253. package/dist/components/marker/marker.d.ts +19 -0
  254. package/dist/components/marker/marker.js +51 -0
  255. package/dist/components/marker/types.d.ts +30 -0
  256. package/dist/components/marker/types.js +16 -0
  257. package/dist/components/menubar/index.d.ts +3 -0
  258. package/dist/components/menubar/index.js +1 -0
  259. package/dist/components/menubar/menubar.d.ts +98 -0
  260. package/dist/components/menubar/menubar.js +284 -0
  261. package/dist/components/menubar/types.d.ts +146 -0
  262. package/dist/components/message/index.d.ts +3 -0
  263. package/dist/components/message/index.js +1 -0
  264. package/dist/components/message/message.d.ts +37 -0
  265. package/dist/components/message/message.js +83 -0
  266. package/dist/components/message/types.d.ts +32 -0
  267. package/dist/components/message-scroller/index.d.ts +4 -0
  268. package/dist/components/message-scroller/index.js +3 -0
  269. package/dist/components/message-scroller/message-scroller-primitive.d.ts +21 -0
  270. package/dist/components/message-scroller/message-scroller-primitive.js +1349 -0
  271. package/dist/components/message-scroller/message-scroller.d.ts +39 -0
  272. package/dist/components/message-scroller/message-scroller.js +103 -0
  273. package/dist/components/message-scroller/types.d.ts +107 -0
  274. package/dist/components/money-input/index.d.ts +3 -0
  275. package/dist/components/money-input/index.js +1 -0
  276. package/dist/components/money-input/money-input.d.ts +7 -0
  277. package/dist/components/money-input/money-input.js +143 -0
  278. package/dist/components/money-input/types.d.ts +20 -0
  279. package/dist/components/morph-image/index.d.ts +3 -0
  280. package/dist/components/morph-image/index.js +3 -0
  281. package/dist/components/morph-image/morph-image.d.ts +8 -0
  282. package/dist/components/morph-image/morph-image.js +155 -0
  283. package/dist/components/morph-image/types.d.ts +38 -0
  284. package/dist/components/morph-image/types.js +18 -0
  285. package/dist/components/native-select/index.d.ts +3 -0
  286. package/dist/components/native-select/index.js +3 -0
  287. package/dist/components/native-select/native-select.d.ts +20 -0
  288. package/dist/components/native-select/native-select.js +57 -0
  289. package/dist/components/native-select/types.d.ts +52 -0
  290. package/dist/components/native-select/types.js +23 -0
  291. package/dist/components/navigation-menu/index.d.ts +3 -0
  292. package/dist/components/navigation-menu/index.js +3 -0
  293. package/dist/components/navigation-menu/navigation-menu.d.ts +50 -0
  294. package/dist/components/navigation-menu/navigation-menu.js +145 -0
  295. package/dist/components/navigation-menu/types.d.ts +79 -0
  296. package/dist/components/navigation-menu/types.js +15 -0
  297. package/dist/components/number-stepper/index.d.ts +3 -0
  298. package/dist/components/number-stepper/index.js +3 -0
  299. package/dist/components/number-stepper/number-stepper.d.ts +9 -0
  300. package/dist/components/number-stepper/number-stepper.js +223 -0
  301. package/dist/components/number-stepper/types.d.ts +47 -0
  302. package/dist/components/number-stepper/types.js +174 -0
  303. package/dist/components/pagination/index.d.ts +3 -0
  304. package/dist/components/pagination/index.js +1 -0
  305. package/dist/components/pagination/pagination.d.ts +43 -0
  306. package/dist/components/pagination/pagination.js +121 -0
  307. package/dist/components/pagination/types.d.ts +37 -0
  308. package/dist/components/phone-input/index.d.ts +3 -0
  309. package/dist/components/phone-input/index.js +1 -0
  310. package/dist/components/phone-input/phone-input.d.ts +7 -0
  311. package/dist/components/phone-input/phone-input.js +54 -0
  312. package/dist/components/phone-input/types.d.ts +12 -0
  313. package/dist/components/popover/index.d.ts +3 -0
  314. package/dist/components/popover/index.js +1 -0
  315. package/dist/components/popover/popover.d.ts +37 -0
  316. package/dist/components/popover/popover.js +100 -0
  317. package/dist/components/popover/types.d.ts +51 -0
  318. package/dist/components/progress/index.d.ts +3 -0
  319. package/dist/components/progress/index.js +1 -0
  320. package/dist/components/progress/progress.d.ts +32 -0
  321. package/dist/components/progress/progress.js +100 -0
  322. package/dist/components/progress/types.d.ts +31 -0
  323. package/dist/components/questionnaire/index.d.ts +3 -0
  324. package/dist/components/questionnaire/index.js +1 -0
  325. package/dist/components/questionnaire/questionnaire-primitive.js +1563 -0
  326. package/dist/components/questionnaire/questionnaire.d.ts +91 -0
  327. package/dist/components/questionnaire/questionnaire.js +315 -0
  328. package/dist/components/questionnaire/types.d.ts +203 -0
  329. package/dist/components/radio-group/index.d.ts +3 -0
  330. package/dist/components/radio-group/index.js +1 -0
  331. package/dist/components/radio-group/radio-group.d.ts +13 -0
  332. package/dist/components/radio-group/radio-group.js +44 -0
  333. package/dist/components/radio-group/types.d.ts +33 -0
  334. package/dist/components/reorder-list/index.d.ts +3 -0
  335. package/dist/components/reorder-list/index.js +1 -0
  336. package/dist/components/reorder-list/reorder-list.d.ts +19 -0
  337. package/dist/components/reorder-list/reorder-list.js +279 -0
  338. package/dist/components/reorder-list/types.d.ts +65 -0
  339. package/dist/components/resizable/index.d.ts +3 -0
  340. package/dist/components/resizable/index.js +1 -0
  341. package/dist/components/resizable/resizable.d.ts +19 -0
  342. package/dist/components/resizable/resizable.js +44 -0
  343. package/dist/components/resizable/types.d.ts +89 -0
  344. package/dist/components/scroll-area/index.d.ts +3 -0
  345. package/dist/components/scroll-area/index.js +1 -0
  346. package/dist/components/scroll-area/scroll-area.d.ts +13 -0
  347. package/dist/components/scroll-area/scroll-area.js +47 -0
  348. package/dist/components/scroll-area/types.d.ts +19 -0
  349. package/dist/components/select/index.d.ts +3 -0
  350. package/dist/components/select/index.js +1 -0
  351. package/dist/components/select/select.d.ts +61 -0
  352. package/dist/components/select/select.js +183 -0
  353. package/dist/components/select/types.d.ts +100 -0
  354. package/dist/components/separator/index.d.ts +3 -0
  355. package/dist/components/separator/index.js +1 -0
  356. package/dist/components/separator/separator.d.ts +7 -0
  357. package/dist/components/separator/separator.js +20 -0
  358. package/dist/components/separator/types.d.ts +9 -0
  359. package/dist/components/sheet/index.d.ts +3 -0
  360. package/dist/components/sheet/index.js +1 -0
  361. package/dist/components/sheet/sheet.d.ts +61 -0
  362. package/dist/components/sheet/sheet.js +155 -0
  363. package/dist/components/sheet/types.d.ts +69 -0
  364. package/dist/components/sidebar/index.d.ts +3 -0
  365. package/dist/components/sidebar/index.js +3 -0
  366. package/dist/components/sidebar/sidebar.d.ts +145 -0
  367. package/dist/components/sidebar/sidebar.js +550 -0
  368. package/dist/components/sidebar/types.d.ts +186 -0
  369. package/dist/components/sidebar/types.js +42 -0
  370. package/dist/components/sidebar/use-mobile.js +21 -0
  371. package/dist/components/skeleton/index.d.ts +3 -0
  372. package/dist/components/skeleton/index.js +1 -0
  373. package/dist/components/skeleton/skeleton.d.ts +8 -0
  374. package/dist/components/skeleton/skeleton.js +18 -0
  375. package/dist/components/skeleton/types.d.ts +5 -0
  376. package/dist/components/slider/index.d.ts +3 -0
  377. package/dist/components/slider/index.js +1 -0
  378. package/dist/components/slider/slider.d.ts +7 -0
  379. package/dist/components/slider/slider.js +106 -0
  380. package/dist/components/slider/types.d.ts +39 -0
  381. package/dist/components/spinner/index.d.ts +3 -0
  382. package/dist/components/spinner/index.js +1 -0
  383. package/dist/components/spinner/spinner.d.ts +7 -0
  384. package/dist/components/spinner/spinner.js +21 -0
  385. package/dist/components/spinner/types.d.ts +8 -0
  386. package/dist/components/stack/index.d.ts +3 -0
  387. package/dist/components/stack/index.js +3 -0
  388. package/dist/components/stack/stack.d.ts +7 -0
  389. package/dist/components/stack/stack.js +29 -0
  390. package/dist/components/stack/types.d.ts +56 -0
  391. package/dist/components/stack/types.js +62 -0
  392. package/dist/components/statistics/index.d.ts +3 -0
  393. package/dist/components/statistics/index.js +3 -0
  394. package/dist/components/statistics/statistics.d.ts +31 -0
  395. package/dist/components/statistics/statistics.js +141 -0
  396. package/dist/components/statistics/types.d.ts +84 -0
  397. package/dist/components/statistics/types.js +27 -0
  398. package/dist/components/switch/index.d.ts +3 -0
  399. package/dist/components/switch/index.js +3 -0
  400. package/dist/components/switch/switch.d.ts +7 -0
  401. package/dist/components/switch/switch.js +32 -0
  402. package/dist/components/switch/types.d.ts +37 -0
  403. package/dist/components/switch/types.js +23 -0
  404. package/dist/components/table/index.d.ts +3 -0
  405. package/dist/components/table/index.js +1 -0
  406. package/dist/components/table/table.d.ts +49 -0
  407. package/dist/components/table/table.js +112 -0
  408. package/dist/components/table/types.d.ts +33 -0
  409. package/dist/components/tabs/index.d.ts +3 -0
  410. package/dist/components/tabs/index.js +3 -0
  411. package/dist/components/tabs/tabs.d.ts +25 -0
  412. package/dist/components/tabs/tabs.js +81 -0
  413. package/dist/components/tabs/types.d.ts +61 -0
  414. package/dist/components/tabs/types.js +18 -0
  415. package/dist/components/textarea/index.d.ts +3 -0
  416. package/dist/components/textarea/index.js +1 -0
  417. package/dist/components/textarea/textarea.d.ts +7 -0
  418. package/dist/components/textarea/textarea.js +17 -0
  419. package/dist/components/textarea/types.d.ts +26 -0
  420. package/dist/components/toast/index.d.ts +3 -0
  421. package/dist/components/toast/index.js +1 -0
  422. package/dist/components/toast/toast.d.ts +63 -0
  423. package/dist/components/toast/toast.js +321 -0
  424. package/dist/components/toast/types.d.ts +120 -0
  425. package/dist/components/toggle/index.d.ts +3 -0
  426. package/dist/components/toggle/index.js +3 -0
  427. package/dist/components/toggle/toggle.d.ts +7 -0
  428. package/dist/components/toggle/toggle.js +32 -0
  429. package/dist/components/toggle/types.d.ts +35 -0
  430. package/dist/components/toggle/types.js +37 -0
  431. package/dist/components/toggle-group/index.d.ts +3 -0
  432. package/dist/components/toggle-group/index.js +1 -0
  433. package/dist/components/toggle-group/toggle-group.d.ts +13 -0
  434. package/dist/components/toggle-group/toggle-group.js +88 -0
  435. package/dist/components/toggle-group/types.d.ts +52 -0
  436. package/dist/components/tooltip/index.d.ts +3 -0
  437. package/dist/components/tooltip/index.js +1 -0
  438. package/dist/components/tooltip/tooltip.d.ts +25 -0
  439. package/dist/components/tooltip/tooltip.js +79 -0
  440. package/dist/components/tooltip/types.d.ts +48 -0
  441. package/dist/components/trace/index.d.ts +3 -0
  442. package/dist/components/trace/index.js +1 -0
  443. package/dist/components/trace/trace.d.ts +52 -0
  444. package/dist/components/trace/trace.js +132 -0
  445. package/dist/components/trace/types.d.ts +54 -0
  446. package/dist/components/typography/index.d.ts +3 -0
  447. package/dist/components/typography/index.js +3 -0
  448. package/dist/components/typography/types.d.ts +29 -0
  449. package/dist/components/typography/types.js +25 -0
  450. package/dist/components/typography/typography.d.ts +7 -0
  451. package/dist/components/typography/typography.js +40 -0
  452. package/dist/components/video-player/index.d.ts +6 -0
  453. package/dist/components/video-player/index.js +5 -0
  454. package/dist/components/video-player/types.d.ts +143 -0
  455. package/dist/components/video-player/use-video-player.js +319 -0
  456. package/dist/components/video-player/video-player-context.d.ts +45 -0
  457. package/dist/components/video-player/video-player-context.js +15 -0
  458. package/dist/components/video-player/video-player-controls.d.ts +38 -0
  459. package/dist/components/video-player/video-player-controls.js +226 -0
  460. package/dist/components/video-player/video-player-overlay.d.ts +13 -0
  461. package/dist/components/video-player/video-player-overlay.js +113 -0
  462. package/dist/components/video-player/video-player.d.ts +8 -0
  463. package/dist/components/video-player/video-player.js +139 -0
  464. package/dist/index.d.ts +259 -0
  465. package/dist/index.js +124 -0
  466. package/dist/lib/floating.d.ts +44 -0
  467. package/dist/lib/focus-source.js +51 -0
  468. package/dist/lib/format.d.ts +67 -0
  469. package/dist/lib/format.js +130 -0
  470. package/dist/lib/locale.d.ts +104 -0
  471. package/dist/lib/locale.js +103 -0
  472. package/dist/lib/part-props.d.ts +15 -0
  473. package/dist/lib/use-controllable-state.js +22 -0
  474. package/dist/lib/utils.d.ts +9 -0
  475. package/dist/lib/utils.js +16 -0
  476. package/dist/styles/index.css +128 -0
  477. package/dist/styles/theme.css +84 -0
  478. package/dist/styles/utilities.css +553 -0
  479. package/package.json +89 -3
@@ -0,0 +1,1349 @@
1
+ "use client";
2
+ import { mergeProps } from "@base-ui/react/merge-props";
3
+ import { createContext, createElement, use, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from "react";
4
+ import { useRender } from "@base-ui/react/use-render";
5
+ // src/components/message-scroller/message-scroller-primitive.ts
6
+ /** Расстояние до края по умолчанию, при котором лента считается прокрученной до конца */
7
+ const DEFAULT_EDGE_THRESHOLD = 8;
8
+ /** Сколько пикселей предыдущего сообщения видно над якорем по умолчанию */
9
+ const DEFAULT_PREVIOUS_PEEK = 64;
10
+ /** Отступ сверху при прокрутке к сообщению по умолчанию */
11
+ const DEFAULT_SCROLL_MARGIN = 0;
12
+ /** Погрешность сравнения позиций в пикселях */
13
+ const POSITION_EPSILON = .5;
14
+ /** Сколько длится программная прокрутка, пока скрыт ползунок, мс */
15
+ const AUTOSCROLL_DURATION = 180;
16
+ /** Клавиши, которыми пользователь прокручивает ленту сам */
17
+ const SCROLL_KEYS = /* @__PURE__ */ new Set([
18
+ "ArrowDown",
19
+ "ArrowUp",
20
+ "End",
21
+ "Home",
22
+ "PageDown",
23
+ "PageUp",
24
+ " "
25
+ ]);
26
+ /** Нечего прокручивать */
27
+ const EMPTY_SCROLLABLE = {
28
+ start: false,
29
+ end: false
30
+ };
31
+ /** Ничего не видно */
32
+ const EMPTY_VISIBILITY = {
33
+ currentAnchorId: null,
34
+ visibleMessageIds: []
35
+ };
36
+ /**
37
+ * Создать хранилище
38
+ * @param initial - начальное значение
39
+ * @param isEqual - одинаковы ли значения: тогда подписчики не оповещаются
40
+ * @returns хранилище
41
+ */
42
+ function createStore(initial, isEqual) {
43
+ let snapshot = initial;
44
+ const listeners = /* @__PURE__ */ new Set();
45
+ return {
46
+ getSnapshot: () => snapshot,
47
+ hasListeners: () => listeners.size > 0,
48
+ setSnapshot: (value) => {
49
+ if (isEqual(snapshot, value)) return;
50
+ snapshot = value;
51
+ listeners.forEach((listener) => listener());
52
+ },
53
+ subscribe: (listener, onFirst, onLast) => {
54
+ const isFirst = listeners.size === 0;
55
+ listeners.add(listener);
56
+ if (isFirst) onFirst?.();
57
+ return () => {
58
+ listeners.delete(listener);
59
+ if (listeners.size === 0) onLast?.();
60
+ };
61
+ }
62
+ };
63
+ }
64
+ /**
65
+ * Одинаково ли, куда можно прокручивать
66
+ * @param previous - прошлое значение
67
+ * @param next - новое значение
68
+ * @returns true, если совпадают
69
+ */
70
+ function isSameScrollable(previous, next) {
71
+ return previous.start === next.start && previous.end === next.end;
72
+ }
73
+ /**
74
+ * Одинаковы ли видимые сообщения
75
+ * @param previous - прошлое значение
76
+ * @param next - новое значение
77
+ * @returns true, если совпадают
78
+ */
79
+ function isSameVisibility(previous, next) {
80
+ if (previous.currentAnchorId !== next.currentAnchorId) return false;
81
+ if (previous.visibleMessageIds.length !== next.visibleMessageIds.length) return false;
82
+ return previous.visibleMessageIds.every((id, index) => id === next.visibleMessageIds[index]);
83
+ }
84
+ /**
85
+ * Число из CSS-значения в пикселях
86
+ * @param value - CSS-значение
87
+ * @returns число или 0
88
+ */
89
+ function parsePixels(value) {
90
+ if (!value) return 0;
91
+ const result = Number.parseFloat(value);
92
+ return Number.isFinite(result) ? result : 0;
93
+ }
94
+ /**
95
+ * Внутренние отступы элемента сверху и снизу
96
+ * @param element - элемент
97
+ * @returns отступы в пикселях
98
+ */
99
+ function getBlockPadding(element) {
100
+ const style = window.getComputedStyle(element);
101
+ return {
102
+ end: parsePixels(style.paddingBlockEnd || style.paddingBottom),
103
+ start: parsePixels(style.paddingBlockStart || style.paddingTop)
104
+ };
105
+ }
106
+ /**
107
+ * Отступы колонки сообщений, внутри которой лежит spacer
108
+ * @param spacer - служебный отступ
109
+ * @returns отступы в пикселях
110
+ */
111
+ function getSpacerParentPadding(spacer) {
112
+ const parent = spacer?.parentElement;
113
+ if (!parent) return {
114
+ end: 0,
115
+ start: 0
116
+ };
117
+ return getBlockPadding(parent);
118
+ }
119
+ /**
120
+ * Промежуток между строками колонки
121
+ * @param element - колонка
122
+ * @returns промежуток в пикселях
123
+ */
124
+ function getRowGap(element) {
125
+ if (!element) return 0;
126
+ const style = window.getComputedStyle(element);
127
+ return parsePixels(style.rowGap === "normal" ? style.gap : style.rowGap);
128
+ }
129
+ /**
130
+ * Сообщения ленты без служебного отступа
131
+ * @param content - колонка сообщений
132
+ * @param spacer - служебный отступ
133
+ * @returns элементы сообщений
134
+ */
135
+ function getMessageElements(content, spacer) {
136
+ const result = [];
137
+ for (const child of content.children) if (child instanceof HTMLElement && child !== spacer) result.push(child);
138
+ return result;
139
+ }
140
+ /**
141
+ * Якорь ли элемент
142
+ * @param element - элемент ленты
143
+ * @returns true, если у элемента scrollAnchor
144
+ */
145
+ function isAnchor(element) {
146
+ return element?.dataset.scrollAnchor === "true";
147
+ }
148
+ /**
149
+ * Первый якорь начиная с индекса
150
+ * @param elements - элементы ленты
151
+ * @param start - индекс начала поиска
152
+ * @returns якорь или null
153
+ */
154
+ function findAnchorFrom(elements, start) {
155
+ for (let index = start; index < elements.length; index++) if (isAnchor(elements[index])) return elements[index];
156
+ return null;
157
+ }
158
+ /**
159
+ * Первый якорь, к которому ещё не прокручивали
160
+ * @param elements - элементы ленты
161
+ * @param handled - якоря, к которым уже прокручивали
162
+ * @returns якорь или null
163
+ */
164
+ function findUnhandledAnchor(elements, handled) {
165
+ for (const element of elements) if (isAnchor(element) && !handled.has(element)) return element;
166
+ return null;
167
+ }
168
+ /**
169
+ * Больше ли одного якоря начиная с индекса
170
+ * @param elements - элементы ленты
171
+ * @param start - индекс начала поиска
172
+ * @returns true, если якорей несколько
173
+ */
174
+ function hasSeveralAnchorsFrom(elements, start) {
175
+ let count = 0;
176
+ for (let index = start; index < elements.length; index++) if (isAnchor(elements[index])) {
177
+ count += 1;
178
+ if (count > 1) return true;
179
+ }
180
+ return false;
181
+ }
182
+ /**
183
+ * Последний якорь ленты
184
+ * @param elements - элементы ленты
185
+ * @returns якорь или null
186
+ */
187
+ function findLastAnchor(elements) {
188
+ for (let index = elements.length - 1; index >= 0; index--) if (isAnchor(elements[index])) return elements[index];
189
+ return null;
190
+ }
191
+ /**
192
+ * Расстояние от верха области до элемента
193
+ * @param element - элемент
194
+ * @param viewport - прокручиваемая область
195
+ * @returns расстояние в пикселях
196
+ */
197
+ function getViewportTop(element, viewport) {
198
+ return element.getBoundingClientRect().top - viewport.getBoundingClientRect().top;
199
+ }
200
+ /**
201
+ * Позиция элемента внутри прокручиваемого содержимого
202
+ * @param element - элемент
203
+ * @param viewport - прокручиваемая область
204
+ * @returns позиция в пикселях от начала содержимого
205
+ */
206
+ function getOffsetInViewport(element, viewport) {
207
+ return getViewportTop(element, viewport) + viewport.scrollTop;
208
+ }
209
+ /**
210
+ * Высота содержимого без служебного отступа
211
+ * @param elements - колонка, отступ и область
212
+ * @returns высота в пикселях
213
+ */
214
+ function getContentHeight({ content, spacer, viewport }) {
215
+ const padding = getBlockPadding(content);
216
+ const viewportTop = viewport.getBoundingClientRect().top;
217
+ const { scrollTop } = viewport;
218
+ let result = padding.start + padding.end;
219
+ for (const element of getMessageElements(content, spacer)) {
220
+ const bottom = element.getBoundingClientRect().bottom - viewportTop + scrollTop + padding.end;
221
+ result = Math.max(result, bottom);
222
+ }
223
+ return result;
224
+ }
225
+ /**
226
+ * Высота служебного отступа, чтобы прокрутить область до позиции
227
+ * @param parameters - колонка, отступ, область и позиция
228
+ * @returns высота в пикселях
229
+ */
230
+ function getSpacerHeight({ scrollTop, ...elements }) {
231
+ return scrollTop + elements.viewport.clientHeight - getContentHeight(elements);
232
+ }
233
+ /**
234
+ * Наибольшая позиция прокрутки
235
+ * @param viewport - прокручиваемая область
236
+ * @returns позиция в пикселях
237
+ */
238
+ function getMaxScrollTop(viewport) {
239
+ return Math.max(0, viewport.scrollHeight - viewport.clientHeight);
240
+ }
241
+ /**
242
+ * Есть ли куда прокручивать
243
+ * @param parameters - колонка, отступ, область и порог края
244
+ * @returns к началу и к концу
245
+ */
246
+ function getScrollable({ content, scrollEdgeThreshold, spacer, viewport }) {
247
+ if (!viewport || !content) return EMPTY_SCROLLABLE;
248
+ const height = getContentHeight({
249
+ content,
250
+ spacer: spacer ?? null,
251
+ viewport
252
+ });
253
+ return {
254
+ start: viewport.scrollTop > scrollEdgeThreshold,
255
+ end: height - viewport.scrollTop - viewport.clientHeight > scrollEdgeThreshold
256
+ };
257
+ }
258
+ /**
259
+ * Видимые сообщения и якорь у верхнего края
260
+ * @param parameters - элементы ленты и отступы
261
+ * @returns видимые сообщения и якорь
262
+ */
263
+ function getVisibility({ content, scrollMargin, scrollPreviousItemPeek, spacer, viewport, visibleMessageIds }) {
264
+ if (!content || !viewport) return EMPTY_VISIBILITY;
265
+ const viewportRect = viewport.getBoundingClientRect();
266
+ const anchorLine = viewportRect.top + scrollMargin + scrollPreviousItemPeek;
267
+ const withoutObserver = typeof IntersectionObserver === "undefined";
268
+ const visible = [];
269
+ let currentAnchorId = null;
270
+ for (const element of getMessageElements(content, spacer ?? null)) {
271
+ const { messageId } = element.dataset;
272
+ if (!messageId) continue;
273
+ const anchor = isAnchor(element);
274
+ const rect = anchor || withoutObserver ? element.getBoundingClientRect() : null;
275
+ let isVisible = visibleMessageIds.has(messageId);
276
+ if (withoutObserver && rect) isVisible = rect.bottom > anchorLine && rect.top < viewportRect.bottom;
277
+ if (isVisible) visible.push(messageId);
278
+ if (anchor && rect && rect.top <= anchorLine + POSITION_EPSILON) currentAnchorId = messageId;
279
+ }
280
+ if (visible.length === 0 && currentAnchorId === null) return EMPTY_VISIBILITY;
281
+ return {
282
+ currentAnchorId,
283
+ visibleMessageIds: visible
284
+ };
285
+ }
286
+ /**
287
+ * Первое видимое сообщение
288
+ * @param elements - колонка, отступ и область
289
+ * @returns сообщение или null
290
+ */
291
+ function findFirstVisibleMessage({ content, spacer, viewport }) {
292
+ const viewportRect = viewport.getBoundingClientRect();
293
+ for (const element of getMessageElements(content, spacer)) {
294
+ if (!element.dataset.messageId) continue;
295
+ const rect = element.getBoundingClientRect();
296
+ if (rect.bottom > viewportRect.top && rect.top < viewportRect.bottom) return element;
297
+ }
298
+ return null;
299
+ }
300
+ /**
301
+ * Позиция прокрутки, при которой элемент выровнен как нужно
302
+ * @param parameters - элемент, область и выравнивание
303
+ * @returns позиция в пикселях
304
+ */
305
+ function getScrollTopForElement({ align, element, scrollMargin, spacer, viewport }) {
306
+ const offset = getOffsetInViewport(element, viewport);
307
+ const { height } = element.getBoundingClientRect();
308
+ const padding = getSpacerParentPadding(spacer);
309
+ if (align === "center") {
310
+ const available = Math.max(0, viewport.clientHeight - padding.start - padding.end);
311
+ return offset - padding.start - (available - height) / 2 - scrollMargin;
312
+ }
313
+ if (align === "end") return offset - viewport.clientHeight + height + padding.end + scrollMargin;
314
+ if (align === "nearest") {
315
+ const bottom = offset + height;
316
+ const visibleTop = viewport.scrollTop + padding.start;
317
+ const visibleBottom = viewport.scrollTop + viewport.clientHeight - padding.end;
318
+ if (offset >= visibleTop && bottom <= visibleBottom) return viewport.scrollTop;
319
+ if (offset < visibleTop) return offset - padding.start - scrollMargin;
320
+ return bottom - viewport.clientHeight + padding.end + scrollMargin;
321
+ }
322
+ return offset - padding.start - scrollMargin;
323
+ }
324
+ const ScrollerContext = createContext(null);
325
+ const RegisterMessageContext = createContext(null);
326
+ /**
327
+ * Контекст ленты или ошибка, если часть вне MessageScroller
328
+ * @returns контекст ленты
329
+ */
330
+ function useScrollerContext() {
331
+ const context = use(ScrollerContext);
332
+ if (!context) throw new Error("Хуки и части MessageScroller должны находиться внутри MessageScroller");
333
+ return context;
334
+ }
335
+ /**
336
+ * Записать элемент в ref любого вида
337
+ * @param ref - ref-функция или объект
338
+ * @param value - элемент
339
+ */
340
+ function assignRef(ref, value) {
341
+ if (typeof ref === "function") {
342
+ ref(value);
343
+ return;
344
+ }
345
+ if (ref) ref.current = value;
346
+ }
347
+ /**
348
+ * Изменяемое состояние и хранилища ленты, созданные один раз
349
+ * @param parameters - параметры поведения
350
+ * @returns ref состояния и хранилища
351
+ */
352
+ function useScrollerState({ autoScroll, defaultScrollPosition, scrollEdgeThreshold, scrollMargin, scrollPreviousItemPeek }) {
353
+ const refs = useRef(null);
354
+ const stores = useRef(null);
355
+ if (refs.current === null) refs.current = {
356
+ autoScroll,
357
+ autoscrolling: false,
358
+ autoscrollingTimeout: null,
359
+ streamingTurn: null,
360
+ content: null,
361
+ defaultScrollPositionApplied: false,
362
+ firstItem: null,
363
+ itemCount: 0,
364
+ lastScrollTop: 0,
365
+ messageElements: /* @__PURE__ */ new Map(),
366
+ mode: autoScroll ? "following-bottom" : "free-scrolling",
367
+ pendingScrollFrame: null,
368
+ pendingScrollToMessage: null,
369
+ prependRestore: null,
370
+ preserveScrollOnPrepend: true,
371
+ root: null,
372
+ scrollEdgeThreshold,
373
+ scrollMargin,
374
+ scrollPreviousItemPeek,
375
+ spacerGap: 0,
376
+ spacerHeight: 0,
377
+ spacer: null,
378
+ stateFrame: null,
379
+ viewport: null,
380
+ visibilityFrame: null,
381
+ visibilityObserver: null,
382
+ visibleMessageIds: /* @__PURE__ */ new Set(),
383
+ handledScrollAnchors: /* @__PURE__ */ new WeakSet()
384
+ };
385
+ if (stores.current === null) stores.current = {
386
+ pendingDefaultScroll: createStore(defaultScrollPosition === "end" || defaultScrollPosition === "last-anchor", (previous, next) => previous === next),
387
+ scrollable: createStore(EMPTY_SCROLLABLE, isSameScrollable),
388
+ visibility: createStore(EMPTY_VISIBILITY, isSameVisibility)
389
+ };
390
+ refs.current.autoScroll = autoScroll;
391
+ refs.current.scrollEdgeThreshold = scrollEdgeThreshold;
392
+ refs.current.scrollMargin = scrollMargin;
393
+ refs.current.scrollPreviousItemPeek = scrollPreviousItemPeek;
394
+ return {
395
+ refs,
396
+ stores: stores.current
397
+ };
398
+ }
399
+ /**
400
+ * Начальная позиция применена: показать ленту
401
+ * @param refs - состояние ленты
402
+ * @param stores - хранилища ленты
403
+ */
404
+ function markDefaultScrollApplied(refs, stores) {
405
+ refs.defaultScrollPositionApplied = true;
406
+ stores.pendingDefaultScroll.setSnapshot(false);
407
+ }
408
+ /**
409
+ * Команды прокрутки: в начало, в конец, к элементу и сообщению
410
+ * @param parameters - состояние и пересчёты
411
+ * @returns команды
412
+ */
413
+ function useScrollCommands({ refs, stores, commitScrollState, scheduleStateCommit, scheduleVisibilitySync }) {
414
+ const setAutoscrolling = useCallback((value) => {
415
+ const state = refs.current;
416
+ if (state.autoscrollingTimeout !== null) {
417
+ window.clearTimeout(state.autoscrollingTimeout);
418
+ state.autoscrollingTimeout = null;
419
+ }
420
+ if (state.autoscrolling !== value) {
421
+ state.autoscrolling = value;
422
+ commitScrollState();
423
+ }
424
+ if (value) state.autoscrollingTimeout = window.setTimeout(() => {
425
+ state.autoscrollingTimeout = null;
426
+ state.autoscrolling = false;
427
+ commitScrollState();
428
+ }, AUTOSCROLL_DURATION);
429
+ }, [refs, commitScrollState]);
430
+ const setSpacerHeight = useCallback((height) => {
431
+ const state = refs.current;
432
+ const { spacer } = state;
433
+ if (!spacer) return;
434
+ const nextHeight = Math.max(0, Math.ceil(height));
435
+ if (state.spacerHeight === nextHeight) return;
436
+ const marginTop = nextHeight > 0 ? `${-state.spacerGap}px` : "";
437
+ state.spacerHeight = nextHeight;
438
+ spacer.hidden = nextHeight === 0;
439
+ spacer.style.height = `${nextHeight}px`;
440
+ spacer.style.marginTop = marginTop;
441
+ }, [refs]);
442
+ const scrollViewportTo = useCallback((top, { behavior = "auto", autoscrolling = false } = {}) => {
443
+ const { viewport } = refs.current;
444
+ if (!viewport) return;
445
+ const target = Math.max(0, top);
446
+ if (Math.abs(viewport.scrollTop - target) <= POSITION_EPSILON) {
447
+ viewport.scrollTop = target;
448
+ commitScrollState();
449
+ return;
450
+ }
451
+ if (autoscrolling) setAutoscrolling(true);
452
+ viewport.scrollTo({
453
+ top: target,
454
+ behavior
455
+ });
456
+ scheduleStateCommit();
457
+ }, [
458
+ refs,
459
+ commitScrollState,
460
+ scheduleStateCommit,
461
+ setAutoscrolling
462
+ ]);
463
+ const scrollToStart = useCallback(({ behavior = "auto" } = {}) => {
464
+ const state = refs.current;
465
+ if (!state.viewport) return false;
466
+ setSpacerHeight(0);
467
+ state.streamingTurn = null;
468
+ state.mode = "free-scrolling";
469
+ scrollViewportTo(0, { behavior });
470
+ scheduleVisibilitySync();
471
+ return true;
472
+ }, [
473
+ refs,
474
+ scheduleVisibilitySync,
475
+ scrollViewportTo,
476
+ setSpacerHeight
477
+ ]);
478
+ const scrollToEnd = useCallback(({ behavior = "auto" } = {}) => {
479
+ const state = refs.current;
480
+ const { viewport } = state;
481
+ if (!viewport) return false;
482
+ setSpacerHeight(0);
483
+ state.streamingTurn = null;
484
+ state.mode = state.autoScroll ? "following-bottom" : "free-scrolling";
485
+ scrollViewportTo(getMaxScrollTop(viewport), {
486
+ autoscrolling: true,
487
+ behavior
488
+ });
489
+ scheduleVisibilitySync();
490
+ return true;
491
+ }, [
492
+ refs,
493
+ scheduleVisibilitySync,
494
+ scrollViewportTo,
495
+ setSpacerHeight
496
+ ]);
497
+ const scrollToElement = useCallback((element, { align = "start", behavior = "auto", scrollMargin = refs.current.scrollMargin } = {}, { keepPreviousPeek = false } = {}) => {
498
+ const state = refs.current;
499
+ const { content, spacer, viewport } = state;
500
+ if (!content || !viewport || !content.contains(element)) return false;
501
+ const top = getScrollTopForElement({
502
+ align,
503
+ element,
504
+ scrollMargin: keepPreviousPeek ? scrollMargin + state.scrollPreviousItemPeek : scrollMargin,
505
+ spacer,
506
+ viewport
507
+ });
508
+ const spacerHeight = getSpacerHeight({
509
+ content,
510
+ scrollTop: top,
511
+ spacer,
512
+ viewport
513
+ });
514
+ setSpacerHeight(spacerHeight);
515
+ state.prependRestore = {
516
+ element,
517
+ viewportTop: getViewportTop(element, viewport)
518
+ };
519
+ state.mode = keepPreviousPeek ? "anchored-to-message" : "settling-jump";
520
+ state.streamingTurn = keepPreviousPeek ? element : null;
521
+ scrollViewportTo(top, { behavior });
522
+ scheduleVisibilitySync();
523
+ return true;
524
+ }, [
525
+ refs,
526
+ scheduleVisibilitySync,
527
+ scrollViewportTo,
528
+ setSpacerHeight
529
+ ]);
530
+ const reanchorToAnchoredMessage = useCallback(() => {
531
+ const { mode, streamingTurn } = refs.current;
532
+ if (!streamingTurn || !streamingTurn.isConnected || mode !== "anchored-to-message") return false;
533
+ return scrollToElement(streamingTurn, { align: "start" }, { keepPreviousPeek: true });
534
+ }, [refs, scrollToElement]);
535
+ const scrollToMessage = useCallback((messageId, options) => {
536
+ const state = refs.current;
537
+ const element = state.messageElements.get(messageId);
538
+ if (element) {
539
+ markDefaultScrollApplied(state, stores);
540
+ state.pendingScrollToMessage = scrollToElement(element, options) ? null : {
541
+ messageId,
542
+ options
543
+ };
544
+ return true;
545
+ }
546
+ if (state.itemCount === 0) {
547
+ state.pendingScrollToMessage = {
548
+ messageId,
549
+ options
550
+ };
551
+ markDefaultScrollApplied(state, stores);
552
+ return true;
553
+ }
554
+ return false;
555
+ }, [
556
+ refs,
557
+ stores,
558
+ scrollToElement
559
+ ]);
560
+ return {
561
+ flushPendingScrollToMessage: useCallback(() => {
562
+ const state = refs.current;
563
+ const pending = state.pendingScrollToMessage;
564
+ if (!pending) return false;
565
+ const element = state.messageElements.get(pending.messageId);
566
+ if (!element || !scrollToElement(element, pending.options)) return false;
567
+ state.pendingScrollToMessage = null;
568
+ markDefaultScrollApplied(state, stores);
569
+ return true;
570
+ }, [
571
+ refs,
572
+ stores,
573
+ scrollToElement
574
+ ]),
575
+ reanchorToAnchoredMessage,
576
+ scrollToElement,
577
+ scrollToEnd,
578
+ scrollToMessage,
579
+ scrollToStart
580
+ };
581
+ }
582
+ /**
583
+ * Логика ленты: режимы прокрутки, якоря, подгрузка истории, видимость
584
+ * @param parameters - параметры поведения
585
+ * @returns контекст и регистрация сообщений
586
+ */
587
+ function useScroller(parameters) {
588
+ const { autoScroll, defaultScrollPosition } = parameters;
589
+ const { refs, stores } = useScrollerState(parameters);
590
+ const defaultPositionRef = useRef(defaultScrollPosition);
591
+ if (defaultPositionRef.current !== defaultScrollPosition) {
592
+ defaultPositionRef.current = defaultScrollPosition;
593
+ refs.current.defaultScrollPositionApplied = false;
594
+ }
595
+ const applyScrollableAttributes = useCallback((scrollable) => {
596
+ const { autoscrolling, root, viewport } = refs.current;
597
+ const tokens = [];
598
+ if (scrollable.start) tokens.push("start");
599
+ if (scrollable.end) tokens.push("end");
600
+ const value = tokens.join(" ");
601
+ for (const element of [root, viewport]) {
602
+ if (!element) continue;
603
+ if (value) element.setAttribute("data-scrollable", value);
604
+ else element.removeAttribute("data-scrollable");
605
+ element.toggleAttribute("data-autoscrolling", autoscrolling);
606
+ }
607
+ }, [refs]);
608
+ const updateMode = useCallback((scrollable) => {
609
+ const state = refs.current;
610
+ const scrollTop = state.viewport?.scrollTop ?? 0;
611
+ const movedUp = scrollTop < state.lastScrollTop - POSITION_EPSILON;
612
+ const isJumping = state.mode === "settling-jump" || state.mode === "anchored-to-message";
613
+ state.lastScrollTop = scrollTop;
614
+ if (state.autoScroll && !scrollable.end && !isJumping) {
615
+ state.mode = "following-bottom";
616
+ return;
617
+ }
618
+ if (state.mode === "following-bottom" && scrollable.end && movedUp && !state.autoscrolling) state.mode = "free-scrolling";
619
+ }, [refs]);
620
+ const commitScrollState = useCallback(() => {
621
+ const state = refs.current;
622
+ const scrollable = getScrollable({
623
+ content: state.content ?? void 0,
624
+ scrollEdgeThreshold: state.scrollEdgeThreshold,
625
+ spacer: state.spacer,
626
+ viewport: state.viewport ?? void 0
627
+ });
628
+ updateMode(scrollable);
629
+ const published = state.mode === "following-bottom" ? {
630
+ start: scrollable.start,
631
+ end: false
632
+ } : scrollable;
633
+ applyScrollableAttributes(published);
634
+ stores.scrollable.setSnapshot(published);
635
+ }, [
636
+ refs,
637
+ stores,
638
+ applyScrollableAttributes,
639
+ updateMode
640
+ ]);
641
+ const scheduleStateCommit = useCallback(() => {
642
+ const state = refs.current;
643
+ if (state.stateFrame !== null) return;
644
+ state.stateFrame = window.requestAnimationFrame(() => {
645
+ state.stateFrame = null;
646
+ commitScrollState();
647
+ });
648
+ }, [refs, commitScrollState]);
649
+ const scheduleVisibilitySync = useCallback(() => {
650
+ const state = refs.current;
651
+ if (!stores.visibility.hasListeners() || state.visibilityFrame !== null) return;
652
+ state.visibilityFrame = window.requestAnimationFrame(() => {
653
+ state.visibilityFrame = null;
654
+ if (!stores.visibility.hasListeners()) return;
655
+ stores.visibility.setSnapshot(getVisibility({
656
+ content: state.content ?? void 0,
657
+ scrollMargin: state.scrollMargin,
658
+ scrollPreviousItemPeek: state.scrollPreviousItemPeek,
659
+ spacer: state.spacer,
660
+ viewport: state.viewport ?? void 0,
661
+ visibleMessageIds: state.visibleMessageIds
662
+ }));
663
+ });
664
+ }, [refs, stores]);
665
+ const { flushPendingScrollToMessage, reanchorToAnchoredMessage, scrollToElement, scrollToEnd, scrollToMessage, scrollToStart } = useScrollCommands({
666
+ refs,
667
+ stores,
668
+ commitScrollState,
669
+ scheduleStateCommit,
670
+ scheduleVisibilitySync
671
+ });
672
+ const restorePrependPosition = useCallback(() => {
673
+ const state = refs.current;
674
+ const restore = state.prependRestore;
675
+ const { viewport } = state;
676
+ if (!restore || !viewport || !restore.element.isConnected) return false;
677
+ const delta = getViewportTop(restore.element, viewport) - restore.viewportTop;
678
+ if (Math.abs(delta) <= POSITION_EPSILON) return false;
679
+ viewport.scrollTop += delta;
680
+ restore.viewportTop = getViewportTop(restore.element, viewport);
681
+ scheduleStateCommit();
682
+ scheduleVisibilitySync();
683
+ return true;
684
+ }, [
685
+ refs,
686
+ scheduleStateCommit,
687
+ scheduleVisibilitySync
688
+ ]);
689
+ const capturePrependAnchor = useCallback(() => {
690
+ const state = refs.current;
691
+ const { content, spacer, viewport } = state;
692
+ if (!content || !viewport) {
693
+ state.prependRestore = null;
694
+ return;
695
+ }
696
+ const element = findFirstVisibleMessage({
697
+ content,
698
+ spacer,
699
+ viewport
700
+ });
701
+ state.prependRestore = element ? {
702
+ element,
703
+ viewportTop: getViewportTop(element, viewport)
704
+ } : null;
705
+ }, [refs]);
706
+ const schedulePendingScroll = useCallback(() => {
707
+ const state = refs.current;
708
+ if (state.pendingScrollFrame !== null) return;
709
+ state.pendingScrollFrame = window.requestAnimationFrame(() => {
710
+ state.pendingScrollFrame = null;
711
+ if (flushPendingScrollToMessage()) capturePrependAnchor();
712
+ });
713
+ }, [
714
+ refs,
715
+ capturePrependAnchor,
716
+ flushPendingScrollToMessage
717
+ ]);
718
+ const applyDefaultScrollPosition = useCallback(() => {
719
+ const state = refs.current;
720
+ if (state.defaultScrollPositionApplied || state.itemCount === 0) return false;
721
+ let applied;
722
+ if (defaultScrollPosition === "last-anchor") {
723
+ const { content, spacer, viewport } = state;
724
+ const lastAnchor = content && viewport ? findLastAnchor(getMessageElements(content, spacer)) : null;
725
+ if (!content || !viewport || !lastAnchor) applied = scrollToEnd({ behavior: "auto" });
726
+ else {
727
+ const offset = getOffsetInViewport(lastAnchor, viewport);
728
+ if (getContentHeight({
729
+ content,
730
+ spacer,
731
+ viewport
732
+ }) - offset <= viewport.clientHeight) applied = scrollToEnd({ behavior: "auto" });
733
+ else applied = scrollToElement(lastAnchor, { align: "start" }, { keepPreviousPeek: true });
734
+ }
735
+ } else if (defaultScrollPosition === "end") applied = scrollToEnd({ behavior: "auto" });
736
+ else applied = scrollToStart({ behavior: "auto" });
737
+ if (!applied) return false;
738
+ markDefaultScrollApplied(state, stores);
739
+ return true;
740
+ }, [
741
+ refs,
742
+ stores,
743
+ defaultScrollPosition,
744
+ scrollToElement,
745
+ scrollToEnd,
746
+ scrollToStart
747
+ ]);
748
+ /**
749
+ * Реакция на изменение состава сообщений
750
+ * @param elements - сообщения сейчас
751
+ * @param previousCount - сколько сообщений было
752
+ * @param previousFirst - первое сообщение раньше
753
+ */
754
+ const reactToContentChange = useCallback((elements, previousCount, previousFirst) => {
755
+ const state = refs.current;
756
+ if (flushPendingScrollToMessage()) return;
757
+ if (previousCount === 0) {
758
+ if (applyDefaultScrollPosition()) return;
759
+ if (elements.length > 0 && state.autoScroll && scrollToEnd({ behavior: "auto" })) return;
760
+ commitScrollState();
761
+ scheduleVisibilitySync();
762
+ return;
763
+ }
764
+ const firstIndex = previousFirst ? elements.indexOf(previousFirst) : -1;
765
+ if (state.preserveScrollOnPrepend && firstIndex > 0) {
766
+ restorePrependPosition();
767
+ return;
768
+ }
769
+ if (elements.length > previousCount) {
770
+ const anchor = findAnchorFrom(elements, previousCount);
771
+ if (anchor) {
772
+ if (state.autoScroll && state.mode === "following-bottom" && hasSeveralAnchorsFrom(elements, previousCount)) {
773
+ scrollToEnd({ behavior: "auto" });
774
+ return;
775
+ }
776
+ scrollToElement(anchor, { align: "start" }, { keepPreviousPeek: true });
777
+ state.handledScrollAnchors.add(anchor);
778
+ return;
779
+ }
780
+ }
781
+ if (elements.length === previousCount) {
782
+ const anchor = findUnhandledAnchor(elements, state.handledScrollAnchors);
783
+ if (anchor) {
784
+ scrollToElement(anchor, { align: "start" }, { keepPreviousPeek: true });
785
+ state.handledScrollAnchors.add(anchor);
786
+ return;
787
+ }
788
+ }
789
+ if (state.mode === "following-bottom" && state.autoScroll) {
790
+ scrollToEnd({ behavior: "auto" });
791
+ return;
792
+ }
793
+ commitScrollState();
794
+ scheduleVisibilitySync();
795
+ }, [
796
+ refs,
797
+ applyDefaultScrollPosition,
798
+ commitScrollState,
799
+ flushPendingScrollToMessage,
800
+ restorePrependPosition,
801
+ scheduleVisibilitySync,
802
+ scrollToElement,
803
+ scrollToEnd
804
+ ]);
805
+ const handleContentChange = useCallback(() => {
806
+ const state = refs.current;
807
+ const { content } = state;
808
+ if (!content) return;
809
+ const elements = getMessageElements(content, state.spacer);
810
+ const previousCount = state.itemCount;
811
+ const previousFirst = state.firstItem;
812
+ state.itemCount = elements.length;
813
+ state.firstItem = elements[0] ?? null;
814
+ reactToContentChange(elements, previousCount, previousFirst);
815
+ capturePrependAnchor();
816
+ }, [
817
+ refs,
818
+ capturePrependAnchor,
819
+ reactToContentChange
820
+ ]);
821
+ const handleResize = useCallback(() => {
822
+ const state = refs.current;
823
+ if (state.mode === "following-bottom" && state.autoScroll) {
824
+ scrollToEnd({ behavior: "auto" });
825
+ return;
826
+ }
827
+ const previousSpacerHeight = state.spacerHeight;
828
+ if (reanchorToAnchoredMessage()) {
829
+ if (state.autoScroll && previousSpacerHeight > 0 && state.spacerHeight === 0) scrollToEnd({ behavior: "auto" });
830
+ return;
831
+ }
832
+ scheduleStateCommit();
833
+ scheduleVisibilitySync();
834
+ }, [
835
+ refs,
836
+ reanchorToAnchoredMessage,
837
+ scheduleStateCommit,
838
+ scheduleVisibilitySync,
839
+ scrollToEnd
840
+ ]);
841
+ const observeVisibility = useCallback(() => {
842
+ const state = refs.current;
843
+ const { viewport } = state;
844
+ if (!viewport || !stores.visibility.hasListeners()) return;
845
+ if (typeof IntersectionObserver === "undefined") {
846
+ scheduleVisibilitySync();
847
+ return;
848
+ }
849
+ if (!state.visibilityObserver) {
850
+ const topMargin = -(state.scrollMargin + state.scrollPreviousItemPeek);
851
+ state.visibilityObserver = new IntersectionObserver((entries) => {
852
+ for (const entry of entries) {
853
+ const { messageId } = entry.target.dataset;
854
+ if (!messageId) continue;
855
+ if (entry.isIntersecting) state.visibleMessageIds.add(messageId);
856
+ else state.visibleMessageIds.delete(messageId);
857
+ }
858
+ scheduleVisibilitySync();
859
+ }, {
860
+ root: viewport,
861
+ rootMargin: `${topMargin}px 0px 0px 0px`,
862
+ threshold: [
863
+ 0,
864
+ .01,
865
+ .5,
866
+ 1
867
+ ]
868
+ });
869
+ }
870
+ for (const element of state.messageElements.values()) state.visibilityObserver.observe(element);
871
+ scheduleVisibilitySync();
872
+ }, [
873
+ refs,
874
+ stores,
875
+ scheduleVisibilitySync
876
+ ]);
877
+ const unobserveVisibility = useCallback(() => {
878
+ const state = refs.current;
879
+ if (state.visibilityFrame !== null) {
880
+ window.cancelAnimationFrame(state.visibilityFrame);
881
+ state.visibilityFrame = null;
882
+ }
883
+ state.visibilityObserver?.disconnect();
884
+ state.visibilityObserver = null;
885
+ state.visibleMessageIds.clear();
886
+ stores.visibility.setSnapshot(EMPTY_VISIBILITY);
887
+ }, [refs, stores]);
888
+ const registerMessage = useCallback((messageId, element, previous) => {
889
+ const state = refs.current;
890
+ if (element) {
891
+ state.messageElements.set(messageId, element);
892
+ state.visibilityObserver?.observe(element);
893
+ scheduleVisibilitySync();
894
+ if (state.pendingScrollToMessage?.messageId === messageId) schedulePendingScroll();
895
+ return;
896
+ }
897
+ if (previous && state.messageElements.get(messageId) === previous) {
898
+ state.messageElements.delete(messageId);
899
+ state.visibleMessageIds.delete(messageId);
900
+ state.visibilityObserver?.unobserve(previous);
901
+ scheduleVisibilitySync();
902
+ }
903
+ }, [
904
+ refs,
905
+ schedulePendingScroll,
906
+ scheduleVisibilitySync
907
+ ]);
908
+ const userScrollIntent = useCallback(() => {
909
+ const state = refs.current;
910
+ if (state.mode !== "free-scrolling") {
911
+ state.streamingTurn = null;
912
+ state.mode = "free-scrolling";
913
+ }
914
+ }, [refs]);
915
+ const setRootElement = useCallback((element) => {
916
+ refs.current.root = element;
917
+ if (element) applyScrollableAttributes(stores.scrollable.getSnapshot());
918
+ }, [
919
+ refs,
920
+ stores,
921
+ applyScrollableAttributes
922
+ ]);
923
+ const setViewportElement = useCallback((element) => {
924
+ refs.current.viewport = element;
925
+ if (element) applyScrollableAttributes(stores.scrollable.getSnapshot());
926
+ }, [
927
+ refs,
928
+ stores,
929
+ applyScrollableAttributes
930
+ ]);
931
+ const setContentElement = useCallback((element) => {
932
+ refs.current.content = element;
933
+ }, [refs]);
934
+ const setSpacerElement = useCallback((element) => {
935
+ refs.current.spacer = element;
936
+ refs.current.spacerGap = getRowGap(element?.parentElement ?? null);
937
+ }, [refs]);
938
+ const syncAfterScroll = useCallback(() => {
939
+ commitScrollState();
940
+ scheduleVisibilitySync();
941
+ capturePrependAnchor();
942
+ }, [
943
+ capturePrependAnchor,
944
+ commitScrollState,
945
+ scheduleVisibilitySync
946
+ ]);
947
+ const context = useMemo(() => ({
948
+ handleContentChange,
949
+ handleResize,
950
+ observeVisibility,
951
+ scrollToEnd,
952
+ scrollToMessage,
953
+ scrollToStart,
954
+ setContentElement,
955
+ setRootElement,
956
+ setSpacerElement,
957
+ setViewportElement,
958
+ stores,
959
+ syncAfterScroll,
960
+ unobserveVisibility,
961
+ userScrollIntent,
962
+ refs
963
+ }), [
964
+ handleContentChange,
965
+ handleResize,
966
+ observeVisibility,
967
+ scrollToEnd,
968
+ scrollToMessage,
969
+ scrollToStart,
970
+ setContentElement,
971
+ setRootElement,
972
+ setSpacerElement,
973
+ setViewportElement,
974
+ stores,
975
+ syncAfterScroll,
976
+ unobserveVisibility,
977
+ userScrollIntent,
978
+ refs
979
+ ]);
980
+ useLayoutEffect(() => {
981
+ if (!applyDefaultScrollPosition() && refs.current.itemCount === 0) stores.pendingDefaultScroll.setSnapshot(false);
982
+ }, [
983
+ refs,
984
+ stores,
985
+ applyDefaultScrollPosition
986
+ ]);
987
+ useEffect(() => () => {
988
+ const state = refs.current;
989
+ for (const frame of [
990
+ state.stateFrame,
991
+ state.visibilityFrame,
992
+ state.pendingScrollFrame
993
+ ]) if (frame !== null) window.cancelAnimationFrame(frame);
994
+ if (state.autoscrollingTimeout !== null) window.clearTimeout(state.autoscrollingTimeout);
995
+ state.stateFrame = null;
996
+ state.visibilityFrame = null;
997
+ state.pendingScrollFrame = null;
998
+ state.autoscrollingTimeout = null;
999
+ state.visibilityObserver?.disconnect();
1000
+ state.visibilityObserver = null;
1001
+ }, [refs]);
1002
+ useLayoutEffect(() => {
1003
+ const state = refs.current;
1004
+ if (autoScroll && state.mode === "following-bottom" && state.itemCount > 0) {
1005
+ scrollToEnd({ behavior: "auto" });
1006
+ return;
1007
+ }
1008
+ commitScrollState();
1009
+ }, [
1010
+ refs,
1011
+ autoScroll,
1012
+ commitScrollState,
1013
+ scrollToEnd
1014
+ ]);
1015
+ return {
1016
+ context,
1017
+ registerMessage
1018
+ };
1019
+ }
1020
+ /**
1021
+ * Команды прокрутки ленты
1022
+ * @returns прокрутить в конец, в начало и к сообщению
1023
+ */
1024
+ function useMessageScroller() {
1025
+ const { scrollToEnd, scrollToMessage, scrollToStart } = useScrollerContext();
1026
+ return useMemo(() => ({
1027
+ scrollToEnd,
1028
+ scrollToMessage,
1029
+ scrollToStart
1030
+ }), [
1031
+ scrollToEnd,
1032
+ scrollToMessage,
1033
+ scrollToStart
1034
+ ]);
1035
+ }
1036
+ /**
1037
+ * Есть ли куда прокручивать ленту
1038
+ * @returns к началу и к концу
1039
+ */
1040
+ function useMessageScrollerScrollable() {
1041
+ const { stores } = useScrollerContext();
1042
+ return useSyncExternalStore(stores.scrollable.subscribe, stores.scrollable.getSnapshot, stores.scrollable.getSnapshot);
1043
+ }
1044
+ /**
1045
+ * Видимые сообщения и якорь у верхнего края: слежение включается, пока есть подписчики
1046
+ * @returns видимые сообщения и якорь
1047
+ */
1048
+ function useMessageScrollerVisibility() {
1049
+ const { observeVisibility, unobserveVisibility, stores } = useScrollerContext();
1050
+ const { visibility } = stores;
1051
+ const subscribe = useCallback((listener) => visibility.subscribe(listener, observeVisibility, unobserveVisibility), [
1052
+ observeVisibility,
1053
+ unobserveVisibility,
1054
+ visibility
1055
+ ]);
1056
+ return useSyncExternalStore(subscribe, visibility.getSnapshot, visibility.getSnapshot);
1057
+ }
1058
+ /**
1059
+ * Начальная позиция ещё не применена
1060
+ * @returns true, пока ленту не прокрутили в начальную позицию
1061
+ */
1062
+ function usePendingDefaultScroll() {
1063
+ const { stores } = useScrollerContext();
1064
+ const { pendingDefaultScroll } = stores;
1065
+ return useSyncExternalStore(pendingDefaultScroll.subscribe, pendingDefaultScroll.getSnapshot, pendingDefaultScroll.getSnapshot);
1066
+ }
1067
+ /**
1068
+ * Провайдер ленты: состояние прокрутки и команды
1069
+ * @param props - пропсы провайдера
1070
+ * @returns контекст ленты
1071
+ */
1072
+ function Provider({ autoScroll = false, children, defaultScrollPosition = "end", scrollEdgeThreshold = DEFAULT_EDGE_THRESHOLD, scrollPreviousItemPeek = DEFAULT_PREVIOUS_PEEK, scrollMargin = DEFAULT_SCROLL_MARGIN }) {
1073
+ const { context, registerMessage } = useScroller({
1074
+ autoScroll,
1075
+ defaultScrollPosition,
1076
+ scrollEdgeThreshold,
1077
+ scrollMargin,
1078
+ scrollPreviousItemPeek
1079
+ });
1080
+ const registerProvider = createElement(RegisterMessageContext, { value: registerMessage }, children);
1081
+ return createElement(ScrollerContext, { value: context }, registerProvider);
1082
+ }
1083
+ /**
1084
+ * Атрибут ленты, которую ещё не прокрутили в начальную позицию
1085
+ * @param pending - позиция не применена
1086
+ * @returns data-pending-scroll или пусто
1087
+ */
1088
+ function getPendingAttributes(pending) {
1089
+ if (pending) return { "data-pending-scroll": "" };
1090
+ return {};
1091
+ }
1092
+ /**
1093
+ * Корень ленты
1094
+ * @param props - пропсы div
1095
+ * @returns div
1096
+ */
1097
+ function Root({ ref, ...props }) {
1098
+ const { setRootElement } = useScrollerContext();
1099
+ const pendingAttributes = getPendingAttributes(usePendingDefaultScroll());
1100
+ /**
1101
+ * Запомнить корень и передать элемент в ref потребителя
1102
+ * @param element - корень
1103
+ */
1104
+ const setRef = useCallback((element) => {
1105
+ setRootElement(element);
1106
+ assignRef(ref, element);
1107
+ }, [ref, setRootElement]);
1108
+ return createElement("div", {
1109
+ ...props,
1110
+ ...pendingAttributes,
1111
+ ref: setRef
1112
+ });
1113
+ }
1114
+ /**
1115
+ * Прокручиваемая область: отличает прокрутку пользователем от программной
1116
+ * @param props - пропсы области
1117
+ * @returns div
1118
+ */
1119
+ function Viewport({ onKeyDown, onScroll, onTouchMove, onWheel, preserveScrollOnPrepend = true, ref, role = "region", tabIndex = 0, ...props }) {
1120
+ const { handleResize, refs, setViewportElement, syncAfterScroll, userScrollIntent } = useScrollerContext();
1121
+ const pendingAttributes = getPendingAttributes(usePendingDefaultScroll());
1122
+ useLayoutEffect(() => {
1123
+ refs.current.preserveScrollOnPrepend = preserveScrollOnPrepend;
1124
+ }, [refs, preserveScrollOnPrepend]);
1125
+ /**
1126
+ * Запомнить область и передать элемент в ref потребителя
1127
+ * @param element - область
1128
+ */
1129
+ const setRef = useCallback((element) => {
1130
+ setViewportElement(element);
1131
+ assignRef(ref, element);
1132
+ }, [ref, setViewportElement]);
1133
+ /**
1134
+ * Прокрутка: пересчитать состояние
1135
+ * @param event - событие прокрутки
1136
+ */
1137
+ const handleScroll = (event) => {
1138
+ syncAfterScroll();
1139
+ onScroll?.(event);
1140
+ };
1141
+ /**
1142
+ * Колесо мыши: пользователь прокручивает сам
1143
+ * @param event - событие колеса
1144
+ */
1145
+ const handleWheel = (event) => {
1146
+ userScrollIntent();
1147
+ onWheel?.(event);
1148
+ };
1149
+ /**
1150
+ * Касание: пользователь прокручивает сам
1151
+ * @param event - событие касания
1152
+ */
1153
+ const handleTouchMove = (event) => {
1154
+ userScrollIntent();
1155
+ onTouchMove?.(event);
1156
+ };
1157
+ /**
1158
+ * Клавиши прокрутки: пользователь прокручивает сам
1159
+ * @param event - событие клавиатуры
1160
+ */
1161
+ const handleKeyDown = (event) => {
1162
+ if (SCROLL_KEYS.has(event.key)) userScrollIntent();
1163
+ onKeyDown?.(event);
1164
+ };
1165
+ useEffect(() => {
1166
+ const viewport = refs.current.viewport;
1167
+ if (!viewport || typeof ResizeObserver === "undefined") return;
1168
+ let frame = 0;
1169
+ const observer = new ResizeObserver(() => {
1170
+ window.cancelAnimationFrame(frame);
1171
+ frame = window.requestAnimationFrame(handleResize);
1172
+ });
1173
+ observer.observe(viewport);
1174
+ return () => {
1175
+ window.cancelAnimationFrame(frame);
1176
+ observer.disconnect();
1177
+ };
1178
+ }, [refs, handleResize]);
1179
+ return createElement("div", {
1180
+ role,
1181
+ tabIndex,
1182
+ onKeyDown: handleKeyDown,
1183
+ onScroll: handleScroll,
1184
+ onTouchMove: handleTouchMove,
1185
+ onWheel: handleWheel,
1186
+ ...props,
1187
+ ...pendingAttributes,
1188
+ ref: setRef
1189
+ });
1190
+ }
1191
+ /**
1192
+ * Колонка сообщений: role=log, в конце служебный отступ для прокрутки якоря к верху
1193
+ * @param props - пропсы колонки
1194
+ * @returns div
1195
+ */
1196
+ function Content({ "aria-relevant": ariaRelevant = "additions", children, ref, role = "log", spacerClassName, ...props }) {
1197
+ const { handleContentChange, handleResize, setContentElement, setSpacerElement } = useScrollerContext();
1198
+ const contentRef = useRef(null);
1199
+ /**
1200
+ * Запомнить колонку и передать элемент в ref потребителя
1201
+ * @param element - колонка
1202
+ */
1203
+ const setRef = useCallback((element) => {
1204
+ contentRef.current = element;
1205
+ setContentElement(element);
1206
+ assignRef(ref, element);
1207
+ }, [ref, setContentElement]);
1208
+ useLayoutEffect(() => {
1209
+ const content = contentRef.current;
1210
+ if (!content) return;
1211
+ handleContentChange();
1212
+ if (typeof MutationObserver === "undefined") return;
1213
+ const observer = new MutationObserver(() => {
1214
+ handleContentChange();
1215
+ });
1216
+ observer.observe(content, { childList: true });
1217
+ return () => observer.disconnect();
1218
+ }, [handleContentChange]);
1219
+ useEffect(() => {
1220
+ const content = contentRef.current;
1221
+ if (!content || typeof ResizeObserver === "undefined") return;
1222
+ let frame = 0;
1223
+ const observer = new ResizeObserver(() => {
1224
+ window.cancelAnimationFrame(frame);
1225
+ frame = window.requestAnimationFrame(handleResize);
1226
+ });
1227
+ observer.observe(content);
1228
+ return () => {
1229
+ window.cancelAnimationFrame(frame);
1230
+ observer.disconnect();
1231
+ };
1232
+ }, [handleResize]);
1233
+ const spacer = createElement("div", {
1234
+ "ref": setSpacerElement,
1235
+ "aria-hidden": "true",
1236
+ "data-message-scroller-spacer": "",
1237
+ "hidden": true,
1238
+ "className": spacerClassName
1239
+ });
1240
+ return createElement("div", {
1241
+ role,
1242
+ "aria-relevant": ariaRelevant,
1243
+ ...props,
1244
+ ref: setRef
1245
+ }, children, spacer);
1246
+ }
1247
+ /**
1248
+ * Элемент ленты: регистрируется по messageId
1249
+ * @param props - пропсы элемента
1250
+ * @returns div
1251
+ */
1252
+ function Item({ messageId, ref, scrollAnchor = false, ...props }) {
1253
+ const registerMessage = use(RegisterMessageContext);
1254
+ const elementRef = useRef(null);
1255
+ if (!registerMessage) throw new Error("MessageScrollerItem должен находиться внутри MessageScroller");
1256
+ /**
1257
+ * Зарегистрировать элемент и передать его в ref потребителя
1258
+ * @param element - элемент ленты
1259
+ */
1260
+ const setRef = useCallback((element) => {
1261
+ const previous = elementRef.current;
1262
+ elementRef.current = element;
1263
+ if (messageId) registerMessage(messageId, element, previous);
1264
+ assignRef(ref, element);
1265
+ }, [
1266
+ messageId,
1267
+ ref,
1268
+ registerMessage
1269
+ ]);
1270
+ return createElement("div", {
1271
+ "ref": setRef,
1272
+ "data-message-id": messageId,
1273
+ "data-scroll-anchor": scrollAnchor ? "true" : "false",
1274
+ ...props
1275
+ });
1276
+ }
1277
+ /**
1278
+ * Атрибут активности кнопки
1279
+ * @param active - есть куда прокручивать
1280
+ * @returns data-active="true" или "false"
1281
+ */
1282
+ function getActiveAttributes(active) {
1283
+ return { "data-active": active ? "true" : "false" };
1284
+ }
1285
+ /**
1286
+ * Кнопка прокрутки: активна, когда в её сторону есть куда прокручивать
1287
+ * @param props - пропсы кнопки
1288
+ * @returns кнопка
1289
+ */
1290
+ function Button({ behavior = "smooth", children, direction = "end", onClick, ref, render, tabIndex, type = "button", ...props }) {
1291
+ const { scrollToEnd, scrollToStart, stores } = useScrollerContext();
1292
+ const { scrollable } = stores;
1293
+ const onClickRef = useRef(onClick);
1294
+ const getActive = useCallback(() => {
1295
+ const snapshot = scrollable.getSnapshot();
1296
+ return direction === "start" ? snapshot.start : snapshot.end;
1297
+ }, [direction, scrollable]);
1298
+ const subscribe = useCallback((listener) => scrollable.subscribe(listener), [scrollable]);
1299
+ const active = useSyncExternalStore(subscribe, getActive, getActive);
1300
+ const buttonTabIndex = active ? tabIndex : -1;
1301
+ useLayoutEffect(() => {
1302
+ onClickRef.current = onClick;
1303
+ }, [onClick]);
1304
+ /**
1305
+ * Нажатие: прокрутить в свою сторону
1306
+ * @param event - событие клика
1307
+ */
1308
+ const handleClick = useCallback((event) => {
1309
+ if (!active) return;
1310
+ onClickRef.current?.(event);
1311
+ if (event.defaultPrevented) return;
1312
+ event.currentTarget.blur();
1313
+ if (direction === "start") scrollToStart({ behavior });
1314
+ else scrollToEnd({ behavior });
1315
+ }, [
1316
+ active,
1317
+ behavior,
1318
+ direction,
1319
+ scrollToEnd,
1320
+ scrollToStart
1321
+ ]);
1322
+ return useRender({
1323
+ defaultTagName: "button",
1324
+ render,
1325
+ ref,
1326
+ state: {
1327
+ active,
1328
+ direction
1329
+ },
1330
+ stateAttributesMapping: { active: getActiveAttributes },
1331
+ props: mergeProps({
1332
+ type,
1333
+ inert: !active,
1334
+ tabIndex: buttonTabIndex,
1335
+ children,
1336
+ onClick: handleClick
1337
+ }, props)
1338
+ });
1339
+ }
1340
+ /** Части ленты сообщений без оформления */
1341
+ const MessageScrollerPrimitive = {
1342
+ Provider,
1343
+ Root,
1344
+ Viewport,
1345
+ Content,
1346
+ Item,
1347
+ Button
1348
+ };
1349
+ export { MessageScrollerPrimitive, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility };