@mediacubeco/expo-template-fsd 2.3.0 → 2.4.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 (333) hide show
  1. package/.claude/commands/git-commit.md +164 -0
  2. package/.claude/rules/architecture.md +38 -0
  3. package/.claude/rules/{prettier-eslint-typescript.mdc → prettier-eslint-typescript.md} +5 -10
  4. package/.claude/settings.json +2 -2
  5. package/.claude/skills/api/SKILL.md +362 -0
  6. package/.claude/skills/api/references/client/overview.md +106 -0
  7. package/.claude/skills/api/references/hooks/index.ts +3 -0
  8. package/.claude/skills/api/references/hooks/use-api-health.ts +25 -0
  9. package/.claude/skills/api/references/hooks/use-api-post.ts +46 -0
  10. package/.claude/skills/api/references/hooks/use-api-posts.ts +24 -0
  11. package/.claude/skills/api/references/mock/post.mock.ts +29 -0
  12. package/.claude/skills/{generate-api-model/references → api/references/model}/entities/common.ts +5 -2
  13. package/.claude/skills/api/references/model/resources/common.ts +9 -0
  14. package/.claude/skills/api/references/queries/health.ts +14 -0
  15. package/.claude/skills/api/references/queries/index.ts +2 -0
  16. package/.claude/skills/api/references/queries/post.ts +23 -0
  17. package/.claude/skills/api/references/repository/health/health.ts +11 -0
  18. package/.claude/skills/api/references/repository/health/health.types.ts +25 -0
  19. package/.claude/skills/api/references/repository/health/index.ts +2 -0
  20. package/.claude/skills/api/references/repository/post/index.ts +9 -0
  21. package/.claude/skills/api/references/repository/post/post.ts +34 -0
  22. package/.claude/skills/api/references/repository/post/post.types.ts +61 -0
  23. package/.claude/skills/code-style/SKILL.md +1025 -1
  24. package/.claude/skills/code-style/references/components/payout-speed-option/index.ts +2 -0
  25. package/.claude/skills/code-style/references/components/payout-speed-option/payout-speed-option.styles.ts +76 -0
  26. package/.claude/skills/code-style/references/components/payout-speed-option/payout-speed-option.tsx +58 -0
  27. package/.claude/skills/code-style/references/components/payout-speed-option/payout-speed-option.types.ts +22 -0
  28. package/.claude/skills/code-style/references/components/post/index.ts +2 -0
  29. package/.claude/skills/code-style/references/components/post/post.styles.ts +19 -0
  30. package/.claude/skills/code-style/references/components/post/post.tsx +21 -0
  31. package/.claude/skills/code-style/references/components/post/post.types.ts +9 -0
  32. package/.claude/skills/code-style/references/components/wrap/index.ts +2 -0
  33. package/.claude/skills/code-style/references/components/wrap/wrap.styles.ts +15 -0
  34. package/.claude/skills/code-style/references/components/wrap/wrap.tsx +31 -0
  35. package/.claude/skills/code-style/references/components/wrap/wrap.types.ts +10 -0
  36. package/.claude/skills/code-style/references/hooks/use-posts.ts +37 -0
  37. package/.claude/skills/code-style/references/pages/post/index.ts +4 -0
  38. package/.claude/skills/code-style/references/pages/post/post-details/index.ts +1 -0
  39. package/.claude/skills/code-style/references/pages/post/post-details/post-details.tsx +13 -0
  40. package/.claude/skills/code-style/references/pages/post/post-guide/index.ts +1 -0
  41. package/.claude/skills/code-style/references/pages/post/post-guide/post-guide.tsx +17 -0
  42. package/.claude/skills/code-style/references/pages/post/post-list/index.ts +1 -0
  43. package/.claude/skills/code-style/references/pages/post/post-list/post-list.styles.ts +17 -0
  44. package/.claude/skills/code-style/references/pages/post/post-list/post-list.tsx +25 -0
  45. package/.claude/skills/code-style/references/pages/post/post-share/index.ts +1 -0
  46. package/.claude/skills/code-style/references/pages/post/post-share/post-share.tsx +13 -0
  47. package/.claude/skills/code-style/references/routes/(stack)/_layout.tsx +18 -0
  48. package/.claude/skills/code-style/references/routes/(stack)/details.tsx +8 -0
  49. package/.claude/skills/code-style/references/routes/(stack)/guide.tsx +8 -0
  50. package/.claude/skills/code-style/references/routes/(stack)/index.tsx +8 -0
  51. package/.claude/skills/code-style/references/routes/(stack)/share.tsx +8 -0
  52. package/.claude/skills/code-style/references/routes/(tabs)/(post-details)/index.tsx +8 -0
  53. package/.claude/skills/code-style/references/routes/(tabs)/(post-list)/index.tsx +8 -0
  54. package/.claude/skills/code-style/references/routes/(tabs)/_layout.tsx +50 -0
  55. package/.claude/skills/code-style/references/routes/_app.tsx +20 -0
  56. package/.claude/skills/code-style/references/routes/guide.tsx +8 -0
  57. package/.claude/skills/code-style/references/routes/share.tsx +8 -0
  58. package/.claude/skills/code-style/references/store/use-post-draft-store.ts +62 -0
  59. package/.claude/skills/design-style/SKILL.md +320 -0
  60. package/.env +3 -1
  61. package/.env.example +3 -1
  62. package/CLAUDE.md +5 -11
  63. package/app.json +6 -3
  64. package/axios.d.ts +5 -0
  65. package/env.d.ts +3 -1
  66. package/package.json +33 -40
  67. package/patches/react-native-screens+4.25.2.patch +118 -0
  68. package/react-query.d.ts +13 -0
  69. package/src/app/(root)/(home)/_layout.tsx +2 -2
  70. package/src/app/(root)/(home)/index.tsx +2 -2
  71. package/src/app/(root)/(settings)/_layout.tsx +2 -2
  72. package/src/app/(root)/(settings)/index.tsx +2 -2
  73. package/src/app/(root)/_layout.tsx +35 -32
  74. package/src/pages/home/home-main.tsx +3 -7
  75. package/src/pages/settings/settings-main.tsx +3 -7
  76. package/src/shared/assets/fonts/index.ts +3 -3
  77. package/src/shared/assets/fonts/sf-pro-text-bold.ttf +0 -0
  78. package/src/shared/assets/fonts/sf-pro-text-regular.ttf +0 -0
  79. package/src/shared/assets/fonts/sf-pro-text-semibold.ttf +0 -0
  80. package/src/shared/assets/icons/back.tsx +21 -0
  81. package/src/shared/assets/icons/close.tsx +21 -0
  82. package/src/shared/assets/icons/common.ts +8 -0
  83. package/src/shared/assets/icons/default.tsx +44 -0
  84. package/src/shared/assets/icons/index.ts +13 -1
  85. package/src/shared/assets/icons/refresh.tsx +23 -0
  86. package/src/shared/assets/icons/share.tsx +38 -0
  87. package/src/shared/assets/icons/type.ts +7 -0
  88. package/src/shared/assets/translations/index.ts +4 -4
  89. package/src/shared/constants/config.ts +13 -0
  90. package/src/shared/constants/environment.ts +16 -4
  91. package/src/shared/constants/index.ts +1 -0
  92. package/src/shared/hooks/index.ts +1 -4
  93. package/src/shared/hooks/use-compose.ts +23 -0
  94. package/src/shared/lib/api/api.client.ts +157 -0
  95. package/src/shared/lib/api/api.provider.tsx +14 -36
  96. package/src/shared/lib/api/api.ts +1 -5
  97. package/src/shared/lib/api/api.types.ts +6 -3
  98. package/src/shared/lib/api/hooks/index.ts +1 -0
  99. package/src/shared/lib/api/index.ts +2 -3
  100. package/src/shared/lib/api/models/api-instance/api-instance.client.ts +50 -0
  101. package/src/shared/lib/api/models/api-instance/api-instance.ts +30 -82
  102. package/src/shared/lib/api/models/api-instance/api-instance.types.ts +16 -18
  103. package/src/shared/lib/api/models/api-instance/api-instance.utils.ts +13 -20
  104. package/src/shared/lib/api/models/api-instance/index.ts +3 -0
  105. package/src/shared/lib/api/queries/index.ts +1 -0
  106. package/src/shared/lib/api/repositories/index.ts +1 -1
  107. package/src/shared/lib/localization/constants/index.ts +16 -5
  108. package/src/shared/lib/localization/index.ts +5 -1
  109. package/src/shared/lib/navigation/hooks/index.ts +8 -3
  110. package/src/shared/{hooks/use-breakpoints.ts → lib/navigation/hooks/use-navigation-breakpoints.ts} +2 -5
  111. package/src/shared/lib/navigation/hooks/use-navigation-dimensions.ts +12 -0
  112. package/src/shared/lib/navigation/hooks/use-navigation-options.tsx +175 -0
  113. package/src/shared/lib/navigation/hooks/{use-navigation.ts → use-navigation-route.ts} +2 -2
  114. package/src/shared/lib/navigation/hooks/use-navigation-safe-area.ts +61 -0
  115. package/src/shared/lib/navigation/hooks/use-navigation-tabs-native.ts +10 -0
  116. package/src/shared/lib/navigation/hooks/use-navigation-tabs.tsx +10 -0
  117. package/src/shared/lib/navigation/hooks/use-navigation-transition.ts +27 -0
  118. package/src/shared/lib/navigation/index.ts +11 -4
  119. package/src/shared/lib/navigation/navigation-tabs-native.context.tsx +5 -0
  120. package/src/shared/lib/navigation/navigation-tabs-native.provider.tsx +17 -0
  121. package/src/shared/lib/navigation/navigation-tabs.context.tsx +10 -0
  122. package/src/shared/lib/navigation/navigation-tabs.provider.tsx +22 -0
  123. package/src/shared/lib/navigation/navigation.provider.tsx +6 -1
  124. package/src/shared/lib/storage/index.ts +1 -1
  125. package/src/shared/lib/storage/storage.provider.tsx +4 -0
  126. package/src/shared/lib/storage/storage.ts +29 -25
  127. package/src/shared/lib/theme/constants/animations.ts +26 -0
  128. package/src/shared/lib/theme/constants/colors.ts +336 -43
  129. package/src/shared/lib/theme/constants/delays.ts +1 -1
  130. package/src/shared/lib/theme/constants/durations.ts +1 -1
  131. package/src/shared/lib/theme/constants/fonts.ts +2 -1
  132. package/src/shared/lib/theme/constants/index.ts +42 -20
  133. package/src/shared/lib/theme/constants/shadows.ts +7 -25
  134. package/src/shared/lib/theme/constants/sizes.ts +11 -2
  135. package/src/shared/lib/theme/constants/transparencies.ts +1 -1
  136. package/src/shared/lib/theme/hooks/use-theme-setup.ts +3 -1
  137. package/src/shared/lib/theme/index.ts +10 -1
  138. package/src/shared/lib/theme/theme.provider.tsx +45 -19
  139. package/src/shared/lib/theme/theme.scope.tsx +43 -0
  140. package/src/shared/lib/theme/theme.types.ts +15 -3
  141. package/src/shared/lib/theme/utils/color.ts +81 -0
  142. package/src/shared/lib/theme/utils/index.ts +2 -0
  143. package/src/shared/lib/theme/utils/snapshot.ts +17 -0
  144. package/src/shared/lib/theme/utils/storage.ts +5 -2
  145. package/src/shared/services/browser/browser.ts +66 -0
  146. package/src/shared/services/browser/index.ts +1 -0
  147. package/src/shared/services/cookies/cookies.ts +73 -0
  148. package/src/shared/services/cookies/index.ts +1 -0
  149. package/src/shared/services/index.ts +5 -0
  150. package/src/shared/services/linking/index.ts +1 -0
  151. package/src/shared/services/linking/linking.ts +22 -0
  152. package/src/shared/services/share/index.ts +1 -0
  153. package/src/shared/services/share/share.ts +28 -0
  154. package/src/shared/services/universal-links/index.ts +104 -0
  155. package/src/shared/services/universal-links/universal-links.ts +25 -0
  156. package/src/shared/services/universal-links/universal-links.types.ts +5 -0
  157. package/src/shared/ui/atoms/blur/blur.tsx +9 -4
  158. package/src/shared/ui/atoms/blur/blur.types.ts +1 -1
  159. package/src/shared/ui/atoms/blur-progress/blur-progress.styles.ts +16 -0
  160. package/src/shared/ui/atoms/blur-progress/blur-progress.tsx +30 -0
  161. package/src/shared/ui/atoms/blur-progress/blur-progress.types.tsx +5 -0
  162. package/src/shared/ui/atoms/blur-progress/index.ts +1 -0
  163. package/src/shared/ui/atoms/glass/glass.tsx +13 -5
  164. package/src/shared/ui/atoms/glass/glass.types.ts +1 -1
  165. package/src/shared/ui/atoms/icon/icon.styles.ts +82 -0
  166. package/src/shared/ui/atoms/icon/icon.tsx +21 -0
  167. package/src/shared/ui/atoms/icon/icon.types.ts +23 -0
  168. package/src/shared/ui/atoms/icon/index.ts +2 -0
  169. package/src/shared/ui/atoms/index.ts +7 -0
  170. package/src/shared/ui/atoms/skeleton/index.ts +2 -0
  171. package/src/shared/ui/atoms/skeleton/skeleton.styles.ts +69 -0
  172. package/src/shared/ui/atoms/skeleton/skeleton.tsx +26 -0
  173. package/src/shared/ui/atoms/skeleton/skeleton.types.ts +9 -0
  174. package/src/shared/ui/atoms/spinner/index.ts +2 -0
  175. package/src/shared/ui/atoms/spinner/spinner.styles.ts +102 -0
  176. package/src/shared/ui/atoms/spinner/spinner.tsx +48 -0
  177. package/src/shared/ui/atoms/spinner/spinner.types.ts +25 -0
  178. package/src/shared/ui/atoms/text/index.ts +2 -0
  179. package/src/shared/ui/atoms/text/text.styles.ts +186 -0
  180. package/src/shared/ui/atoms/text/text.tsx +37 -0
  181. package/src/shared/ui/atoms/text/text.types.ts +35 -0
  182. package/src/shared/ui/atoms/touch/components/haptics/haptics.styles.ts +21 -0
  183. package/src/shared/ui/atoms/touch/components/haptics/haptics.tsx +28 -0
  184. package/src/shared/ui/atoms/touch/components/haptics/haptics.types.ts +17 -0
  185. package/src/shared/ui/atoms/touch/components/haptics/index.ts +2 -0
  186. package/src/shared/ui/atoms/touch/components/index.ts +3 -0
  187. package/src/shared/ui/atoms/touch/components/ripple/index.ts +2 -0
  188. package/src/shared/ui/atoms/touch/components/ripple/ripple.styles.ts +78 -0
  189. package/src/shared/ui/atoms/touch/components/ripple/ripple.tsx +29 -0
  190. package/src/shared/ui/atoms/touch/components/ripple/ripple.types.ts +17 -0
  191. package/src/shared/ui/atoms/touch/components/spring/index.ts +2 -0
  192. package/src/shared/ui/atoms/touch/components/spring/spring.styles.ts +31 -0
  193. package/src/shared/ui/atoms/touch/components/spring/spring.tsx +28 -0
  194. package/src/shared/ui/atoms/touch/components/spring/spring.types.ts +17 -0
  195. package/src/shared/ui/atoms/touch/index.ts +2 -0
  196. package/src/shared/ui/atoms/touch/touch.styles.ts +14 -0
  197. package/src/shared/ui/atoms/touch/touch.tsx +137 -0
  198. package/src/shared/ui/atoms/touch/touch.types.ts +27 -0
  199. package/src/shared/ui/atoms/visibility/index.ts +2 -0
  200. package/src/shared/ui/atoms/visibility/visibility.styles.ts +85 -0
  201. package/src/shared/ui/atoms/visibility/visibility.tsx +61 -0
  202. package/src/shared/ui/atoms/visibility/visibility.types.ts +34 -0
  203. package/src/shared/ui/molecules/keyboard-area/keyboard-area.styles.ts +11 -9
  204. package/src/shared/ui/molecules/keyboard-area/keyboard-area.tsx +3 -6
  205. package/src/shared/ui/molecules/refresh-control/refresh-control.tsx +3 -5
  206. package/src/shared/ui/molecules/refresh-control/refresh-control.types.ts +1 -1
  207. package/src/shared/ui/molecules/slider/components/indicator/components/dot/dot.styles.ts +41 -0
  208. package/src/shared/ui/molecules/slider/components/indicator/components/dot/dot.tsx +12 -0
  209. package/src/shared/ui/molecules/slider/components/indicator/components/dot/dot.types.ts +13 -0
  210. package/src/shared/ui/molecules/slider/components/indicator/components/dot/index.ts +2 -0
  211. package/src/shared/ui/molecules/slider/components/indicator/index.ts +2 -0
  212. package/src/shared/ui/molecules/slider/components/indicator/indicator.styles.ts +20 -0
  213. package/src/shared/ui/molecules/slider/components/indicator/indicator.tsx +22 -0
  214. package/src/shared/ui/molecules/slider/components/indicator/indicator.types.ts +10 -0
  215. package/src/shared/ui/molecules/slider/index.ts +3 -0
  216. package/src/shared/ui/molecules/slider/slider.styles.ts +68 -0
  217. package/src/shared/ui/molecules/slider/slider.tsx +64 -0
  218. package/src/shared/ui/molecules/slider/slider.types.ts +28 -0
  219. package/src/shared/ui/organisms/bar/bar.styles.ts +100 -0
  220. package/src/shared/ui/organisms/bar/bar.tsx +89 -0
  221. package/src/shared/ui/organisms/bar/bar.types.ts +49 -0
  222. package/src/shared/ui/organisms/bar/index.ts +2 -0
  223. package/src/shared/ui/organisms/bar-tab/bar-tab.tsx +9 -0
  224. package/src/shared/ui/organisms/bar-tab/bar-tab.types.ts +5 -0
  225. package/src/shared/ui/organisms/bar-tab/components/index.ts +1 -0
  226. package/src/shared/ui/organisms/bar-tab/components/tab/index.ts +2 -0
  227. package/src/shared/ui/organisms/bar-tab/components/tab/tab.styles.ts +21 -0
  228. package/src/shared/ui/organisms/bar-tab/components/tab/tab.tsx +18 -0
  229. package/src/shared/ui/organisms/bar-tab/components/tab/tab.types.ts +5 -0
  230. package/src/shared/ui/organisms/bar-tab/index.ts +2 -0
  231. package/src/shared/ui/organisms/bar-tab-navigation/bar-tab-navigation.styles.ts +22 -0
  232. package/src/shared/ui/organisms/bar-tab-navigation/bar-tab-navigation.tsx +66 -0
  233. package/src/shared/ui/organisms/bar-tab-navigation/bar-tab-navigation.types.ts +5 -0
  234. package/src/shared/ui/organisms/bar-tab-navigation/components/index.ts +1 -0
  235. package/src/shared/ui/organisms/bar-tab-navigation/components/tab-navigation/index.ts +2 -0
  236. package/src/shared/ui/organisms/bar-tab-navigation/components/tab-navigation/tab-navigation.styles.ts +56 -0
  237. package/src/shared/ui/organisms/bar-tab-navigation/components/tab-navigation/tab-navigation.tsx +30 -0
  238. package/src/shared/ui/organisms/bar-tab-navigation/components/tab-navigation/tab-navigation.types.ts +7 -0
  239. package/src/shared/ui/organisms/bar-tab-navigation/index.ts +2 -0
  240. package/src/shared/ui/organisms/index.ts +4 -1
  241. package/src/shared/ui/organisms/slider/components/indicator/components/dot/dot.styles.ts +41 -0
  242. package/src/shared/ui/organisms/slider/components/indicator/components/dot/dot.tsx +12 -0
  243. package/src/shared/ui/organisms/slider/components/indicator/components/dot/dot.types.ts +13 -0
  244. package/src/shared/ui/organisms/slider/components/indicator/components/dot/index.ts +2 -0
  245. package/src/shared/ui/organisms/slider/components/indicator/index.ts +2 -0
  246. package/src/shared/ui/organisms/slider/components/indicator/indicator.styles.ts +20 -0
  247. package/src/shared/ui/organisms/slider/components/indicator/indicator.tsx +22 -0
  248. package/src/shared/ui/organisms/slider/components/indicator/indicator.types.ts +10 -0
  249. package/src/shared/ui/organisms/slider/index.ts +3 -0
  250. package/src/shared/ui/organisms/slider/slider.styles.ts +68 -0
  251. package/src/shared/ui/organisms/slider/slider.tsx +64 -0
  252. package/src/shared/ui/organisms/slider/slider.types.ts +28 -0
  253. package/src/shared/ui/templates/index.ts +3 -0
  254. package/src/shared/ui/templates/modal-container/index.ts +13 -0
  255. package/src/shared/ui/templates/modal-container/modal-container-list.tsx +45 -0
  256. package/src/shared/ui/templates/modal-container/modal-container-scroll.tsx +66 -0
  257. package/src/shared/ui/templates/modal-container/modal-container-section-list.tsx +46 -0
  258. package/src/shared/ui/templates/modal-container/modal-container.hooks.tsx +172 -0
  259. package/src/shared/ui/templates/modal-container/modal-container.styles.ts +121 -0
  260. package/src/shared/ui/templates/modal-container/modal-container.types.ts +69 -0
  261. package/src/shared/ui/templates/screen-container/index.ts +1 -1
  262. package/src/shared/ui/templates/screen-container/screen-container-list.tsx +28 -10
  263. package/src/shared/ui/templates/screen-container/screen-container-scroll.tsx +28 -10
  264. package/src/shared/ui/templates/screen-container/screen-container-section-list.tsx +29 -11
  265. package/src/shared/ui/templates/screen-container/screen-container.hooks.tsx +79 -8
  266. package/src/shared/ui/templates/screen-container/screen-container.styles.ts +105 -15
  267. package/src/shared/ui/templates/screen-container/screen-container.types.tsx +34 -7
  268. package/src/shared/ui/templates/screen-header/components/index.ts +6 -0
  269. package/src/shared/ui/templates/screen-header/components/screen-header-description/index.ts +2 -0
  270. package/src/shared/ui/templates/screen-header/components/screen-header-description/screen-header-description.tsx +15 -0
  271. package/src/shared/ui/templates/screen-header/components/screen-header-description/screen-header-description.types.ts +11 -0
  272. package/src/shared/ui/templates/screen-header/components/screen-header-icon/index.ts +2 -0
  273. package/src/shared/ui/templates/screen-header/components/screen-header-icon/screen-header-icon.styles.ts +44 -0
  274. package/src/shared/ui/templates/screen-header/components/screen-header-icon/screen-header-icon.tsx +65 -0
  275. package/src/shared/ui/templates/screen-header/components/screen-header-icon/screen-header-icon.types.ts +14 -0
  276. package/src/shared/ui/templates/screen-header/components/screen-header-icon-back/index.ts +2 -0
  277. package/src/shared/ui/templates/screen-header/components/screen-header-icon-back/screen-header-icon-back.tsx +17 -0
  278. package/src/shared/ui/templates/screen-header/components/screen-header-icon-back/screen-header-icon-back.types.ts +6 -0
  279. package/src/shared/ui/templates/screen-header/components/screen-header-icon-close/index.ts +2 -0
  280. package/src/shared/ui/templates/screen-header/components/screen-header-icon-close/screen-header-icon-close.tsx +27 -0
  281. package/src/shared/ui/templates/screen-header/components/screen-header-icon-close/screen-header-icon-close.types.ts +10 -0
  282. package/src/shared/ui/templates/screen-header/components/screen-header-info/index.ts +2 -0
  283. package/src/shared/ui/templates/screen-header/components/screen-header-info/screen-header-info.tsx +15 -0
  284. package/src/shared/ui/templates/screen-header/components/screen-header-info/screen-header-info.types.ts +10 -0
  285. package/src/shared/ui/templates/screen-header/components/screen-header-title/index.ts +2 -0
  286. package/src/shared/ui/templates/screen-header/components/screen-header-title/screen-header-title.tsx +19 -0
  287. package/src/shared/ui/templates/screen-header/components/screen-header-title/screen-header-title.types.ts +13 -0
  288. package/src/shared/ui/templates/screen-header/index.ts +3 -0
  289. package/src/shared/ui/templates/screen-header/screen-header.styles.ts +49 -0
  290. package/src/shared/ui/templates/screen-header/screen-header.tsx +26 -0
  291. package/src/shared/ui/templates/screen-header/screen-header.types.ts +15 -0
  292. package/src/shared/ui/templates/web-container/index.ts +2 -0
  293. package/src/shared/ui/templates/web-container/web-container.styles.ts +34 -0
  294. package/src/shared/ui/templates/web-container/web-container.tsx +172 -0
  295. package/src/shared/ui/templates/web-container/web-container.types.ts +21 -0
  296. package/.claude/rules/app-routing.mdc +0 -47
  297. package/.claude/rules/exports-at-end.mdc +0 -55
  298. package/.claude/rules/kebab-case-naming.mdc +0 -29
  299. package/.claude/rules/mediacubeco-packages.mdc +0 -56
  300. package/.claude/skills/generate-api-model/SKILL.md +0 -230
  301. package/.claude/skills/generate-api-model/references/resources/common.ts +0 -8
  302. package/.claude/skills/generate-api-model-mock/SKILL.md +0 -56
  303. package/.claude/skills/git-commit/SKILL.md +0 -139
  304. package/.gitignore +0 -34
  305. package/src/shared/assets/icons/like.tsx +0 -17
  306. package/src/shared/hooks/use-animated.ts +0 -200
  307. package/src/shared/hooks/use-mixins.ts +0 -36
  308. package/src/shared/hooks/use-safe-area.ts +0 -75
  309. package/src/shared/lib/api/repositories/auth/auth.ts +0 -23
  310. package/src/shared/lib/api/repositories/auth/auth.types.ts +0 -22
  311. package/src/shared/lib/api/repositories/auth/index.ts +0 -1
  312. package/src/shared/lib/navigation/hooks/use-navigation-native-tabs.ts +0 -10
  313. package/src/shared/lib/navigation/hooks/use-navigation-options.ts +0 -123
  314. package/src/shared/lib/navigation/navigation-native-tabs.context.tsx +0 -5
  315. package/src/shared/lib/navigation/navigation-native-tabs.provider.tsx +0 -12
  316. package/src/shared/lib/theme/constants/borders.ts +0 -8
  317. package/src/shared/lib/theme/constants/weights.ts +0 -13
  318. /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/index.ts +0 -0
  319. /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/post/index.ts +0 -0
  320. /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/post/post-comment.ts +0 -0
  321. /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/post/post.ts +0 -0
  322. /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/user/index.ts +0 -0
  323. /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/user/user-settings.ts +0 -0
  324. /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/user/user-verification.ts +0 -0
  325. /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/user/user.ts +0 -0
  326. /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/index.ts +0 -0
  327. /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/post/index.ts +0 -0
  328. /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/post/post-comment.ts +0 -0
  329. /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/post/post.ts +0 -0
  330. /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/user/index.ts +0 -0
  331. /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/user/user-settings.ts +0 -0
  332. /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/user/user-verification.ts +0 -0
  333. /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/user/user.ts +0 -0
