react-native-terra-ui 0.7.1 → 0.8.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 (257) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +1 -1
  3. package/lib/module/components/avatar/Avatar.js +20 -20
  4. package/lib/module/components/avatar/Avatar.js.map +1 -1
  5. package/lib/module/components/button/Button.js +11 -11
  6. package/lib/module/components/button/Button.js.map +1 -1
  7. package/lib/module/components/chip/Chip.js +32 -32
  8. package/lib/module/components/chip/Chip.js.map +1 -1
  9. package/lib/module/components/empty-state/EmptyState.js +3 -3
  10. package/lib/module/components/empty-state/EmptyState.js.map +1 -1
  11. package/lib/module/components/header/parts/HeaderGradientOverlay.js +6 -5
  12. package/lib/module/components/header/parts/HeaderGradientOverlay.js.map +1 -1
  13. package/lib/module/components/header/variants/LargeTitleHeader/index.js +30 -32
  14. package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -1
  15. package/lib/module/components/header/variants/TitleHeader/index.js +26 -18
  16. package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -1
  17. package/lib/module/components/icon/Icon.js +3 -3
  18. package/lib/module/components/page-indicator/hooks/use-page-indicator-colors.js +2 -2
  19. package/lib/module/components/screen/Screen.js +63 -34
  20. package/lib/module/components/screen/Screen.js.map +1 -1
  21. package/lib/module/components/screen/ScreenContext.js +28 -9
  22. package/lib/module/components/screen/ScreenContext.js.map +1 -1
  23. package/lib/module/components/screen/ScreenFlatList.js +4 -162
  24. package/lib/module/components/screen/ScreenFlatList.js.map +1 -1
  25. package/lib/module/components/screen/ScreenList.js +81 -97
  26. package/lib/module/components/screen/ScreenList.js.map +1 -1
  27. package/lib/module/components/screen/ScreenScrollView.js +64 -115
  28. package/lib/module/components/screen/ScreenScrollView.js.map +1 -1
  29. package/lib/module/components/screen/hooks/use-screen-ref.js +18 -0
  30. package/lib/module/components/screen/hooks/use-screen-ref.js.map +1 -0
  31. package/lib/module/components/screen/hooks/use-screen-scroll-handler.js +19 -0
  32. package/lib/module/components/screen/hooks/use-screen-scroll-handler.js.map +1 -0
  33. package/lib/module/components/screen/index.js.map +1 -1
  34. package/lib/module/components/screen/parts/ScreenFrame.js +47 -0
  35. package/lib/module/components/screen/parts/ScreenFrame.js.map +1 -0
  36. package/lib/module/components/screen/parts/ScreenHeaderAppearance.js +43 -0
  37. package/lib/module/components/screen/parts/ScreenHeaderAppearance.js.map +1 -0
  38. package/lib/module/components/screen/parts/ScreenPlaceholder.js +11 -13
  39. package/lib/module/components/screen/parts/ScreenPlaceholder.js.map +1 -1
  40. package/lib/module/components/screen/parts/ScreenSafeArea.android.js +27 -0
  41. package/lib/module/components/screen/parts/ScreenSafeArea.android.js.map +1 -0
  42. package/lib/module/components/screen/parts/ScreenSafeArea.js +26 -0
  43. package/lib/module/components/screen/parts/ScreenSafeArea.js.map +1 -0
  44. package/lib/module/components/screen/utils.js +18 -9
  45. package/lib/module/components/screen/utils.js.map +1 -1
  46. package/lib/module/components/spinner/Spinner.js +2 -2
  47. package/lib/module/components/text/Text.js +1 -1
  48. package/lib/module/components/toast/Toast.js +13 -13
  49. package/lib/module/components/toast/Toast.js.map +1 -1
  50. package/lib/module/components/toolbar/Toolbar.js +2 -2
  51. package/lib/module/context/ThemeProvider.js +22 -39
  52. package/lib/module/context/ThemeProvider.js.map +1 -1
  53. package/lib/module/context/index.js +1 -1
  54. package/lib/module/context/index.js.map +1 -1
  55. package/lib/module/theme/breakpoints.js +1 -1
  56. package/lib/module/theme/define-config.js +21 -0
  57. package/lib/module/theme/define-config.js.map +1 -0
  58. package/lib/module/theme/diagnostics.js +13 -0
  59. package/lib/module/theme/diagnostics.js.map +1 -0
  60. package/lib/module/theme/errors.js +15 -0
  61. package/lib/module/theme/errors.js.map +1 -0
  62. package/lib/module/theme/index.js +7 -1
  63. package/lib/module/theme/index.js.map +1 -1
  64. package/lib/module/theme/legacy-tokens.js +119 -0
  65. package/lib/module/theme/legacy-tokens.js.map +1 -0
  66. package/lib/module/theme/registry.js +122 -130
  67. package/lib/module/theme/registry.js.map +1 -1
  68. package/lib/module/theme/resolve-config.js +346 -0
  69. package/lib/module/theme/resolve-config.js.map +1 -0
  70. package/lib/module/theme/runtime.js +57 -12
  71. package/lib/module/theme/runtime.js.map +1 -1
  72. package/lib/module/theme/screen-margin.js +6 -14
  73. package/lib/module/theme/screen-margin.js.map +1 -1
  74. package/lib/module/theme/screen-padding.js +17 -0
  75. package/lib/module/theme/screen-padding.js.map +1 -0
  76. package/lib/module/theme/selection-store.js +75 -0
  77. package/lib/module/theme/selection-store.js.map +1 -0
  78. package/lib/module/theme/theme.js +81 -36
  79. package/lib/module/theme/theme.js.map +1 -1
  80. package/lib/module/theme/tokens/canonical-color-keys.js +102 -0
  81. package/lib/module/theme/tokens/canonical-color-keys.js.map +1 -0
  82. package/lib/module/theme/tokens/dark.js +72 -88
  83. package/lib/module/theme/tokens/dark.js.map +1 -1
  84. package/lib/module/theme/tokens/light.js +69 -83
  85. package/lib/module/theme/tokens/light.js.map +1 -1
  86. package/lib/module/theme/tokens/primitives.js +14 -14
  87. package/lib/module/theme/tokens/primitives.js.map +1 -1
  88. package/lib/module/theme/tokens/validate.js +238 -0
  89. package/lib/module/theme/tokens/validate.js.map +1 -0
  90. package/lib/module/theme/unistyles-adapter.js +77 -0
  91. package/lib/module/theme/unistyles-adapter.js.map +1 -0
  92. package/lib/module/utils/accent-generator.js +476 -0
  93. package/lib/module/utils/accent-generator.js.map +1 -0
  94. package/lib/module/utils/accent-utils.js +52 -35
  95. package/lib/module/utils/accent-utils.js.map +1 -1
  96. package/lib/module/utils/color-utils.js +224 -7
  97. package/lib/module/utils/color-utils.js.map +1 -1
  98. package/lib/module/utils/deep-merge.js +1 -1
  99. package/lib/module/utils/deep-merge.js.map +1 -1
  100. package/lib/module/utils/resolve-theme-color.js +2 -2
  101. package/lib/typescript/src/components/chip/types.d.ts +1 -1
  102. package/lib/typescript/src/components/empty-state/EmptyState.d.ts +1 -1
  103. package/lib/typescript/src/components/empty-state/EmptyState.d.ts.map +1 -1
  104. package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts +1 -1
  105. package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts.map +1 -1
  106. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts +6 -5
  107. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -1
  108. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +11 -5
  109. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -1
  110. package/lib/typescript/src/components/icon/Icon.d.ts +2 -2
  111. package/lib/typescript/src/components/screen/Screen.d.ts +45 -16
  112. package/lib/typescript/src/components/screen/Screen.d.ts.map +1 -1
  113. package/lib/typescript/src/components/screen/ScreenContext.d.ts +42 -26
  114. package/lib/typescript/src/components/screen/ScreenContext.d.ts.map +1 -1
  115. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts +5 -46
  116. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts.map +1 -1
  117. package/lib/typescript/src/components/screen/ScreenList.d.ts +62 -50
  118. package/lib/typescript/src/components/screen/ScreenList.d.ts.map +1 -1
  119. package/lib/typescript/src/components/screen/ScreenScrollView.d.ts +18 -58
  120. package/lib/typescript/src/components/screen/ScreenScrollView.d.ts.map +1 -1
  121. package/lib/typescript/src/components/screen/hooks/use-screen-ref.d.ts +5 -0
  122. package/lib/typescript/src/components/screen/hooks/use-screen-ref.d.ts.map +1 -0
  123. package/lib/typescript/src/components/screen/hooks/use-screen-scroll-handler.d.ts +53 -0
  124. package/lib/typescript/src/components/screen/hooks/use-screen-scroll-handler.d.ts.map +1 -0
  125. package/lib/typescript/src/components/screen/index.d.ts +2 -2
  126. package/lib/typescript/src/components/screen/index.d.ts.map +1 -1
  127. package/lib/typescript/src/components/screen/parts/ScreenFrame.d.ts +6 -0
  128. package/lib/typescript/src/components/screen/parts/ScreenFrame.d.ts.map +1 -0
  129. package/lib/typescript/src/components/screen/parts/ScreenHeaderAppearance.d.ts +21 -0
  130. package/lib/typescript/src/components/screen/parts/ScreenHeaderAppearance.d.ts.map +1 -0
  131. package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts +6 -8
  132. package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts.map +1 -1
  133. package/lib/typescript/src/components/screen/parts/ScreenSafeArea.android.d.ts +7 -0
  134. package/lib/typescript/src/components/screen/parts/ScreenSafeArea.android.d.ts.map +1 -0
  135. package/lib/typescript/src/components/screen/parts/ScreenSafeArea.d.ts +6 -0
  136. package/lib/typescript/src/components/screen/parts/ScreenSafeArea.d.ts.map +1 -0
  137. package/lib/typescript/src/components/screen/types.d.ts +27 -32
  138. package/lib/typescript/src/components/screen/types.d.ts.map +1 -1
  139. package/lib/typescript/src/components/screen/utils.d.ts +9 -8
  140. package/lib/typescript/src/components/screen/utils.d.ts.map +1 -1
  141. package/lib/typescript/src/components/text/Text.d.ts +1 -1
  142. package/lib/typescript/src/components/text/Text.d.ts.map +1 -1
  143. package/lib/typescript/src/components/toolbar/Toolbar.d.ts +2 -2
  144. package/lib/typescript/src/context/ThemeProvider.d.ts +29 -12
  145. package/lib/typescript/src/context/ThemeProvider.d.ts.map +1 -1
  146. package/lib/typescript/src/context/index.d.ts +1 -1
  147. package/lib/typescript/src/context/index.d.ts.map +1 -1
  148. package/lib/typescript/src/theme/breakpoints.d.ts +1 -1
  149. package/lib/typescript/src/theme/define-config.d.ts +23 -0
  150. package/lib/typescript/src/theme/define-config.d.ts.map +1 -0
  151. package/lib/typescript/src/theme/diagnostics.d.ts +4 -0
  152. package/lib/typescript/src/theme/diagnostics.d.ts.map +1 -0
  153. package/lib/typescript/src/theme/errors.d.ts +10 -0
  154. package/lib/typescript/src/theme/errors.d.ts.map +1 -0
  155. package/lib/typescript/src/theme/index.d.ts +10 -2
  156. package/lib/typescript/src/theme/index.d.ts.map +1 -1
  157. package/lib/typescript/src/theme/legacy-tokens.d.ts +187 -0
  158. package/lib/typescript/src/theme/legacy-tokens.d.ts.map +1 -0
  159. package/lib/typescript/src/theme/registry.d.ts +12 -17
  160. package/lib/typescript/src/theme/registry.d.ts.map +1 -1
  161. package/lib/typescript/src/theme/resolve-config.d.ts +99 -0
  162. package/lib/typescript/src/theme/resolve-config.d.ts.map +1 -0
  163. package/lib/typescript/src/theme/runtime.d.ts +25 -4
  164. package/lib/typescript/src/theme/runtime.d.ts.map +1 -1
  165. package/lib/typescript/src/theme/screen-margin.d.ts +6 -14
  166. package/lib/typescript/src/theme/screen-margin.d.ts.map +1 -1
  167. package/lib/typescript/src/theme/screen-padding.d.ts +15 -0
  168. package/lib/typescript/src/theme/screen-padding.d.ts.map +1 -0
  169. package/lib/typescript/src/theme/selection-store.d.ts +41 -0
  170. package/lib/typescript/src/theme/selection-store.d.ts.map +1 -0
  171. package/lib/typescript/src/theme/theme.d.ts +54 -1
  172. package/lib/typescript/src/theme/theme.d.ts.map +1 -1
  173. package/lib/typescript/src/theme/tokens/canonical-color-keys.d.ts +95 -0
  174. package/lib/typescript/src/theme/tokens/canonical-color-keys.d.ts.map +1 -0
  175. package/lib/typescript/src/theme/tokens/dark.d.ts +56 -63
  176. package/lib/typescript/src/theme/tokens/dark.d.ts.map +1 -1
  177. package/lib/typescript/src/theme/tokens/light.d.ts +56 -71
  178. package/lib/typescript/src/theme/tokens/light.d.ts.map +1 -1
  179. package/lib/typescript/src/theme/tokens/primitives.d.ts +11 -10
  180. package/lib/typescript/src/theme/tokens/primitives.d.ts.map +1 -1
  181. package/lib/typescript/src/theme/tokens/validate.d.ts +34 -0
  182. package/lib/typescript/src/theme/tokens/validate.d.ts.map +1 -0
  183. package/lib/typescript/src/theme/types.d.ts +166 -86
  184. package/lib/typescript/src/theme/types.d.ts.map +1 -1
  185. package/lib/typescript/src/theme/unistyles-adapter.d.ts +33 -0
  186. package/lib/typescript/src/theme/unistyles-adapter.d.ts.map +1 -0
  187. package/lib/typescript/src/utils/accent-generator.d.ts +103 -0
  188. package/lib/typescript/src/utils/accent-generator.d.ts.map +1 -0
  189. package/lib/typescript/src/utils/accent-utils.d.ts +15 -2
  190. package/lib/typescript/src/utils/accent-utils.d.ts.map +1 -1
  191. package/lib/typescript/src/utils/color-utils.d.ts +59 -2
  192. package/lib/typescript/src/utils/color-utils.d.ts.map +1 -1
  193. package/lib/typescript/src/utils/deep-merge.d.ts +1 -0
  194. package/lib/typescript/src/utils/deep-merge.d.ts.map +1 -1
  195. package/lib/typescript/src/utils/resolve-theme-color.d.ts +2 -2
  196. package/package.json +1 -1
  197. package/src/components/avatar/Avatar.tsx +20 -20
  198. package/src/components/button/Button.tsx +11 -11
  199. package/src/components/chip/Chip.tsx +32 -32
  200. package/src/components/chip/types.ts +1 -1
  201. package/src/components/empty-state/EmptyState.tsx +4 -4
  202. package/src/components/header/parts/HeaderGradientOverlay.tsx +5 -4
  203. package/src/components/header/variants/LargeTitleHeader/index.tsx +31 -30
  204. package/src/components/header/variants/TitleHeader/index.tsx +33 -22
  205. package/src/components/icon/Icon.tsx +4 -4
  206. package/src/components/page-indicator/hooks/use-page-indicator-colors.ts +2 -2
  207. package/src/components/screen/Screen.tsx +89 -43
  208. package/src/components/screen/ScreenContext.tsx +48 -28
  209. package/src/components/screen/ScreenFlatList.tsx +5 -191
  210. package/src/components/screen/ScreenList.tsx +197 -175
  211. package/src/components/screen/ScreenScrollView.tsx +92 -167
  212. package/src/components/screen/hooks/use-screen-ref.ts +24 -0
  213. package/src/components/screen/hooks/use-screen-scroll-handler.ts +31 -0
  214. package/src/components/screen/index.ts +8 -2
  215. package/src/components/screen/parts/ScreenFrame.tsx +49 -0
  216. package/src/components/screen/parts/ScreenHeaderAppearance.tsx +52 -0
  217. package/src/components/screen/parts/ScreenPlaceholder.tsx +16 -16
  218. package/src/components/screen/parts/ScreenSafeArea.android.tsx +22 -0
  219. package/src/components/screen/parts/ScreenSafeArea.tsx +20 -0
  220. package/src/components/screen/types.ts +29 -32
  221. package/src/components/screen/utils.ts +24 -11
  222. package/src/components/spinner/Spinner.tsx +2 -2
  223. package/src/components/text/Text.tsx +2 -2
  224. package/src/components/toast/Toast.tsx +13 -13
  225. package/src/components/toolbar/Toolbar.tsx +4 -4
  226. package/src/context/ThemeProvider.tsx +47 -51
  227. package/src/context/index.ts +1 -1
  228. package/src/theme/breakpoints.ts +1 -1
  229. package/src/theme/define-config.ts +27 -0
  230. package/src/theme/diagnostics.ts +11 -0
  231. package/src/theme/errors.ts +14 -0
  232. package/src/theme/index.ts +40 -1
  233. package/src/theme/legacy-tokens.ts +227 -0
  234. package/src/theme/registry.ts +119 -137
  235. package/src/theme/resolve-config.ts +406 -0
  236. package/src/theme/runtime.ts +52 -14
  237. package/src/theme/screen-margin.ts +6 -25
  238. package/src/theme/screen-padding.ts +26 -0
  239. package/src/theme/selection-store.ts +76 -0
  240. package/src/theme/theme.ts +104 -39
  241. package/src/theme/tokens/canonical-color-keys.ts +107 -0
  242. package/src/theme/tokens/dark.ts +91 -98
  243. package/src/theme/tokens/light.ts +88 -93
  244. package/src/theme/tokens/primitives.ts +14 -14
  245. package/src/theme/tokens/validate.ts +271 -0
  246. package/src/theme/types.ts +184 -95
  247. package/src/theme/unistyles-adapter.ts +75 -0
  248. package/src/utils/accent-generator.ts +530 -0
  249. package/src/utils/accent-utils.ts +66 -39
  250. package/src/utils/color-utils.ts +225 -8
  251. package/src/utils/deep-merge.ts +1 -1
  252. package/src/utils/resolve-theme-color.ts +2 -2
  253. package/lib/module/components/screen/parts/BottomSafeArea.js +0 -62
  254. package/lib/module/components/screen/parts/BottomSafeArea.js.map +0 -1
  255. package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts +0 -30
  256. package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts.map +0 -1
  257. package/src/components/screen/parts/BottomSafeArea.tsx +0 -59
