react-native-terra-ui 0.1.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 (379) hide show
  1. package/README.md +70 -0
  2. package/lib/module/components/avatar/Avatar.js +121 -0
  3. package/lib/module/components/avatar/Avatar.js.map +1 -0
  4. package/lib/module/components/avatar/index.js +4 -0
  5. package/lib/module/components/avatar/index.js.map +1 -0
  6. package/lib/module/components/avatar/types.js +4 -0
  7. package/lib/module/components/avatar/types.js.map +1 -0
  8. package/lib/module/components/box/Box.js +49 -0
  9. package/lib/module/components/box/Box.js.map +1 -0
  10. package/lib/module/components/box/index.js +4 -0
  11. package/lib/module/components/box/index.js.map +1 -0
  12. package/lib/module/components/box/utils.js +95 -0
  13. package/lib/module/components/box/utils.js.map +1 -0
  14. package/lib/module/components/button/Button.js +281 -0
  15. package/lib/module/components/button/Button.js.map +1 -0
  16. package/lib/module/components/button/index.js +4 -0
  17. package/lib/module/components/button/index.js.map +1 -0
  18. package/lib/module/components/header/Header.js +25 -0
  19. package/lib/module/components/header/Header.js.map +1 -0
  20. package/lib/module/components/header/header-buttons.js +65 -0
  21. package/lib/module/components/header/header-buttons.js.map +1 -0
  22. package/lib/module/components/header/index.js +7 -0
  23. package/lib/module/components/header/index.js.map +1 -0
  24. package/lib/module/components/header/variants/LargeTitleHeader/index.js +335 -0
  25. package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -0
  26. package/lib/module/components/header/variants/TitleHeader/index.js +184 -0
  27. package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -0
  28. package/lib/module/components/icon/Icon.js +53 -0
  29. package/lib/module/components/icon/Icon.js.map +1 -0
  30. package/lib/module/components/icon/index.js +4 -0
  31. package/lib/module/components/icon/index.js.map +1 -0
  32. package/lib/module/components/icon/utils.js +188 -0
  33. package/lib/module/components/icon/utils.js.map +1 -0
  34. package/lib/module/components/index.js +16 -0
  35. package/lib/module/components/index.js.map +1 -0
  36. package/lib/module/components/page-indicator/PageIndicator.js +49 -0
  37. package/lib/module/components/page-indicator/PageIndicator.js.map +1 -0
  38. package/lib/module/components/page-indicator/PageIndicatorRoot.js +24 -0
  39. package/lib/module/components/page-indicator/PageIndicatorRoot.js.map +1 -0
  40. package/lib/module/components/page-indicator/hooks/use-page-indicator-colors.js +24 -0
  41. package/lib/module/components/page-indicator/hooks/use-page-indicator-colors.js.map +1 -0
  42. package/lib/module/components/page-indicator/hooks/use-page-indicator-core.js +20 -0
  43. package/lib/module/components/page-indicator/hooks/use-page-indicator-core.js.map +1 -0
  44. package/lib/module/components/page-indicator/hooks/use-page-indicator-progress.js +68 -0
  45. package/lib/module/components/page-indicator/hooks/use-page-indicator-progress.js.map +1 -0
  46. package/lib/module/components/page-indicator/index.js +5 -0
  47. package/lib/module/components/page-indicator/index.js.map +1 -0
  48. package/lib/module/components/page-indicator/utils.js +127 -0
  49. package/lib/module/components/page-indicator/utils.js.map +1 -0
  50. package/lib/module/components/page-indicator/variants/DotIndicator/InactiveDot.js +50 -0
  51. package/lib/module/components/page-indicator/variants/DotIndicator/InactiveDot.js.map +1 -0
  52. package/lib/module/components/page-indicator/variants/DotIndicator/LoadingOrbit.js +165 -0
  53. package/lib/module/components/page-indicator/variants/DotIndicator/LoadingOrbit.js.map +1 -0
  54. package/lib/module/components/page-indicator/variants/DotIndicator/TravelingPill.js +60 -0
  55. package/lib/module/components/page-indicator/variants/DotIndicator/TravelingPill.js.map +1 -0
  56. package/lib/module/components/page-indicator/variants/DotIndicator/index.js +87 -0
  57. package/lib/module/components/page-indicator/variants/DotIndicator/index.js.map +1 -0
  58. package/lib/module/components/page-indicator/variants/DotIndicator/utils.js +28 -0
  59. package/lib/module/components/page-indicator/variants/DotIndicator/utils.js.map +1 -0
  60. package/lib/module/components/page-indicator/variants/PillIndicator/PillIndicatorDot.js +53 -0
  61. package/lib/module/components/page-indicator/variants/PillIndicator/PillIndicatorDot.js.map +1 -0
  62. package/lib/module/components/page-indicator/variants/PillIndicator/index.js +76 -0
  63. package/lib/module/components/page-indicator/variants/PillIndicator/index.js.map +1 -0
  64. package/lib/module/components/page-indicator/variants/PillIndicator/utils.js +47 -0
  65. package/lib/module/components/page-indicator/variants/PillIndicator/utils.js.map +1 -0
  66. package/lib/module/components/portal/PortalContext.js +20 -0
  67. package/lib/module/components/portal/PortalContext.js.map +1 -0
  68. package/lib/module/components/portal/PortalHost.js +23 -0
  69. package/lib/module/components/portal/PortalHost.js.map +1 -0
  70. package/lib/module/components/portal/PortalProvider.js +40 -0
  71. package/lib/module/components/portal/PortalProvider.js.map +1 -0
  72. package/lib/module/components/portal/index.js +6 -0
  73. package/lib/module/components/portal/index.js.map +1 -0
  74. package/lib/module/components/screen/Screen.js +100 -0
  75. package/lib/module/components/screen/Screen.js.map +1 -0
  76. package/lib/module/components/screen/ScreenContext.js +66 -0
  77. package/lib/module/components/screen/ScreenContext.js.map +1 -0
  78. package/lib/module/components/screen/ScreenFlatList.js +75 -0
  79. package/lib/module/components/screen/ScreenFlatList.js.map +1 -0
  80. package/lib/module/components/screen/ScreenScrollView.js +82 -0
  81. package/lib/module/components/screen/ScreenScrollView.js.map +1 -0
  82. package/lib/module/components/screen/index.js +7 -0
  83. package/lib/module/components/screen/index.js.map +1 -0
  84. package/lib/module/components/screen/utils.js +13 -0
  85. package/lib/module/components/screen/utils.js.map +1 -0
  86. package/lib/module/components/slot/Slot.js +38 -0
  87. package/lib/module/components/slot/Slot.js.map +1 -0
  88. package/lib/module/components/slot/index.js +10 -0
  89. package/lib/module/components/slot/index.js.map +1 -0
  90. package/lib/module/components/slot/utils.js +55 -0
  91. package/lib/module/components/slot/utils.js.map +1 -0
  92. package/lib/module/components/spinner/Spinner.js +132 -0
  93. package/lib/module/components/spinner/Spinner.js.map +1 -0
  94. package/lib/module/components/spinner/index.js +4 -0
  95. package/lib/module/components/spinner/index.js.map +1 -0
  96. package/lib/module/components/surface/Surface.js +74 -0
  97. package/lib/module/components/surface/Surface.js.map +1 -0
  98. package/lib/module/components/surface/index.js +4 -0
  99. package/lib/module/components/surface/index.js.map +1 -0
  100. package/lib/module/components/text/Text.js +67 -0
  101. package/lib/module/components/text/Text.js.map +1 -0
  102. package/lib/module/components/text/index.js +4 -0
  103. package/lib/module/components/text/index.js.map +1 -0
  104. package/lib/module/components/toast/Toast.js +350 -0
  105. package/lib/module/components/toast/Toast.js.map +1 -0
  106. package/lib/module/components/toast/ToastProvider.js +272 -0
  107. package/lib/module/components/toast/ToastProvider.js.map +1 -0
  108. package/lib/module/components/toast/index.js +5 -0
  109. package/lib/module/components/toast/index.js.map +1 -0
  110. package/lib/module/components/toast/parts/ToastAnimatedItem.js +168 -0
  111. package/lib/module/components/toast/parts/ToastAnimatedItem.js.map +1 -0
  112. package/lib/module/components/toast/types.js +4 -0
  113. package/lib/module/components/toast/types.js.map +1 -0
  114. package/lib/module/components/toast/utils/controller.js +19 -0
  115. package/lib/module/components/toast/utils/controller.js.map +1 -0
  116. package/lib/module/context/ThemeProvider.js +66 -0
  117. package/lib/module/context/ThemeProvider.js.map +1 -0
  118. package/lib/module/context/index.js +4 -0
  119. package/lib/module/context/index.js.map +1 -0
  120. package/lib/module/index.js +8 -0
  121. package/lib/module/index.js.map +1 -0
  122. package/lib/module/package.json +1 -0
  123. package/lib/module/theme/configure.js +152 -0
  124. package/lib/module/theme/configure.js.map +1 -0
  125. package/lib/module/theme/index.js +22 -0
  126. package/lib/module/theme/index.js.map +1 -0
  127. package/lib/module/theme/runtime.js +25 -0
  128. package/lib/module/theme/runtime.js.map +1 -0
  129. package/lib/module/theme/theme.js +121 -0
  130. package/lib/module/theme/theme.js.map +1 -0
  131. package/lib/module/theme/tokens/dark.js +109 -0
  132. package/lib/module/theme/tokens/dark.js.map +1 -0
  133. package/lib/module/theme/tokens/light.js +115 -0
  134. package/lib/module/theme/tokens/light.js.map +1 -0
  135. package/lib/module/theme/tokens/primitives.js +228 -0
  136. package/lib/module/theme/tokens/primitives.js.map +1 -0
  137. package/lib/module/theme/types.js +4 -0
  138. package/lib/module/theme/types.js.map +1 -0
  139. package/lib/module/types.js +2 -0
  140. package/lib/module/types.js.map +1 -0
  141. package/lib/module/utils/accent-utils.js +52 -0
  142. package/lib/module/utils/accent-utils.js.map +1 -0
  143. package/lib/module/utils/color-utils.js +53 -0
  144. package/lib/module/utils/color-utils.js.map +1 -0
  145. package/lib/module/utils/deep-merge.js +28 -0
  146. package/lib/module/utils/deep-merge.js.map +1 -0
  147. package/lib/module/utils/resolve-theme-color.js +20 -0
  148. package/lib/module/utils/resolve-theme-color.js.map +1 -0
  149. package/lib/module/utils/typography.js +13 -0
  150. package/lib/module/utils/typography.js.map +1 -0
  151. package/lib/module/utils/unflatten.js +35 -0
  152. package/lib/module/utils/unflatten.js.map +1 -0
  153. package/lib/typescript/package.json +1 -0
  154. package/lib/typescript/src/components/avatar/Avatar.d.ts +3 -0
  155. package/lib/typescript/src/components/avatar/Avatar.d.ts.map +1 -0
  156. package/lib/typescript/src/components/avatar/index.d.ts +3 -0
  157. package/lib/typescript/src/components/avatar/index.d.ts.map +1 -0
  158. package/lib/typescript/src/components/avatar/types.d.ts +30 -0
  159. package/lib/typescript/src/components/avatar/types.d.ts.map +1 -0
  160. package/lib/typescript/src/components/box/Box.d.ts +12 -0
  161. package/lib/typescript/src/components/box/Box.d.ts.map +1 -0
  162. package/lib/typescript/src/components/box/index.d.ts +4 -0
  163. package/lib/typescript/src/components/box/index.d.ts.map +1 -0
  164. package/lib/typescript/src/components/box/utils.d.ts +39 -0
  165. package/lib/typescript/src/components/box/utils.d.ts.map +1 -0
  166. package/lib/typescript/src/components/button/Button.d.ts +46 -0
  167. package/lib/typescript/src/components/button/Button.d.ts.map +1 -0
  168. package/lib/typescript/src/components/button/index.d.ts +3 -0
  169. package/lib/typescript/src/components/button/index.d.ts.map +1 -0
  170. package/lib/typescript/src/components/header/Header.d.ts +22 -0
  171. package/lib/typescript/src/components/header/Header.d.ts.map +1 -0
  172. package/lib/typescript/src/components/header/header-buttons.d.ts +16 -0
  173. package/lib/typescript/src/components/header/header-buttons.d.ts.map +1 -0
  174. package/lib/typescript/src/components/header/index.d.ts +7 -0
  175. package/lib/typescript/src/components/header/index.d.ts.map +1 -0
  176. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts +25 -0
  177. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -0
  178. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +28 -0
  179. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -0
  180. package/lib/typescript/src/components/icon/Icon.d.ts +22 -0
  181. package/lib/typescript/src/components/icon/Icon.d.ts.map +1 -0
  182. package/lib/typescript/src/components/icon/index.d.ts +3 -0
  183. package/lib/typescript/src/components/icon/index.d.ts.map +1 -0
  184. package/lib/typescript/src/components/icon/utils.d.ts +4 -0
  185. package/lib/typescript/src/components/icon/utils.d.ts.map +1 -0
  186. package/lib/typescript/src/components/index.d.ts +14 -0
  187. package/lib/typescript/src/components/index.d.ts.map +1 -0
  188. package/lib/typescript/src/components/page-indicator/PageIndicator.d.ts +48 -0
  189. package/lib/typescript/src/components/page-indicator/PageIndicator.d.ts.map +1 -0
  190. package/lib/typescript/src/components/page-indicator/PageIndicatorRoot.d.ts +13 -0
  191. package/lib/typescript/src/components/page-indicator/PageIndicatorRoot.d.ts.map +1 -0
  192. package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-colors.d.ts +14 -0
  193. package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-colors.d.ts.map +1 -0
  194. package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-core.d.ts +11 -0
  195. package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-core.d.ts.map +1 -0
  196. package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-progress.d.ts +19 -0
  197. package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-progress.d.ts.map +1 -0
  198. package/lib/typescript/src/components/page-indicator/index.d.ts +4 -0
  199. package/lib/typescript/src/components/page-indicator/index.d.ts.map +1 -0
  200. package/lib/typescript/src/components/page-indicator/utils.d.ts +36 -0
  201. package/lib/typescript/src/components/page-indicator/utils.d.ts.map +1 -0
  202. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/InactiveDot.d.ts +14 -0
  203. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/InactiveDot.d.ts.map +1 -0
  204. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/LoadingOrbit.d.ts +11 -0
  205. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/LoadingOrbit.d.ts.map +1 -0
  206. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/TravelingPill.d.ts +15 -0
  207. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/TravelingPill.d.ts.map +1 -0
  208. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/index.d.ts +32 -0
  209. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/index.d.ts.map +1 -0
  210. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/utils.d.ts +14 -0
  211. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/utils.d.ts.map +1 -0
  212. package/lib/typescript/src/components/page-indicator/variants/PillIndicator/PillIndicatorDot.d.ts +16 -0
  213. package/lib/typescript/src/components/page-indicator/variants/PillIndicator/PillIndicatorDot.d.ts.map +1 -0
  214. package/lib/typescript/src/components/page-indicator/variants/PillIndicator/index.d.ts +28 -0
  215. package/lib/typescript/src/components/page-indicator/variants/PillIndicator/index.d.ts.map +1 -0
  216. package/lib/typescript/src/components/page-indicator/variants/PillIndicator/utils.d.ts +21 -0
  217. package/lib/typescript/src/components/page-indicator/variants/PillIndicator/utils.d.ts.map +1 -0
  218. package/lib/typescript/src/components/portal/PortalContext.d.ts +19 -0
  219. package/lib/typescript/src/components/portal/PortalContext.d.ts.map +1 -0
  220. package/lib/typescript/src/components/portal/PortalHost.d.ts +7 -0
  221. package/lib/typescript/src/components/portal/PortalHost.d.ts.map +1 -0
  222. package/lib/typescript/src/components/portal/PortalProvider.d.ts +11 -0
  223. package/lib/typescript/src/components/portal/PortalProvider.d.ts.map +1 -0
  224. package/lib/typescript/src/components/portal/index.d.ts +6 -0
  225. package/lib/typescript/src/components/portal/index.d.ts.map +1 -0
  226. package/lib/typescript/src/components/screen/Screen.d.ts +53 -0
  227. package/lib/typescript/src/components/screen/Screen.d.ts.map +1 -0
  228. package/lib/typescript/src/components/screen/ScreenContext.d.ts +64 -0
  229. package/lib/typescript/src/components/screen/ScreenContext.d.ts.map +1 -0
  230. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts +26 -0
  231. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts.map +1 -0
  232. package/lib/typescript/src/components/screen/ScreenScrollView.d.ts +36 -0
  233. package/lib/typescript/src/components/screen/ScreenScrollView.d.ts.map +1 -0
  234. package/lib/typescript/src/components/screen/index.d.ts +9 -0
  235. package/lib/typescript/src/components/screen/index.d.ts.map +1 -0
  236. package/lib/typescript/src/components/screen/utils.d.ts +10 -0
  237. package/lib/typescript/src/components/screen/utils.d.ts.map +1 -0
  238. package/lib/typescript/src/components/slot/Slot.d.ts +17 -0
  239. package/lib/typescript/src/components/slot/Slot.d.ts.map +1 -0
  240. package/lib/typescript/src/components/slot/index.d.ts +8 -0
  241. package/lib/typescript/src/components/slot/index.d.ts.map +1 -0
  242. package/lib/typescript/src/components/slot/utils.d.ts +17 -0
  243. package/lib/typescript/src/components/slot/utils.d.ts.map +1 -0
  244. package/lib/typescript/src/components/spinner/Spinner.d.ts +18 -0
  245. package/lib/typescript/src/components/spinner/Spinner.d.ts.map +1 -0
  246. package/lib/typescript/src/components/spinner/index.d.ts +3 -0
  247. package/lib/typescript/src/components/spinner/index.d.ts.map +1 -0
  248. package/lib/typescript/src/components/surface/Surface.d.ts +31 -0
  249. package/lib/typescript/src/components/surface/Surface.d.ts.map +1 -0
  250. package/lib/typescript/src/components/surface/index.d.ts +3 -0
  251. package/lib/typescript/src/components/surface/index.d.ts.map +1 -0
  252. package/lib/typescript/src/components/text/Text.d.ts +23 -0
  253. package/lib/typescript/src/components/text/Text.d.ts.map +1 -0
  254. package/lib/typescript/src/components/text/index.d.ts +3 -0
  255. package/lib/typescript/src/components/text/index.d.ts.map +1 -0
  256. package/lib/typescript/src/components/toast/Toast.d.ts +71 -0
  257. package/lib/typescript/src/components/toast/Toast.d.ts.map +1 -0
  258. package/lib/typescript/src/components/toast/ToastProvider.d.ts +4 -0
  259. package/lib/typescript/src/components/toast/ToastProvider.d.ts.map +1 -0
  260. package/lib/typescript/src/components/toast/index.d.ts +5 -0
  261. package/lib/typescript/src/components/toast/index.d.ts.map +1 -0
  262. package/lib/typescript/src/components/toast/parts/ToastAnimatedItem.d.ts +18 -0
  263. package/lib/typescript/src/components/toast/parts/ToastAnimatedItem.d.ts.map +1 -0
  264. package/lib/typescript/src/components/toast/types.d.ts +62 -0
  265. package/lib/typescript/src/components/toast/types.d.ts.map +1 -0
  266. package/lib/typescript/src/components/toast/utils/controller.d.ts +5 -0
  267. package/lib/typescript/src/components/toast/utils/controller.d.ts.map +1 -0
  268. package/lib/typescript/src/context/ThemeProvider.d.ts +29 -0
  269. package/lib/typescript/src/context/ThemeProvider.d.ts.map +1 -0
  270. package/lib/typescript/src/context/index.d.ts +3 -0
  271. package/lib/typescript/src/context/index.d.ts.map +1 -0
  272. package/lib/typescript/src/index.d.ts +4 -0
  273. package/lib/typescript/src/index.d.ts.map +1 -0
  274. package/lib/typescript/src/theme/configure.d.ts +47 -0
  275. package/lib/typescript/src/theme/configure.d.ts.map +1 -0
  276. package/lib/typescript/src/theme/index.d.ts +11 -0
  277. package/lib/typescript/src/theme/index.d.ts.map +1 -0
  278. package/lib/typescript/src/theme/runtime.d.ts +7 -0
  279. package/lib/typescript/src/theme/runtime.d.ts.map +1 -0
  280. package/lib/typescript/src/theme/theme.d.ts +22 -0
  281. package/lib/typescript/src/theme/theme.d.ts.map +1 -0
  282. package/lib/typescript/src/theme/tokens/dark.d.ts +100 -0
  283. package/lib/typescript/src/theme/tokens/dark.d.ts.map +1 -0
  284. package/lib/typescript/src/theme/tokens/light.d.ts +106 -0
  285. package/lib/typescript/src/theme/tokens/light.d.ts.map +1 -0
  286. package/lib/typescript/src/theme/tokens/primitives.d.ts +212 -0
  287. package/lib/typescript/src/theme/tokens/primitives.d.ts.map +1 -0
  288. package/lib/typescript/src/theme/types.d.ts +273 -0
  289. package/lib/typescript/src/theme/types.d.ts.map +1 -0
  290. package/lib/typescript/src/types.d.ts +4 -0
  291. package/lib/typescript/src/types.d.ts.map +1 -0
  292. package/lib/typescript/src/utils/accent-utils.d.ts +4 -0
  293. package/lib/typescript/src/utils/accent-utils.d.ts.map +1 -0
  294. package/lib/typescript/src/utils/color-utils.d.ts +11 -0
  295. package/lib/typescript/src/utils/color-utils.d.ts.map +1 -0
  296. package/lib/typescript/src/utils/deep-merge.d.ts +11 -0
  297. package/lib/typescript/src/utils/deep-merge.d.ts.map +1 -0
  298. package/lib/typescript/src/utils/resolve-theme-color.d.ts +12 -0
  299. package/lib/typescript/src/utils/resolve-theme-color.d.ts.map +1 -0
  300. package/lib/typescript/src/utils/typography.d.ts +6 -0
  301. package/lib/typescript/src/utils/typography.d.ts.map +1 -0
  302. package/lib/typescript/src/utils/unflatten.d.ts +12 -0
  303. package/lib/typescript/src/utils/unflatten.d.ts.map +1 -0
  304. package/package.json +137 -0
  305. package/src/components/avatar/Avatar.tsx +146 -0
  306. package/src/components/avatar/index.ts +8 -0
  307. package/src/components/avatar/types.ts +35 -0
  308. package/src/components/box/Box.tsx +59 -0
  309. package/src/components/box/index.ts +8 -0
  310. package/src/components/box/utils.ts +181 -0
  311. package/src/components/button/Button.tsx +313 -0
  312. package/src/components/button/index.ts +8 -0
  313. package/src/components/header/Header.ts +22 -0
  314. package/src/components/header/header-buttons.tsx +103 -0
  315. package/src/components/header/index.ts +11 -0
  316. package/src/components/header/variants/LargeTitleHeader/index.tsx +390 -0
  317. package/src/components/header/variants/TitleHeader/index.tsx +194 -0
  318. package/src/components/icon/Icon.tsx +74 -0
  319. package/src/components/icon/index.ts +2 -0
  320. package/src/components/icon/utils.tsx +147 -0
  321. package/src/components/index.ts +13 -0
  322. package/src/components/page-indicator/PageIndicator.tsx +100 -0
  323. package/src/components/page-indicator/PageIndicatorRoot.tsx +41 -0
  324. package/src/components/page-indicator/hooks/use-page-indicator-colors.ts +65 -0
  325. package/src/components/page-indicator/hooks/use-page-indicator-core.ts +34 -0
  326. package/src/components/page-indicator/hooks/use-page-indicator-progress.ts +122 -0
  327. package/src/components/page-indicator/index.ts +13 -0
  328. package/src/components/page-indicator/utils.ts +208 -0
  329. package/src/components/page-indicator/variants/DotIndicator/InactiveDot.tsx +70 -0
  330. package/src/components/page-indicator/variants/DotIndicator/LoadingOrbit.tsx +201 -0
  331. package/src/components/page-indicator/variants/DotIndicator/TravelingPill.tsx +80 -0
  332. package/src/components/page-indicator/variants/DotIndicator/index.tsx +145 -0
  333. package/src/components/page-indicator/variants/DotIndicator/utils.ts +28 -0
  334. package/src/components/page-indicator/variants/PillIndicator/PillIndicatorDot.tsx +107 -0
  335. package/src/components/page-indicator/variants/PillIndicator/index.tsx +149 -0
  336. package/src/components/page-indicator/variants/PillIndicator/utils.ts +63 -0
  337. package/src/components/portal/PortalContext.tsx +27 -0
  338. package/src/components/portal/PortalHost.tsx +20 -0
  339. package/src/components/portal/PortalProvider.tsx +44 -0
  340. package/src/components/portal/index.ts +5 -0
  341. package/src/components/screen/Screen.tsx +163 -0
  342. package/src/components/screen/ScreenContext.tsx +144 -0
  343. package/src/components/screen/ScreenFlatList.tsx +121 -0
  344. package/src/components/screen/ScreenScrollView.tsx +130 -0
  345. package/src/components/screen/index.ts +8 -0
  346. package/src/components/screen/utils.ts +17 -0
  347. package/src/components/slot/Slot.tsx +56 -0
  348. package/src/components/slot/index.ts +8 -0
  349. package/src/components/slot/utils.ts +61 -0
  350. package/src/components/spinner/Spinner.tsx +153 -0
  351. package/src/components/spinner/index.ts +2 -0
  352. package/src/components/surface/Surface.tsx +100 -0
  353. package/src/components/surface/index.ts +2 -0
  354. package/src/components/text/Text.tsx +115 -0
  355. package/src/components/text/index.ts +2 -0
  356. package/src/components/toast/Toast.tsx +477 -0
  357. package/src/components/toast/ToastProvider.tsx +403 -0
  358. package/src/components/toast/index.ts +26 -0
  359. package/src/components/toast/parts/ToastAnimatedItem.tsx +184 -0
  360. package/src/components/toast/types.ts +75 -0
  361. package/src/components/toast/utils/controller.ts +23 -0
  362. package/src/context/ThemeProvider.tsx +97 -0
  363. package/src/context/index.ts +2 -0
  364. package/src/index.tsx +6 -0
  365. package/src/theme/configure.ts +215 -0
  366. package/src/theme/index.ts +72 -0
  367. package/src/theme/runtime.ts +27 -0
  368. package/src/theme/theme.ts +152 -0
  369. package/src/theme/tokens/dark.ts +117 -0
  370. package/src/theme/tokens/light.ts +123 -0
  371. package/src/theme/tokens/primitives.ts +232 -0
  372. package/src/theme/types.ts +384 -0
  373. package/src/types.ts +1 -0
  374. package/src/utils/accent-utils.ts +58 -0
  375. package/src/utils/color-utils.ts +51 -0
  376. package/src/utils/deep-merge.ts +36 -0
  377. package/src/utils/resolve-theme-color.ts +27 -0
  378. package/src/utils/typography.ts +15 -0
  379. package/src/utils/unflatten.ts +36 -0