@@ -0,0 +1,12 @@
1
+ import Animated from 'react-native-reanimated'
2
+ import { useDotStyles } from './dot.styles'
3
+ import { type DotProps } from './dot.types'
4
+
5
+ /** Одна точка индикатора-«пилюли»: расширяется и подсвечивается по мере приближения своего слайда. */
6
+ const Dot = ({ index, scrollX, itemWidth }: DotProps) => {
7
+ const { styles, animatedStyle } = useDotStyles({ index, scrollX, itemWidth })
8
+
9
+ return <Animated.View style={[styles.dot, animatedStyle]} />
10
+ }
11
+
12
+ export default Dot
@@ -0,0 +1,13 @@
1
+ import { type SharedValue } from 'react-native-reanimated'
2
+
3
+ /** Пропсы одной точки индикатора: её позиция в ряду и общий прогресс скролла слайдера. */
4
+ type DotProps = {
5
+ index: number
6
+ scrollX: SharedValue<number>
7
+ itemWidth: SharedValue<number>
8
+ }
9
+
10
+ /** Контекст стиль-хука точки — совпадает с её пропсами. */
11
+ type DotStylesContext = DotProps
12
+
13
+ export type { DotProps, DotStylesContext }
@@ -0,0 +1,2 @@
1
+ export { default as Dot } from './dot'
2
+ export type { DotProps } from './dot.types'
@@ -0,0 +1,2 @@
1
+ export { default as Indicator } from './indicator'
2
+ export type { IndicatorProps } from './indicator.types'
@@ -0,0 +1,20 @@
1
+ import { StyleSheet } from 'react-native'
2
+ import { useThemeMode } from '../../../../../lib'
3
+
4
+ /** Визуальная модель индикатора: горизонтальный ряд точек по центру с равными промежутками. */
5
+ const useIndicatorStyles = () => {
6
+ const { SIZES } = useThemeMode()
7
+
8
+ const styles = StyleSheet.create({
9
+ wrapper: {
10
+ flexDirection: 'row',
11
+ alignItems: 'center',
12
+ justifyContent: 'center',
13
+ gap: SIZES['M'],
14
+ },
15
+ })
16
+
17
+ return { styles }
18
+ }
19
+
20
+ export { useIndicatorStyles }
@@ -0,0 +1,22 @@
1
+ import { useMemo } from 'react'
2
+ import { View } from 'react-native'
3
+ import { Dot } from './components/dot'
4
+ import { useIndicatorStyles } from './indicator.styles'
5
+ import { type IndicatorProps } from './indicator.types'
6
+
7
+ /** Индикатор текущего слайда: ряд точек, где активная плавно расширяется в «пилюлю» вслед за скроллом. */
8
+ const Indicator = ({ itemCount, scrollX, itemWidth }: IndicatorProps) => {
9
+ const { styles } = useIndicatorStyles()
10
+
11
+ const dotList = useMemo(() => Array.from({ length: itemCount }, (_, index) => index), [itemCount])
12
+
13
+ return (
14
+ <View style={styles.wrapper}>
15
+ {dotList.map((index) => (
16
+ <Dot key={index} index={index} scrollX={scrollX} itemWidth={itemWidth} />
17
+ ))}
18
+ </View>
19
+ )
20
+ }
21
+
22
+ export default Indicator
@@ -0,0 +1,10 @@
1
+ import { type SharedValue } from 'react-native-reanimated'
2
+
3
+ /** Пропсы индикатора слайдов: число точек и общий прогресс скролла слайдера с шириной слайда. */
4
+ type IndicatorProps = {
5
+ itemCount: number
6
+ scrollX: SharedValue<number>
7
+ itemWidth: SharedValue<number>
8
+ }
9
+
10
+ export type { IndicatorProps }
@@ -0,0 +1,3 @@
1
+ export { Indicator, type IndicatorProps } from './components/indicator'
2
+ export { default as Slider } from './slider'
3
+ export type { SliderRef, SliderProps } from './slider.types'
@@ -0,0 +1,68 @@
1
+ import { DEFAULT_LAYOUT, useLayout } from '@mediacubeco/base-react-native/hooks'
2
+ import { useCallback, useEffect, useMemo, useState } from 'react'
3
+ import { StyleSheet } from 'react-native'
4
+ import { useNavigationDimensions } from '../../../lib'
5
+ import { type SliderStylesContext } from './slider.types'
6
+
7
+ const useSliderStyles = ({
8
+ itemCount,
9
+ itemWidth,
10
+ itemGap,
11
+ sliderItemWidth,
12
+ sliderItemGap,
13
+ }: SliderStylesContext) => {
14
+ const [wrapperLayout, setLayout] = useState(DEFAULT_LAYOUT)
15
+ const { WIDTH } = useNavigationDimensions()
16
+ const handleWrapperLayout = useLayout({ setLayout })
17
+
18
+ const SLIDER_ITEM_WIDTH = itemWidth ?? wrapperLayout.width
19
+ const SLIDER_ITEM_GAP = itemGap ?? (WIDTH - SLIDER_ITEM_WIDTH) / 2
20
+
21
+ const styles = StyleSheet.create({
22
+ wrapper: {},
23
+ content: {
24
+ overflow: 'visible',
25
+ },
26
+ container: {
27
+ gap: SLIDER_ITEM_GAP,
28
+ },
29
+ item: {
30
+ width: SLIDER_ITEM_WIDTH,
31
+ },
32
+ })
33
+
34
+ const snapToOffsets = useMemo(() => {
35
+ return Array.from(
36
+ { length: itemCount },
37
+ (_, index) => index * (SLIDER_ITEM_WIDTH + SLIDER_ITEM_GAP)
38
+ )
39
+ }, [itemCount, SLIDER_ITEM_WIDTH, SLIDER_ITEM_GAP])
40
+
41
+ const getItemLayout = useCallback(
42
+ (_: unknown, index: number) => ({
43
+ length: SLIDER_ITEM_WIDTH,
44
+ offset: (SLIDER_ITEM_WIDTH + SLIDER_ITEM_GAP) * index,
45
+ index,
46
+ }),
47
+ [SLIDER_ITEM_WIDTH, SLIDER_ITEM_GAP]
48
+ )
49
+
50
+ useEffect(() => {
51
+ if (sliderItemWidth) {
52
+ sliderItemWidth.set(SLIDER_ITEM_WIDTH)
53
+ }
54
+
55
+ if (sliderItemGap) {
56
+ sliderItemGap.set(SLIDER_ITEM_GAP)
57
+ }
58
+ }, [sliderItemWidth, sliderItemGap, SLIDER_ITEM_WIDTH, SLIDER_ITEM_GAP])
59
+
60
+ return {
61
+ styles,
62
+ snapToOffsets,
63
+ getItemLayout,
64
+ handleWrapperLayout,
65
+ }
66
+ }
67
+
68
+ export { useSliderStyles }
@@ -0,0 +1,64 @@
1
+ import { runCallback } from '@mediacubeco/base/utils'
2
+ import { useCallback } from 'react'
3
+ import { type ListRenderItem, View } from 'react-native'
4
+ import Animated from 'react-native-reanimated'
5
+ import { useSliderStyles } from './slider.styles'
6
+ import { type SliderProps } from './slider.types'
7
+
8
+ const Slider = <Item,>({
9
+ ref,
10
+ style,
11
+ data = [],
12
+ itemWidth,
13
+ itemGap,
14
+ sliderItemWidth,
15
+ sliderItemGap,
16
+ onScroll,
17
+ onScrollBeginDrag,
18
+ onMomentumScrollEnd,
19
+ renderItem: inputRenderItem,
20
+ }: SliderProps<Item>) => {
21
+ const itemCount = data.length
22
+
23
+ const { styles, snapToOffsets, getItemLayout, handleWrapperLayout } = useSliderStyles({
24
+ itemCount,
25
+ itemWidth,
26
+ itemGap,
27
+ sliderItemWidth,
28
+ sliderItemGap,
29
+ })
30
+
31
+ const renderItem: ListRenderItem<Item> = useCallback(
32
+ ({ item, index, separators }) => {
33
+ return (
34
+ <View style={styles.item}>{runCallback(inputRenderItem, { item, index, separators })}</View>
35
+ )
36
+ },
37
+ [styles.item, inputRenderItem]
38
+ )
39
+
40
+ return (
41
+ <View style={[styles.wrapper, style]} onLayout={handleWrapperLayout}>
42
+ <Animated.FlatList
43
+ ref={ref}
44
+ data={data}
45
+ style={styles.content}
46
+ contentContainerStyle={styles.container}
47
+ scrollEventThrottle={16}
48
+ decelerationRate={'fast'}
49
+ snapToAlignment={'center'}
50
+ snapToOffsets={snapToOffsets}
51
+ horizontal={true}
52
+ showsVerticalScrollIndicator={false}
53
+ showsHorizontalScrollIndicator={false}
54
+ onScroll={onScroll}
55
+ onScrollBeginDrag={onScrollBeginDrag}
56
+ onMomentumScrollEnd={onMomentumScrollEnd}
57
+ getItemLayout={getItemLayout}
58
+ renderItem={renderItem}
59
+ />
60
+ </View>
61
+ )
62
+ }
63
+
64
+ export default Slider
@@ -0,0 +1,28 @@
1
+ import { type FlatListProps, type ListRenderItem, type ViewProps } from 'react-native'
2
+ import type Animated from 'react-native-reanimated'
3
+ import { type AnimatedProps, type AnimatedRef, type SharedValue } from 'react-native-reanimated'
4
+
5
+ type SliderRef = Animated.FlatList
6
+
7
+ type SliderProps<Item = unknown> = {
8
+ ref?: AnimatedRef<SliderRef>
9
+ style?: ViewProps['style']
10
+ data: Item[]
11
+ itemWidth?: number
12
+ itemGap?: number
13
+ sliderItemWidth?: SharedValue<number>
14
+ sliderItemGap?: SharedValue<number>
15
+ onScroll?: AnimatedProps<FlatListProps<Item>>['onScroll']
16
+ onScrollBeginDrag?: FlatListProps<Item>['onScrollBeginDrag']
17
+ onMomentumScrollEnd?: FlatListProps<Item>['onMomentumScrollEnd']
18
+ renderItem: ListRenderItem<Item>
19
+ }
20
+
21
+ type SliderStylesContext = Pick<
22
+ SliderProps,
23
+ 'itemWidth' | 'itemGap' | 'sliderItemWidth' | 'sliderItemGap'
24
+ > & {
25
+ itemCount: number
26
+ }
27
+
28
+ export type { SliderRef, SliderProps, SliderStylesContext }
@@ -1 +1,4 @@
1
+ export * from './modal-container'
1
2
  export * from './screen-container'
