create-du-app 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (421) hide show
  1. package/README.md +56 -36
  2. package/package.json +6 -5
  3. package/src/generate.js +246 -15
  4. package/src/index.js +8 -8
  5. package/src/prompts.js +1 -1
  6. package/src/registry.js +3 -1
  7. package/templates/mobile/expo/.env.example +5 -0
  8. package/templates/mobile/expo/.eslintrc.js +7 -0
  9. package/templates/mobile/expo/.prettierrc.js +7 -0
  10. package/templates/mobile/expo/.svgrrc.js +9 -0
  11. package/templates/mobile/expo/README.md +42 -7
  12. package/templates/mobile/expo/_gitignore +20 -0
  13. package/templates/mobile/expo/_package.json +62 -1
  14. package/templates/mobile/expo/app.json +18 -0
  15. package/templates/mobile/expo/babel.config.js +21 -0
  16. package/templates/mobile/expo/index.js +5 -0
  17. package/templates/mobile/expo/metro.config.js +31 -0
  18. package/templates/mobile/expo/src/app/App.tsx +24 -0
  19. package/templates/mobile/expo/src/app/app-provider.tsx +36 -0
  20. package/templates/mobile/expo/src/app/config/translation.ts +26 -0
  21. package/templates/mobile/expo/src/app/index.ts +1 -0
  22. package/templates/mobile/expo/src/app/navigation/app-route-type.ts +27 -0
  23. package/templates/mobile/expo/src/app/navigation/bottom-tabs.tsx +34 -0
  24. package/templates/mobile/expo/src/app/navigation/index.tsx +14 -0
  25. package/templates/mobile/expo/src/app/stores/auth.store.ts +33 -0
  26. package/templates/mobile/expo/src/app/stores/common.store.ts +19 -0
  27. package/templates/mobile/expo/src/app/stores/index.ts +3 -0
  28. package/templates/mobile/expo/src/app/stores/loading.store.ts +22 -0
  29. package/templates/mobile/expo/src/assets/Images/empty-list.png +0 -0
  30. package/templates/mobile/expo/src/assets/Images/index.ts +5 -0
  31. package/templates/mobile/expo/src/assets/Images/screen-bg-gradian.png +0 -0
  32. package/templates/mobile/expo/src/assets/i18n/en.json +12 -0
  33. package/templates/mobile/expo/src/assets/i18n/fr.json +12 -0
  34. package/templates/mobile/expo/src/assets/lotties/index.ts +3 -0
  35. package/templates/mobile/expo/src/assets/lotties/loading.json +1 -0
  36. package/templates/mobile/expo/src/assets/svgs/arrow-left.svg +3 -0
  37. package/templates/mobile/expo/src/assets/svgs/arrow-right.svg +3 -0
  38. package/templates/mobile/expo/src/assets/svgs/calendar.svg +12 -0
  39. package/templates/mobile/expo/src/assets/svgs/check.svg +3 -0
  40. package/templates/mobile/expo/src/assets/svgs/close.svg +3 -0
  41. package/templates/mobile/expo/src/assets/svgs/eye-hide.svg +3 -0
  42. package/templates/mobile/expo/src/assets/svgs/eye.svg +4 -0
  43. package/templates/mobile/expo/src/assets/svgs/index.ts +29 -0
  44. package/templates/mobile/expo/src/assets/svgs/minus.svg +3 -0
  45. package/templates/mobile/expo/src/assets/svgs/plus.svg +3 -0
  46. package/templates/mobile/expo/src/assets/svgs/search.svg +3 -0
  47. package/templates/mobile/expo/src/assets/svgs/toast-error.svg +5 -0
  48. package/templates/mobile/expo/src/assets/svgs/toast-success.svg +4 -0
  49. package/templates/mobile/expo/src/core/api/endpoints.ts +8 -0
  50. package/templates/mobile/expo/src/core/api/example.api.ts +53 -0
  51. package/templates/mobile/expo/src/core/api/index.ts +4 -0
  52. package/templates/mobile/expo/src/core/components/common/index.ts +1 -0
  53. package/templates/mobile/expo/src/core/components/common/list-empty/index.ts +2 -0
  54. package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.tsx +30 -0
  55. package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.type.ts +5 -0
  56. package/templates/mobile/expo/src/core/components/forms/hf-date-time.tsx +301 -0
  57. package/templates/mobile/expo/src/core/components/forms/hf-password-input.tsx +153 -0
  58. package/templates/mobile/expo/src/core/components/forms/hf-text-input.tsx +59 -0
  59. package/templates/mobile/expo/src/core/components/forms/hf-time-picker.tsx +117 -0
  60. package/templates/mobile/expo/src/core/components/forms/index.ts +4 -0
  61. package/templates/mobile/expo/src/core/components/index.ts +5 -0
  62. package/templates/mobile/expo/src/core/components/loading/index.ts +1 -0
  63. package/templates/mobile/expo/src/core/components/loading/loading-app/index.ts +1 -0
  64. package/templates/mobile/expo/src/core/components/loading/loading-app/loading-app.tsx +50 -0
  65. package/templates/mobile/expo/src/core/components/offline/index.ts +1 -0
  66. package/templates/mobile/expo/src/core/components/offline/offline-banner.tsx +186 -0
  67. package/templates/mobile/expo/src/core/components/screen/index.ts +1 -0
  68. package/templates/mobile/expo/src/core/components/screen/screen-container/index.ts +1 -0
  69. package/templates/mobile/expo/src/core/components/screen/screen-container/screen-container.tsx +252 -0
  70. package/templates/mobile/expo/src/core/components/splash/index.ts +1 -0
  71. package/templates/mobile/expo/src/core/components/splash/splash-overlay/index.ts +1 -0
  72. package/templates/mobile/expo/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
  73. package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
  74. package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
  75. package/templates/mobile/expo/src/core/components/ui/animated-list-item/index.ts +2 -0
  76. package/templates/mobile/expo/src/core/components/ui/app-image/app-image.tsx +104 -0
  77. package/templates/mobile/expo/src/core/components/ui/app-image/app-image.type.ts +19 -0
  78. package/templates/mobile/expo/src/core/components/ui/app-image/index.ts +2 -0
  79. package/templates/mobile/expo/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
  80. package/templates/mobile/expo/src/core/components/ui/asset-placeholder/index.ts +1 -0
  81. package/templates/mobile/expo/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
  82. package/templates/mobile/expo/src/core/components/ui/avatar-image/index.ts +1 -0
  83. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
  84. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
  85. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/index.ts +2 -0
  86. package/templates/mobile/expo/src/core/components/ui/button/button.style.ts +146 -0
  87. package/templates/mobile/expo/src/core/components/ui/button/button.tsx +97 -0
  88. package/templates/mobile/expo/src/core/components/ui/button/button.type.ts +47 -0
  89. package/templates/mobile/expo/src/core/components/ui/button/index.ts +4 -0
  90. package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.tsx +127 -0
  91. package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
  92. package/templates/mobile/expo/src/core/components/ui/checkbox/index.ts +2 -0
  93. package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
  94. package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
  95. package/templates/mobile/expo/src/core/components/ui/collapsible-section/index.ts +2 -0
  96. package/templates/mobile/expo/src/core/components/ui/components.registry.json +313 -0
  97. package/templates/mobile/expo/src/core/components/ui/field/field-frame.tsx +62 -0
  98. package/templates/mobile/expo/src/core/components/ui/field/field.shared.ts +31 -0
  99. package/templates/mobile/expo/src/core/components/ui/header/header.tsx +196 -0
  100. package/templates/mobile/expo/src/core/components/ui/header/header.type.ts +30 -0
  101. package/templates/mobile/expo/src/core/components/ui/header/index.ts +2 -0
  102. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
  103. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.tsx +66 -0
  104. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
  105. package/templates/mobile/expo/src/core/components/ui/icon-button/index.ts +2 -0
  106. package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.tsx +107 -0
  107. package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
  108. package/templates/mobile/expo/src/core/components/ui/image-slider/index.ts +2 -0
  109. package/templates/mobile/expo/src/core/components/ui/index.ts +25 -0
  110. package/templates/mobile/expo/src/core/components/ui/label/index.ts +2 -0
  111. package/templates/mobile/expo/src/core/components/ui/label/label.tsx +36 -0
  112. package/templates/mobile/expo/src/core/components/ui/label/label.type.ts +12 -0
  113. package/templates/mobile/expo/src/core/components/ui/modal/index.ts +2 -0
  114. package/templates/mobile/expo/src/core/components/ui/modal/modal.tsx +62 -0
  115. package/templates/mobile/expo/src/core/components/ui/modal/modal.type.ts +11 -0
  116. package/templates/mobile/expo/src/core/components/ui/otp-input/index.ts +2 -0
  117. package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.tsx +129 -0
  118. package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
  119. package/templates/mobile/expo/src/core/components/ui/page-dots/index.ts +1 -0
  120. package/templates/mobile/expo/src/core/components/ui/page-dots/page-dots.tsx +60 -0
  121. package/templates/mobile/expo/src/core/components/ui/radio/index.ts +2 -0
  122. package/templates/mobile/expo/src/core/components/ui/radio/radio.tsx +121 -0
  123. package/templates/mobile/expo/src/core/components/ui/radio/radio.type.ts +20 -0
  124. package/templates/mobile/expo/src/core/components/ui/screen/index.ts +1 -0
  125. package/templates/mobile/expo/src/core/components/ui/screen/screen-gradient.tsx +33 -0
  126. package/templates/mobile/expo/src/core/components/ui/search-box/index.ts +2 -0
  127. package/templates/mobile/expo/src/core/components/ui/search-box/search-box.tsx +162 -0
  128. package/templates/mobile/expo/src/core/components/ui/search-box/search-box.type.ts +26 -0
  129. package/templates/mobile/expo/src/core/components/ui/segmented-control/index.ts +2 -0
  130. package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
  131. package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
  132. package/templates/mobile/expo/src/core/components/ui/skeleton/index.ts +2 -0
  133. package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.tsx +106 -0
  134. package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
  135. package/templates/mobile/expo/src/core/components/ui/success-state/index.ts +1 -0
  136. package/templates/mobile/expo/src/core/components/ui/success-state/success-state.tsx +68 -0
  137. package/templates/mobile/expo/src/core/components/ui/tabs/index.ts +2 -0
  138. package/templates/mobile/expo/src/core/components/ui/tabs/tabs.tsx +273 -0
  139. package/templates/mobile/expo/src/core/components/ui/tabs/tabs.type.ts +21 -0
  140. package/templates/mobile/expo/src/core/components/ui/tag-input/index.ts +2 -0
  141. package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.tsx +146 -0
  142. package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
  143. package/templates/mobile/expo/src/core/components/ui/text-area/index.ts +2 -0
  144. package/templates/mobile/expo/src/core/components/ui/text-area/text-area.tsx +90 -0
  145. package/templates/mobile/expo/src/core/components/ui/text-area/text-area.type.ts +20 -0
  146. package/templates/mobile/expo/src/core/components/ui/text-field/index.ts +2 -0
  147. package/templates/mobile/expo/src/core/components/ui/text-field/text-field.tsx +116 -0
  148. package/templates/mobile/expo/src/core/components/ui/text-field/text-field.type.ts +21 -0
  149. package/templates/mobile/expo/src/core/components/ui/toggle/index.ts +2 -0
  150. package/templates/mobile/expo/src/core/components/ui/toggle/toggle.tsx +110 -0
  151. package/templates/mobile/expo/src/core/components/ui/toggle/toggle.type.ts +19 -0
  152. package/templates/mobile/expo/src/core/constants/external-urls.constant.ts +5 -0
  153. package/templates/mobile/expo/src/core/constants/hard-data.constant.ts +0 -0
  154. package/templates/mobile/expo/src/core/constants/index.ts +2 -0
  155. package/templates/mobile/expo/src/core/constants/type.constant.ts +3 -0
  156. package/templates/mobile/expo/src/core/context/index.ts +1 -0
  157. package/templates/mobile/expo/src/core/context/shared-transition-context.tsx +35 -0
  158. package/templates/mobile/expo/src/core/hook/index.ts +8 -0
  159. package/templates/mobile/expo/src/core/hook/useActiveRouteName.ts +63 -0
  160. package/templates/mobile/expo/src/core/hook/useAppNavigation.tsx +7 -0
  161. package/templates/mobile/expo/src/core/hook/useBottomInset.tsx +26 -0
  162. package/templates/mobile/expo/src/core/hook/useDebounce.tsx +16 -0
  163. package/templates/mobile/expo/src/core/hook/useEndReached.tsx +46 -0
  164. package/templates/mobile/expo/src/core/hook/useManualRefetch.ts +56 -0
  165. package/templates/mobile/expo/src/core/hook/useNetworkStatus.ts +68 -0
  166. package/templates/mobile/expo/src/core/hook/useTimeout.tsx +30 -0
  167. package/templates/mobile/expo/src/core/index.ts +7 -0
  168. package/templates/mobile/expo/src/core/services/api.service.ts +230 -0
  169. package/templates/mobile/expo/src/core/services/device-id.service.ts +23 -0
  170. package/templates/mobile/expo/src/core/services/index.ts +3 -0
  171. package/templates/mobile/expo/src/core/services/session-end.bridge.ts +26 -0
  172. package/templates/mobile/expo/src/core/theme/dark.theme.ts +10 -0
  173. package/templates/mobile/expo/src/core/theme/index.ts +5 -0
  174. package/templates/mobile/expo/src/core/theme/light.theme.ts +44 -0
  175. package/templates/mobile/expo/src/core/theme/theme-context.tsx +82 -0
  176. package/templates/mobile/expo/src/core/theme/theme.types.ts +26 -0
  177. package/templates/mobile/expo/src/core/theme/use-themed-styles.ts +25 -0
  178. package/templates/mobile/expo/src/core/utils/color.util.tsx +198 -0
  179. package/templates/mobile/expo/src/core/utils/date.util.ts +97 -0
  180. package/templates/mobile/expo/src/core/utils/device-locale.util.ts +22 -0
  181. package/templates/mobile/expo/src/core/utils/emitter/index.ts +161 -0
  182. package/templates/mobile/expo/src/core/utils/emitter/type.ts +40 -0
  183. package/templates/mobile/expo/src/core/utils/enum.util.tsx +15 -0
  184. package/templates/mobile/expo/src/core/utils/font.util.tsx +42 -0
  185. package/templates/mobile/expo/src/core/utils/func.util.ts +48 -0
  186. package/templates/mobile/expo/src/core/utils/greeting.util.ts +20 -0
  187. package/templates/mobile/expo/src/core/utils/image-format.util.ts +117 -0
  188. package/templates/mobile/expo/src/core/utils/image-picker.util.ts +84 -0
  189. package/templates/mobile/expo/src/core/utils/index.ts +18 -0
  190. package/templates/mobile/expo/src/core/utils/linking.util.ts +16 -0
  191. package/templates/mobile/expo/src/core/utils/navigation.util.tsx +100 -0
  192. package/templates/mobile/expo/src/core/utils/number-format.ts +28 -0
  193. package/templates/mobile/expo/src/core/utils/query-client.util.ts +35 -0
  194. package/templates/mobile/expo/src/core/utils/query-persister.util.ts +36 -0
  195. package/templates/mobile/expo/src/core/utils/schema.util.tsx +2713 -0
  196. package/templates/mobile/expo/src/core/utils/size.util.tsx +74 -0
  197. package/templates/mobile/expo/src/core/utils/storage.util.tsx +53 -0
  198. package/templates/mobile/expo/src/core/utils/toast.util.tsx +151 -0
  199. package/templates/mobile/expo/src/core/utils/translator.util.tsx +23 -0
  200. package/templates/mobile/expo/src/core/utils/typography.util.tsx +69 -0
  201. package/templates/mobile/expo/src/core/utils/validate.util.tsx +13 -0
  202. package/templates/mobile/expo/src/declarations.d.ts +54 -0
  203. package/templates/mobile/expo/src/modules/home/home.screen.tsx +33 -0
  204. package/templates/mobile/expo/src/modules/profile/profile.screen.tsx +29 -0
  205. package/templates/mobile/expo/src/scripts/link-fonts.js +60 -0
  206. package/templates/mobile/expo/src/scripts/sync-images.js +56 -0
  207. package/templates/mobile/expo/src/scripts/sync-svgs.js +50 -0
  208. package/templates/mobile/expo/src/types/models.d.ts +24 -0
  209. package/templates/mobile/expo/tsconfig.json +19 -0
  210. package/templates/mobile/rn/.env.example +5 -0
  211. package/templates/mobile/rn/.eslintrc.js +7 -0
  212. package/templates/mobile/rn/.prettierrc.js +7 -0
  213. package/templates/mobile/rn/.svgrrc.js +9 -0
  214. package/templates/mobile/rn/README.md +40 -7
  215. package/templates/mobile/rn/_gitignore +24 -0
  216. package/templates/mobile/rn/_package.json +67 -1
  217. package/templates/mobile/rn/app.json +4 -0
  218. package/templates/mobile/rn/babel.config.js +18 -0
  219. package/templates/mobile/rn/index.js +8 -0
  220. package/templates/mobile/rn/metro.config.js +33 -0
  221. package/templates/mobile/rn/src/app/App.tsx +24 -0
  222. package/templates/mobile/rn/src/app/app-provider.tsx +36 -0
  223. package/templates/mobile/rn/src/app/config/translation.ts +26 -0
  224. package/templates/mobile/rn/src/app/index.ts +1 -0
  225. package/templates/mobile/rn/src/app/navigation/app-route-type.ts +27 -0
  226. package/templates/mobile/rn/src/app/navigation/bottom-tabs.tsx +34 -0
  227. package/templates/mobile/rn/src/app/navigation/index.tsx +14 -0
  228. package/templates/mobile/rn/src/app/stores/auth.store.ts +33 -0
  229. package/templates/mobile/rn/src/app/stores/common.store.ts +19 -0
  230. package/templates/mobile/rn/src/app/stores/index.ts +3 -0
  231. package/templates/mobile/rn/src/app/stores/loading.store.ts +22 -0
  232. package/templates/mobile/rn/src/assets/Images/empty-list.png +0 -0
  233. package/templates/mobile/rn/src/assets/Images/index.ts +5 -0
  234. package/templates/mobile/rn/src/assets/Images/screen-bg-gradian.png +0 -0
  235. package/templates/mobile/rn/src/assets/i18n/en.json +12 -0
  236. package/templates/mobile/rn/src/assets/i18n/fr.json +12 -0
  237. package/templates/mobile/rn/src/assets/lotties/index.ts +3 -0
  238. package/templates/mobile/rn/src/assets/lotties/loading.json +1 -0
  239. package/templates/mobile/rn/src/assets/svgs/arrow-left.svg +3 -0
  240. package/templates/mobile/rn/src/assets/svgs/arrow-right.svg +3 -0
  241. package/templates/mobile/rn/src/assets/svgs/calendar.svg +12 -0
  242. package/templates/mobile/rn/src/assets/svgs/check.svg +3 -0
  243. package/templates/mobile/rn/src/assets/svgs/close.svg +3 -0
  244. package/templates/mobile/rn/src/assets/svgs/eye-hide.svg +3 -0
  245. package/templates/mobile/rn/src/assets/svgs/eye.svg +4 -0
  246. package/templates/mobile/rn/src/assets/svgs/index.ts +29 -0
  247. package/templates/mobile/rn/src/assets/svgs/minus.svg +3 -0
  248. package/templates/mobile/rn/src/assets/svgs/plus.svg +3 -0
  249. package/templates/mobile/rn/src/assets/svgs/search.svg +3 -0
  250. package/templates/mobile/rn/src/assets/svgs/toast-error.svg +5 -0
  251. package/templates/mobile/rn/src/assets/svgs/toast-success.svg +4 -0
  252. package/templates/mobile/rn/src/core/api/endpoints.ts +8 -0
  253. package/templates/mobile/rn/src/core/api/example.api.ts +53 -0
  254. package/templates/mobile/rn/src/core/api/index.ts +4 -0
  255. package/templates/mobile/rn/src/core/components/common/index.ts +1 -0
  256. package/templates/mobile/rn/src/core/components/common/list-empty/index.ts +2 -0
  257. package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.tsx +30 -0
  258. package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.type.ts +5 -0
  259. package/templates/mobile/rn/src/core/components/forms/hf-date-time.tsx +301 -0
  260. package/templates/mobile/rn/src/core/components/forms/hf-password-input.tsx +153 -0
  261. package/templates/mobile/rn/src/core/components/forms/hf-text-input.tsx +59 -0
  262. package/templates/mobile/rn/src/core/components/forms/hf-time-picker.tsx +117 -0
  263. package/templates/mobile/rn/src/core/components/forms/index.ts +4 -0
  264. package/templates/mobile/rn/src/core/components/index.ts +5 -0
  265. package/templates/mobile/rn/src/core/components/loading/index.ts +1 -0
  266. package/templates/mobile/rn/src/core/components/loading/loading-app/index.ts +1 -0
  267. package/templates/mobile/rn/src/core/components/loading/loading-app/loading-app.tsx +50 -0
  268. package/templates/mobile/rn/src/core/components/offline/index.ts +1 -0
  269. package/templates/mobile/rn/src/core/components/offline/offline-banner.tsx +186 -0
  270. package/templates/mobile/rn/src/core/components/screen/index.ts +1 -0
  271. package/templates/mobile/rn/src/core/components/screen/screen-container/index.ts +1 -0
  272. package/templates/mobile/rn/src/core/components/screen/screen-container/screen-container.tsx +252 -0
  273. package/templates/mobile/rn/src/core/components/splash/index.ts +1 -0
  274. package/templates/mobile/rn/src/core/components/splash/splash-overlay/index.ts +1 -0
  275. package/templates/mobile/rn/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
  276. package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
  277. package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
  278. package/templates/mobile/rn/src/core/components/ui/animated-list-item/index.ts +2 -0
  279. package/templates/mobile/rn/src/core/components/ui/app-image/app-image.tsx +104 -0
  280. package/templates/mobile/rn/src/core/components/ui/app-image/app-image.type.ts +19 -0
  281. package/templates/mobile/rn/src/core/components/ui/app-image/index.ts +2 -0
  282. package/templates/mobile/rn/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
  283. package/templates/mobile/rn/src/core/components/ui/asset-placeholder/index.ts +1 -0
  284. package/templates/mobile/rn/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
  285. package/templates/mobile/rn/src/core/components/ui/avatar-image/index.ts +1 -0
  286. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
  287. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
  288. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/index.ts +2 -0
  289. package/templates/mobile/rn/src/core/components/ui/button/button.style.ts +146 -0
  290. package/templates/mobile/rn/src/core/components/ui/button/button.tsx +97 -0
  291. package/templates/mobile/rn/src/core/components/ui/button/button.type.ts +47 -0
  292. package/templates/mobile/rn/src/core/components/ui/button/index.ts +4 -0
  293. package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.tsx +127 -0
  294. package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
  295. package/templates/mobile/rn/src/core/components/ui/checkbox/index.ts +2 -0
  296. package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
  297. package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
  298. package/templates/mobile/rn/src/core/components/ui/collapsible-section/index.ts +2 -0
  299. package/templates/mobile/rn/src/core/components/ui/components.registry.json +313 -0
  300. package/templates/mobile/rn/src/core/components/ui/field/field-frame.tsx +62 -0
  301. package/templates/mobile/rn/src/core/components/ui/field/field.shared.ts +31 -0
  302. package/templates/mobile/rn/src/core/components/ui/header/header.tsx +196 -0
  303. package/templates/mobile/rn/src/core/components/ui/header/header.type.ts +30 -0
  304. package/templates/mobile/rn/src/core/components/ui/header/index.ts +2 -0
  305. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
  306. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.tsx +66 -0
  307. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
  308. package/templates/mobile/rn/src/core/components/ui/icon-button/index.ts +2 -0
  309. package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.tsx +107 -0
  310. package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
  311. package/templates/mobile/rn/src/core/components/ui/image-slider/index.ts +2 -0
  312. package/templates/mobile/rn/src/core/components/ui/index.ts +25 -0
  313. package/templates/mobile/rn/src/core/components/ui/label/index.ts +2 -0
  314. package/templates/mobile/rn/src/core/components/ui/label/label.tsx +36 -0
  315. package/templates/mobile/rn/src/core/components/ui/label/label.type.ts +12 -0
  316. package/templates/mobile/rn/src/core/components/ui/modal/index.ts +2 -0
  317. package/templates/mobile/rn/src/core/components/ui/modal/modal.tsx +62 -0
  318. package/templates/mobile/rn/src/core/components/ui/modal/modal.type.ts +11 -0
  319. package/templates/mobile/rn/src/core/components/ui/otp-input/index.ts +2 -0
  320. package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.tsx +129 -0
  321. package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
  322. package/templates/mobile/rn/src/core/components/ui/page-dots/index.ts +1 -0
  323. package/templates/mobile/rn/src/core/components/ui/page-dots/page-dots.tsx +60 -0
  324. package/templates/mobile/rn/src/core/components/ui/radio/index.ts +2 -0
  325. package/templates/mobile/rn/src/core/components/ui/radio/radio.tsx +121 -0
  326. package/templates/mobile/rn/src/core/components/ui/radio/radio.type.ts +20 -0
  327. package/templates/mobile/rn/src/core/components/ui/screen/index.ts +1 -0
  328. package/templates/mobile/rn/src/core/components/ui/screen/screen-gradient.tsx +33 -0
  329. package/templates/mobile/rn/src/core/components/ui/search-box/index.ts +2 -0
  330. package/templates/mobile/rn/src/core/components/ui/search-box/search-box.tsx +162 -0
  331. package/templates/mobile/rn/src/core/components/ui/search-box/search-box.type.ts +26 -0
  332. package/templates/mobile/rn/src/core/components/ui/segmented-control/index.ts +2 -0
  333. package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
  334. package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
  335. package/templates/mobile/rn/src/core/components/ui/skeleton/index.ts +2 -0
  336. package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.tsx +106 -0
  337. package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
  338. package/templates/mobile/rn/src/core/components/ui/success-state/index.ts +1 -0
  339. package/templates/mobile/rn/src/core/components/ui/success-state/success-state.tsx +68 -0
  340. package/templates/mobile/rn/src/core/components/ui/tabs/index.ts +2 -0
  341. package/templates/mobile/rn/src/core/components/ui/tabs/tabs.tsx +273 -0
  342. package/templates/mobile/rn/src/core/components/ui/tabs/tabs.type.ts +21 -0
  343. package/templates/mobile/rn/src/core/components/ui/tag-input/index.ts +2 -0
  344. package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.tsx +146 -0
  345. package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
  346. package/templates/mobile/rn/src/core/components/ui/text-area/index.ts +2 -0
  347. package/templates/mobile/rn/src/core/components/ui/text-area/text-area.tsx +90 -0
  348. package/templates/mobile/rn/src/core/components/ui/text-area/text-area.type.ts +20 -0
  349. package/templates/mobile/rn/src/core/components/ui/text-field/index.ts +2 -0
  350. package/templates/mobile/rn/src/core/components/ui/text-field/text-field.tsx +116 -0
  351. package/templates/mobile/rn/src/core/components/ui/text-field/text-field.type.ts +21 -0
  352. package/templates/mobile/rn/src/core/components/ui/toggle/index.ts +2 -0
  353. package/templates/mobile/rn/src/core/components/ui/toggle/toggle.tsx +110 -0
  354. package/templates/mobile/rn/src/core/components/ui/toggle/toggle.type.ts +19 -0
  355. package/templates/mobile/rn/src/core/constants/external-urls.constant.ts +5 -0
  356. package/templates/mobile/rn/src/core/constants/hard-data.constant.ts +0 -0
  357. package/templates/mobile/rn/src/core/constants/index.ts +2 -0
  358. package/templates/mobile/rn/src/core/constants/type.constant.ts +3 -0
  359. package/templates/mobile/rn/src/core/context/index.ts +1 -0
  360. package/templates/mobile/rn/src/core/context/shared-transition-context.tsx +35 -0
  361. package/templates/mobile/rn/src/core/hook/index.ts +8 -0
  362. package/templates/mobile/rn/src/core/hook/useActiveRouteName.ts +63 -0
  363. package/templates/mobile/rn/src/core/hook/useAppNavigation.tsx +7 -0
  364. package/templates/mobile/rn/src/core/hook/useBottomInset.tsx +26 -0
  365. package/templates/mobile/rn/src/core/hook/useDebounce.tsx +16 -0
  366. package/templates/mobile/rn/src/core/hook/useEndReached.tsx +46 -0
  367. package/templates/mobile/rn/src/core/hook/useManualRefetch.ts +56 -0
  368. package/templates/mobile/rn/src/core/hook/useNetworkStatus.ts +68 -0
  369. package/templates/mobile/rn/src/core/hook/useTimeout.tsx +30 -0
  370. package/templates/mobile/rn/src/core/index.ts +7 -0
  371. package/templates/mobile/rn/src/core/services/api.service.ts +230 -0
  372. package/templates/mobile/rn/src/core/services/device-id.service.ts +23 -0
  373. package/templates/mobile/rn/src/core/services/index.ts +3 -0
  374. package/templates/mobile/rn/src/core/services/session-end.bridge.ts +26 -0
  375. package/templates/mobile/rn/src/core/theme/dark.theme.ts +10 -0
  376. package/templates/mobile/rn/src/core/theme/index.ts +5 -0
  377. package/templates/mobile/rn/src/core/theme/light.theme.ts +44 -0
  378. package/templates/mobile/rn/src/core/theme/theme-context.tsx +82 -0
  379. package/templates/mobile/rn/src/core/theme/theme.types.ts +26 -0
  380. package/templates/mobile/rn/src/core/theme/use-themed-styles.ts +25 -0
  381. package/templates/mobile/rn/src/core/utils/color.util.tsx +198 -0
  382. package/templates/mobile/rn/src/core/utils/date.util.ts +97 -0
  383. package/templates/mobile/rn/src/core/utils/device-locale.util.ts +22 -0
  384. package/templates/mobile/rn/src/core/utils/emitter/index.ts +161 -0
  385. package/templates/mobile/rn/src/core/utils/emitter/type.ts +40 -0
  386. package/templates/mobile/rn/src/core/utils/enum.util.tsx +15 -0
  387. package/templates/mobile/rn/src/core/utils/font.util.tsx +42 -0
  388. package/templates/mobile/rn/src/core/utils/func.util.ts +48 -0
  389. package/templates/mobile/rn/src/core/utils/greeting.util.ts +20 -0
  390. package/templates/mobile/rn/src/core/utils/image-format.util.ts +117 -0
  391. package/templates/mobile/rn/src/core/utils/image-picker.util.ts +84 -0
  392. package/templates/mobile/rn/src/core/utils/index.ts +18 -0
  393. package/templates/mobile/rn/src/core/utils/linking.util.ts +16 -0
  394. package/templates/mobile/rn/src/core/utils/navigation.util.tsx +100 -0
  395. package/templates/mobile/rn/src/core/utils/number-format.ts +28 -0
  396. package/templates/mobile/rn/src/core/utils/query-client.util.ts +35 -0
  397. package/templates/mobile/rn/src/core/utils/query-persister.util.ts +36 -0
  398. package/templates/mobile/rn/src/core/utils/schema.util.tsx +2713 -0
  399. package/templates/mobile/rn/src/core/utils/size.util.tsx +74 -0
  400. package/templates/mobile/rn/src/core/utils/storage.util.tsx +53 -0
  401. package/templates/mobile/rn/src/core/utils/toast.util.tsx +151 -0
  402. package/templates/mobile/rn/src/core/utils/translator.util.tsx +23 -0
  403. package/templates/mobile/rn/src/core/utils/typography.util.tsx +69 -0
  404. package/templates/mobile/rn/src/core/utils/validate.util.tsx +13 -0
  405. package/templates/mobile/rn/src/declarations.d.ts +54 -0
  406. package/templates/mobile/rn/src/modules/home/home.screen.tsx +33 -0
  407. package/templates/mobile/rn/src/modules/profile/profile.screen.tsx +29 -0
  408. package/templates/mobile/rn/src/scripts/link-fonts.js +60 -0
  409. package/templates/mobile/rn/src/scripts/sync-images.js +56 -0
  410. package/templates/mobile/rn/src/scripts/sync-svgs.js +50 -0
  411. package/templates/mobile/rn/src/types/models.d.ts +24 -0
  412. package/templates/mobile/rn/tsconfig.json +21 -0
  413. package/templates/shared/README.md +21 -6
  414. package/templates/shared/_package.json +26 -4
  415. package/templates/shared/src/api-endpoints.ts +8 -0
  416. package/templates/shared/src/enums.ts +34 -0
  417. package/templates/shared/src/external-urls.ts +5 -0
  418. package/templates/shared/src/index.ts +15 -0
  419. package/templates/shared/tsconfig.json +8 -0
  420. package/templates/shared/tsup.config.ts +9 -0
  421. package/templates/shared/index.js +0 -4