@@ -0,0 +1,227 @@
1
+ import { deepMerge } from "#utils/deep-merge";
2
+ import type { CanonicalThemeColor, TerraTheme, TerraThemeOverride } from "./types";
3
+
4
+ /** Legacy color names. Explicit action/status-property aliases come first to resolve synonyms. */
5
+ export const legacyColorAliases = {
6
+ "action.bg.primary": "accent.solid.bg",
7
+ "action.bg.primary.active": "accent.solid.bg.pressed",
8
+ "action.bg.primary.disabled": "accent.solid.bg.disabled",
9
+ "action.bg.primary.hover": "accent.solid.bg.hover",
10
+ "action.bg.primary.subtle": "accent.soft.bg",
11
+ "action.bg.primary.subtle.active": "accent.soft.bg.pressed",
12
+ "action.bg.primary.subtle.disabled": "accent.soft.bg.disabled",
13
+ "action.bg.primary.subtle.hover": "accent.soft.bg.hover",
14
+ "action.bg.secondary": "neutral.solid.bg",
15
+ "action.bg.secondary.subtle": "neutral.soft.bg",
16
+ "action.bg.secondary.subtle.active": "neutral.soft.bg.pressed",
17
+ "action.bg.secondary.subtle.disabled": "neutral.soft.bg.disabled",
18
+ "action.bg.secondary.subtle.hover": "neutral.soft.bg.hover",
19
+ "action.fg.primary": "accent.solid.fg",
20
+ "action.fg.primary.subtle": "accent.soft.fg",
21
+ "action.fg.secondary": "neutral.solid.fg",
22
+ "action.fg.secondary.subtle": "neutral.soft.fg",
23
+ "status.danger.bg": "danger.solid.bg",
24
+ "status.danger.bg.subtle": "danger.soft.bg",
25
+ "status.danger.border": "danger.solid.border",
26
+ "status.danger.border.subtle": "danger.soft.border",
27
+ "status.danger.fg": "danger.solid.fg",
28
+ "status.danger.fg.subtle": "danger.soft.fg",
29
+ "status.info.bg": "info.solid.bg",
30
+ "status.info.bg.subtle": "info.soft.bg",
31
+ "status.info.border": "info.solid.border",
32
+ "status.info.border.subtle": "info.soft.border",
33
+ "status.info.fg": "info.solid.fg",
34
+ "status.info.fg.subtle": "info.soft.fg",
35
+ "status.success.bg": "success.solid.bg",
36
+ "status.success.bg.subtle": "success.soft.bg",
37
+ "status.success.border": "success.solid.border",
38
+ "status.success.border.subtle": "success.soft.border",
39
+ "status.success.fg": "success.solid.fg",
40
+ "status.success.fg.subtle": "success.soft.fg",
41
+ "status.warning.bg": "warning.solid.bg",
42
+ "status.warning.bg.subtle": "warning.soft.bg",
43
+ "status.warning.border": "warning.solid.border",
44
+ "status.warning.border.subtle": "warning.soft.border",
45
+ "status.warning.fg": "warning.solid.fg",
46
+ "status.warning.fg.subtle": "warning.soft.fg",
47
+ "background": "surface.canvas",
48
+ "border.accent": "accent.border",
49
+ "border.focus": "focus.ring",
50
+ "status.danger": "danger.solid.bg",
51
+ "status.info": "info.solid.bg",
52
+ "status.success": "success.solid.bg",
53
+ "status.warning": "warning.solid.bg",
54
+ "surface.accent": "accent.solid.bg",
55
+ "surface.accent.subtle": "accent.soft.bg",
56
+ "surface.overlay": "backdrop",
57
+ "text.accent": "accent.text",
58
+ "text.default": "text.primary",
59
+ "text.muted": "text.secondary",
60
+ "text.on-accent": "accent.solid.fg",
61
+ "text.on-accent-subtle": "accent.soft.fg",
62
+ "text.subtle": "text.tertiary",
63
+ } as const satisfies Record<keyof LegacyThemeColor, keyof CanonicalThemeColor>;
64
+
65
+ export interface LegacyThemeColor {
66
+ /** @deprecated Use `accent.solid.bg`. */
67
+ "action.bg.primary": string;
68
+ /** @deprecated Use `accent.solid.bg.pressed`. */
69
+ "action.bg.primary.active": string;
70
+ /** @deprecated Use `accent.solid.bg.disabled`. */
71
+ "action.bg.primary.disabled": string;
72
+ /** @deprecated Use `accent.solid.bg.hover`. */
73
+ "action.bg.primary.hover": string;
74
+ /** @deprecated Use `accent.soft.bg`. */
75
+ "action.bg.primary.subtle": string;
76
+ /** @deprecated Use `accent.soft.bg.pressed`. */
77
+ "action.bg.primary.subtle.active": string;
78
+ /** @deprecated Use `accent.soft.bg.disabled`. */
79
+ "action.bg.primary.subtle.disabled": string;
80
+ /** @deprecated Use `accent.soft.bg.hover`. */
81
+ "action.bg.primary.subtle.hover": string;
82
+ /** @deprecated Use `neutral.solid.bg`. */
83
+ "action.bg.secondary": string;
84
+ /** @deprecated Use `neutral.soft.bg`. */
85
+ "action.bg.secondary.subtle": string;
86
+ /** @deprecated Use `neutral.soft.bg.pressed`. */
87
+ "action.bg.secondary.subtle.active": string;
88
+ /** @deprecated Use `neutral.soft.bg.disabled`. */
89
+ "action.bg.secondary.subtle.disabled": string;
90
+ /** @deprecated Use `neutral.soft.bg.hover`. */
91
+ "action.bg.secondary.subtle.hover": string;
92
+ /** @deprecated Use `accent.solid.fg`. */
93
+ "action.fg.primary": string;
94
+ /** @deprecated Use `accent.soft.fg`. */
95
+ "action.fg.primary.subtle": string;
96
+ /** @deprecated Use `neutral.solid.fg`. */
97
+ "action.fg.secondary": string;
98
+ /** @deprecated Use `neutral.soft.fg`. */
99
+ "action.fg.secondary.subtle": string;
100
+ /** @deprecated Use `danger.solid.bg`. */
101
+ "status.danger.bg": string;
102
+ /** @deprecated Use `danger.soft.bg`. */
103
+ "status.danger.bg.subtle": string;
104
+ /** @deprecated Use `danger.solid.border`. */
105
+ "status.danger.border": string;
106
+ /** @deprecated Use `danger.soft.border`. */
107
+ "status.danger.border.subtle": string;
108
+ /** @deprecated Use `danger.solid.fg`. */
109
+ "status.danger.fg": string;
110
+ /** @deprecated Use `danger.soft.fg`. */
111
+ "status.danger.fg.subtle": string;
112
+ /** @deprecated Use `info.solid.bg`. */
113
+ "status.info.bg": string;
114
+ /** @deprecated Use `info.soft.bg`. */
115
+ "status.info.bg.subtle": string;
116
+ /** @deprecated Use `info.solid.border`. */
117
+ "status.info.border": string;
118
+ /** @deprecated Use `info.soft.border`. */
119
+ "status.info.border.subtle": string;
120
+ /** @deprecated Use `info.solid.fg`. */
121
+ "status.info.fg": string;
122
+ /** @deprecated Use `info.soft.fg`. */
123
+ "status.info.fg.subtle": string;
124
+ /** @deprecated Use `success.solid.bg`. */
125
+ "status.success.bg": string;
126
+ /** @deprecated Use `success.soft.bg`. */
127
+ "status.success.bg.subtle": string;
128
+ /** @deprecated Use `success.solid.border`. */
129
+ "status.success.border": string;
130
+ /** @deprecated Use `success.soft.border`. */
131
+ "status.success.border.subtle": string;
132
+ /** @deprecated Use `success.solid.fg`. */
133
+ "status.success.fg": string;
134
+ /** @deprecated Use `success.soft.fg`. */
135
+ "status.success.fg.subtle": string;
136
+ /** @deprecated Use `warning.solid.bg`. */
137
+ "status.warning.bg": string;
138
+ /** @deprecated Use `warning.soft.bg`. */
139
+ "status.warning.bg.subtle": string;
140
+ /** @deprecated Use `warning.solid.border`. */
141
+ "status.warning.border": string;
142
+ /** @deprecated Use `warning.soft.border`. */
143
+ "status.warning.border.subtle": string;
144
+ /** @deprecated Use `warning.solid.fg`. */
145
+ "status.warning.fg": string;
146
+ /** @deprecated Use `warning.soft.fg`. */
147
+ "status.warning.fg.subtle": string;
148
+ /** @deprecated Use `surface.canvas`. */
149
+ "background": string;
150
+ /** @deprecated Use `accent.border`. */
151
+ "border.accent": string;
152
+ /** @deprecated Use `focus.ring`. */
153
+ "border.focus": string;
154
+ /** @deprecated Use `danger.solid.bg`. */
155
+ "status.danger": string;
156
+ /** @deprecated Use `info.solid.bg`. */
157
+ "status.info": string;
158
+ /** @deprecated Use `success.solid.bg`. */
159
+ "status.success": string;
160
+ /** @deprecated Use `warning.solid.bg`. */
161
+ "status.warning": string;
162
+ /** @deprecated Use `accent.solid.bg`. */
163
+ "surface.accent": string;
164
+ /** @deprecated Use `accent.soft.bg`. */
165
+ "surface.accent.subtle": string;
166
+ /** @deprecated Use `backdrop`. */
167
+ "surface.overlay": string;
168
+ /** @deprecated Use `accent.text`. */
169
+ "text.accent": string;
170
+ /** @deprecated Use `text.primary`. */
171
+ "text.default": string;
172
+ /** @deprecated Use `text.secondary`. */
173
+ "text.muted": string;
174
+ /** @deprecated Use `accent.solid.fg`. */
175
+ "text.on-accent": string;
176
+ /** @deprecated Use `accent.soft.fg`. */
177
+ "text.on-accent-subtle": string;
178
+ /** @deprecated Use `text.tertiary`. */
179
+ "text.subtle": string;
180
+ }
181
+
182
+ /** Normalize each authored layer before merging, so aliases respect layer precedence. */
183
+ export function normalizeTokenOverride(override?: TerraThemeOverride): TerraThemeOverride | undefined {
184
+ if (!override) return override;
185
+ const next = { ...override };
186
+ if (override.color) {
187
+ const color = { ...override.color };
188
+ for (const [legacy, canonical] of Object.entries(legacyColorAliases)) {
189
+ const value = override.color[legacy as keyof LegacyThemeColor];
190
+ if (color[canonical] === undefined && value !== undefined) color[canonical] = value;
191
+ delete color[legacy as keyof LegacyThemeColor];
192
+ }
193
+ next.color = color;
194
+ }
195
+ const screen = override.layout?.screen;
196
+ if (screen) {
197
+ const { margin, padding, ...rest } = screen;
198
+ next.layout = {
199
+ ...override.layout,
200
+ screen: { ...rest, padding: deepMerge(margin ?? {}, padding) },
201
+ };
202
+ }
203
+ return next;
204
+ }
205
+
206
+ /** Materialize aliases for direct theme reads as well as component color props. */
207
+ export function withLegacyTokens(
208
+ theme: Omit<TerraTheme, "color" | "layout"> & {
209
+ color: CanonicalThemeColor;
210
+ layout: Omit<TerraTheme["layout"], "screen"> & {
211
+ screen: Pick<TerraTheme["layout"]["screen"], "padding">;
212
+ };
213
+ }
214
+ ): TerraTheme {
215
+ const color = { ...theme.color } as TerraTheme["color"];
216
+ for (const [legacy, canonical] of Object.entries(legacyColorAliases)) {
217
+ color[legacy as keyof LegacyThemeColor] = color[canonical];
218
+ }
219
+ return {
220
+ ...theme,
221
+ color,
222
+ layout: {
223
+ ...theme.layout,
224
+ screen: { ...theme.layout.screen, margin: theme.layout.screen.padding },
225
+ },
226
+ };
227
+ }
@@ -1,16 +1,21 @@
1
- import { StyleSheet, UnistylesRuntime } from "react-native-unistyles";
2
-
3
- import { normalizeAccent } from "#utils/accent-utils";
4
- import { deepMerge } from "#utils/deep-merge";
5
- import { breakpoints } from "./breakpoints";
1
+ /**
2
+ * Installation lifecycle: `configureTerraUI` / `bootstrapTerraUI` and the
3
+ * read-only getters components use (icons, image renderer, component defaults).
4
+ *
5
+ * Theme resolution lives in `resolve-config.ts`; native calls in
6
+ * `unistyles-adapter.ts`; the selection snapshot and installed config in
7
+ * `selection-store.ts`. This module orchestrates them in a fixed order:
8
+ * build and validate (pure, may throw) → native work → publish state.
9
+ */
6
10
  import { defaultIcons } from "./defaults/icons";