3
+ export * from './screen-header'
4
+ export * from './web-container'
@@ -0,0 +1,13 @@
1
+ export { default as ModalContainerList } from './modal-container-list'
2
+ export { default as ModalContainerScroll } from './modal-container-scroll'
3
+ export { default as ModalContainerSectionList } from './modal-container-section-list'
4
+ export type {
5
+ ModalContainerScrollProps,
6
+ ModalContainerListProps,
7
+ ModalContainerSectionListProps,
8
+ ModalContainerListRenderItemInfo,
9
+ ModalContainerSectionListRenderItemInfo,
10
+ ModalContainerSectionListRenderHeaderInfo,
11
+ } from './modal-container.types'
12
+
13
+ // TODO: extends of screen-container
@@ -0,0 +1,45 @@
1
+ import { FlatList, View } from 'react-native'
2
+ import { useModalContainerProps } from './modal-container.hooks'
3
+ import { type ModalContainerListProps } from './modal-container.types'
4
+
5
+ /**
6
+ * Контейнер модалки поверх FlatList: список облегает контент и скроллится при переполнении.
7
+ * Высота списка задаётся явно (`bodyHeight` = `min(контент, MAX)`), поэтому короткий список
8
+ * облегается, а длинный упирается в максимум и виртуализируется. Шапка и футер — абсолютные
9
+ * оверлеи с блюром поверх списка, поэтому элементы плавно уходят под них.
10
+ */
11
+ const ModalContainerList = <Item,>(props: ModalContainerListProps<Item>) => {
12
+ const {
13
+ ref,
14
+ header,
15
+ footer,
16
+ scrollGuard,
17
+ isOverflow: _isOverflow,
18
+ bodyHeight,
19
+ bottomOffset: _bottomOffset,
20
+ handleContentLayout: _handleContentLayout,
21
+ handleContentSizeChange,
22
+ wrapperStyle,
23
+ bodyStyle: _bodyStyle,
24
+ outerStyle,
25
+ innerStyle,
26
+ ...flatListProps
27
+ } = useModalContainerProps<ModalContainerListProps<Item>>(props)
28
+
29
+ return (
30
+ <View style={wrapperStyle}>
31
+ {scrollGuard}
32
+ <FlatList
33
+ ref={ref}
34
+ style={[{ height: bodyHeight }, outerStyle]}
35
+ contentContainerStyle={innerStyle}
36
+ onContentSizeChange={handleContentSizeChange}
37
+ {...flatListProps}
38
+ />
39
+ {header}
40
+ {footer}
41
+ </View>
42
+ )
43
+ }
44
+
45
+ export default ModalContainerList
@@ -0,0 +1,66 @@
1
+ import { type ReactNode, type Ref } from 'react'
2
+ import { type ScrollView, type ScrollViewProps, View } from 'react-native'
3
+ import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'
4
+ import { useModalContainerProps } from './modal-container.hooks'
5
+ import { type ModalContainerScrollProps } from './modal-container.types'
6
+
7
+ /**
8
+ * Тело скролл-варианта — `KeyboardAwareScrollView`: доскролливает сфокусированный инпут выше
9
+ * клавиатуры и приподнятого футера (`bottomOffset`) и добавляет ghost-отступ снизу, чтобы
10
+ * контент за клавиатурой оставался досягаемым. Окно formSheet не ресайзится ни на одной из
11
+ * платформ (лист сдвигает система), поэтому обе живут на клавиатурных событиях
12
+ * keyboard-controller.
13
+ */
14
+ const ScrollBody = KeyboardAwareScrollView as (
15
+ props: ScrollViewProps & { ref?: Ref<ScrollView>; bottomOffset?: number }
16
+ ) => ReactNode
17
+
18
+ /**
19
+ * Контейнер модалки со скроллом: короткий контент облегается плоской `View`, длинный
20
+ * скроллится внутри `ScrollView`. Шапка и футер — абсолютные оверлеи с блюром поверх тела,
21
+ * поэтому контент плавно уходит под них.
22
+ */
23
+ const ModalContainerScroll = (props: ModalContainerScrollProps) => {
24
+ const {
25
+ ref,
26
+ header,
27
+ footer,
28
+ scrollGuard,
29
+ isOverflow,
30
+ bodyHeight: _bodyHeight,
31
+ bottomOffset,
32
+ handleContentLayout,
33
+ handleContentSizeChange: _handleContentSizeChange,
34
+ wrapperStyle,
35
+ bodyStyle,
36
+ outerStyle,
37
+ innerStyle,
38
+ children,
39
+ ...scrollViewProps
40
+ } = useModalContainerProps<ModalContainerScrollProps>(props)
41
+
42
+ return (
43
+ <View style={wrapperStyle}>
44
+ {scrollGuard}
45
+ {isOverflow ? (
46
+ <ScrollBody
47
+ ref={ref}
48
+ style={[bodyStyle, outerStyle]}
49
+ contentContainerStyle={innerStyle}
50
+ bottomOffset={bottomOffset}
51
+ {...scrollViewProps}
52
+ >
53
+ {children}
54
+ </ScrollBody>
55
+ ) : (
56
+ <View style={innerStyle} collapsable={false} onLayout={handleContentLayout}>
57
+ {children}
58
+ </View>
59
+ )}
60
+ {header}
61
+ {footer}
62
+ </View>
63
+ )
64
+ }
65
+
66
+ export default ModalContainerScroll
@@ -0,0 +1,46 @@
1
+ import { SectionList, View } from 'react-native'
2
+ import { useModalContainerProps } from './modal-container.hooks'
3
+ import { type ModalContainerSectionListProps } from './modal-container.types'
4
+
5
+ /**
6
+ * Контейнер модалки поверх SectionList: секции облегают контент и скроллятся при переполнении.
7
+ * Высота списка задаётся явно (`bodyHeight` = `min(контент, MAX)`), поэтому короткие секции
8
+ * облегаются, а длинные упираются в максимум и виртуализируются. Шапка и футер — абсолютные
9
+ * оверлеи с блюром поверх списка, поэтому элементы плавно уходят под них.
10
+ */
11
+ const ModalContainerSectionList = <Item,>(props: ModalContainerSectionListProps<Item>) => {
12
+ const {
13
+ ref,
14
+ header,
15
+ footer,
16
+ scrollGuard,
17
+ isOverflow: _isOverflow,
18
+ bodyHeight,
19
+ bottomOffset: _bottomOffset,
20
+ handleContentLayout: _handleContentLayout,
21
+ handleContentSizeChange,
22
+ wrapperStyle,
23
+ bodyStyle: _bodyStyle,
24
+ outerStyle,
25
+ innerStyle,
26
+ ...sectionListProps
27
+ } = useModalContainerProps<ModalContainerSectionListProps<Item>>(props)
28
+
29
+ return (
30
+ <View style={wrapperStyle}>
31
+ {scrollGuard}
32
+ <SectionList
33
+ ref={ref}
34
+ style={[{ height: bodyHeight }, outerStyle]}
35
+ contentContainerStyle={innerStyle}
36
+ onContentSizeChange={handleContentSizeChange}
37
+ stickySectionHeadersEnabled={false}
38
+ {...sectionListProps}
39
+ />
40
+ {header}
41
+ {footer}
42
+ </View>
43
+ )
44
+ }
45
+
46
+ export default ModalContainerSectionList
@@ -0,0 +1,172 @@
1
+ import { useCallback, useMemo, useState } from 'react'
2
+ import { type LayoutChangeEvent, useWindowDimensions, View } from 'react-native'
3
+ import { useAnimatedKeyboard } from 'react-native-keyboard-controller'
4
+ import Animated, { useAnimatedStyle } from 'react-native-reanimated'
5
+ import { LinearGradient } from 'expo-linear-gradient'
6
+ import { BlurProgress } from '../../atoms'
7
+ import { useModalContainerStyles } from './modal-container.styles'
8
+ import {
9
+ type ModalContainerListProps,
10
+ type ModalContainerScrollProps,
11
+ type ModalContainerSectionListProps,
12
+ } from './modal-container.types'
13
+
14
+ /**
15
+ * Собирает общие пропсы для всех вариантов контейнера модалки. Шапка и футер — абсолютные
16
+ * оверлеи с прогрессивным блюром (`BlurProgress`): контент скроллится под ними и плавно
17
+ * растворяется у краёв. Тело отступает от краёв на замеренные высоты оверлеев
18
+ * (`paddingTop`/`paddingBottom`), поэтому крайние элементы не прячутся под шапкой/футером.
19
+ * Высоты шапки и футера снимаются через `onLayout`, высота контента — через `onLayout`
20
+ * плоского тела (скролл-вариант) или `onContentSizeChange` списка. Пока контент помещается в
21
+ * `MAX_SHEET_HEIGHT`, тело облегает его; когда выше — включается `isOverflow`: враппер
22
+ * фиксируется по высоте (`wrapperCapped`), тело начинает скроллиться. Нижний отступ — из
23
+ * сырого safe-area, без таб-бар-логики `useNavigationSafeArea`. Поверх блюра оверлеев лежит градиент
24
+ * цвета фона, растворяющий контент у кромок листа. Клавиатура: лист целиком сдвигает система
25
+ * (UIKit на iOS, транслейт react-native-screens на Android) — вверх до упора в
26
+ * `SHEET_KEYBOARD_TOP`, — а не поместившийся над клавиатурой остаток листа компенсирует
27
+ * `footerLiftStyle`, приподнимая футер внутри листа на величину перекрытия (детент при этом
28
+ * не меняется, иначе его анимация сбивает клавиатурный сдвиг листа и лист
29
+ * «переоткрывается»). `bottomOffset` — зазор для `KeyboardAwareScrollView` скролл-варианта:
30
+ * сфокусированный инпут доскролливается выше приподнятого футера. `nestedScrollEnabled`
31
+ * обязателен: на Android formSheet управляется `BottomSheetBehavior`, и без nested scrolling
32
+ * он забирает жест «палец вниз» себе — закрывает лист вместо скролла контента вверх.
33
+ */
34
+ const useModalContainerProps = <
35
+ Props extends
36
+ | ModalContainerScrollProps
37
+ | ModalContainerListProps<any>
38
+ | ModalContainerSectionListProps<any>,
39
+ >({
40
+ // omit
41
+ // @ts-ignore
42
+ style,
43
+ // @ts-ignore
44
+ contentContainerStyle,
45
+ // native
46
+ header: initHeader,
47
+ footer: initFooter,
48
+ wrapperStyle,
49
+ outerStyle,
50
+ innerStyle,
51
+ ...props
52
+ }: Props) => {
53
+ const {
54
+ styles,
55
+ headerGradientColorList,
56
+ footerGradientColorList,
57
+ MAX_SHEET_HEIGHT,
58
+ SHEET_KEYBOARD_TOP,
59
+ TOP,
60
+ BOTTOM,
61
+ } = useModalContainerStyles()
62
+
63
+ const withHeader = !!initHeader
64
+ const withFooter = !!initFooter
65
+
66
+ const [headerHeight, setHeaderHeight] = useState(0)
67
+ const [footerHeight, setFooterHeight] = useState(0)
68
+ const [contentHeight, setContentHeight] = useState(0)
69
+
70
+ const handleHeaderLayout = useCallback((event: LayoutChangeEvent) => {
71
+ setHeaderHeight(event.nativeEvent.layout.height)
72
+ }, [])
73
+
74
+ const handleFooterLayout = useCallback((event: LayoutChangeEvent) => {
75
+ setFooterHeight(event.nativeEvent.layout.height)
76
+ }, [])
77
+
78
+ const handleContentLayout = useCallback((event: LayoutChangeEvent) => {
79
+ setContentHeight(event.nativeEvent.layout.height)
80
+ }, [])
81
+
82
+ const handleContentSizeChange = useCallback((_width: number, height: number) => {
83
+ setContentHeight(height)
84
+ }, [])
85
+
86
+ const isOverflow = contentHeight > 0 && contentHeight > MAX_SHEET_HEIGHT
87
+ const bodyHeight =
88
+ contentHeight > 0 ? Math.min(contentHeight, MAX_SHEET_HEIGHT) : MAX_SHEET_HEIGHT
89
+
90
+ const { height: windowHeight } = useWindowDimensions()
91
+ const { height: keyboardHeight } = useAnimatedKeyboard()
92
+
93
+ const footerLiftStyle = useAnimatedStyle(() => {
94
+ const overlap = Math.max(
95
+ 0,
96
+ SHEET_KEYBOARD_TOP + bodyHeight + keyboardHeight.value - windowHeight
97
+ )
98
+
99
+ return { transform: [{ translateY: -overlap }] }
100
+ }, [SHEET_KEYBOARD_TOP, bodyHeight, windowHeight])
101
+
102
+ const contentPadding = {
103
+ paddingTop: withHeader ? headerHeight : TOP,
104
+ paddingBottom: withFooter ? footerHeight : BOTTOM,
105
+ }
106
+
107
+ /**
108
+ * Первый ребёнок враппера — обрывает поиск скролла в react-native-screens: для formSheet
109
+ * RNS идёт по цепочке первых сабвью и найденному скроллу насильно ставит frame размером с
110
+ * нативный экран листа (выше видимой области), из-за чего низ контента невозможно
111
+ * доскроллить. Нулевая вью первой в цепочке прячет скролл от этой «коррекции», и его
112
+ * размером управляет обычный layout.
113
+ */
114
+ const scrollGuard = <View style={styles.scrollGuard} collapsable={false} />
115
+
116
+ const header = useMemo(() => {
117
+ return (
118
+ initHeader && (
119
+ <View style={styles.header} collapsable={false} onLayout={handleHeaderLayout}>
120
+ <BlurProgress direction={'blurredTopClearBottom'} style={styles.blur} />
121
+ <LinearGradient colors={headerGradientColorList} style={styles.blur} />
122
+ {initHeader}
123
+ </View>
124
+ )
125
+ )
126
+ }, [styles.header, styles.blur, headerGradientColorList, initHeader, handleHeaderLayout])
127
+
128
+ const footer = useMemo(() => {
129
+ return (
130
+ initFooter && (
131
+ <Animated.View style={[styles.footerShell, footerLiftStyle]} collapsable={false}>
132
+ <View style={styles.footer} collapsable={false} onLayout={handleFooterLayout}>
133
+ <BlurProgress direction={'blurredBottomClearTop'} style={styles.blur} />
134
+ <LinearGradient colors={footerGradientColorList} style={styles.blur} />
135
+ {initFooter}
136
+ </View>
137
+ </Animated.View>
138
+ )
139
+ )
140
+ }, [
141
+ styles.footerShell,
142
+ styles.footer,
143
+ styles.blur,
144
+ footerGradientColorList,
145
+ footerLiftStyle,
146
+ initFooter,
147
+ handleFooterLayout,
148
+ ])
149
+
150
+ return {
151
+ ...props,
152
+ header,
153
+ footer,
154
+ scrollGuard,
155
+ isOverflow,
156
+ bodyHeight,
157
+ bottomOffset: footerHeight + BOTTOM,
158
+ handleContentLayout,
159
+ handleContentSizeChange,
160
+ wrapperStyle: [styles.wrapper, isOverflow && styles.wrapperCapped, wrapperStyle],
161
+ bodyStyle: styles.body,
162
+ outerStyle,
163
+ innerStyle: [styles.content, contentPadding, innerStyle],
164
+ showsVerticalScrollIndicator: false,
165
+ showsHorizontalScrollIndicator: false,
166
+ nestedScrollEnabled: true,
167
+ keyboardDismissMode: 'interactive' as const,
168
+ keyboardShouldPersistTaps: 'handled' as const,
169
+ }
170
+ }
171
+
172
+ export { useModalContainerProps }