@@ -0,0 +1,93 @@
1
+ import { ImgScreenBgGradian } from '@src/assets/Images';
2
+ import { verticalScale } from '@src/core';
3
+ import React, { useEffect } from 'react';
4
+ import { Image, Platform, StyleSheet } from 'react-native';
5
+ import Animated, {
6
+ useAnimatedStyle,
7
+ useSharedValue,
8
+ withTiming,
9
+ } from 'react-native-reanimated';
10
+
11
+ const LOGO = require('@src/assets/splash/logo-white.png');
12
+
13
+ const FADE_OUT_MS = 350;
14
+
15
+ interface SplashOverlayProps {
16
+ /**
17
+ * Flip to `true` once the app is ready (launch route resolved + native
18
+ * splash hidden). The overlay then fades out and unmounts itself.
19
+ */
20
+ hidden: boolean;
21
+ }
22
+
23
+ /**
24
+ * Android-only fake splash.
25
+ *
26
+ * iOS renders a full-bleed gradient splash via the native storyboard
27
+ * (with-splash-screen-fix plugin). Android 12+ can only show a centred icon on
28
+ * a solid colour, so after the native splash hands off we cover the first
29
+ * frames with this React overlay — the shared `ImgScreenBgGradian` background
30
+ * + logo — then fade it out, giving Android a gradient launch look. No-op on
31
+ * iOS, which keeps its full-bleed native storyboard splash.
32
+ */
33
+ const SplashOverlay = ({ hidden }: SplashOverlayProps) => {
34
+ const opacity = useSharedValue(1);
35
+ const gone = useSharedValue(false);
36
+
37
+ useEffect(() => {
38
+ if (hidden) {
39
+ opacity.value = withTiming(0, { duration: FADE_OUT_MS }, finished => {
40
+ if (finished) {
41
+ gone.value = true;
42
+ }
43
+ });
44
+ }
45
+ }, [hidden, opacity, gone]);
46
+
47
+ const animatedStyle = useAnimatedStyle(() => ({
48
+ opacity: opacity.value,
49
+ // Drop out of the tree (and stop intercepting touches) once faded.
50
+ display: gone.value ? 'none' : 'flex',
51
+ }));
52
+
53
+ if (Platform.OS !== 'android') {
54
+ return null;
55
+ }
56
+
57
+ return (
58
+ <Animated.View
59
+ style={[styles.container, animatedStyle]}
60
+ pointerEvents="none">
61
+ <Image
62
+ source={ImgScreenBgGradian}
63
+ style={styles.image}
64
+ />
65
+
66
+ <Image source={LOGO} style={styles.logo} resizeMode="contain" />
67
+ </Animated.View>
68
+ );
69
+ };
70
+
71
+ const styles = StyleSheet.create({
72
+ container: {
73
+ position: 'absolute',
74
+ top: 0,
75
+ left: 0,
76
+ right: 0,
77
+ bottom: 0,
78
+ zIndex: 9999,
79
+ justifyContent: 'center',
80
+ alignItems: 'center',
81
+ },
82
+ image: {
83
+ position: 'absolute',
84
+ height: '100%',
85
+ width: '100%'
86
+ },
87
+ logo: {
88
+ height: verticalScale(160),
89
+ width: verticalScale(110)
90
+ },
91
+ });
92
+
93
+ export default SplashOverlay;
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { Pressable } from 'react-native';
3
+ import Animated, { FadeInRight, FadeOutRight, Layout } from 'react-native-reanimated';
4
+ import { AnimatedListItemProps } from './animated-list-item.type';
5
+
6
+ const ANIMATION_DELAY_STAGGER = 50;
7
+ const ANIMATION_DURATION = 200;
8
+ const ENTERING_BASE = FadeInRight.duration(ANIMATION_DURATION).springify();
9
+ const EXITING = FadeOutRight.duration(ANIMATION_DURATION);
10
+ const LAYOUT = Layout.springify();
11
+ const PressableAnimated = Animated.createAnimatedComponent(Pressable);
12
+
13
+ const AnimatedListItem: React.FC<AnimatedListItemProps> = ({
14
+ children,
15
+ index,
16
+ style,
17
+ delayStagger = ANIMATION_DELAY_STAGGER,
18
+ onPress,
19
+ }) => {
20
+ const entering = ENTERING_BASE.delay(index * delayStagger);
21
+
22
+ if (onPress) {
23
+ return (
24
+ <PressableAnimated
25
+ entering={entering}
26
+ exiting={EXITING}
27
+ layout={LAYOUT}
28
+ style={style}
29
+ onPress={onPress}
30
+ >
31
+ {children}
32
+ </PressableAnimated>
33
+ );
34
+ }
35
+
36
+ return (
37
+ <Animated.View
38
+ entering={entering}
39
+ exiting={EXITING}
40
+ layout={LAYOUT}
41
+ style={style}
42
+ >
43
+ {children}
44
+ </Animated.View>
45
+ );
46
+ };
47
+
48
+ export default AnimatedListItem;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { StyleProp, ViewStyle } from 'react-native';
3
+
4
+ export interface AnimatedListItemProps {
5
+ children: React.ReactNode;
6
+ index: number;
7
+ style?: StyleProp<ViewStyle>;
8
+ delayStagger?: number;
9
+ onPress?: () => void;
10
+ }
@@ -0,0 +1,2 @@
1
+ export { default as AnimatedListItem } from './animated-list-item';
2
+ export * from './animated-list-item.type';
@@ -0,0 +1,104 @@
1
+ import { ThemeColors, useThemedStyles } from '@src/core/theme';
2
+ import React, { useEffect, useRef, useState } from 'react';
3
+ import {
4
+ Animated,
5
+ Image,
6
+ ImageStyle,
7
+ StyleProp,
8
+ StyleSheet,
9
+ View,
10
+ } from 'react-native';
11
+ import FastImage, { ImageStyle as FastImageStyle } from 'react-native-fast-image';
12
+ import { AppImageProps } from './app-image.type';
13
+
14
+ const ImageSkeleton: React.FC<{ style?: StyleProp<ImageStyle> }> = ({ style }) => {
15
+ const styles = useThemedStyles(makeStyles);
16
+ const shimmer = useRef(new Animated.Value(0)).current;
17
+
18
+ useEffect(() => {
19
+ const loop = Animated.loop(
20
+ Animated.sequence([
21
+ Animated.timing(shimmer, { toValue: 1, duration: 900, useNativeDriver: true }),
22
+ Animated.timing(shimmer, { toValue: 0, duration: 900, useNativeDriver: true }),
23
+ ]),
24
+ );
25
+ loop.start();
26
+ return () => loop.stop();
27
+ }, [shimmer]);
28
+
29
+ const opacity = shimmer.interpolate({ inputRange: [0, 1], outputRange: [0.4, 0.9] });
30
+
31
+ return <Animated.View style={[styles.skeleton, style, { opacity }]} />;
32
+ };
33
+
34
+ const AppImage: React.FC<AppImageProps> = ({ style, source, resizeMode, containerStyle, immutable, ...rest }) => {
35
+ const styles = useThemedStyles(makeStyles);
36
+ // Start true so the shimmer shows immediately on mount and stays up until the
37
+ // first load resolves; flipping it false on load-end/error reveals the image.
38
+ // For an immutable source the bitmap is already cached on remount, so starting
39
+ // false avoids a shimmer flash before onLoadEnd fires (LIFEMASTER-364).
40
+ const [loading, setLoading] = useState(!immutable);
41
+
42
+ // Handle empty or invalid source. Keep `style` so the slot preserves its
43
+ // dimensions (e.g. a hero/cover) instead of collapsing to 0px while the real
44
+ // source is still loading or missing.
45
+ if (!source || (typeof source === 'object' && 'uri' in source && !source.uri)) {
46
+ return <View style={style} />;
47
+ }
48
+
49
+ // Local image or non-http/https uri
50
+ if (
51
+ typeof source === 'number' ||
52
+ !(source?.uri?.startsWith('http://') || source?.uri?.startsWith('https://'))
53
+ ) {
54
+ return <Image style={style} source={source} resizeMode={resizeMode} {...rest} />;
55
+ }
56
+
57
+ // Remote image with loading indicator
58
+ // `source` is narrowed to `ImageURISource` here: the `number` case returned above.
59
+ const uri = source.uri ?? '';
60
+ return (
61
+ <View style={[styles.container, containerStyle]}>
62
+ <FastImage
63
+ // Default (mutable) path remounts on URL change so a re-uploaded avatar
64
+ // reloads instead of FastImage keeping the old native view + cached
65
+ // bitmap; `cache: web` revalidates a stable URL (server fix is a unique
66
+ // filename per upload, LIFEMASTER-260). An `immutable` source has a
67
+ // content-addressed URL, so we skip the remount and cache it immutably —
68
+ // the bitmap is served instantly on remount with no reload/flash
69
+ // (LIFEMASTER-364).
70
+ key={immutable ? undefined : uri}
71
+ style={style as FastImageStyle}
72
+ source={{
73
+ uri: uri || '',
74
+ priority: FastImage.priority.normal,
75
+ cache: immutable
76
+ ? FastImage.cacheControl.immutable
77
+ : FastImage.cacheControl.web,
78
+ }}
79
+ // For an immutable (already-cached) source, don't re-raise the shimmer on
80
+ // load-start: the bitmap is there instantly, so a flash to loading would
81
+ // be the very reload artifact we're removing (LIFEMASTER-364).
82
+ onLoadStart={() => !immutable && setLoading(true)}
83
+ onLoadEnd={() => setLoading(false)}
84
+ onError={() => setLoading(false)}
85
+ resizeMode={resizeMode}
86
+ />
87
+ {loading && <ImageSkeleton style={style} />}
88
+ </View>
89
+ );
90
+ };
91
+
92
+ export default AppImage;
93
+
94
+ const makeStyles = (c: ThemeColors) =>
95
+ StyleSheet.create({
96
+ container: {
97
+ justifyContent: 'center',
98
+ alignItems: 'center',
99
+ },
100
+ skeleton: {
101
+ position: 'absolute',
102
+ backgroundColor: c.hE3E3E3,
103
+ },
104
+ });
@@ -0,0 +1,19 @@
1
+ import { ImagePropsBase, ImageStyle, ImageURISource, StyleProp, ViewStyle } from 'react-native';
2
+ import { ResizeMode } from 'react-native-fast-image';
3
+
4
+ export interface AppImageProps extends ImagePropsBase {
5
+ source: ImageURISource | number;
6
+ style?: StyleProp<ImageStyle>;
7
+ containerStyle?: StyleProp<ViewStyle>;
8
+ resizeMode?: ResizeMode;
9
+ /**
10
+ * Treat the remote image as immutable (stable URL → content never changes), so
11
+ * FastImage serves the cached bitmap instantly on every remount instead of
12
+ * revalidating over the network. Use for content-addressed images (e.g. player
13
+ * covers): it stops the artwork from flashing/reloading when the same image
14
+ * remounts — like expanding the mini-player to full screen on Android
15
+ * (LIFEMASTER-364). Leave off for images that can change behind a stable URL
16
+ * (e.g. a re-uploaded avatar), which need the default revalidating `web` cache.
17
+ */
18
+ immutable?: boolean;
19
+ }
@@ -0,0 +1,2 @@
1
+ export { default as AppImage } from './app-image';
2
+ export * from './app-image.type';
@@ -0,0 +1,76 @@
1
+ import { Colors, Font, fontSize, Radius } from '@src/core/utils'
2
+ import React from 'react'
3
+ import { DimensionValue, StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-native'
4
+
5
+ interface AssetPlaceholderProps {
6
+ /** Asset name from the FE handoff asset checklist (e.g. 'onboarding-clarity'). */
7
+ name: string
8
+ /** Figma node id the asset comes from. */
9
+ figmaNodeId?: string
10
+ width?: DimensionValue
11
+ height?: DimensionValue
12
+ rounded?: boolean
13
+ style?: StyleProp<ViewStyle>
14
+ }
15
+
16
+ /**
17
+ * Placeholder for an image / illustration / icon that is NOT yet exported from
18
+ * Figma. Reserves the correct space and labels the missing asset so the layout
19
+ * stays intact until the real asset is imported.
20
+ *
21
+ * Replace every usage with the real asset listed in the FE handoff checklist.
22
+ *
23
+ * @example
24
+ * <AssetPlaceholder name="onboarding-clarity" figmaNodeId="16:6784" width={240} height={240} rounded />
25
+ */
26
+ const AssetPlaceholder = ({
27
+ name,
28
+ figmaNodeId,
29
+ width = '100%',
30
+ height = 160,
31
+ rounded = false,
32
+ style,
33
+ }: AssetPlaceholderProps) => {
34
+ return (
35
+ <View
36
+ style={[
37
+ styles.box,
38
+ { width, height },
39
+ rounded && styles.rounded,
40
+ style,
41
+ ]}
42
+ accessibilityRole="image"
43
+ accessibilityLabel={`asset:${name}`}
44
+ >
45
+ <Text style={styles.label}>{name}</Text>
46
+ {figmaNodeId ? <Text style={styles.node}>{figmaNodeId}</Text> : null}
47
+ </View>
48
+ )
49
+ }
50
+
51
+ export default AssetPlaceholder
52
+
53
+ const styles = StyleSheet.create({
54
+ box: {
55
+ alignItems: 'center',
56
+ justifyContent: 'center',
57
+ borderWidth: 1,
58
+ borderStyle: 'dashed',
59
+ borderColor: Colors.bd_neutral_faded,
60
+ backgroundColor: Colors.bg_input,
61
+ borderRadius: Radius.radius_lg,
62
+ },
63
+ rounded: {
64
+ borderRadius: Radius.radius_full,
65
+ },
66
+ label: {
67
+ fontFamily: Font.dmSansMedium,
68
+ fontSize: fontSize(12),
69
+ color: Colors.fg_neutral_faded,
70
+ },
71
+ node: {
72
+ fontFamily: Font.dmSansRegular,
73
+ fontSize: fontSize(10),
74
+ color: Colors.fg_neutral_faded,
75
+ },
76
+ })
@@ -0,0 +1 @@
1
+ export { default as AssetPlaceholder } from './asset-placeholder'
@@ -0,0 +1,90 @@
1
+ import { AppImage } from '@src/core/components/ui/app-image';
2
+ import React from 'react';
3
+ import {
4
+ ImageSourcePropType,
5
+ ImageURISource,
6
+ StyleProp,
7
+ StyleSheet,
8
+ ImageStyle,
9
+ Text,
10
+ } from 'react-native';
11
+ import LinearGradient from 'react-native-linear-gradient';
12
+
13
+ interface AvatarImageProps {
14
+ source: ImageSourcePropType;
15
+ /** Used for the empty-URI fallback: the first letter is shown over the
16
+ * brand gradient disc. */
17
+ name?: string;
18
+ /** @deprecated No longer used (the disc sizes itself from `style`). */
19
+ size?: number;
20
+ style?: StyleProp<ImageStyle>;
21
+ }
22
+
23
+ const hasUri = (src: ImageSourcePropType): boolean => {
24
+ if (typeof src === 'number') return true;
25
+ if (Array.isArray(src)) return true;
26
+ return !!(src as { uri?: string }).uri;
27
+ };
28
+
29
+ const initialOf = (name?: string): string => {
30
+ const first = name?.trim().charAt(0);
31
+ return first ? first.toUpperCase() : '';
32
+ };
33
+
34
+ /**
35
+ * Avatar circle. Renders the image when the URI is set; otherwise falls back to
36
+ * a brand-purple gradient disc showing the first letter of `name`.
37
+ */
38
+ const AvatarImage = ({ source, name, style }: AvatarImageProps) => {
39
+ if (hasUri(source)) {
40
+ return (
41
+ <AppImage
42
+ source={source as ImageURISource | number}
43
+ style={style}
44
+ resizeMode="cover"
45
+ />
46
+ );
47
+ }
48
+
49
+ // No URI → brand gradient disc with the first initial of the name.
50
+ const flatStyle = StyleSheet.flatten(style) ?? {};
51
+ const diameter =
52
+ typeof flatStyle.width === 'number' ? flatStyle.width : undefined;
53
+ const initial = initialOf(name);
54
+
55
+ return (
56
+ <LinearGradient
57
+ colors={['#3F1881', '#8B5CF6']}
58
+ start={{ x: 0, y: 0 }}
59
+ end={{ x: 1, y: 1 }}
60
+ style={[flatStyle, styles.fill]}
61
+ >
62
+ {!!initial && (
63
+ <Text
64
+ style={[
65
+ styles.initial,
66
+ diameter ? { fontSize: diameter * 0.4 } : null,
67
+ ]}
68
+ numberOfLines={1}
69
+ allowFontScaling={false}
70
+ >
71
+ {initial}
72
+ </Text>
73
+ )}
74
+ </LinearGradient>
75
+ );
76
+ };
77
+
78
+ export default AvatarImage;
79
+
80
+ const styles = StyleSheet.create({
81
+ fill: {
82
+ alignItems: 'center',
83
+ justifyContent: 'center',
84
+ overflow: 'hidden',
85
+ },
86
+ initial: {
87
+ color: '#FFFFFF',
88
+ fontWeight: '600',
89
+ },
90
+ });
@@ -0,0 +1 @@
1
+ export { default as AvatarImage } from './avatar-image';
@@ -0,0 +1,145 @@
1
+ import React, { FC, useEffect, useRef, useState } from 'react';
2
+ import {
3
+ Modal,
4
+ Pressable,
5
+ StyleSheet,
6
+ View,
7
+ Animated,
8
+ Dimensions,
9
+ } from 'react-native';
10
+ import { verticalScale, Radius } from '../../../utils';
11
+ import { ThemeColors, useThemedStyles } from '@src/core/theme';
12
+ import { BottomSheetProps } from './bottom-sheet.type';
13
+
14
+ const BottomSheet: FC<BottomSheetProps> = (props) => {
15
+ const {
16
+ visible,
17
+ animationType = 'none',
18
+ children,
19
+ styleContent = {},
20
+ onHideModal = () => {},
21
+ } = props;
22
+
23
+ const styles = useThemedStyles(makeStyles);
24
+
25
+ const screenHeight = Dimensions.get('window').height;
26
+ const slideAnim = useRef(new Animated.Value(screenHeight)).current;
27
+ const opacityAnim = useRef(new Animated.Value(0)).current;
28
+ const [modalVisible, setModalVisible] = useState(false);
29
+
30
+ useEffect(() => {
31
+ if (visible) {
32
+ // Show modal first
33
+ setModalVisible(true);
34
+ // Reset to initial position before animating in
35
+ slideAnim.setValue(screenHeight);
36
+ opacityAnim.setValue(0);
37
+
38
+ // Small delay to ensure values are set before animation starts
39
+ requestAnimationFrame(() => {
40
+ Animated.parallel([
41
+ Animated.spring(slideAnim, {
42
+ toValue: 0,
43
+ tension: 65,
44
+ friction: 11,
45
+ useNativeDriver: true,
46
+ }),
47
+ Animated.timing(opacityAnim, {
48
+ toValue: 1,
49
+ duration: 200,
50
+ useNativeDriver: true,
51
+ }),
52
+ ]).start();
53
+ });
54
+ } else {
55
+ // Animate out first, then hide modal
56
+ Animated.parallel([
57
+ Animated.timing(slideAnim, {
58
+ toValue: screenHeight,
59
+ duration: 250,
60
+ useNativeDriver: true,
61
+ }),
62
+ Animated.timing(opacityAnim, {
63
+ toValue: 0,
64
+ duration: 200,
65
+ useNativeDriver: true,
66
+ }),
67
+ ]).start(() => {
68
+ // Hide modal after animation completes
69
+ setModalVisible(false);
70
+ });
71
+ }
72
+ }, [visible, slideAnim, opacityAnim, screenHeight]);
73
+
74
+ return (
75
+ <Modal
76
+ visible={modalVisible}
77
+ animationType={animationType}
78
+ transparent
79
+ statusBarTranslucent
80
+ onRequestClose={onHideModal}
81
+ >
82
+ <View style={styles.backdrop}>
83
+ <Pressable
84
+ style={StyleSheet.absoluteFill}
85
+ onPress={onHideModal}
86
+ >
87
+ <Animated.View
88
+ style={[
89
+ StyleSheet.absoluteFill,
90
+ styles.backdropOverlay,
91
+ {
92
+ opacity: opacityAnim,
93
+ },
94
+ ]}
95
+ />
96
+ </Pressable>
97
+ <Animated.View
98
+ style={[
99
+ styles.content,
100
+ styleContent,
101
+ {
102
+ transform: [{ translateY: slideAnim }],
103
+ },
104
+ ]}
105
+ >
106
+ {/* flexShrink lets the inner content honour the 80% maxHeight
107
+ cap so tall sheets (e.g. the audio preview) shrink their
108
+ content instead of clipping the bottom (the CTA). */}
109
+ <Pressable
110
+ style={styles.childrenWrap}
111
+ onPress={(e) => e.stopPropagation()}
112
+ >
113
+ {children}
114
+ </Pressable>
115
+ </Animated.View>
116
+ </View>
117
+ </Modal>
118
+ );
119
+ };
120
+
121
+ export default BottomSheet;
122
+
123
+ const makeStyles = (c: ThemeColors) =>
124
+ StyleSheet.create({
125
+ backdrop: {
126
+ flex: 1,
127
+ justifyContent: 'flex-end',
128
+ },
129
+ backdropOverlay: {
130
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
131
+ },
132
+ content: {
133
+ width: '100%',
134
+ backgroundColor: c.hFFFFFF,
135
+ borderTopLeftRadius: Radius.radius_xl,
136
+ borderTopRightRadius: Radius.radius_xl,
137
+ maxHeight: '80%',
138
+ minHeight: verticalScale(200),
139
+ },
140
+ // Shrink to fit within `content`'s maxHeight so tall children co rather
141
+ // than overflow (and get clipped at the bottom).
142
+ childrenWrap: {
143
+ flexShrink: 1,
144
+ },
145
+ });
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { ModalProps, StyleProp, ViewStyle } from 'react-native';
3
+
4
+ export type BottomSheetProps = {
5
+ visible: boolean;
6
+ animationType?: ModalProps['animationType'];
7
+ onHideModal?: () => void;
8
+ styleContent?: StyleProp<ViewStyle>;
9
+ children?: React.ReactNode;
10
+ };
@@ -0,0 +1,2 @@
1
+ export { default as BottomSheet } from './bottom-sheet';
2
+ export * from './bottom-sheet.type';