@@ -0,0 +1,144 @@
1
+ import {
2
+ createContext,
3
+ type ReactNode,
4
+ useCallback,
5
+ useContext,
6
+ useMemo,
7
+ useState,
8
+ } from 'react';
9
+
10
+ import Animated, {
11
+ type AnimatedRef,
12
+ type ScrollHandlerProcessed,
13
+ type SharedValue,
14
+ useAnimatedRef,
15
+ useAnimatedScrollHandler,
16
+ useSharedValue,
17
+ } from 'react-native-reanimated';
18
+
19
+ export type ScreenScrollRef = AnimatedRef<Animated.ScrollView>;
20
+
21
+ export interface ScreenContextValue {
22
+ /**
23
+ * Whether scroll containers apply `layout.screen.margin` padding.
24
+ * Overridable per `Screen.ScrollView` / `Screen.FlatList`.
25
+ */
26
+ margins: boolean;
27
+ /** Whether a `Screen.Header` is present. Used by scroll containers to add top inset. */
28
+ hasHeader: boolean;
29
+ /**
30
+ * Live vertical scroll offset of the screen's scroll container, driven by
31
+ * `Screen.ScrollView` / `Screen.FlatList`. Headers (and consumer UI) read it
32
+ * to animate on scroll.
33
+ */
34
+ scrollY: SharedValue<number>;
35
+ /**
36
+ * Scroll distance (px) over which the active header collapses — measured by
37
+ * the header (e.g. large-title portal height, parallax hero height). `0`
38
+ * disables snap and collapse-range animations (`Header.Title` screens).
39
+ */
40
+ headerCollapseHeight: SharedValue<number>;
41
+ /**
42
+ * Native `snapToOffsets` for the scroll container. `[0, height]` when a
43
+ * collapsible header is active; `undefined` otherwise.
44
+ */
45
+ headerSnapOffsets: number[] | undefined;
46
+ /** Updates collapse height for animations and native snap offsets. */
47
+ setHeaderCollapseHeight: (height: number) => void;
48
+ /** Shared scroll container ref — bound by `Screen.ScrollView` / `Screen.FlatList`. */
49
+ scrollRef: ScreenScrollRef;
50
+ /** Scroll handler that mirrors offset into `scrollY`. */
51
+ scrollHandler: ScrollHandlerProcessed<Record<string, unknown>>;
52
+ /** Whether the screen is hosted inside a tab navigator (affects edges). */
53
+ isInTabView: boolean;
54
+ }
55
+
56
+ export const ScreenContext = createContext<ScreenContextValue | null>(null);
57
+
58
+ export interface ScreenScrollProviderProps {
59
+ children: ReactNode;
60
+ isInTabView: boolean;
61
+ /** Apply `layout.screen.margin` to scroll containers. Default true. */
62
+ margins?: boolean;
63
+ /** Whether a `Screen.Header` child is present. Default false. */
64
+ hasHeader?: boolean;
65
+ }
66
+
67
+ /**
68
+ * Owns the screen's shared scroll ref, offset, snap offsets, and header collapse
69
+ * metrics. Header variants call {@link ScreenContextValue.setHeaderCollapseHeight};
70
+ * scroll containers bind the ref via {@link useScreen}.
71
+ */
72
+ export function ScreenScrollProvider({
73
+ children,
74
+ isInTabView,
75
+ margins = true,
76
+ hasHeader = false,
77
+ }: ScreenScrollProviderProps) {
78
+ const scrollY = useSharedValue(0);
79
+ const headerCollapseHeight = useSharedValue(0);
80
+ const [headerSnapOffsets, setHeaderSnapOffsets] = useState<number[]>();
81
+ const scrollRef = useAnimatedRef<Animated.ScrollView>();
82
+
83
+ const setHeaderCollapseHeight = useCallback(
84
+ (height: number) => {
85
+ const rounded = Math.round(height);
86
+ headerCollapseHeight.value = rounded;
87
+ setHeaderSnapOffsets(rounded > 0 ? [0, rounded] : undefined);
88
+ },
89
+ [headerCollapseHeight]
90
+ );
91
+
92
+ const scrollHandler = useAnimatedScrollHandler({
93
+ onScroll: (event) => {
94
+ scrollY.value = event.contentOffset.y;
95
+ },
96
+ });
97
+
98
+ const value = useMemo<ScreenContextValue>(
99
+ () => ({
100
+ margins,
101
+ hasHeader,
102
+ scrollY,
103
+ headerCollapseHeight,
104
+ headerSnapOffsets,
105
+ setHeaderCollapseHeight,
106
+ scrollRef,
107
+ scrollHandler,
108
+ isInTabView,
109
+ }),
110
+ [
111
+ margins,
112
+ hasHeader,
113
+ scrollY,
114
+ headerCollapseHeight,
115
+ headerSnapOffsets,
116
+ setHeaderCollapseHeight,
117
+ scrollRef,
118
+ scrollHandler,
119
+ isInTabView,
120
+ ]
121
+ );
122
+
123
+ return (
124
+ <ScreenContext.Provider value={value}>{children}</ScreenContext.Provider>
125
+ );
126
+ }
127
+
128
+ /**
129
+ * Access the enclosing `Screen`'s shared scroll state, ref, and snap offsets.
130
+ *
131
+ * @example
132
+ * ```tsx
133
+ * const { scrollY, scrollRef, headerSnapOffsets } = useScreen();
134
+ * ```
135
+ *
136
+ * @throws if called outside a `Screen`.
137
+ */
138
+ export function useScreen(): ScreenContextValue {
139
+ const context = useContext(ScreenContext);
140
+ if (!context) {
141
+ throw new Error('useScreen must be used within a Screen component');
142
+ }
143
+ return context;
144
+ }
@@ -0,0 +1,121 @@
1
+ import {
2
+ createElement,
3
+ type FunctionComponent,
4
+ isValidElement,
5
+ type ReactElement,
6
+ } from 'react';
7
+ import {
8
+ type FlatListProps,
9
+ type NativeScrollEvent,
10
+ type NativeSyntheticEvent,
11
+ View,
12
+ } from 'react-native';
13
+
14
+ import Animated, {
15
+ type AnimatedRef,
16
+ useAnimatedScrollHandler,
17
+ useComposedEventHandler,
18
+ } from 'react-native-reanimated';
19
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
20
+ import { useUnistyles } from 'react-native-unistyles';
21
+
22
+ import { PortalHost } from '../portal';
23
+ import { useScreen } from './ScreenContext';
24
+ import { resolveScreenContentInsets } from './utils';
25
+
26
+ export interface ScreenFlatListProps<T>
27
+ extends Omit<FlatListProps<T>, 'CellRendererComponent'> {
28
+ /**
29
+ * Apply `layout.screen.margin` padding. Defaults to the enclosing `Screen`'s
30
+ * `margins` value.
31
+ */
32
+ margins?: boolean;
33
+ /**
34
+ * Extra bottom padding on the list content — clearance for a tab bar or home
35
+ * indicator. Defaults to the `layout.screen.margin.y` token when margins are
36
+ * enabled, otherwise `0`.
37
+ */
38
+ bottomInset?: number;
39
+ }
40
+
41
+ function renderListHeader(
42
+ ListHeaderComponent: FlatListProps<unknown>['ListHeaderComponent']
43
+ ): ReactElement | null {
44
+ if (!ListHeaderComponent) return null;
45
+ if (isValidElement(ListHeaderComponent)) return ListHeaderComponent;
46
+ return createElement(ListHeaderComponent as FunctionComponent);
47
+ }
48
+
49
+ /**
50
+ * `FlatList` variant of `Screen.ScrollView`: binds the screen's shared scroll
51
+ * ref, mirrors offset into `scrollY`, hosts the portal target above the list,
52
+ * and applies token-driven content padding.
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * <Screen.FlatList data={items} renderItem={({ item }) => <Row item={item} />} />
57
+ * ```
58
+ */
59
+ export function ScreenFlatList<T>({
60
+ style,
61
+ contentContainerStyle,
62
+ ListHeaderComponent,
63
+ margins,
64
+ bottomInset,
65
+ onScroll,
66
+ ...rest
67
+ }: ScreenFlatListProps<T>) {
68
+ const { theme } = useUnistyles();
69
+ const { top } = useSafeAreaInsets();
70
+ const {
71
+ scrollRef,
72
+ scrollHandler,
73
+ headerSnapOffsets,
74
+ margins: screenMargins,
75
+ hasHeader,
76
+ } = useScreen();
77
+
78
+ const margin = theme.layout.screen.margin;
79
+ const marginsEnabled = margins ?? screenMargins;
80
+ const compactHeaderHeight = hasHeader ? top + theme.layout.header.height : 0;
81
+ const { contentPadding } = resolveScreenContentInsets(
82
+ margin,
83
+ marginsEnabled,
84
+ bottomInset,
85
+ compactHeaderHeight
86
+ );
87
+
88
+ const jsScrollHandler = useAnimatedScrollHandler(
89
+ (event) => {
90
+ if (onScroll)
91
+ onScroll(event as unknown as NativeSyntheticEvent<NativeScrollEvent>);
92
+ },
93
+ [onScroll]
94
+ );
95
+
96
+ const composedHandler = useComposedEventHandler([
97
+ scrollHandler,
98
+ jsScrollHandler,
99
+ ]);
100
+
101
+ const headerComponent = (
102
+ <View>
103
+ <PortalHost />
104
+ {renderListHeader(ListHeaderComponent)}
105
+ </View>
106
+ );
107
+
108
+ return (
109
+ <Animated.FlatList
110
+ ref={scrollRef as unknown as AnimatedRef<Animated.FlatList<T>>}
111
+ style={style}
112
+ contentContainerStyle={[contentPadding, contentContainerStyle]}
113
+ ListHeaderComponent={headerComponent}
114
+ showsVerticalScrollIndicator={false}
115
+ scrollEventThrottle={16}
116
+ snapToOffsets={headerSnapOffsets}
117
+ {...rest}
118
+ onScroll={composedHandler}
119
+ />
120
+ );
121
+ }
@@ -0,0 +1,130 @@
1
+ import type { ReactNode } from 'react';
2
+ import {
3
+ type NativeScrollEvent,
4
+ type NativeSyntheticEvent,
5
+ type ScrollViewProps,
6
+ View,
7
+ } from 'react-native';
8
+
9
+ import Animated, {
10
+ type ScrollHandlerProcessed,
11
+ useAnimatedScrollHandler,
12
+ useComposedEventHandler,
13
+ } from 'react-native-reanimated';
14
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
15
+ import { useUnistyles } from 'react-native-unistyles';
16
+
17
+ import { PortalHost } from '../portal';
18
+ import { useScreen } from './ScreenContext';
19
+ import { resolveScreenContentInsets } from './utils';
20
+
21
+ export interface ScreenScrollViewProps extends ScrollViewProps {
22
+ children?: ReactNode;
23
+ /**
24
+ * Apply `layout.screen.margin` padding. Defaults to the enclosing `Screen`'s
25
+ * `margins` value.
26
+ */
27
+ hasMargins?: boolean;
28
+ /**
29
+ * Extra bottom padding on the scroll content — clearance for a tab bar or
30
+ * home indicator. Defaults to the `layout.screen.margin.y` token when margins
31
+ * are enabled, otherwise `0`.
32
+ */
33
+ bottomInset?: number;
34
+ /**
35
+ * Optional Reanimated scroll handler composed with the screen's own scroll
36
+ * tracking and any JS `onScroll` callback.
37
+ */
38
+ scrollHandler?: ScrollHandlerProcessed<Record<string, unknown>>;
39
+ }
40
+
41
+ /**
42
+ * Scrollable content container for a `Screen`. Binds the screen's shared scroll
43
+ * ref, mirrors offset into `scrollY`, hosts the portal target at the top (where
44
+ * header content is injected), and applies token-driven content padding.
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * <Screen.ScrollView>
49
+ * <Text>Scrollable content</Text>
50
+ * </Screen.ScrollView>
51
+ * ```
52
+ */
53
+ export function ScreenScrollView({
54
+ children,
55
+ style,
56
+ contentContainerStyle,
57
+ hasMargins,
58
+ bottomInset,
59
+ scrollHandler: externalScrollHandler,
60
+ onScroll: onScrollProp,
61
+ horizontal = false,
62
+ ...rest
63
+ }: ScreenScrollViewProps) {
64
+ const { theme } = useUnistyles();
65
+ const { top } = useSafeAreaInsets();
66
+ const {
67
+ scrollRef,
68
+ scrollHandler: screenScrollHandler,
69
+ headerSnapOffsets,
70
+ margins: screenMargins,
71
+ hasHeader,
72
+ } = useScreen();
73
+
74
+ const jsScrollHandler = useAnimatedScrollHandler(
75
+ onScrollProp
76
+ ? {
77
+ onScroll: (event) => {
78
+ onScrollProp({
79
+ nativeEvent: event,
80
+ } as unknown as NativeSyntheticEvent<NativeScrollEvent>);
81
+ },
82
+ }
83
+ : {}
84
+ );
85
+
86
+ const handlers: ScrollHandlerProcessed<Record<string, unknown>>[] = [
87
+ screenScrollHandler,
88
+ ...(externalScrollHandler ? [externalScrollHandler] : []),
89
+ ...(onScrollProp ? [jsScrollHandler] : []),
90
+ ];
91
+
92
+ const composedHandler = useComposedEventHandler(handlers);
93
+
94
+ const margin = theme.layout.screen.margin;
95
+ const marginsEnabled = hasMargins ?? screenMargins;
96
+ const compactHeaderHeight = hasHeader ? top + theme.layout.header.height : 0;
97
+ const { contentPadding } = resolveScreenContentInsets(
98
+ margin,
99
+ marginsEnabled,
100
+ bottomInset,
101
+ compactHeaderHeight
102
+ );
103
+
104
+ return (
105
+ <Animated.ScrollView
106
+ ref={scrollRef}
107
+ style={style}
108
+ horizontal={horizontal}
109
+ showsVerticalScrollIndicator={false}
110
+ scrollEventThrottle={16}
111
+ contentContainerStyle={contentPadding}
112
+ snapToOffsets={headerSnapOffsets}
113
+ {...rest}
114
+ onScroll={composedHandler}
115
+ >
116
+ <View>
117
+ <PortalHost />
118
+ </View>
119
+ <View
120
+ style={[
121
+ contentContainerStyle,
122
+ { flexDirection: horizontal ? 'row' : 'column' },
123
+ ]}
124
+ >
125
+ {/* content spacing */}
126
+ {children}
127
+ </View>
128
+ </Animated.ScrollView>
129
+ );
130
+ }
@@ -0,0 +1,8 @@
1
+ export type { ScreenHeaderProps, ScreenProps } from './Screen';
2
+ export { Screen } from './Screen';
3
+ export type { ScreenContextValue } from './ScreenContext';
4
+ export { useScreen } from './ScreenContext';
5
+ export type { ScreenFlatListProps } from './ScreenFlatList';
6
+ export { ScreenFlatList } from './ScreenFlatList';
7
+ export type { ScreenScrollViewProps } from './ScreenScrollView';
8
+ export { ScreenScrollView } from './ScreenScrollView';
@@ -0,0 +1,17 @@
1
+ import type { LayoutTokens } from '#theme/types';
2
+
3
+ export function resolveScreenContentInsets(
4
+ margin: LayoutTokens['screen']['margin'],
5
+ margins: boolean,
6
+ bottomInset?: number,
7
+ topInset?: number
8
+ ) {
9
+ return {
10
+ contentPadding: {
11
+ flexGrow: 1 as const,
12
+ paddingTop: topInset ?? 0,
13
+ paddingHorizontal: margins ? margin.x : 0,
14
+ paddingBottom: bottomInset ?? (margins ? margin.y : 0),
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,56 @@
1
+ import {
2
+ cloneElement,
3
+ isValidElement,
4
+ type ReactElement,
5
+ type ReactNode,
6
+ type Ref,
7
+ } from 'react';
8
+
9
+ import { composeRefs, mergeProps } from './utils';
10
+
11
+ type AnyProps = Record<string, unknown>;
12
+
13
+ export interface SlotProps {
14
+ /** Props (incl. pre-merged `style` and optional `ref`) to merge onto the child. */
15
+ slotProps: AnyProps;
16
+ /** Name used in dev warnings. */
17
+ ownerName?: string;
18
+ children: ReactNode;
19
+ }
20
+
21
+ /**
22
+ * Radix-style Slot: merges `slotProps` onto a single child element instead of
23
+ * rendering a wrapper. Composes `on*` handlers (child first), `style`
24
+ * (`[slotStyle, childStyle]`), and refs.
25
+ */
26
+ export function Slot({
27
+ slotProps,
28
+ ownerName = 'Component',
29
+ children,
30
+ }: SlotProps): ReactElement | null {
31
+ if (!isValidElement(children)) {
32
+ if (__DEV__ && children != null) {
33
+ console.error(
34
+ `[${ownerName}] \`asChild\` expects exactly one React element child.`
35
+ );
36
+ }
37
+ return null;
38
+ }
39
+
40
+ const child = children as ReactElement;
41
+ const childProps = (child.props ?? {}) as AnyProps;
42
+
43
+ const { ref: slotRef, ...restSlotProps } = slotProps;
44
+ const { ref: childRef, ...restChildProps } = childProps;
45
+
46
+ const merged = mergeProps(restSlotProps, restChildProps);
47
+ const composedRef = composeRefs(
48
+ slotRef as Ref<unknown> | undefined,
49
+ childRef as Ref<unknown> | undefined
50
+ );
51
+
52
+ return cloneElement(child, {
53
+ ...merged,
54
+ ref: composedRef,
55
+ } as Partial<unknown> & AnyProps);
56
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Low-level composition helpers for advanced use cases (custom primitives,
3
+ * `asChild` patterns). Most apps should use component APIs directly.
4
+ */
5
+
6
+ export type { SlotProps } from './Slot';
7
+ export { Slot } from './Slot';
8
+ export { composeRefs, mergeProps } from './utils';
@@ -0,0 +1,61 @@
1
+ import type { MutableRefObject, Ref } from 'react';
2
+
3
+ function assignRef<T>(ref: Ref<T> | undefined, value: T | null): void {
4
+ if (typeof ref === 'function') {
5
+ ref(value);
6
+ } else if (ref != null) {
7
+ (ref as MutableRefObject<T | null>).current = value;
8
+ }
9
+ }
10
+
11
+ /** Composes multiple refs so each receives the node. */
12
+ export function composeRefs<T>(...refs: (Ref<T> | undefined)[]): Ref<T> {
13
+ return (node: T | null) => {
14
+ for (const ref of refs) {
15
+ assignRef(ref, node);
16
+ }
17
+ };
18
+ }
19
+
20
+ type AnyProps = Record<string, unknown>;
21
+
22
+ const isEventHandler = (key: string): boolean => /^on[A-Z]/.test(key);
23
+
24
+ /**
25
+ * Merges slot props with the child's own props (child wins), following the
26
+ * Radix Slot rules:
27
+ * - `on*` handlers are composed (child runs first, then slot),
28
+ * - `style` is merged as `[slotStyle, childStyle]`,
29
+ * - all other child props override slot props.
30
+ *
31
+ * `ref`/`style` are intentionally not handled here — the caller composes refs
32
+ * and passes a pre-merged `style` via `slotProps`.
33
+ */
34
+ export function mergeProps(
35
+ slotProps: AnyProps,
36
+ childProps: AnyProps
37
+ ): AnyProps {
38
+ const merged: AnyProps = { ...slotProps };
39
+
40
+ for (const key of Object.keys(childProps)) {
41
+ const slotValue = slotProps[key];
42
+ const childValue = childProps[key];
43
+
44
+ if (isEventHandler(key)) {
45
+ if (typeof slotValue === 'function' && typeof childValue === 'function') {
46
+ merged[key] = (...args: unknown[]) => {
47
+ (childValue as (...a: unknown[]) => void)(...args);
48
+ (slotValue as (...a: unknown[]) => void)(...args);
49
+ };
50
+ } else {
51
+ merged[key] = childValue ?? slotValue;
52
+ }
53
+ } else if (key === 'style') {
54
+ merged.style = [slotValue, childValue].filter(Boolean);
55
+ } else {
56
+ merged[key] = childValue;
57
+ }
58
+ }
59
+
60
+ return merged;
61
+ }
@@ -0,0 +1,153 @@
1
+ import { memo, useEffect, useId } from 'react';
2
+
3
+ import Animated, {
4
+ cancelAnimation,
5
+ Easing,
6
+ useAnimatedStyle,
7
+ useSharedValue,
8
+ withRepeat,
9
+ withTiming,
10
+ } from 'react-native-reanimated';
11
+ import Svg, { Defs, G, LinearGradient, Path, Stop } from 'react-native-svg';
12
+
13
+ import { useUnistyles } from 'react-native-unistyles';
14
+
15
+ import type { ColorToken } from '#theme/types';
16
+ import { resolveThemeColor } from '#utils/resolve-theme-color';
17
+
18
+ export type SpinnerSize = 'sm' | 'md' | 'lg';
19
+
20
+ export interface SpinnerProps {
21
+ /** Spinner diameter preset. Defaults to `'md'`. */
22
+ size?: SpinnerSize;
23
+ /** Theme color token or raw CSS color. Defaults to the accent color. */
24
+ color?: ColorToken;
25
+ }
26
+
27
+ const DIMENSION: Record<SpinnerSize, number> = { sm: 16, md: 24, lg: 32 };
28
+
29
+ const ROTATION_DURATION = 900;
30
+
31
+ // HeroUI spinner arcs — two gradient-filled segments in a 24×24 viewBox.
32
+ const LEFT_ARC =
33
+ 'M8.749.021a1.5 1.5 0 0 1 .497 2.958A7.5 7.5 0 0 0 3 10.375a7.5 7.5 0 0 0 7.5 7.5v3c-5.799 0-10.5-4.7-10.5-10.5C0 5.23 3.726.865 8.749.021';
34
+ const RIGHT_ARC =
35
+ 'M15.392 2.673a1.5 1.5 0 0 1 2.119-.115A10.48 10.48 0 0 1 21 10.375c0 5.8-4.701 10.5-10.5 10.5v-3a7.5 7.5 0 0 0 5.007-13.084a1.5 1.5 0 0 1-.115-2.118';
36
+ const ARC_TRANSFORM = 'translate(1.5 1.625)';
37
+
38
+ interface SpinnerIconProps {
39
+ size: number;
40
+ color: string;
41
+ gradientId1: string;
42
+ gradientId2: string;
43
+ }
44
+
45
+ const SpinnerIcon = memo(function SpinnerIcon({
46
+ size,
47
+ color,
48
+ gradientId1,
49
+ gradientId2,
50
+ }: SpinnerIconProps) {
51
+ return (
52
+ <Svg width={size} height={size} viewBox="0 0 24 24">
53
+ <Defs>
54
+ <LinearGradient
55
+ id={gradientId1}
56
+ x1="50%"
57
+ x2="50%"
58
+ y1="5.271%"
59
+ y2="91.793%"
60
+ >
61
+ <Stop offset="0%" stopColor={color} />
62
+ <Stop offset="100%" stopColor={color} stopOpacity={0.55} />
63
+ </LinearGradient>
64
+ <LinearGradient
65
+ id={gradientId2}
66
+ x1="50%"
67
+ x2="50%"
68
+ y1="15.24%"
69
+ y2="87.15%"
70
+ >
71
+ <Stop offset="0%" stopColor={color} stopOpacity={0} />
72
+ <Stop offset="100%" stopColor={color} stopOpacity={0.55} />
73
+ </LinearGradient>
74
+ </Defs>
75
+ <G fill="none">
76
+ <Path
77
+ d={LEFT_ARC}
78
+ fill={`url(#${gradientId1})`}
79
+ transform={ARC_TRANSFORM}
80
+ />
81
+ <Path
82
+ d={RIGHT_ARC}
83
+ fill={`url(#${gradientId2})`}
84
+ transform={ARC_TRANSFORM}
85
+ />
86
+ </G>
87
+ </Svg>
88
+ );
89
+ });
90
+
91
+ /**
92
+ * Animated loading spinner. Used by `Button` when `isLoading` is set.
93
+ *
94
+ * @example
95
+ * ```tsx
96
+ * <Spinner size="md" color="content.accent" />
97
+ * ```
98
+ */
99
+ export function Spinner({ size = 'md', color = 'text.accent' }: SpinnerProps) {
100
+ const { theme } = useUnistyles();
101
+ const id = useId();
102
+ const gradientId1 = `spinner-def-1-${id}`;
103
+ const gradientId2 = `spinner-def-2-${id}`;
104
+ const resolvedColor =
105
+ resolveThemeColor(color, theme) ??
106
+ (theme.color as unknown as Record<string, string | undefined>)[
107
+ 'text.accent'
108
+ ] ??
109
+ '';
110
+
111
+ const dim = DIMENSION[size];
112
+ const rotation = useSharedValue(0);
113
+
114
+ useEffect(() => {
115
+ // Fast refresh can remount without running cleanup — cancel any stale loop first.
116
+ cancelAnimation(rotation);
117
+ rotation.value = 0;
118
+ rotation.value = withRepeat(
119
+ withTiming(360, {
120
+ duration: ROTATION_DURATION,
121
+ easing: Easing.linear,
122
+ }),
123
+ -1,
124
+ false
125
+ );
126
+
127
+ return () => {
128
+ cancelAnimation(rotation);
129
+ rotation.value = 0;
130
+ };
131
+ }, [rotation]);
132
+
133
+ const animStyle = useAnimatedStyle(() => ({
134
+ transform: [{ rotate: `${rotation.value}deg` }],
135
+ }));
136
+
137
+ return (
138
+ <Animated.View
139
+ accessible
140
+ accessibilityRole="progressbar"
141
+ accessibilityLabel="Loading"
142
+ collapsable={false}
143
+ style={[{ width: dim, height: dim }, animStyle]}
144
+ >
145
+ <SpinnerIcon
146
+ size={dim}
147
+ color={resolvedColor}
148
+ gradientId1={gradientId1}
149
+ gradientId2={gradientId2}
150
+ />
151
+ </Animated.View>
152
+ );
153
+ }
@@ -0,0 +1,2 @@
1
+ export type { SpinnerProps, SpinnerSize } from './Spinner';
2
+ export { Spinner } from './Spinner';