react-native-terra-ui 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/lib/module/components/avatar/Avatar.js +118 -35
  3. package/lib/module/components/avatar/Avatar.js.map +1 -1
  4. package/lib/module/components/avatar/index.js.map +1 -1
  5. package/lib/module/components/box/Box.js +15 -2
  6. package/lib/module/components/box/Box.js.map +1 -1
  7. package/lib/module/components/box/index.js.map +1 -1
  8. package/lib/module/components/box/utils.js +5 -2
  9. package/lib/module/components/box/utils.js.map +1 -1
  10. package/lib/module/components/button/Button.js +7 -14
  11. package/lib/module/components/button/Button.js.map +1 -1
  12. package/lib/module/components/button/index.js.map +1 -1
  13. package/lib/module/components/chip/Chip.js +372 -0
  14. package/lib/module/components/chip/Chip.js.map +1 -0
  15. package/lib/module/components/chip/index.js +4 -0
  16. package/lib/module/components/chip/index.js.map +1 -0
  17. package/lib/module/components/chip/types.js +2 -0
  18. package/lib/module/components/chip/types.js.map +1 -0
  19. package/lib/module/components/chip/utils.js +36 -0
  20. package/lib/module/components/chip/utils.js.map +1 -0
  21. package/lib/module/components/empty-state/EmptyState.js +200 -0
  22. package/lib/module/components/empty-state/EmptyState.js.map +1 -0
  23. package/lib/module/components/empty-state/index.js +4 -0
  24. package/lib/module/components/empty-state/index.js.map +1 -0
  25. package/lib/module/components/empty-state/types.js +2 -0
  26. package/lib/module/components/empty-state/types.js.map +1 -0
  27. package/lib/module/components/header/Header.js.map +1 -1
  28. package/lib/module/components/header/header-buttons.js +31 -36
  29. package/lib/module/components/header/header-buttons.js.map +1 -1
  30. package/lib/module/components/header/index.js +1 -1
  31. package/lib/module/components/header/index.js.map +1 -1
  32. package/lib/module/components/header/parts/HeaderGradientOverlay.js +38 -0
  33. package/lib/module/components/header/parts/HeaderGradientOverlay.js.map +1 -0
  34. package/lib/module/components/header/variants/LargeTitleHeader/index.js +71 -101
  35. package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -1
  36. package/lib/module/components/header/variants/TitleHeader/index.js +34 -65
  37. package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -1
  38. package/lib/module/components/icon/Icon.js +21 -9
  39. package/lib/module/components/icon/Icon.js.map +1 -1
  40. package/lib/module/components/icon/index.js.map +1 -1
  41. package/lib/module/components/image/Image.js +54 -0
  42. package/lib/module/components/image/Image.js.map +1 -0
  43. package/lib/module/components/image/index.js +4 -0
  44. package/lib/module/components/image/index.js.map +1 -0
  45. package/lib/module/components/image/types.js +4 -0
  46. package/lib/module/components/image/types.js.map +1 -0
  47. package/lib/module/components/index.js +4 -0
  48. package/lib/module/components/index.js.map +1 -1
  49. package/lib/module/components/page-indicator/PageIndicator.js.map +1 -1
  50. package/lib/module/components/page-indicator/PageIndicatorRoot.js.map +1 -1
  51. package/lib/module/components/page-indicator/hooks/use-page-indicator-colors.js +1 -1
  52. package/lib/module/components/page-indicator/hooks/use-page-indicator-colors.js.map +1 -1
  53. package/lib/module/components/page-indicator/hooks/use-page-indicator-core.js.map +1 -1
  54. package/lib/module/components/page-indicator/hooks/use-page-indicator-progress.js.map +1 -1
  55. package/lib/module/components/page-indicator/index.js.map +1 -1
  56. package/lib/module/components/page-indicator/variants/DotIndicator/InactiveDot.js.map +1 -1
  57. package/lib/module/components/page-indicator/variants/DotIndicator/LoadingOrbit.js.map +1 -1
  58. package/lib/module/components/page-indicator/variants/DotIndicator/TravelingPill.js.map +1 -1
  59. package/lib/module/components/page-indicator/variants/DotIndicator/index.js.map +1 -1
  60. package/lib/module/components/page-indicator/variants/PillIndicator/PillIndicatorDot.js.map +1 -1
  61. package/lib/module/components/page-indicator/variants/PillIndicator/index.js.map +1 -1
  62. package/lib/module/components/portal/Portal.js +42 -0
  63. package/lib/module/components/portal/Portal.js.map +1 -0
  64. package/lib/module/components/portal/PortalContext.js +1 -16
  65. package/lib/module/components/portal/PortalContext.js.map +1 -1
  66. package/lib/module/components/portal/PortalHost.js +16 -14
  67. package/lib/module/components/portal/PortalHost.js.map +1 -1
  68. package/lib/module/components/portal/PortalProvider.js +51 -19
  69. package/lib/module/components/portal/PortalProvider.js.map +1 -1
  70. package/lib/module/components/portal/index.js +1 -1
  71. package/lib/module/components/portal/index.js.map +1 -1
  72. package/lib/module/components/screen/Screen.js +26 -29
  73. package/lib/module/components/screen/Screen.js.map +1 -1
  74. package/lib/module/components/screen/ScreenContext.js +7 -5
  75. package/lib/module/components/screen/ScreenContext.js.map +1 -1
  76. package/lib/module/components/screen/ScreenFlashList.js +144 -0
  77. package/lib/module/components/screen/ScreenFlashList.js.map +1 -0
  78. package/lib/module/components/screen/ScreenFlatList.js +88 -27
  79. package/lib/module/components/screen/ScreenFlatList.js.map +1 -1
  80. package/lib/module/components/screen/ScreenScrollView.js +101 -34
  81. package/lib/module/components/screen/ScreenScrollView.js.map +1 -1
  82. package/lib/module/components/screen/index.js +1 -0
  83. package/lib/module/components/screen/index.js.map +1 -1
  84. package/lib/module/components/screen/parts/BottomSafeArea.js +62 -0
  85. package/lib/module/components/screen/parts/BottomSafeArea.js.map +1 -0
  86. package/lib/module/components/screen/parts/ScreenPlaceholder.js +50 -0
  87. package/lib/module/components/screen/parts/ScreenPlaceholder.js.map +1 -0
  88. package/lib/module/components/screen/types.js +4 -0
  89. package/lib/module/components/screen/types.js.map +1 -0
  90. package/lib/module/components/screen/utils.js +12 -9
  91. package/lib/module/components/screen/utils.js.map +1 -1
  92. package/lib/module/components/slot/Slot.js.map +1 -1
  93. package/lib/module/components/slot/index.js.map +1 -1
  94. package/lib/module/components/spinner/Spinner.js +1 -1
  95. package/lib/module/components/spinner/Spinner.js.map +1 -1
  96. package/lib/module/components/spinner/index.js.map +1 -1
  97. package/lib/module/components/surface/Surface.js +16 -9
  98. package/lib/module/components/surface/Surface.js.map +1 -1
  99. package/lib/module/components/surface/index.js.map +1 -1
  100. package/lib/module/components/text/Text.js +25 -7
  101. package/lib/module/components/text/Text.js.map +1 -1
  102. package/lib/module/components/text/index.js.map +1 -1
  103. package/lib/module/components/toast/Toast.js +8 -8
  104. package/lib/module/components/toast/Toast.js.map +1 -1
  105. package/lib/module/components/toast/ToastProvider.js.map +1 -1
  106. package/lib/module/components/toast/index.js.map +1 -1
  107. package/lib/module/components/toolbar/Toolbar.js +268 -0
  108. package/lib/module/components/toolbar/Toolbar.js.map +1 -0
  109. package/lib/module/components/toolbar/index.js +4 -0
  110. package/lib/module/components/toolbar/index.js.map +1 -0
  111. package/lib/module/context/ThemeProvider.js +5 -2
  112. package/lib/module/context/ThemeProvider.js.map +1 -1
  113. package/lib/module/context/index.js.map +1 -1
  114. package/lib/module/index.js.map +1 -1
  115. package/lib/module/{components/icon/utils.js → theme/defaults/icons.js} +1 -1
  116. package/lib/module/theme/defaults/icons.js.map +1 -0
  117. package/lib/module/theme/defaults/image.js +30 -0
  118. package/lib/module/theme/defaults/image.js.map +1 -0
  119. package/lib/module/theme/index.js +6 -6
  120. package/lib/module/theme/index.js.map +1 -1
  121. package/lib/module/theme/{configure.js → registry.js} +23 -30
  122. package/lib/module/theme/registry.js.map +1 -0
  123. package/lib/module/theme/runtime.js +1 -1
  124. package/lib/module/theme/runtime.js.map +1 -1
  125. package/lib/module/theme/theme.js +92 -11
  126. package/lib/module/theme/theme.js.map +1 -1
  127. package/lib/module/theme/tokens/dark.js +59 -68
  128. package/lib/module/theme/tokens/dark.js.map +1 -1
  129. package/lib/module/theme/tokens/light.js +49 -67
  130. package/lib/module/theme/tokens/light.js.map +1 -1
  131. package/lib/module/theme/tokens/primitives.js +241 -20
  132. package/lib/module/theme/tokens/primitives.js.map +1 -1
  133. package/lib/module/utils/accent-utils.js +4 -4
  134. package/lib/module/utils/accent-utils.js.map +1 -1
  135. package/lib/typescript/src/components/avatar/Avatar.d.ts.map +1 -1
  136. package/lib/typescript/src/components/avatar/types.d.ts +5 -5
  137. package/lib/typescript/src/components/avatar/types.d.ts.map +1 -1
  138. package/lib/typescript/src/components/box/Box.d.ts.map +1 -1
  139. package/lib/typescript/src/components/box/utils.d.ts +6 -1
  140. package/lib/typescript/src/components/box/utils.d.ts.map +1 -1
  141. package/lib/typescript/src/components/button/Button.d.ts +2 -2
  142. package/lib/typescript/src/components/button/Button.d.ts.map +1 -1
  143. package/lib/typescript/src/components/chip/Chip.d.ts +24 -0
  144. package/lib/typescript/src/components/chip/Chip.d.ts.map +1 -0
  145. package/lib/typescript/src/components/chip/index.d.ts +4 -0
  146. package/lib/typescript/src/components/chip/index.d.ts.map +1 -0
  147. package/lib/typescript/src/components/chip/types.d.ts +8 -0
  148. package/lib/typescript/src/components/chip/types.d.ts.map +1 -0
  149. package/lib/typescript/src/components/chip/utils.d.ts +8 -0
  150. package/lib/typescript/src/components/chip/utils.d.ts.map +1 -0
  151. package/lib/typescript/src/components/empty-state/EmptyState.d.ts +45 -0
  152. package/lib/typescript/src/components/empty-state/EmptyState.d.ts.map +1 -0
  153. package/lib/typescript/src/components/empty-state/index.d.ts +4 -0
  154. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -0
  155. package/lib/typescript/src/components/empty-state/types.d.ts +6 -0
  156. package/lib/typescript/src/components/empty-state/types.d.ts.map +1 -0
  157. package/lib/typescript/src/components/header/header-buttons.d.ts +27 -0
  158. package/lib/typescript/src/components/header/header-buttons.d.ts.map +1 -1
  159. package/lib/typescript/src/components/header/index.d.ts +1 -1
  160. package/lib/typescript/src/components/header/index.d.ts.map +1 -1
  161. package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts +7 -0
  162. package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts.map +1 -0
  163. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts +1 -1
  164. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -1
  165. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +19 -8
  166. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -1
  167. package/lib/typescript/src/components/icon/Icon.d.ts +1 -1
  168. package/lib/typescript/src/components/icon/Icon.d.ts.map +1 -1
  169. package/lib/typescript/src/components/image/Image.d.ts +16 -0
  170. package/lib/typescript/src/components/image/Image.d.ts.map +1 -0
  171. package/lib/typescript/src/components/image/index.d.ts +3 -0
  172. package/lib/typescript/src/components/image/index.d.ts.map +1 -0
  173. package/lib/typescript/src/components/image/types.d.ts +7 -0
  174. package/lib/typescript/src/components/image/types.d.ts.map +1 -0
  175. package/lib/typescript/src/components/index.d.ts +4 -0
  176. package/lib/typescript/src/components/index.d.ts.map +1 -1
  177. package/lib/typescript/src/components/page-indicator/PageIndicator.d.ts +1 -1
  178. package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-colors.d.ts +1 -1
  179. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/index.d.ts +1 -1
  180. package/lib/typescript/src/components/page-indicator/variants/PillIndicator/index.d.ts +1 -1
  181. package/lib/typescript/src/components/portal/Portal.d.ts +26 -0
  182. package/lib/typescript/src/components/portal/Portal.d.ts.map +1 -0
  183. package/lib/typescript/src/components/portal/PortalContext.d.ts +11 -14
  184. package/lib/typescript/src/components/portal/PortalContext.d.ts.map +1 -1
  185. package/lib/typescript/src/components/portal/PortalHost.d.ts +9 -4
  186. package/lib/typescript/src/components/portal/PortalHost.d.ts.map +1 -1
  187. package/lib/typescript/src/components/portal/PortalProvider.d.ts +3 -3
  188. package/lib/typescript/src/components/portal/PortalProvider.d.ts.map +1 -1
  189. package/lib/typescript/src/components/portal/index.d.ts +4 -2
  190. package/lib/typescript/src/components/portal/index.d.ts.map +1 -1
  191. package/lib/typescript/src/components/screen/Screen.d.ts +19 -14
  192. package/lib/typescript/src/components/screen/Screen.d.ts.map +1 -1
  193. package/lib/typescript/src/components/screen/ScreenContext.d.ts +12 -9
  194. package/lib/typescript/src/components/screen/ScreenContext.d.ts.map +1 -1
  195. package/lib/typescript/src/components/screen/ScreenFlashList.d.ts +43 -0
  196. package/lib/typescript/src/components/screen/ScreenFlashList.d.ts.map +1 -0
  197. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts +17 -7
  198. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts.map +1 -1
  199. package/lib/typescript/src/components/screen/ScreenScrollView.d.ts +51 -16
  200. package/lib/typescript/src/components/screen/ScreenScrollView.d.ts.map +1 -1
  201. package/lib/typescript/src/components/screen/index.d.ts +3 -0
  202. package/lib/typescript/src/components/screen/index.d.ts.map +1 -1
  203. package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts +30 -0
  204. package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts.map +1 -0
  205. package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts +22 -0
  206. package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts.map +1 -0
  207. package/lib/typescript/src/components/screen/types.d.ts +42 -0
  208. package/lib/typescript/src/components/screen/types.d.ts.map +1 -0
  209. package/lib/typescript/src/components/screen/utils.d.ts +9 -9
  210. package/lib/typescript/src/components/screen/utils.d.ts.map +1 -1
  211. package/lib/typescript/src/components/spinner/Spinner.d.ts +1 -1
  212. package/lib/typescript/src/components/surface/Surface.d.ts +8 -1
  213. package/lib/typescript/src/components/surface/Surface.d.ts.map +1 -1
  214. package/lib/typescript/src/components/text/Text.d.ts +1 -1
  215. package/lib/typescript/src/components/text/Text.d.ts.map +1 -1
  216. package/lib/typescript/src/components/toast/Toast.d.ts +1 -1
  217. package/lib/typescript/src/components/toolbar/Toolbar.d.ts +115 -0
  218. package/lib/typescript/src/components/toolbar/Toolbar.d.ts.map +1 -0
  219. package/lib/typescript/src/components/toolbar/index.d.ts +3 -0
  220. package/lib/typescript/src/components/toolbar/index.d.ts.map +1 -0
  221. package/lib/typescript/src/context/ThemeProvider.d.ts.map +1 -1
  222. package/lib/typescript/src/{components/icon/utils.d.ts → theme/defaults/icons.d.ts} +2 -2
  223. package/lib/typescript/src/theme/defaults/icons.d.ts.map +1 -0
  224. package/lib/typescript/src/theme/defaults/image.d.ts +4 -0
  225. package/lib/typescript/src/theme/defaults/image.d.ts.map +1 -0
  226. package/lib/typescript/src/theme/index.d.ts +7 -7
  227. package/lib/typescript/src/theme/index.d.ts.map +1 -1
  228. package/lib/typescript/src/theme/{configure.d.ts → registry.d.ts} +1 -1
  229. package/lib/typescript/src/theme/registry.d.ts.map +1 -0
  230. package/lib/typescript/src/theme/theme.d.ts +21 -1
  231. package/lib/typescript/src/theme/theme.d.ts.map +1 -1
  232. package/lib/typescript/src/theme/tokens/dark.d.ts +41 -64
  233. package/lib/typescript/src/theme/tokens/dark.d.ts.map +1 -1
  234. package/lib/typescript/src/theme/tokens/light.d.ts +44 -65
  235. package/lib/typescript/src/theme/tokens/light.d.ts.map +1 -1
  236. package/lib/typescript/src/theme/tokens/primitives.d.ts +228 -17
  237. package/lib/typescript/src/theme/tokens/primitives.d.ts.map +1 -1
  238. package/lib/typescript/src/theme/types.d.ts +66 -36
  239. package/lib/typescript/src/theme/types.d.ts.map +1 -1
  240. package/lib/typescript/src/utils/accent-utils.d.ts +1 -1
  241. package/lib/typescript/src/utils/resolve-theme-color.d.ts +1 -1
  242. package/lib/typescript/src/utils/typography.d.ts +1 -1
  243. package/package.json +28 -12
  244. package/skills/terra-ui/SKILL.md +193 -0
  245. package/skills/terra-ui/references/recipes.md +198 -0
  246. package/skills/terra-ui/references/troubleshooting.md +112 -0
  247. package/src/components/avatar/Avatar.tsx +128 -38
  248. package/src/components/avatar/types.ts +5 -5
  249. package/src/components/box/Box.tsx +13 -1
  250. package/src/components/box/utils.ts +10 -0
  251. package/src/components/button/Button.tsx +7 -15
  252. package/src/components/chip/Chip.tsx +444 -0
  253. package/src/components/chip/index.ts +3 -0
  254. package/src/components/chip/types.ts +15 -0
  255. package/src/components/chip/utils.ts +34 -0
  256. package/src/components/empty-state/EmptyState.tsx +209 -0
  257. package/src/components/empty-state/index.ts +3 -0
  258. package/src/components/empty-state/types.ts +5 -0
  259. package/src/components/header/header-buttons.tsx +33 -45
  260. package/src/components/header/index.ts +2 -0
  261. package/src/components/header/parts/HeaderGradientOverlay.tsx +29 -0
  262. package/src/components/header/variants/LargeTitleHeader/index.tsx +68 -102
  263. package/src/components/header/variants/TitleHeader/index.tsx +47 -56
  264. package/src/components/icon/Icon.tsx +20 -15
  265. package/src/components/image/Image.tsx +48 -0
  266. package/src/components/image/index.ts +2 -0
  267. package/src/components/image/types.ts +7 -0
  268. package/src/components/index.ts +4 -0
  269. package/src/components/portal/Portal.tsx +55 -0
  270. package/src/components/portal/PortalContext.tsx +14 -22
  271. package/src/components/portal/PortalHost.tsx +17 -10
  272. package/src/components/portal/PortalProvider.tsx +58 -21
  273. package/src/components/portal/index.ts +4 -2
  274. package/src/components/screen/Screen.tsx +45 -59
  275. package/src/components/screen/ScreenContext.tsx +19 -12
  276. package/src/components/screen/ScreenFlashList.tsx +196 -0
  277. package/src/components/screen/ScreenFlatList.tsx +100 -39
  278. package/src/components/screen/ScreenScrollView.tsx +114 -53
  279. package/src/components/screen/index.ts +3 -0
  280. package/src/components/screen/parts/BottomSafeArea.tsx +60 -0
  281. package/src/components/screen/parts/ScreenPlaceholder.tsx +55 -0
  282. package/src/components/screen/types.ts +43 -0
  283. package/src/components/screen/utils.ts +29 -15
  284. package/src/components/surface/Surface.tsx +17 -20
  285. package/src/components/text/Text.tsx +23 -5
  286. package/src/components/toast/Toast.tsx +8 -8
  287. package/src/components/toolbar/Toolbar.tsx +353 -0
  288. package/src/components/toolbar/index.ts +6 -0
  289. package/src/context/ThemeProvider.tsx +5 -4
  290. package/src/{components/icon/utils.tsx → theme/defaults/icons.tsx} +1 -1
  291. package/src/theme/defaults/image.tsx +34 -0
  292. package/src/theme/index.ts +1 -1
  293. package/src/theme/{configure.ts → registry.ts} +19 -34
  294. package/src/theme/runtime.ts +1 -1
  295. package/src/theme/theme.ts +127 -13
  296. package/src/theme/tokens/dark.ts +60 -69
  297. package/src/theme/tokens/light.ts +50 -68
  298. package/src/theme/tokens/primitives.ts +248 -20
  299. package/src/theme/types.ts +67 -35
  300. package/src/utils/accent-utils.ts +4 -4
  301. package/lib/module/components/icon/utils.js.map +0 -1
  302. package/lib/module/theme/configure.js.map +0 -1
  303. package/lib/typescript/src/components/icon/utils.d.ts.map +0 -1
  304. package/lib/typescript/src/theme/configure.d.ts.map +0 -1
