react-native-terra-ui 0.1.1 → 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.
- package/CHANGELOG.md +90 -0
- package/lib/module/components/avatar/Avatar.js +118 -35
- package/lib/module/components/avatar/Avatar.js.map +1 -1
- package/lib/module/components/box/Box.js +15 -2
- package/lib/module/components/box/Box.js.map +1 -1
- package/lib/module/components/box/utils.js +4 -1
- package/lib/module/components/box/utils.js.map +1 -1
- package/lib/module/components/button/Button.js +7 -14
- package/lib/module/components/button/Button.js.map +1 -1
- package/lib/module/components/chip/Chip.js +372 -0
- package/lib/module/components/chip/Chip.js.map +1 -0
- package/lib/module/components/chip/index.js +4 -0
- package/lib/module/components/chip/index.js.map +1 -0
- package/lib/module/components/chip/types.js +2 -0
- package/lib/module/components/chip/types.js.map +1 -0
- package/lib/module/components/chip/utils.js +36 -0
- package/lib/module/components/chip/utils.js.map +1 -0
- package/lib/module/components/empty-state/EmptyState.js +200 -0
- package/lib/module/components/empty-state/EmptyState.js.map +1 -0
- package/lib/module/components/empty-state/index.js +4 -0
- package/lib/module/components/empty-state/index.js.map +1 -0
- package/lib/module/components/empty-state/types.js +2 -0
- package/lib/module/components/empty-state/types.js.map +1 -0
- package/lib/module/components/header/header-buttons.js +31 -36
- package/lib/module/components/header/header-buttons.js.map +1 -1
- package/lib/module/components/header/index.js +1 -1
- package/lib/module/components/header/index.js.map +1 -1
- package/lib/module/components/header/parts/HeaderGradientOverlay.js +38 -0
- package/lib/module/components/header/parts/HeaderGradientOverlay.js.map +1 -0
- package/lib/module/components/header/variants/LargeTitleHeader/index.js +69 -99
- package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -1
- package/lib/module/components/header/variants/TitleHeader/index.js +32 -63
- package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -1
- package/lib/module/components/icon/Icon.js +20 -8
- package/lib/module/components/icon/Icon.js.map +1 -1
- package/lib/module/components/image/Image.js +54 -0
- package/lib/module/components/image/Image.js.map +1 -0
- package/lib/module/components/image/index.js +4 -0
- package/lib/module/components/image/index.js.map +1 -0
- package/lib/module/components/image/types.js +4 -0
- package/lib/module/components/image/types.js.map +1 -0
- package/lib/module/components/index.js +4 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/portal/Portal.js +42 -0
- package/lib/module/components/portal/Portal.js.map +1 -0
- package/lib/module/components/portal/PortalContext.js +1 -16
- package/lib/module/components/portal/PortalContext.js.map +1 -1
- package/lib/module/components/portal/PortalHost.js +16 -14
- package/lib/module/components/portal/PortalHost.js.map +1 -1
- package/lib/module/components/portal/PortalProvider.js +51 -19
- package/lib/module/components/portal/PortalProvider.js.map +1 -1
- package/lib/module/components/portal/index.js +1 -1
- package/lib/module/components/portal/index.js.map +1 -1
- package/lib/module/components/screen/Screen.js +25 -28
- package/lib/module/components/screen/Screen.js.map +1 -1
- package/lib/module/components/screen/ScreenContext.js +7 -5
- package/lib/module/components/screen/ScreenContext.js.map +1 -1
- package/lib/module/components/screen/ScreenFlashList.js +144 -0
- package/lib/module/components/screen/ScreenFlashList.js.map +1 -0
- package/lib/module/components/screen/ScreenFlatList.js +88 -27
- package/lib/module/components/screen/ScreenFlatList.js.map +1 -1
- package/lib/module/components/screen/ScreenScrollView.js +101 -34
- package/lib/module/components/screen/ScreenScrollView.js.map +1 -1
- package/lib/module/components/screen/index.js +1 -0
- package/lib/module/components/screen/index.js.map +1 -1
- package/lib/module/components/screen/parts/BottomSafeArea.js +62 -0
- package/lib/module/components/screen/parts/BottomSafeArea.js.map +1 -0
- package/lib/module/components/screen/parts/ScreenPlaceholder.js +50 -0
- package/lib/module/components/screen/parts/ScreenPlaceholder.js.map +1 -0
- package/lib/module/components/screen/types.js +4 -0
- package/lib/module/components/screen/types.js.map +1 -0
- package/lib/module/components/screen/utils.js +12 -9
- package/lib/module/components/screen/utils.js.map +1 -1
- package/lib/module/components/surface/Surface.js +16 -9
- package/lib/module/components/surface/Surface.js.map +1 -1
- package/lib/module/components/text/Text.js +23 -5
- package/lib/module/components/text/Text.js.map +1 -1
- package/lib/module/components/toast/Toast.js +8 -8
- package/lib/module/components/toast/Toast.js.map +1 -1
- package/lib/module/components/toolbar/Toolbar.js +268 -0
- package/lib/module/components/toolbar/Toolbar.js.map +1 -0
- package/lib/module/components/toolbar/index.js +4 -0
- package/lib/module/components/toolbar/index.js.map +1 -0
- package/lib/module/context/ThemeProvider.js +5 -2
- package/lib/module/context/ThemeProvider.js.map +1 -1
- package/lib/module/{components/icon/utils.js → theme/defaults/icons.js} +1 -1
- package/lib/module/theme/defaults/icons.js.map +1 -0
- package/lib/module/theme/defaults/image.js +30 -0
- package/lib/module/theme/defaults/image.js.map +1 -0
- package/lib/module/theme/index.js +1 -1
- package/lib/module/theme/{configure.js → registry.js} +21 -28
- package/lib/module/theme/registry.js.map +1 -0
- package/lib/module/theme/runtime.js +1 -1
- package/lib/module/theme/theme.js +91 -10
- package/lib/module/theme/theme.js.map +1 -1
- package/lib/module/theme/tokens/dark.js +59 -68
- package/lib/module/theme/tokens/dark.js.map +1 -1
- package/lib/module/theme/tokens/light.js +49 -67
- package/lib/module/theme/tokens/light.js.map +1 -1
- package/lib/module/theme/tokens/primitives.js +241 -20
- package/lib/module/theme/tokens/primitives.js.map +1 -1
- package/lib/module/utils/accent-utils.js +4 -4
- package/lib/module/utils/accent-utils.js.map +1 -1
- package/lib/typescript/src/components/avatar/Avatar.d.ts.map +1 -1
- package/lib/typescript/src/components/avatar/types.d.ts +4 -4
- package/lib/typescript/src/components/avatar/types.d.ts.map +1 -1
- package/lib/typescript/src/components/box/Box.d.ts.map +1 -1
- package/lib/typescript/src/components/box/utils.d.ts +6 -1
- package/lib/typescript/src/components/box/utils.d.ts.map +1 -1
- package/lib/typescript/src/components/button/Button.d.ts +1 -1
- package/lib/typescript/src/components/button/Button.d.ts.map +1 -1
- package/lib/typescript/src/components/chip/Chip.d.ts +24 -0
- package/lib/typescript/src/components/chip/Chip.d.ts.map +1 -0
- package/lib/typescript/src/components/chip/index.d.ts +4 -0
- package/lib/typescript/src/components/chip/index.d.ts.map +1 -0
- package/lib/typescript/src/components/chip/types.d.ts +8 -0
- package/lib/typescript/src/components/chip/types.d.ts.map +1 -0
- package/lib/typescript/src/components/chip/utils.d.ts +8 -0
- package/lib/typescript/src/components/chip/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/empty-state/EmptyState.d.ts +45 -0
- package/lib/typescript/src/components/empty-state/EmptyState.d.ts.map +1 -0
- package/lib/typescript/src/components/empty-state/index.d.ts +4 -0
- package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -0
- package/lib/typescript/src/components/empty-state/types.d.ts +6 -0
- package/lib/typescript/src/components/empty-state/types.d.ts.map +1 -0
- package/lib/typescript/src/components/header/header-buttons.d.ts +27 -0
- package/lib/typescript/src/components/header/header-buttons.d.ts.map +1 -1
- package/lib/typescript/src/components/header/index.d.ts +1 -1
- package/lib/typescript/src/components/header/index.d.ts.map +1 -1
- package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts +7 -0
- package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts.map +1 -0
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts +1 -1
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -1
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +18 -7
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -1
- package/lib/typescript/src/components/icon/Icon.d.ts.map +1 -1
- package/lib/typescript/src/components/image/Image.d.ts +16 -0
- package/lib/typescript/src/components/image/Image.d.ts.map +1 -0
- package/lib/typescript/src/components/image/index.d.ts +3 -0
- package/lib/typescript/src/components/image/index.d.ts.map +1 -0
- package/lib/typescript/src/components/image/types.d.ts +7 -0
- package/lib/typescript/src/components/image/types.d.ts.map +1 -0
- package/lib/typescript/src/components/index.d.ts +4 -0
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/components/portal/Portal.d.ts +26 -0
- package/lib/typescript/src/components/portal/Portal.d.ts.map +1 -0
- package/lib/typescript/src/components/portal/PortalContext.d.ts +11 -14
- package/lib/typescript/src/components/portal/PortalContext.d.ts.map +1 -1
- package/lib/typescript/src/components/portal/PortalHost.d.ts +9 -4
- package/lib/typescript/src/components/portal/PortalHost.d.ts.map +1 -1
- package/lib/typescript/src/components/portal/PortalProvider.d.ts +3 -3
- package/lib/typescript/src/components/portal/PortalProvider.d.ts.map +1 -1
- package/lib/typescript/src/components/portal/index.d.ts +4 -2
- package/lib/typescript/src/components/portal/index.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/Screen.d.ts +18 -13
- package/lib/typescript/src/components/screen/Screen.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/ScreenContext.d.ts +12 -9
- package/lib/typescript/src/components/screen/ScreenContext.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/ScreenFlashList.d.ts +43 -0
- package/lib/typescript/src/components/screen/ScreenFlashList.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/ScreenFlatList.d.ts +17 -7
- package/lib/typescript/src/components/screen/ScreenFlatList.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/ScreenScrollView.d.ts +51 -16
- package/lib/typescript/src/components/screen/ScreenScrollView.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/index.d.ts +3 -0
- package/lib/typescript/src/components/screen/index.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts +30 -0
- package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts +22 -0
- package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/types.d.ts +42 -0
- package/lib/typescript/src/components/screen/types.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/utils.d.ts +9 -9
- package/lib/typescript/src/components/screen/utils.d.ts.map +1 -1
- package/lib/typescript/src/components/surface/Surface.d.ts +7 -0
- package/lib/typescript/src/components/surface/Surface.d.ts.map +1 -1
- package/lib/typescript/src/components/text/Text.d.ts.map +1 -1
- package/lib/typescript/src/components/toolbar/Toolbar.d.ts +115 -0
- package/lib/typescript/src/components/toolbar/Toolbar.d.ts.map +1 -0
- package/lib/typescript/src/components/toolbar/index.d.ts +3 -0
- package/lib/typescript/src/components/toolbar/index.d.ts.map +1 -0
- package/lib/typescript/src/context/ThemeProvider.d.ts.map +1 -1
- package/lib/typescript/src/{components/icon/utils.d.ts → theme/defaults/icons.d.ts} +2 -2
- package/lib/typescript/src/theme/defaults/icons.d.ts.map +1 -0
- package/lib/typescript/src/theme/defaults/image.d.ts +4 -0
- package/lib/typescript/src/theme/defaults/image.d.ts.map +1 -0
- package/lib/typescript/src/theme/index.d.ts +1 -1
- package/lib/typescript/src/theme/index.d.ts.map +1 -1
- package/lib/typescript/src/theme/{configure.d.ts → registry.d.ts} +1 -1
- package/lib/typescript/src/theme/registry.d.ts.map +1 -0
- package/lib/typescript/src/theme/theme.d.ts +21 -1
- package/lib/typescript/src/theme/theme.d.ts.map +1 -1
- package/lib/typescript/src/theme/tokens/dark.d.ts +41 -64
- package/lib/typescript/src/theme/tokens/dark.d.ts.map +1 -1
- package/lib/typescript/src/theme/tokens/light.d.ts +44 -65
- package/lib/typescript/src/theme/tokens/light.d.ts.map +1 -1
- package/lib/typescript/src/theme/tokens/primitives.d.ts +228 -17
- package/lib/typescript/src/theme/tokens/primitives.d.ts.map +1 -1
- package/lib/typescript/src/theme/types.d.ts +65 -35
- package/lib/typescript/src/theme/types.d.ts.map +1 -1
- package/package.json +23 -9
- package/skills/terra-ui/SKILL.md +193 -0
- package/skills/terra-ui/references/recipes.md +198 -0
- package/skills/terra-ui/references/troubleshooting.md +112 -0
- package/src/components/avatar/Avatar.tsx +128 -38
- package/src/components/avatar/types.ts +5 -5
- package/src/components/box/Box.tsx +13 -1
- package/src/components/box/utils.ts +10 -0
- package/src/components/button/Button.tsx +7 -15
- package/src/components/chip/Chip.tsx +444 -0
- package/src/components/chip/index.ts +3 -0
- package/src/components/chip/types.ts +15 -0
- package/src/components/chip/utils.ts +34 -0
- package/src/components/empty-state/EmptyState.tsx +209 -0
- package/src/components/empty-state/index.ts +3 -0
- package/src/components/empty-state/types.ts +5 -0
- package/src/components/header/header-buttons.tsx +33 -45
- package/src/components/header/index.ts +2 -0
- package/src/components/header/parts/HeaderGradientOverlay.tsx +29 -0
- package/src/components/header/variants/LargeTitleHeader/index.tsx +68 -102
- package/src/components/header/variants/TitleHeader/index.tsx +47 -56
- package/src/components/icon/Icon.tsx +20 -15
- package/src/components/image/Image.tsx +48 -0
- package/src/components/image/index.ts +2 -0
- package/src/components/image/types.ts +7 -0
- package/src/components/index.ts +4 -0
- package/src/components/portal/Portal.tsx +55 -0
- package/src/components/portal/PortalContext.tsx +14 -22
- package/src/components/portal/PortalHost.tsx +17 -10
- package/src/components/portal/PortalProvider.tsx +58 -21
- package/src/components/portal/index.ts +4 -2
- package/src/components/screen/Screen.tsx +45 -59
- package/src/components/screen/ScreenContext.tsx +19 -12
- package/src/components/screen/ScreenFlashList.tsx +196 -0
- package/src/components/screen/ScreenFlatList.tsx +100 -39
- package/src/components/screen/ScreenScrollView.tsx +114 -53
- package/src/components/screen/index.ts +3 -0
- package/src/components/screen/parts/BottomSafeArea.tsx +60 -0
- package/src/components/screen/parts/ScreenPlaceholder.tsx +55 -0
- package/src/components/screen/types.ts +43 -0
- package/src/components/screen/utils.ts +29 -15
- package/src/components/surface/Surface.tsx +17 -20
- package/src/components/text/Text.tsx +23 -5
- package/src/components/toast/Toast.tsx +8 -8
- package/src/components/toolbar/Toolbar.tsx +353 -0
- package/src/components/toolbar/index.ts +6 -0
- package/src/context/ThemeProvider.tsx +5 -4
- package/src/{components/icon/utils.tsx → theme/defaults/icons.tsx} +1 -1
- package/src/theme/defaults/image.tsx +34 -0
- package/src/theme/index.ts +1 -1
- package/src/theme/{configure.ts → registry.ts} +19 -34
- package/src/theme/runtime.ts +1 -1
- package/src/theme/theme.ts +127 -13
- package/src/theme/tokens/dark.ts +60 -69
- package/src/theme/tokens/light.ts +50 -68
- package/src/theme/tokens/primitives.ts +248 -20
- package/src/theme/types.ts +67 -35
- package/src/utils/accent-utils.ts +4 -4
- package/lib/module/components/icon/utils.js.map +0 -1
- package/lib/module/theme/configure.js.map +0 -1
- package/lib/typescript/src/components/icon/utils.d.ts.map +0 -1
- package/lib/typescript/src/theme/configure.d.ts.map +0 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
Symptom → cause. Most Terra UI problems are setup problems, and most of them fail
|
|
4
|
+
**silently** rather than throwing.
|
|
5
|
+
|
|
6
|
+
## Components render unstyled, or the app crashes on launch
|
|
7
|
+
|
|
8
|
+
Expo Go. Unistyles v3 requires native modules, so Expo Go cannot run it. Build a
|
|
9
|
+
development client:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npx expo run:ios # or run:android
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Styles do not change when the theme changes
|
|
16
|
+
|
|
17
|
+
The Unistyles Babel plugin is not processing the library. Add it to `autoProcessImports`,
|
|
18
|
+
and keep the worklets plugin last:
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
plugins: [
|
|
22
|
+
['react-native-unistyles/plugin', {
|
|
23
|
+
root: path.resolve(__dirname, 'src'),
|
|
24
|
+
autoProcessImports: ['react-native-terra-ui'],
|
|
25
|
+
}],
|
|
26
|
+
'react-native-worklets/plugin', // MUST be last
|
|
27
|
+
]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Clear the cache after changing Babel config — `npx expo start --clear`.
|
|
31
|
+
|
|
32
|
+
The same symptom appears in **your own** components if you built the style object in the
|
|
33
|
+
component body from `useUnistyles()`. Move it into `StyleSheet.create`.
|
|
34
|
+
|
|
35
|
+
## My theme config is ignored
|
|
36
|
+
|
|
37
|
+
Import order. Something imported `react-native-terra-ui` (the root entry) before your
|
|
38
|
+
`configureTerraUI()` call ran, so the auto-default won and later calls are no-ops.
|
|
39
|
+
|
|
40
|
+
- The bootstrap file must import from `react-native-terra-ui/theme` only.
|
|
41
|
+
- It must be the **first** import in your app entry, before `expo-router/entry` or your
|
|
42
|
+
`AppRegistry` registration.
|
|
43
|
+
|
|
44
|
+
There is no warning for this. Verify with `getIsConfigured()` from
|
|
45
|
+
`react-native-terra-ui/theme` before your call.
|
|
46
|
+
|
|
47
|
+
## An icon renders nothing
|
|
48
|
+
|
|
49
|
+
The name is not registered. Only eight semantic names ship with the library
|
|
50
|
+
(`navigation.back/forward/close`, `status.info/success/warning/danger`, `person`); every
|
|
51
|
+
other name must be in **both** the `TerraUI.IconRegistry` declaration and the `icons` map
|
|
52
|
+
passed to `configureTerraUI`. A missing name logs a dev warning and renders `null` — the
|
|
53
|
+
layout still reserves space, so it looks like a blank gap.
|
|
54
|
+
|
|
55
|
+
## Content sits under the header, or too far below it
|
|
56
|
+
|
|
57
|
+
Use a `Screen` scroll container (`Screen.ScrollView` / `.FlatList` / `.FlashList`) rather
|
|
58
|
+
than a plain `ScrollView`. They reserve the header height plus the top inset as content
|
|
59
|
+
padding. Do not add your own top padding on top of that.
|
|
60
|
+
|
|
61
|
+
If content lands too *low* on first paint in a native tab screen, `react-native-screens`
|
|
62
|
+
is also applying UIKit's automatic content inset. Opt out at the navigator — on an
|
|
63
|
+
expo-router native tab that is `disableAutomaticContentInsets` on the trigger.
|
|
64
|
+
|
|
65
|
+
## `Header.LargeTitle` does not collapse
|
|
66
|
+
|
|
67
|
+
It reads scroll position from the `Screen` context, so it needs both halves:
|
|
68
|
+
|
|
69
|
+
- rendered through `Screen.Header` — `<Screen.Header as={Header.LargeTitle} title="…" />`
|
|
70
|
+
(or nested inside it, the older form), as a direct child of `Screen`, and
|
|
71
|
+
- paired with `Screen.ScrollView` / `.FlatList` / `.FlashList` in the same `Screen`.
|
|
72
|
+
|
|
73
|
+
A plain `ScrollView` sibling will not drive it.
|
|
74
|
+
|
|
75
|
+
## `useScreen()` / toast hooks throw
|
|
76
|
+
|
|
77
|
+
They require their provider. `useScreen()` must be under a `Screen`; `useToast()` and
|
|
78
|
+
`Toast.show()` need `ToastProvider` mounted (it is **not** included in `TerraUIProvider`).
|
|
79
|
+
|
|
80
|
+
## A token prop does not work
|
|
81
|
+
|
|
82
|
+
Token style props (`p`, `gap`, `bg`, `radius`, `row`, …) exist on `Box` and `Surface`
|
|
83
|
+
only. Every other component takes explicit props — read its types.
|
|
84
|
+
|
|
85
|
+
## A color token has no effect
|
|
86
|
+
|
|
87
|
+
Colors are flat dotted keys, not nested objects:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
theme.color['surface.default'] // ✅
|
|
91
|
+
theme.color.surface.default // ✗ undefined
|
|
92
|
+
theme.color.background // ✅ the one single-segment token
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
An unknown key yields `undefined`, which React Native ignores silently.
|
|
96
|
+
|
|
97
|
+
## My `Surface` has a radius or shadow I did not ask for
|
|
98
|
+
|
|
99
|
+
`Surface` reads `radius` and `elevation` from `configureTerraUI({ components: { surface } })`.
|
|
100
|
+
That is the point of it — but if you want a neutral container, use `Box`.
|
|
101
|
+
|
|
102
|
+
## Types are missing or stale after upgrading
|
|
103
|
+
|
|
104
|
+
The package ships declarations at `lib/typescript`. Reinstall, and restart the TS server in
|
|
105
|
+
your editor. If you use the `"source"` export condition (monorepo/Metro), you are compiling
|
|
106
|
+
the library's TypeScript directly, so its own type errors surface in your build.
|
|
107
|
+
|
|
108
|
+
## Something looks wrong and none of the above applies
|
|
109
|
+
|
|
110
|
+
Read the component's source — it ships in the package under
|
|
111
|
+
`node_modules/react-native-terra-ui/src/components/<name>/`. The JSDoc on each prop states
|
|
112
|
+
its default, and it is the version you actually have installed.
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { Text, View } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import { useUnistyles } from 'react-native-unistyles';
|
|
4
|
+
import { StyleSheet, useUnistyles } from 'react-native-unistyles';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import type { TerraTheme } from '#theme/types';
|
|
6
|
+
import type { RadiusKey } from '#theme/types';
|
|
8
7
|
|
|
9
8
|
import { Icon } from '../icon';
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
AvatarProps,
|
|
13
|
-
AvatarShape,
|
|
14
|
-
AvatarSize,
|
|
15
|
-
AvatarVariant,
|
|
16
|
-
} from './types';
|
|
9
|
+
import { Image } from '../image';
|
|
10
|
+
import type { AvatarProps, AvatarShape, AvatarSize } from './types';
|
|
17
11
|
|
|
18
12
|
const SIZE_PX: Record<AvatarSize, number> = {
|
|
19
13
|
xs: 24,
|
|
@@ -48,28 +42,14 @@ function getBorderRadius(
|
|
|
48
42
|
return 0;
|
|
49
43
|
}
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (color === 'default') {
|
|
60
|
-
return { bg: c['surface.sunken'], fg: c['text.muted'] };
|
|
61
|
-
}
|
|
62
|
-
if (color === 'accent') {
|
|
63
|
-
return variant === 'soft'
|
|
64
|
-
? { bg: c['action.bg.subtle'], fg: c['action.fg.subtle'] }
|
|
65
|
-
: { bg: c['action.bg.primary'], fg: c['action.fg.primary'] };
|
|
66
|
-
}
|
|
67
|
-
return variant === 'soft'
|
|
68
|
-
? {
|
|
69
|
-
bg: get(`status.bg.${color}.subtle`),
|
|
70
|
-
fg: get(`status.fg.${color}.subtle`),
|
|
71
|
-
}
|
|
72
|
-
: { bg: get(`status.bg.${color}`), fg: get(`status.fg.${color}`) };
|
|
45
|
+
// `Image` clips its own content to a `RadiusKey` theme token, independent of
|
|
46
|
+
// Avatar's own container radius — this keeps the two in sync per `shape`.
|
|
47
|
+
// `'full'` (theme.radius.full) clamps to a circle at any size, same as the
|
|
48
|
+
// container's computed `sizePx / 2`.
|
|
49
|
+
function getImageRadius(shape: AvatarShape): RadiusKey {
|
|
50
|
+
if (shape === 'circle') return 'full';
|
|
51
|
+
if (shape === 'rounded') return 'md';
|
|
52
|
+
return 'none';
|
|
73
53
|
}
|
|
74
54
|
|
|
75
55
|
export function Avatar({
|
|
@@ -79,19 +59,21 @@ export function Avatar({
|
|
|
79
59
|
name,
|
|
80
60
|
fallback,
|
|
81
61
|
contentFit = 'cover',
|
|
82
|
-
color = '
|
|
83
|
-
variant = '
|
|
62
|
+
color = 'secondary',
|
|
63
|
+
variant = 'solid',
|
|
84
64
|
style,
|
|
85
65
|
...rest
|
|
86
66
|
}: AvatarProps) {
|
|
87
67
|
const { theme } = useUnistyles();
|
|
88
68
|
const [hasError, setHasError] = useState(false);
|
|
69
|
+
styles.useVariants({ color, variant });
|
|
89
70
|
|
|
90
71
|
const sizePx = SIZE_PX[size];
|
|
91
72
|
const borderRadius = getBorderRadius(shape, sizePx, theme.radius.md);
|
|
92
73
|
const showFallback = !source || hasError;
|
|
93
74
|
|
|
94
|
-
const
|
|
75
|
+
const bg = styles.base.backgroundColor ?? '';
|
|
76
|
+
const fg = styles.base.color ?? '';
|
|
95
77
|
|
|
96
78
|
const fallbackContent =
|
|
97
79
|
fallback !== undefined ? (
|
|
@@ -112,8 +94,6 @@ export function Avatar({
|
|
|
112
94
|
<Icon name="person" size={sizePx} color={fg} />
|
|
113
95
|
);
|
|
114
96
|
|
|
115
|
-
const ImageComponent = getImageComponent();
|
|
116
|
-
|
|
117
97
|
return (
|
|
118
98
|
<View
|
|
119
99
|
{...rest}
|
|
@@ -133,9 +113,10 @@ export function Avatar({
|
|
|
133
113
|
{showFallback ? (
|
|
134
114
|
fallbackContent
|
|
135
115
|
) : (
|
|
136
|
-
<
|
|
116
|
+
<Image
|
|
137
117
|
source={source}
|
|
138
118
|
style={{ width: sizePx, height: sizePx }}
|
|
119
|
+
radius={getImageRadius(shape)}
|
|
139
120
|
contentFit={contentFit}
|
|
140
121
|
accessibilityLabel={name}
|
|
141
122
|
onError={() => setHasError(true)}
|
|
@@ -144,3 +125,112 @@ export function Avatar({
|
|
|
144
125
|
</View>
|
|
145
126
|
);
|
|
146
127
|
}
|
|
128
|
+
|
|
129
|
+
// ─── Styles ───────────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
// One compound variant per (color, variant) cell of the spec's token table —
|
|
132
|
+
// `variants` below only registers the axis names/options for the type
|
|
133
|
+
// system; the actual bg/fg values all come from `compoundVariants`.
|
|
134
|
+
const styles = StyleSheet.create((theme) => {
|
|
135
|
+
const c = theme.color;
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
base: {
|
|
139
|
+
variants: {
|
|
140
|
+
color: {
|
|
141
|
+
secondary: {},
|
|
142
|
+
primary: {},
|
|
143
|
+
success: {},
|
|
144
|
+
warning: {},
|
|
145
|
+
danger: {},
|
|
146
|
+
},
|
|
147
|
+
variant: {
|
|
148
|
+
solid: {},
|
|
149
|
+
soft: {},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
compoundVariants: [
|
|
153
|
+
{
|
|
154
|
+
color: 'secondary',
|
|
155
|
+
variant: 'solid',
|
|
156
|
+
styles: {
|
|
157
|
+
backgroundColor: c['action.bg.secondary'],
|
|
158
|
+
color: c['action.fg.secondary'],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
color: 'secondary',
|
|
163
|
+
variant: 'soft',
|
|
164
|
+
styles: {
|
|
165
|
+
backgroundColor: c['action.bg.secondary.subtle'],
|
|
166
|
+
color: c['action.fg.secondary.subtle'],
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
color: 'primary',
|
|
171
|
+
variant: 'solid',
|
|
172
|
+
styles: {
|
|
173
|
+
backgroundColor: c['action.bg.primary'],
|
|
174
|
+
color: c['action.fg.primary'],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
color: 'primary',
|
|
179
|
+
variant: 'soft',
|
|
180
|
+
styles: {
|
|
181
|
+
backgroundColor: c['action.bg.primary.subtle'],
|
|
182
|
+
color: c['action.fg.primary.subtle'],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
color: 'success',
|
|
187
|
+
variant: 'solid',
|
|
188
|
+
styles: {
|
|
189
|
+
backgroundColor: c['status.success.bg'],
|
|
190
|
+
color: c['status.success.fg'],
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
color: 'success',
|
|
195
|
+
variant: 'soft',
|
|
196
|
+
styles: {
|
|
197
|
+
backgroundColor: c['status.success.bg.subtle'],
|
|
198
|
+
color: c['status.success.fg.subtle'],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
color: 'warning',
|
|
203
|
+
variant: 'solid',
|
|
204
|
+
styles: {
|
|
205
|
+
backgroundColor: c['status.warning.bg'],
|
|
206
|
+
color: c['status.warning.fg'],
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
color: 'warning',
|
|
211
|
+
variant: 'soft',
|
|
212
|
+
styles: {
|
|
213
|
+
backgroundColor: c['status.warning.bg.subtle'],
|
|
214
|
+
color: c['status.warning.fg.subtle'],
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
color: 'danger',
|
|
219
|
+
variant: 'solid',
|
|
220
|
+
styles: {
|
|
221
|
+
backgroundColor: c['status.danger.bg'],
|
|
222
|
+
color: c['status.danger.fg'],
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
color: 'danger',
|
|
227
|
+
variant: 'soft',
|
|
228
|
+
styles: {
|
|
229
|
+
backgroundColor: c['status.danger.bg.subtle'],
|
|
230
|
+
color: c['status.danger.fg.subtle'],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
});
|
|
@@ -7,13 +7,13 @@ export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
|
7
7
|
export type AvatarShape = 'circle' | 'rounded' | 'square';
|
|
8
8
|
/** Semantic color scheme for the fallback state (initials / placeholder icon). */
|
|
9
9
|
export type AvatarColor =
|
|
10
|
-
| '
|
|
11
|
-
| '
|
|
10
|
+
| 'secondary'
|
|
11
|
+
| 'primary'
|
|
12
12
|
| 'success'
|
|
13
13
|
| 'warning'
|
|
14
14
|
| 'danger';
|
|
15
15
|
/** Visual weight of the fallback background. */
|
|
16
|
-
export type AvatarVariant = '
|
|
16
|
+
export type AvatarVariant = 'solid' | 'soft';
|
|
17
17
|
|
|
18
18
|
export interface AvatarProps extends ViewProps {
|
|
19
19
|
/** Predefined size token. Defaults to `'md'`. */
|
|
@@ -28,8 +28,8 @@ export interface AvatarProps extends ViewProps {
|
|
|
28
28
|
fallback?: ReactNode;
|
|
29
29
|
/** How the image fills its container. Defaults to `'cover'`. */
|
|
30
30
|
contentFit?: TerraImageContentFit;
|
|
31
|
-
/** Fallback color theme. Defaults to `'
|
|
31
|
+
/** Fallback color theme. Defaults to `'secondary'`. */
|
|
32
32
|
color?: AvatarColor;
|
|
33
|
-
/** Visual style variant. Defaults to `'
|
|
33
|
+
/** Visual style variant. Defaults to `'solid'`. */
|
|
34
34
|
variant?: AvatarVariant;
|
|
35
35
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ComponentRef, forwardRef } from 'react';
|
|
2
2
|
import { View, type ViewProps } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import { useUnistyles } from 'react-native-unistyles';
|
|
4
|
+
import { StyleSheet, useUnistyles } from 'react-native-unistyles';
|
|
5
5
|
|
|
6
6
|
import { Slot } from '../slot';
|
|
7
7
|
import {
|
|
@@ -36,6 +36,7 @@ export const Box = forwardRef<ComponentRef<typeof View>, BoxProps>(function Box(
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const mergedStyle = [
|
|
39
|
+
styles.root,
|
|
39
40
|
resolveBoxStyle(styleProps as BoxStyleProps, theme),
|
|
40
41
|
style,
|
|
41
42
|
];
|
|
@@ -57,3 +58,14 @@ export const Box = forwardRef<ComponentRef<typeof View>, BoxProps>(function Box(
|
|
|
57
58
|
</View>
|
|
58
59
|
);
|
|
59
60
|
});
|
|
61
|
+
|
|
62
|
+
// ─── Styles ───────────────────────────────────────────────────────────────────
|
|
63
|
+
//
|
|
64
|
+
// Box has no static style of its own — this stylesheet exists so the
|
|
65
|
+
// Unistyles Babel plugin swaps the `View` imported above for its Unistyles-aware
|
|
66
|
+
// counterpart, which registers the ShadowNode with the native side. That is what
|
|
67
|
+
// keeps a Unistyles style passed in through `style` updating on theme change;
|
|
68
|
+
// without it the style freezes at whichever theme was active when it was
|
|
69
|
+
// created, and only callers that happen to subscribe to the theme themselves
|
|
70
|
+
// would appear to work.
|
|
71
|
+
const styles = StyleSheet.create(() => ({ root: {} }));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { StyleSheet, type ViewStyle } from 'react-native';
|
|
2
2
|
import type {
|
|
3
3
|
ColorToken,
|
|
4
|
+
ElevationKey,
|
|
4
5
|
RadiusKey,
|
|
5
6
|
SpacingKey,
|
|
6
7
|
TerraTheme,
|
|
@@ -51,6 +52,11 @@ export interface BoxStyleProps {
|
|
|
51
52
|
radius?: RadiusKey;
|
|
52
53
|
borderWidth?: BorderWidthInput;
|
|
53
54
|
borderColor?: ColorToken;
|
|
55
|
+
/**
|
|
56
|
+
* Drop-shadow depth — platform-aware shadow plus Android elevation. A shadow
|
|
57
|
+
* only renders over a non-transparent `bg`.
|
|
58
|
+
*/
|
|
59
|
+
elevation?: ElevationKey;
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
const ALIGN_MAP: Record<Align, NonNullable<ViewStyle['alignItems']>> = {
|
|
@@ -147,6 +153,9 @@ export function resolveBoxStyle(
|
|
|
147
153
|
const borderColor = resolveThemeColor(props.borderColor, theme);
|
|
148
154
|
if (borderColor !== undefined) style.borderColor = borderColor;
|
|
149
155
|
}
|
|
156
|
+
if (props.elevation !== undefined) {
|
|
157
|
+
Object.assign(style, theme.elevation[props.elevation]);
|
|
158
|
+
}
|
|
150
159
|
|
|
151
160
|
return style;
|
|
152
161
|
}
|
|
@@ -178,4 +187,5 @@ export const BOX_STYLE_PROP_KEYS: readonly (keyof BoxStyleProps)[] = [
|
|
|
178
187
|
'radius',
|
|
179
188
|
'borderWidth',
|
|
180
189
|
'borderColor',
|
|
190
|
+
'elevation',
|
|
181
191
|
];
|
|
@@ -11,7 +11,7 @@ import { Pressable } from 'react-native';
|
|
|
11
11
|
|
|
12
12
|
import { StyleSheet, useUnistyles } from 'react-native-unistyles';
|
|
13
13
|
|
|
14
|
-
import { getDefaultRadius } from '#theme/
|
|
14
|
+
import { getDefaultRadius } from '#theme/registry';
|
|
15
15
|
import type {
|
|
16
16
|
ColorToken,
|
|
17
17
|
FontWeightToken,
|
|
@@ -27,7 +27,6 @@ import { Text } from '../text';
|
|
|
27
27
|
export type ButtonVariant =
|
|
28
28
|
| 'primary'
|
|
29
29
|
| 'secondary'
|
|
30
|
-
| 'neutral'
|
|
31
30
|
| 'outline'
|
|
32
31
|
| 'ghost'
|
|
33
32
|
| 'danger';
|
|
@@ -90,17 +89,10 @@ function getColors(variant: ButtonVariant, theme: TerraTheme): ButtonColors {
|
|
|
90
89
|
};
|
|
91
90
|
case 'secondary':
|
|
92
91
|
return {
|
|
93
|
-
bg: get('action.bg.
|
|
94
|
-
border: get('action.bg.
|
|
92
|
+
bg: get('action.bg.secondary'),
|
|
93
|
+
border: get('action.bg.secondary'),
|
|
95
94
|
borderWidth: 0,
|
|
96
|
-
fg: get('action.fg.
|
|
97
|
-
};
|
|
98
|
-
case 'neutral':
|
|
99
|
-
return {
|
|
100
|
-
bg: get('action.bg.neutral.hover'),
|
|
101
|
-
border: get('action.bg.neutral.hover'),
|
|
102
|
-
borderWidth: 0,
|
|
103
|
-
fg: get('action.fg.neutral'),
|
|
95
|
+
fg: get('action.fg.secondary'),
|
|
104
96
|
};
|
|
105
97
|
case 'outline':
|
|
106
98
|
return {
|
|
@@ -118,10 +110,10 @@ function getColors(variant: ButtonVariant, theme: TerraTheme): ButtonColors {
|
|
|
118
110
|
};
|
|
119
111
|
case 'danger':
|
|
120
112
|
return {
|
|
121
|
-
bg: get('status.bg
|
|
122
|
-
border: get('status.bg
|
|
113
|
+
bg: get('status.danger.bg'),
|
|
114
|
+
border: get('status.danger.bg'),
|
|
123
115
|
borderWidth: 0,
|
|
124
|
-
fg: get('status.fg
|
|
116
|
+
fg: get('status.danger.fg'),
|
|
125
117
|
};
|
|
126
118
|
}
|
|
127
119
|
}
|