7
11
  import { defaultImage } from "./defaults/image";
8
- import { buildRadiusScale, buildSpacingScale, defaultDarkTheme, defaultLightTheme } from "./theme";
12
+ import { warnContrastDiagnostics } from "./diagnostics";
13
+ import { normalizeThemeInput, resolveThemeScheme, resolveThemes } from "./resolve-config";
14
+ import { getInstalledConfig, publishThemeSelection, setInstalledConfig } from "./selection-store";
9
15
  import type {
10
16
  ComponentDefaults,
11
17
  DefaultRadiusToken,
12
18
  ElevationKey,
13
- NormalizedAccent,
14
19
  RadiusComponent,
15
20
  Scheme,
16
21
  TerraConfig,
@@ -18,8 +23,10 @@ import type {
18
23
  TerraImageComponent,
19
24
  TerraSemanticIconName,
20
25
  TerraTheme,
21
- TerraThemeOverride,
26
+ TerraThemeInput,
27
+ ThemeMode,
22
28
  } from "./types";
29
+ import { applyMode, configureUnistyles, hasConfiguredUnistyles, updateThemes } from "./unistyles-adapter";
23
30
 
24
31
  // ─── Radius / elevation defaults ─────────────────────────────────────────────
25
32
 
@@ -28,7 +35,7 @@ const DEFAULT_RADIUS: DefaultRadiusToken = "md";
28
35
  /** Fallback when the surface elevation is not configured. */
29
36
  const DEFAULT_ELEVATION: ElevationKey = "none";
30
37
 
31
- type ResolvedRadius = Record<RadiusComponent, DefaultRadiusToken>;
38
+ type ResolvedRadius = Readonly<Record<RadiusComponent, DefaultRadiusToken>>;
32
39
 
33
40
  /** Resolves each component's default radius from the `components` config. */
34
41
  const resolveDefaultRadius = (components?: ComponentDefaults): ResolvedRadius => ({
@@ -44,111 +51,93 @@ declare module "react-native-unistyles" {
44
51
  }
45
52
  }
46
53
 
47
- interface Registry {
48
- baseLight: TerraTheme;
49
- baseDark: TerraTheme;
50
- accents: Record<string, NormalizedAccent>;
51
- currentAccent?: string;
52
- defaultRadius: ResolvedRadius;
53
- surfaceElevation: ElevationKey;
54
- icons: Record<string, TerraIconComponent>;
55
- imageComponent: TerraImageComponent;
56
- configured: boolean;
54
+ // ─── Private registry state ──────────────────────────────────────────────────
55
+
56
+ /** Adapters and component defaults, installed at configure time only. */
57
+ interface RegistrySettings {
58
+ readonly defaultRadius: ResolvedRadius;
59
+ readonly surfaceElevation: ElevationKey;
60
+ readonly icons: Readonly<Partial<Record<string, TerraIconComponent>>>;
61
+ readonly imageComponent: TerraImageComponent;
57
62
  }
58
63
 
59
- const registry: Registry = {
60
- baseLight: defaultLightTheme,
61
- baseDark: defaultDarkTheme,
62
- accents: {},
63
- currentAccent: undefined,
64
- defaultRadius: resolveDefaultRadius(),
65
- surfaceElevation: DEFAULT_ELEVATION,
66
- icons: {},
67
- imageComponent: defaultImage,
68
- configured: false,
69
- };
64
+ const buildSettings = (config: TerraConfig): RegistrySettings => ({
65
+ defaultRadius: resolveDefaultRadius(config.components),
66
+ surfaceElevation: config.components?.surface?.elevation ?? DEFAULT_ELEVATION,
67
+ icons: { ...(config.icons ?? {}) },
68
+ imageComponent: config.image ?? defaultImage,
69
+ });
70
+
71
+ let settings: RegistrySettings = buildSettings({});
72
+
73
+ /**
74
+ * Installation state. `unconfigured` → nothing has run; `fallback` → the
75
+ * provider's placeholder defaults are live and an app call still replaces
76
+ * them; `app` → the app configured, later calls are ignored with a warning.
77
+ */
78
+ let installation: "unconfigured" | "fallback" | "app" = "unconfigured";
70
79
 
71
- /** @internal */
72
- export const getRegistry = (): Registry => registry;
80
+ // ─── Getters (read-only) ─────────────────────────────────────────────────────
73
81
 
74
- export const getIsConfigured = (): boolean => registry.configured;
82
+ export const getIsConfigured = (): boolean => installation !== "unconfigured";
75
83
 
76
- export const getAccentNames = (): string[] => Object.keys(registry.accents);
84
+ /** Names of the registered accents, in declaration order. Returns a fresh array. */
85
+ export const getAccentNames = (): string[] => [...getInstalledConfig().accentNames];
77
86
 
78
- export const getImageComponent = (): TerraImageComponent => registry.imageComponent;
87
+ export const getImageComponent = (): TerraImageComponent => settings.imageComponent;
79
88
 
80
89
  export const getIcon = (name: string): TerraIconComponent | undefined =>
81
- registry.icons[name] ?? defaultIcons[name as TerraSemanticIconName];
90
+ settings.icons[name] ?? defaultIcons[name as TerraSemanticIconName];
82
91
 
83
92
  export const getRequiredIcon = (name: TerraSemanticIconName): TerraIconComponent =>
84
- registry.icons[name] ?? defaultIcons[name];
93
+ settings.icons[name] ?? defaultIcons[name];
85
94
 
86
95
  /**
87
96
  * The configured default corner radius for a component (Button, Surface).
88
97
  * Set via {@link configureTerraUI}; defaults to `'md'`.
89
98
  */
90
99
  export const getDefaultRadius = (component: RadiusComponent): DefaultRadiusToken =>
91
- registry.defaultRadius[component];
100
+ settings.defaultRadius[component];
92
101
 
93
102
  /**
94
103
  * The configured default drop-shadow depth for Surface (`'none'` = no shadow).
95
104
  * Set via {@link configureTerraUI}; defaults to `'none'`.
96
105
  */
97
- export const getSurfaceElevation = (): ElevationKey => registry.surfaceElevation;
106
+ export const getSurfaceElevation = (): ElevationKey => settings.surfaceElevation;
98
107
 
99
- /** Resolves a scheme's theme = base ⊕ the named accent's partial override. */
108
+ /**
109
+ * Resolves one scheme's theme from the installed configuration with the named
110
+ * accent overlaid. Omitting `accentName` selects the base theme (no accent —
111
+ * not the configured `defaultAccent`). An unknown name warns in development
112
+ * and falls back to the base theme. Pure with respect to runtime state: it
113
+ * never changes the applied selection.
114
+ */
100
115
  export function resolveTheme(scheme: Scheme, accentName?: string): TerraTheme {
101
- const base = scheme === "light" ? registry.baseLight : registry.baseDark;
102
- const accent = accentName ? registry.accents[accentName] : undefined;
103
- return deepMerge(base, accent ? accent[scheme] : undefined);
104
- }
105
-
106
- const mergeThemeOverride = (theme: TerraTheme, override?: TerraThemeOverride): TerraTheme => {
107
- if (!override) return theme;
108
-
109
- const { radius, spacing, ...rest } = override;
110
- let next = deepMerge(theme, rest);
111
-
112
- if (radius) {
113
- const { base, ...radiusTokens } = radius;
114
- if (base != null) {
115
- next = deepMerge(next, { radius: buildRadiusScale(base) });
116
- }
117
- next = deepMerge(next, { radius: radiusTokens });
118
- }
119
-
120
- if (spacing) {
121
- const { base, ...spacingTokens } = spacing;
122
- if (base != null) {
123
- next = deepMerge(next, { spacing: buildSpacingScale(base) });
116
+ const config = getInstalledConfig();
117
+ let accent: string | null = null;
118
+ if (accentName !== undefined) {
119
+ if (config.accentNames.includes(accentName)) {
120
+ accent = accentName;
121
+ } else if (__DEV__) {
122
+ console.warn(`[react-native-terra-ui] Unknown accent "${accentName}"; resolving the base theme.`);
124
123
  }
125
- next = deepMerge(next, { spacing: spacingTokens });
126
124
  }
125
+ return resolveThemeScheme(config, scheme, accent).theme;
126
+ }
127
127
 
128
- return next;
129
- };
130
-
131
- /**
132
- * Whether the live configuration is the provider's fallback rather than an
133
- * app's own call. A fallback is a placeholder, so a real call still replaces
134
- * it; two real calls remain an error.
135
- */
136
- let configuredByFallback = false;
137
-
138
- /**
139
- * Whether `StyleSheet.configure` has run. Tracked separately from
140
- * {@link Registry.configured} because it may only happen once per app.
141
- */
142
- let unistylesConfigured = false;
128
+ // ─── Configuration ───────────────────────────────────────────────────────────
143
129
 
144
130
  /**
145
131
  * Configure Terra UI's themes. Call ONCE, as early as possible — before any
146
132
  * component renders (e.g. top of the app entry, imported from
147
133
  * `react-native-terra-ui/theme`). With no arguments it registers the default
148
134
  * theme; `<TerraUIProvider>` calls it for you if you haven't.
135
+ *
136
+ * Invalid input throws {@link TerraConfigError} before anything is installed,
137
+ * so a corrected retry is still the app's one configure call.
149
138
  */
150
139
  export function configureTerraUI(config: TerraConfig = {}): void {
151
- if (registry.configured && !configuredByFallback) {
140
+ if (installation === "app") {
152
141
  if (__DEV__ && Object.keys(config).length > 0) {
153
142
  console.warn(
154
143
  "[react-native-terra-ui] configureTerraUI() was called more than once; the later call was ignored."
@@ -157,8 +146,7 @@ export function configureTerraUI(config: TerraConfig = {}): void {
157
146
  return;
158
147
  }
159
148
 
160
- configuredByFallback = false;
161
- applyConfig(config);
149
+ applyConfig(config, "app");
162
150
  }
163
151
 
164
152
  /**
@@ -174,68 +162,62 @@ export function configureTerraUI(config: TerraConfig = {}): void {
174
162
  * @internal
175
163
  */
176
164
  export function bootstrapTerraUI(): void {
177
- if (registry.configured) {
165
+ if (installation !== "unconfigured") {
178
166
  return;
179
167
  }
180
168
 
181
- applyConfig({});
182
- configuredByFallback = true;
169
+ applyConfig({}, "fallback");
183
170
  }
184
171
 
185
- function applyConfig(config: TerraConfig): void {
186
- registry.configured = true;
187
-
188
- registry.baseLight = mergeThemeOverride(
189
- mergeThemeOverride(defaultLightTheme, config.shared),
190
- config.schemes?.light
191
- );
192
- registry.baseDark = mergeThemeOverride(
193
- mergeThemeOverride(defaultDarkTheme, config.shared),
194
- config.schemes?.dark
195
- );
196
-
197
- registry.defaultRadius = resolveDefaultRadius(config.components);
198
- registry.surfaceElevation = config.components?.surface?.elevation ?? DEFAULT_ELEVATION;
199
- registry.icons = config.icons ?? {};
200
- registry.imageComponent = config.image ?? defaultImage;
201
-
202
- registry.accents = {};
203
- for (const [name, accent] of Object.entries(config.accents ?? {})) {
204
- registry.accents[name] = normalizeAccent(accent);
205
- }
206
- const { defaultAccent } = config;
207
- registry.currentAccent = defaultAccent && registry.accents[defaultAccent] ? defaultAccent : undefined;
208
-
209
- const themes = {
210
- light: resolveTheme("light", registry.currentAccent),
211
- dark: resolveTheme("dark", registry.currentAccent),
212
- };
213
-
214
- const adaptive = config.adaptiveThemes ?? true;
215
- const settings = config.initialScheme
216
- ? { initialTheme: config.initialScheme }
217
- : adaptive
218
- ? { adaptiveThemes: true as const }
219
- : { initialTheme: "light" as const };
220
-
221
- if (unistylesConfigured) {
222
- // `StyleSheet.configure` is a one-time startup call — calling it again
223
- // leaves the themes it already registered in place, which would strand an
224
- // app's config behind the fallback's defaults. Hand the rebuilt themes to
225
- // the runtime instead, the same way `applyAccent` swaps a palette.
226
- UnistylesRuntime.updateTheme("light", () => themes.light);
227
- UnistylesRuntime.updateTheme("dark", () => themes.dark);
228
-
229
- if ("adaptiveThemes" in settings) {
230
- UnistylesRuntime.setAdaptiveThemes(true);
231
- } else {
232
- UnistylesRuntime.setAdaptiveThemes(false);
233
- UnistylesRuntime.setTheme(settings.initialTheme);
172
+ /**
173
+ * Derives the starting theme mode. `mode` wins; otherwise the deprecated
174
+ * settings keep their existing meaning: `initialScheme` pins that scheme,
175
+ * `adaptiveThemes: false` starts in light, and the default follows the system.
176
+ */
177
+ function deriveMode(config: TerraConfig): ThemeMode {
178
+ const hasLegacy = config.initialScheme !== undefined || config.adaptiveThemes !== undefined;
179
+ if (config.mode !== undefined) {
180
+ if (__DEV__ && hasLegacy) {
181
+ console.warn(
182
+ "[react-native-terra-ui] configureTerraUI(): `mode` was supplied together with the deprecated `initialScheme` / `adaptiveThemes`; `mode` wins and the deprecated settings are ignored."
183
+ );
234
184
  }
185
+ return config.mode;
186
+ }
187
+ if (config.initialScheme !== undefined) return config.initialScheme;
188
+ if (config.adaptiveThemes === false) return "light";
189
+ return "system";
190
+ }
235
191
 
236
- return;
192
+ const pickThemeInput = (config: TerraConfig): TerraThemeInput => ({
193
+ shared: config.shared,
194
+ schemes: config.schemes,
195
+ accents: config.accents,
196
+ defaultAccent: config.defaultAccent,
197
+ });
198
+
199
+ function applyConfig(config: TerraConfig, kind: "fallback" | "app"): void {
200
+ // (a) Pure: validate and build everything. Throws leave ALL state untouched.
201
+ const themeConfig = normalizeThemeInput(pickThemeInput(config));
202
+ const mode = deriveMode(config);
203
+ const { themes, diagnostics } = resolveThemes(themeConfig, themeConfig.defaultAccent);
204
+ const nextSettings = buildSettings(config);
205
+
206
+ // (b) Native. `StyleSheet.configure` runs once per app; after that the
207
+ // rebuilt themes reach the runtime the same way an accent change does, so an
208
+ // app's config is never stranded behind the fallback's defaults.
209
+ if (hasConfiguredUnistyles()) {
210
+ updateThemes(themes);
211
+ applyMode(mode);
212
+ } else {
213
+ configureUnistyles({ themes, mode });
237
214
  }
238
215
 
239
- StyleSheet.configure({ themes, settings, breakpoints });
240
- unistylesConfigured = true;
216
+ // (c) Publish only after native work succeeded.
217
+ installation = kind;
218
+ settings = nextSettings;
219
+ setInstalledConfig(themeConfig);
220
+ publishThemeSelection({ accent: themeConfig.defaultAccent ?? undefined, mode });
221
+
222
+ warnContrastDiagnostics(diagnostics);
241
223
  }