@@ -0,0 +1,60 @@
1
+ import type { ComponentType } from 'react';
2
+
3
+ type SafeAreaViewProps = {
4
+ edges: { readonly bottom: true };
5
+ insetType: 'all';
6
+ style: { readonly flex: 0 };
7
+ };
8
+
9
+ /**
10
+ * `SafeAreaView` is not re-exported from the `react-native-screens` root, so it
11
+ * has to be reached by path — and it has to be the TypeScript source entry, the
12
+ * one the package points React Native at. The built `lib/module` copy of the
13
+ * native component never went through React Native's codegen, so loading it
14
+ * fails at bundle time with "Could not find component config for native
15
+ * component".
16
+ *
17
+ * `require` rather than `import` so the dependency's own `.tsx` is not pulled
18
+ * into this package's build, where it trips compiler options it was never
19
+ * written against.
20
+ */
21
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
22
+ const { SafeAreaView } =
23
+ require('react-native-screens/src/components/safe-area') as {
24
+ SafeAreaView: ComponentType<SafeAreaViewProps>;
25
+ };
26
+
27
+ const BOTTOM_EDGE = { bottom: true } as const;
28
+ const SPACER = { flex: 0 } as const;
29
+
30
+ /**
31
+ * Bottom clearance that measures itself.
32
+ *
33
+ * The insets Unistyles reports are the window's, which cover the system bars and
34
+ * nothing else — 24dp for the gesture bar on a Pixel — while a native tab bar
35
+ * sits on top of them for another 80dp and overlaps the content beneath it.
36
+ * `react-native-screens` tracks that second one separately, as an *interface*
37
+ * inset, and reports it to its own `SafeAreaView`; asking a view is the only way
38
+ * to learn it. Neither `runtime.insets.bottom` nor `useSafeAreaInsets()` knows
39
+ * about it — both read 24 on a tab screen — and a constant would be wrong the
40
+ * moment the bar is absent, which is every pushed screen.
41
+ *
42
+ * `insetType="all"` covers system and interface both, so this view owns the
43
+ * whole bottom edge — which is why the scroll containers pad only their margin;
44
+ * adding the window inset there too would double-count the gesture bar.
45
+ * `"interface"` alone would be wrong: it measures the same on a tab screen (the
46
+ * bar's inset already spans the gesture bar beneath it) but reports nothing on a
47
+ * pushed screen, which would then lose its gesture-bar clearance.
48
+ *
49
+ * `insetType` is Android-only; on iOS the bottom safe area of a view inside a
50
+ * tab controller already includes the bar. Measured on a tab screen: 132dp on a
51
+ * Pixel, 95pt on an iPhone 17 — against the 24dp / 34pt the window insets
52
+ * report, which is the gap this closes.
53
+ *
54
+ * `flex: 0` cancels the `flex: 1` that `SafeAreaView` sets on itself, which
55
+ * would otherwise stretch it into the leftover space of a list shorter than the
56
+ * viewport. With no children, its height is exactly the padding it applies.
57
+ */
58
+ export function BottomSafeArea() {
59
+ return <SafeAreaView edges={BOTTOM_EDGE} insetType="all" style={SPACER} />;
60
+ }
@@ -0,0 +1,55 @@
1
+ import type { ReactElement, ReactNode } from 'react';
2
+ import { View } from 'react-native';
3
+
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+
6
+ import { Spinner } from '../../spinner';
7
+ import type { ScreenScrollableProps } from '../types';
8
+
9
+ /**
10
+ * Centres a loading/empty placeholder in whatever space the content leaves.
11
+ * Grows so the placeholder sits in the middle of the content area rather than
12
+ * hugging the top, which needs the content container to grow as well.
13
+ */
14
+ export function ScreenPlaceholder({ children }: { children: ReactNode }) {
15
+ return <View style={styles.placeholder}>{children}</View>;
16
+ }
17
+
18
+ interface PlaceholderInput
19
+ extends Pick<
20
+ ScreenScrollableProps,
21
+ 'loading' | 'LoadingComponent' | 'EmptyComponent'
22
+ > {
23
+ /** Whether the container has nothing to render (empty `data`, no children). */
24
+ isEmpty: boolean;
25
+ }
26
+
27
+ /**
28
+ * The placeholder a scroll container should show instead of its content, or
29
+ * `null` to render the content. Loading wins over empty: while data is in
30
+ * flight there is nothing to be empty about yet.
31
+ */
32
+ export function renderScreenPlaceholder({
33
+ loading,
34
+ LoadingComponent,
35
+ EmptyComponent,
36
+ isEmpty,
37
+ }: PlaceholderInput): ReactElement | null {
38
+ if (loading) {
39
+ return (
40
+ <ScreenPlaceholder>{LoadingComponent ?? <Spinner />}</ScreenPlaceholder>
41
+ );
42
+ }
43
+ if (isEmpty && EmptyComponent) {
44
+ return <ScreenPlaceholder>{EmptyComponent}</ScreenPlaceholder>;
45
+ }
46
+ return null;
47
+ }
48
+
49
+ const styles = StyleSheet.create(() => ({
50
+ placeholder: {
51
+ flexGrow: 1,
52
+ alignItems: 'center',
53
+ justifyContent: 'center',
54
+ },
55
+ }));
@@ -0,0 +1,43 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ /**
4
+ * Which axes of the `layout.screen.margin` token a scroll container pads its
5
+ * content with.
6
+ *
7
+ * `'y'` pads the top and bottom of the content area. It stacks on top of the
8
+ * structural insets — the header height and the safe area — which apply
9
+ * whether or not margins are on.
10
+ */
11
+ export type ScreenMargins = 'all' | 'x' | 'y' | 'none';
12
+
13
+ /**
14
+ * Props shared by `Screen.ScrollView`, `Screen.FlatList` and
15
+ * `Screen.FlashList`, so a screen can swap one container for another without
16
+ * relearning its API.
17
+ */
18
+ export interface ScreenScrollableProps {
19
+ /** Lay content out along the x axis. Defaults to `false`. */
20
+ horizontal?: boolean;
21
+ /**
22
+ * Renders a centred placeholder in the content area instead of the content —
23
+ * spares every screen from hand-rolling a loading branch while its data
24
+ * arrives. The `Screen.Header` still renders, so the chrome stays put.
25
+ */
26
+ loading?: boolean;
27
+ /**
28
+ * Node shown while `loading`. Defaults to a `Spinner`; it is centred in the
29
+ * content area either way.
30
+ */
31
+ LoadingComponent?: ReactNode;
32
+ /**
33
+ * Node shown, centred, when there is nothing to render and `loading` is
34
+ * false — an empty `data` array on the list containers, no children on
35
+ * `Screen.ScrollView`. Omit it to render nothing.
36
+ */
37
+ EmptyComponent?: ReactNode;
38
+ /**
39
+ * Which axes get `layout.screen.margin` padding. Defaults to the enclosing
40
+ * `Screen`'s `margins`.
41
+ */
42
+ margins?: ScreenMargins;
43
+ }
@@ -1,17 +1,31 @@
1
- import type { LayoutTokens } from '#theme/types';
1
+ import {
2
+ type ComponentType,
3
+ createElement,
4
+ isValidElement,
5
+ type ReactElement,
6
+ } from 'react';
2
7
 
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
- };
8
+ import type { ScreenMargins } from './types';
9
+
10
+ export type ListHeaderComponent =
11
+ | ComponentType
12
+ | ReactElement
13
+ | null
14
+ | undefined;
15
+
16
+ /** Renders a `ListHeaderComponent`-shaped prop (element, component, or nullish) into an element. */
17
+ export function renderListHeader(
18
+ ListHeaderComponent: ListHeaderComponent
19
+ ): ReactElement | null {
20
+ if (!ListHeaderComponent) return null;
21
+ if (isValidElement(ListHeaderComponent)) return ListHeaderComponent;
22
+ return createElement(ListHeaderComponent);
17
23
  }
24
+
25
+ /** Whether `margins` pads the horizontal axis. */
26
+ export const hasXMargin = (margins: ScreenMargins): boolean =>
27
+ margins === 'all' || margins === 'x';
28
+
29
+ /** Whether `margins` pads the vertical axis (the bottom of the content area). */
30
+ export const hasYMargin = (margins: ScreenMargins): boolean =>
31
+ margins === 'all' || margins === 'y';
@@ -1,9 +1,7 @@
1
1
  import { type ComponentRef, forwardRef } from 'react';
2
- import type { View, ViewStyle } from 'react-native';
2
+ import type { View } from 'react-native';
3
3
 
4
- import { useUnistyles } from 'react-native-unistyles';
5
-
6
- import { getDefaultRadius, getSurfaceElevation } from '#theme/configure';
4
+ import { getDefaultRadius, getSurfaceElevation } from '#theme/registry';
7
5
  import type { ColorToken, ElevationKey } from '#theme/types';
8
6
 
9
7
  import { type BorderWidthInput, Box, type BoxProps } from '../box';
@@ -42,10 +40,16 @@ const VARIANT_BORDER: Record<
42
40
  transparent: {},
43
41
  };
44
42
 
43
+ /**
44
+ * Picks the elevation *token key* — never a resolved shadow, so this stays
45
+ * theme-independent. `transparent` collapses to `'none'` because a shadow needs
46
+ * an opaque fill behind it to render.
47
+ */
45
48
  const resolveElevation = (
46
49
  variant: SurfaceVariant,
47
50
  elevation?: ElevationKey
48
51
  ): ElevationKey => {
52
+ if (variant === 'transparent') return 'none';
49
53
  if (elevation !== undefined) return elevation;
50
54
  if (variant === 'raised') return 'sm';
51
55
  return getSurfaceElevation();
@@ -57,6 +61,13 @@ const resolveElevation = (
57
61
  * default (overridable via `radius`), and an optional drop shadow via
58
62
  * `elevation`. All other Box style-props are accepted.
59
63
  *
64
+ * Every visual decision is expressed as a **theme token key** handed to `Box`,
65
+ * which resolves it against the active theme. Surface therefore never reads the
66
+ * theme itself — no `useUnistyles()` in the render path, and no stylesheet
67
+ * crossing a component boundary (a Unistyles style handed to `Box` would land
68
+ * on a plain `View` the Babel plugin has not processed, and so would freeze at
69
+ * whichever theme was active when it was created).
70
+ *
60
71
  * @example
61
72
  * ```tsx
62
73
  * <Surface variant="raised" elevation="md" p="4" radius="lg">
@@ -66,24 +77,10 @@ const resolveElevation = (
66
77
  */
67
78
  export const Surface = forwardRef<ComponentRef<typeof View>, SurfaceProps>(
68
79
  function Surface(
69
- {
70
- variant = 'base',
71
- elevation,
72
- radius,
73
- borderWidth,
74
- borderColor,
75
- style,
76
- ...rest
77
- },
80
+ { variant = 'base', elevation, radius, borderWidth, borderColor, ...rest },
78
81
  ref
79
82
  ) {
80
- const { theme } = useUnistyles();
81
- const resolvedElevation = resolveElevation(variant, elevation);
82
83
  const variantBorder = VARIANT_BORDER[variant];
83
- const shadowStyle =
84
- variant !== 'transparent' && resolvedElevation !== 'none'
85
- ? (theme.elevation[resolvedElevation] as ViewStyle)
86
- : undefined;
87
84
 
88
85
  return (
89
86
  <Box
@@ -92,7 +89,7 @@ export const Surface = forwardRef<ComponentRef<typeof View>, SurfaceProps>(
92
89
  radius={radius ?? getDefaultRadius('surface')}
93
90
  borderWidth={borderWidth ?? variantBorder.borderWidth}
94
91
  borderColor={borderColor ?? variantBorder.borderColor}
95
- style={shadowStyle ? [shadowStyle, style] : style}
92
+ elevation={resolveElevation(variant, elevation)}
96
93
  {...rest}
97
94
  />
98
95
  );
@@ -5,7 +5,7 @@ import {
5
5
  type TextStyle,
6
6
  } from 'react-native';
7
7
 
8
- import { useUnistyles } from 'react-native-unistyles';
8
+ import { StyleSheet, useUnistyles } from 'react-native-unistyles';
9
9
  import type {
10
10
  ColorToken,
11
11
  FontWeightToken,
@@ -77,7 +77,11 @@ export const Text = forwardRef<ComponentRef<typeof RNText>, TextProps>(
77
77
  const v = theme.typography.variants[resolvedVariant];
78
78
  const effectiveWeight: FontWeightToken = weight ?? v.fontWeight;
79
79
  const fontFamily = theme.typography.fonts[effectiveWeight];
80
- const isSystemFont = fontFamily === SYSTEM_FONT;
80
+ const italicFamily = italic
81
+ ? theme.typography.fontsItalic?.[effectiveWeight]
82
+ : undefined;
83
+ const resolvedFamily = italicFamily ?? fontFamily;
84
+ const isSystemFont = resolvedFamily === SYSTEM_FONT;
81
85
 
82
86
  const base: Mutable<TextStyle> = {
83
87
  fontSize: v.fontSize,
@@ -92,10 +96,13 @@ export const Text = forwardRef<ComponentRef<typeof RNText>, TextProps>(
92
96
  };
93
97
  // System font: apply numeric weight. Custom font: family encodes the weight.
94
98
  if (isSystemFont) base.fontWeight = FONT_WEIGHT_VALUE[effectiveWeight];
95
- else base.fontFamily = fontFamily;
99
+ else base.fontFamily = resolvedFamily;
96
100
 
97
101
  if (align) base.textAlign = align;
98
- if (italic) base.fontStyle = 'italic';
102
+ // A dedicated italic family already renders slanted; `fontStyle` is only
103
+ // needed (and only works) for the system font or as a best-effort
104
+ // fallback when no italic family is configured for this weight.
105
+ if (italic && !italicFamily) base.fontStyle = 'italic';
99
106
  if (underline && strikeThrough)
100
107
  base.textDecorationLine = 'underline line-through';
101
108
  else if (underline) base.textDecorationLine = 'underline';
@@ -104,7 +111,7 @@ export const Text = forwardRef<ComponentRef<typeof RNText>, TextProps>(
104
111
  return (
105
112
  <RNText
106
113
  ref={ref}
107
- style={[base, style]}
114
+ style={[styles.root, base, style]}
108
115
  maxFontSizeMultiplier={maxFontSizeMultiplier ?? v.maxFontSizeMultiplier}
109
116
  numberOfLines={isTruncated ? 1 : numberOfLines}
110
117
  ellipsizeMode={isTruncated ? 'tail' : ellipsizeMode}
@@ -113,3 +120,14 @@ export const Text = forwardRef<ComponentRef<typeof RNText>, TextProps>(
113
120
  );
114
121
  }
115
122
  );
123
+
124
+ // ─── Styles ───────────────────────────────────────────────────────────────────
125
+ //
126
+ // Text has no static style of its own — this stylesheet exists so the
127
+ // Unistyles Babel plugin swaps the `RNText` imported above for its Unistyles-aware
128
+ // counterpart, which registers the ShadowNode with the native side. That is what
129
+ // keeps a Unistyles style passed in through `style` updating on theme change;
130
+ // without it the style freezes at whichever theme was active when it was
131
+ // created, and only callers that happen to subscribe to the theme themselves
132
+ // would appear to work.
133
+ const styles = StyleSheet.create(() => ({ root: {} }));
@@ -78,7 +78,7 @@ function getToastColors(variant: ToastVariant, theme: TerraTheme): ToastColors {
78
78
  case 'accent':
79
79
  return {
80
80
  ...base,
81
- title: get('action.fg.subtle'),
81
+ title: get('action.fg.primary.subtle'),
82
82
  icon: get('text.accent'),
83
83
  actionBg: get('action.bg.primary'),
84
84
  actionFg: get('action.fg.primary'),
@@ -89,18 +89,18 @@ function getToastColors(variant: ToastVariant, theme: TerraTheme): ToastColors {
89
89
  case 'danger':
90
90
  return {
91
91
  ...base,
92
- title: get(`status.bg.${variant}`),
93
- icon: get(`status.bg.${variant}`),
94
- actionBg: get(`status.bg.${variant}`),
95
- actionFg: get(`status.fg.${variant}`),
92
+ title: get(`status.${variant}.bg`),
93
+ icon: get(`status.${variant}.bg`),
94
+ actionBg: get(`status.${variant}.bg`),
95
+ actionFg: get(`status.${variant}.fg`),
96
96
  };
97
97
  case 'default':
98
98
  return {
99
99
  ...base,
100
100
  title: get('text.default'),
101
101
  icon: get('text.subtle'),
102
- actionBg: get('action.bg.neutral.hover'),
103
- actionFg: get('action.fg.neutral'),
102
+ actionBg: get('action.bg.secondary'),
103
+ actionFg: get('action.fg.secondary'),
104
104
  };
105
105
  }
106
106
  }
@@ -242,7 +242,7 @@ export interface ToastActionProps
242
242
  function renderActionChildren(children: ReactNode, color: string): ReactNode {
243
243
  if (typeof children === 'string' || typeof children === 'number') {
244
244
  return (
245
- <Text variant="label-lg" weight="semibold" style={{ color }}>
245
+ <Text variant="label-md" weight="semibold" style={{ color }}>
246
246
  {children}
247
247
  </Text>
248
248
  );