react-native-terra-ui 0.13.2 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/lib/module/components/accordion/parts/AccordionContent.js +2 -2
- package/lib/module/components/accordion/parts/AccordionContent.js.map +1 -1
- package/lib/module/components/accordion/parts/AccordionTrigger.js +4 -4
- package/lib/module/components/accordion/parts/AccordionTrigger.js.map +1 -1
- package/lib/module/components/avatar/Avatar.js +3 -3
- package/lib/module/components/avatar/Avatar.js.map +1 -1
- package/lib/module/components/badge/Badge.js +2 -2
- package/lib/module/components/badge/Badge.js.map +1 -1
- package/lib/module/components/button/Button.js +3 -3
- package/lib/module/components/button/Button.js.map +1 -1
- package/lib/module/components/chip/Chip.js +2 -2
- package/lib/module/components/chip/Chip.js.map +1 -1
- package/lib/module/components/empty-state/EmptyState.js +6 -6
- package/lib/module/components/empty-state/EmptyState.js.map +1 -1
- package/lib/module/components/header/hooks/use-large-title-header.js +8 -1
- package/lib/module/components/header/hooks/use-large-title-header.js.map +1 -1
- package/lib/module/components/header/parts/HeaderBar.js +7 -13
- package/lib/module/components/header/parts/HeaderBar.js.map +1 -1
- package/lib/module/components/header/variants/LargeTitleHeader/index.js +8 -0
- package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -1
- package/lib/module/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.js +11 -20
- package/lib/module/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.js.map +1 -1
- package/lib/module/components/header/variants/TitleHeader/index.js +4 -0
- package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -1
- package/lib/module/components/image/Image.js +11 -5
- package/lib/module/components/image/Image.js.map +1 -1
- package/lib/module/components/image/utils.js +11 -0
- package/lib/module/components/image/utils.js.map +1 -0
- package/lib/module/components/input/parts/InputField.js +3 -3
- package/lib/module/components/input/parts/InputField.js.map +1 -1
- package/lib/module/components/screen/ScreenContext.js +4 -1
- package/lib/module/components/screen/ScreenContext.js.map +1 -1
- package/lib/module/components/screen/ScreenList.js +7 -4
- package/lib/module/components/screen/ScreenList.js.map +1 -1
- package/lib/module/components/screen/ScreenScrollView.js +6 -3
- package/lib/module/components/screen/ScreenScrollView.js.map +1 -1
- package/lib/module/components/screen/parts/ScreenHeaderContent.js +5 -1
- package/lib/module/components/screen/parts/ScreenHeaderContent.js.map +1 -1
- package/lib/module/components/surface/Surface.js +1 -1
- package/lib/module/components/text/Text.js +30 -8
- package/lib/module/components/text/Text.js.map +1 -1
- package/lib/module/components/toast/Toast.js +1 -1
- package/lib/module/components/toast/Toast.js.map +1 -1
- package/lib/module/components/toolbar/Toolbar.js +2 -2
- package/lib/module/components/toolbar/Toolbar.js.map +1 -1
- package/lib/module/theme/tokens/primitives.js +90 -84
- package/lib/module/theme/tokens/primitives.js.map +1 -1
- package/lib/typescript/src/components/header/hooks/use-large-title-header.d.ts.map +1 -1
- package/lib/typescript/src/components/header/parts/HeaderBar.d.ts +4 -1
- package/lib/typescript/src/components/header/parts/HeaderBar.d.ts.map +1 -1
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts +6 -1
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -1
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.d.ts +1 -1
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.d.ts.map +1 -1
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +9 -2
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -1
- package/lib/typescript/src/components/image/Image.d.ts +3 -3
- package/lib/typescript/src/components/image/Image.d.ts.map +1 -1
- package/lib/typescript/src/components/image/index.d.ts +1 -1
- package/lib/typescript/src/components/image/index.d.ts.map +1 -1
- package/lib/typescript/src/components/image/types.d.ts +7 -0
- package/lib/typescript/src/components/image/types.d.ts.map +1 -1
- package/lib/typescript/src/components/image/utils.d.ts +3 -0
- package/lib/typescript/src/components/image/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/ScreenContext.d.ts +8 -0
- package/lib/typescript/src/components/screen/ScreenContext.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/ScreenList.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/ScreenScrollView.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/parts/ScreenHeaderContent.d.ts +5 -1
- package/lib/typescript/src/components/screen/parts/ScreenHeaderContent.d.ts.map +1 -1
- package/lib/typescript/src/components/surface/Surface.d.ts +1 -1
- package/lib/typescript/src/components/text/Text.d.ts +1 -1
- package/lib/typescript/src/components/text/Text.d.ts.map +1 -1
- package/lib/typescript/src/theme/tokens/primitives.d.ts +60 -65
- package/lib/typescript/src/theme/tokens/primitives.d.ts.map +1 -1
- package/lib/typescript/src/theme/types.d.ts +8 -2
- package/lib/typescript/src/theme/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/accordion/parts/AccordionContent.tsx +2 -2
- package/src/components/accordion/parts/AccordionTrigger.tsx +2 -2
- package/src/components/avatar/Avatar.tsx +3 -3
- package/src/components/badge/Badge.tsx +2 -2
- package/src/components/button/Button.tsx +3 -3
- package/src/components/chip/Chip.tsx +2 -2
- package/src/components/empty-state/EmptyState.tsx +3 -3
- package/src/components/header/hooks/use-large-title-header.ts +7 -1
- package/src/components/header/parts/HeaderBar.tsx +10 -13
- package/src/components/header/variants/LargeTitleHeader/index.tsx +13 -0
- package/src/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.tsx +14 -22
- package/src/components/header/variants/TitleHeader/index.tsx +12 -1
- package/src/components/image/Image.tsx +7 -4
- package/src/components/image/index.ts +1 -1
- package/src/components/image/types.ts +8 -0
- package/src/components/image/utils.ts +11 -0
- package/src/components/input/parts/InputField.tsx +4 -4
- package/src/components/screen/ScreenContext.tsx +12 -0
- package/src/components/screen/ScreenList.tsx +14 -3
- package/src/components/screen/ScreenScrollView.tsx +12 -2
- package/src/components/screen/parts/ScreenHeaderContent.tsx +5 -1
- package/src/components/surface/Surface.tsx +1 -1
- package/src/components/text/Text.tsx +30 -11
- package/src/components/toast/Toast.tsx +1 -1
- package/src/components/toolbar/Toolbar.tsx +2 -2
- package/src/theme/tokens/primitives.ts +90 -84
- package/src/theme/types.ts +25 -6
|
@@ -5,17 +5,23 @@ import Animated from "react-native-reanimated";
|
|
|
5
5
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
6
6
|
|
|
7
7
|
import { FONT_WEIGHT_VALUE, SYSTEM_FONT } from "#utils/typography";
|
|
8
|
+
import { Text as ThemedText } from "../../../../text";
|
|
8
9
|
import { isTextTitle } from "../../../utils";
|
|
9
10
|
import type { LargeTitleHeaderProps } from "..";
|
|
10
11
|
|
|
11
12
|
export function LargeTitleContent({
|
|
12
13
|
title,
|
|
14
|
+
largeTitleVariant = "large-title",
|
|
15
|
+
largeTitleWeight,
|
|
13
16
|
overline,
|
|
14
17
|
subtitle,
|
|
15
18
|
hasAccessoryBelow,
|
|
16
19
|
onHeightChange,
|
|
17
20
|
animatedStyle,
|
|
18
|
-
}: Pick<
|
|
21
|
+
}: Pick<
|
|
22
|
+
LargeTitleHeaderProps,
|
|
23
|
+
"title" | "largeTitleVariant" | "largeTitleWeight" | "overline" | "subtitle"
|
|
24
|
+
> & {
|
|
19
25
|
/** Reserve a gap before a sticky accessory below this block. */
|
|
20
26
|
hasAccessoryBelow: boolean;
|
|
21
27
|
/** Reports the large-title block's layout height in dp. */
|
|
@@ -37,19 +43,16 @@ export function LargeTitleContent({
|
|
|
37
43
|
</Text>
|
|
38
44
|
)}
|
|
39
45
|
{isTextTitle(title) ? (
|
|
40
|
-
<
|
|
41
|
-
style={styles.largeTitle}
|
|
42
|
-
maxFontSizeMultiplier={theme.typography.variants["headline-lg"].maxFontSizeMultiplier}
|
|
43
|
-
>
|
|
46
|
+
<ThemedText variant={largeTitleVariant} weight={largeTitleWeight}>
|
|
44
47
|
{title}
|
|
45
|
-
</
|
|
48
|
+
</ThemedText>
|
|
46
49
|
) : (
|
|
47
50
|
title
|
|
48
51
|
)}
|
|
49
52
|
{!!subtitle && (
|
|
50
53
|
<Text
|
|
51
54
|
style={styles.subtitle}
|
|
52
|
-
maxFontSizeMultiplier={theme.typography.variants["body
|
|
55
|
+
maxFontSizeMultiplier={theme.typography.variants["body"].maxFontSizeMultiplier}
|
|
53
56
|
>
|
|
54
57
|
{subtitle}
|
|
55
58
|
</Text>
|
|
@@ -64,10 +67,7 @@ const styles = StyleSheet.create((theme) => {
|
|
|
64
67
|
const overlineWeight = theme.typography.variants["label-sm"].fontWeight;
|
|
65
68
|
const overlineFont = theme.typography.fonts[overlineWeight];
|
|
66
69
|
const isSystemOverlineFont = overlineFont === SYSTEM_FONT;
|
|
67
|
-
const
|
|
68
|
-
const titleFont = theme.typography.fonts[titleWeight];
|
|
69
|
-
const isSystemTitleFont = titleFont === SYSTEM_FONT;
|
|
70
|
-
const subtitleWeight = theme.typography.variants["body-lg"].fontWeight;
|
|
70
|
+
const subtitleWeight = theme.typography.variants["body"].fontWeight;
|
|
71
71
|
const subtitleFont = theme.typography.fonts[subtitleWeight];
|
|
72
72
|
const isSystemSubtitleFont = subtitleFont === SYSTEM_FONT;
|
|
73
73
|
|
|
@@ -86,18 +86,10 @@ const styles = StyleSheet.create((theme) => {
|
|
|
86
86
|
fontWeight: isSystemOverlineFont ? FONT_WEIGHT_VALUE[overlineWeight] : undefined,
|
|
87
87
|
color: (theme.color as unknown as Record<string, string | undefined>)["text.tertiary"] ?? "",
|
|
88
88
|
},
|
|
89
|
-
largeTitle: {
|
|
90
|
-
fontSize: theme.typography.variants["headline-lg"].fontSize,
|
|
91
|
-
lineHeight: theme.typography.variants["headline-lg"].lineHeight,
|
|
92
|
-
letterSpacing: theme.typography.variants["headline-lg"].letterSpacing,
|
|
93
|
-
fontFamily: isSystemTitleFont ? undefined : titleFont,
|
|
94
|
-
fontWeight: isSystemTitleFont ? FONT_WEIGHT_VALUE[titleWeight] : undefined,
|
|
95
|
-
color: (theme.color as unknown as Record<string, string | undefined>)["text.primary"] ?? "",
|
|
96
|
-
},
|
|
97
89
|
subtitle: {
|
|
98
|
-
fontSize: theme.typography.variants["body
|
|
99
|
-
lineHeight: theme.typography.variants["body
|
|
100
|
-
letterSpacing: theme.typography.variants["body
|
|
90
|
+
fontSize: theme.typography.variants["body"].fontSize,
|
|
91
|
+
lineHeight: theme.typography.variants["body"].lineHeight,
|
|
92
|
+
letterSpacing: theme.typography.variants["body"].letterSpacing,
|
|
101
93
|
fontFamily: isSystemSubtitleFont ? undefined : subtitleFont,
|
|
102
94
|
fontWeight: isSystemSubtitleFont ? FONT_WEIGHT_VALUE[subtitleWeight] : undefined,
|
|
103
95
|
color: (theme.color as unknown as Record<string, string | undefined>)["text.tertiary"] ?? "",
|
|
@@ -3,7 +3,7 @@ import { View } from "react-native";
|
|
|
3
3
|
|
|
4
4
|
import { StyleSheet } from "react-native-unistyles";
|
|
5
5
|
|
|
6
|
-
import type { ColorToken } from "#theme/types";
|
|
6
|
+
import type { ColorToken, FontWeightToken, TextVariant } from "#theme/types";
|
|
7
7
|
import { useHeaderBackground } from "../../../screen/parts/ScreenHeaderAppearance";
|
|
8
8
|
import { ScreenContext } from "../../../screen/ScreenContext";
|
|
9
9
|
import type { HeaderDismissProps } from "../../header-buttons";
|
|
@@ -19,6 +19,13 @@ export interface TitleHeaderProps extends HeaderDismissProps {
|
|
|
19
19
|
* the header only places it according to `titleAlignment`.
|
|
20
20
|
*/
|
|
21
21
|
title?: ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Typography variant for a text title in the compact bar. Defaults to
|
|
24
|
+
* `'headline'`. Ignored when `title` is a custom node.
|
|
25
|
+
*/
|
|
26
|
+
titleVariant?: TextVariant;
|
|
27
|
+
/** Font weight for a text title in the compact bar. Defaults to the variant's weight. */
|
|
28
|
+
titleWeight?: FontWeightToken;
|
|
22
29
|
/** Optional secondary line shown under the title, in the `label-sm` style. */
|
|
23
30
|
subtitle?: string;
|
|
24
31
|
/**
|
|
@@ -58,6 +65,8 @@ export interface TitleHeaderProps extends HeaderDismissProps {
|
|
|
58
65
|
/** Compact header. Screen owns the safe area when present; standalone headers reserve it here. */
|
|
59
66
|
export function TitleHeader({
|
|
60
67
|
title,
|
|
68
|
+
titleVariant,
|
|
69
|
+
titleWeight,
|
|
61
70
|
subtitle,
|
|
62
71
|
leadingActions,
|
|
63
72
|
trailingActions,
|
|
@@ -79,6 +88,8 @@ export function TitleHeader({
|
|
|
79
88
|
<HeaderBar
|
|
80
89
|
{...dismissProps}
|
|
81
90
|
title={title}
|
|
91
|
+
titleVariant={titleVariant}
|
|
92
|
+
titleWeight={titleWeight}
|
|
82
93
|
subtitle={subtitle}
|
|
83
94
|
titleAlignment={titleAlignment}
|
|
84
95
|
color={color}
|
|
@@ -5,6 +5,7 @@ import { StyleSheet } from "react-native-unistyles";
|
|
|
5
5
|
import { getImageComponent } from "#theme/registry";
|
|
6
6
|
import type { RadiusKey } from "#theme/types";
|
|
7
7
|
import type { ImageProps } from "./types";
|
|
8
|
+
import { toAspectRatio } from "./utils";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Renders the image implementation configured via
|
|
@@ -14,8 +15,8 @@ import type { ImageProps } from "./types";
|
|
|
14
15
|
* ```tsx
|
|
15
16
|
* <Image
|
|
16
17
|
* source={{ uri: 'https://example.com/photo.jpg' }}
|
|
17
|
-
*
|
|
18
|
-
*
|
|
18
|
+
* width={320}
|
|
19
|
+
* aspect="16:9"
|
|
19
20
|
* />
|
|
20
21
|
* ```
|
|
21
22
|
*/
|
|
@@ -23,6 +24,7 @@ export function Image({
|
|
|
23
24
|
contentFit = "cover",
|
|
24
25
|
width,
|
|
25
26
|
height,
|
|
27
|
+
aspect,
|
|
26
28
|
radius = "none",
|
|
27
29
|
style,
|
|
28
30
|
// Defaults to `contentFit` here (rather than leaving it to each backend's
|
|
@@ -34,7 +36,7 @@ export function Image({
|
|
|
34
36
|
const ImageComponent = getImageComponent();
|
|
35
37
|
|
|
36
38
|
return (
|
|
37
|
-
<View style={[styles.container(width, height, radius), style]}>
|
|
39
|
+
<View style={[styles.container(width, height, toAspectRatio(aspect), radius), style]}>
|
|
38
40
|
<ImageComponent
|
|
39
41
|
contentFit={contentFit}
|
|
40
42
|
placeholderContentFit={placeholderContentFit}
|
|
@@ -46,9 +48,10 @@ export function Image({
|
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
const styles = StyleSheet.create((_theme) => ({
|
|
49
|
-
container: (w: number | undefined, h: number | undefined, r: RadiusKey) => ({
|
|
51
|
+
container: (w: number | undefined, h: number | undefined, ratio: number | undefined, r: RadiusKey) => ({
|
|
50
52
|
...(w ? { width: w } : {}),
|
|
51
53
|
...(h ? { height: h } : {}),
|
|
54
|
+
...(ratio ? { aspectRatio: ratio } : {}),
|
|
52
55
|
borderRadius: _theme.radius[r],
|
|
53
56
|
overflow: "hidden",
|
|
54
57
|
}),
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Image } from "./Image";
|
|
2
|
-
export type { ImageProps } from "./types";
|
|
2
|
+
export type { ImageAspect, ImageProps } from "./types";
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { RadiusKey, TerraImageProps } from "#theme/types";
|
|
2
2
|
|
|
3
|
+
/** Width-to-height ratio. A number is used as is; `"3:4"` means 3 wide by 4 tall. */
|
|
4
|
+
export type ImageAspect = number | "1:1" | "3:4" | "16:9";
|
|
5
|
+
|
|
3
6
|
export interface ImageProps extends TerraImageProps {
|
|
4
7
|
width?: number;
|
|
5
8
|
height?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Ratio that derives the missing dimension from the one given. Ignored when
|
|
11
|
+
* both `width` and `height` are set.
|
|
12
|
+
*/
|
|
13
|
+
aspect?: ImageAspect;
|
|
6
14
|
radius?: RadiusKey;
|
|
7
15
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ImageAspect } from "./types";
|
|
2
|
+
|
|
3
|
+
const PRESET_RATIOS: Record<Exclude<ImageAspect, number>, number> = {
|
|
4
|
+
"1:1": 1,
|
|
5
|
+
"3:4": 3 / 4,
|
|
6
|
+
"16:9": 16 / 9,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function toAspectRatio(aspect: ImageAspect | undefined) {
|
|
10
|
+
return typeof aspect === "string" ? PRESET_RATIOS[aspect] : aspect;
|
|
11
|
+
}
|
|
@@ -9,10 +9,10 @@ import { FONT_WEIGHT_VALUE, SYSTEM_FONT } from "#utils/typography";
|
|
|
9
9
|
import type { InputBaseProps, InputSize } from "../types";
|
|
10
10
|
|
|
11
11
|
/** Typographic role of the text, per size. */
|
|
12
|
-
const TEXT_VARIANT: Record<InputSize, "
|
|
13
|
-
sm: "
|
|
14
|
-
md: "
|
|
15
|
-
lg: "body
|
|
12
|
+
const TEXT_VARIANT: Record<InputSize, "subheadline" | "callout" | "body"> = {
|
|
13
|
+
sm: "subheadline",
|
|
14
|
+
md: "callout",
|
|
15
|
+
lg: "body",
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -78,6 +78,14 @@ export interface ScreenContextValue {
|
|
|
78
78
|
* accessory; zero releases the reserved space.
|
|
79
79
|
*/
|
|
80
80
|
setHeaderAccessoryHeight: (height: number) => void;
|
|
81
|
+
/**
|
|
82
|
+
* Whether the header renders a large title in the scroll content. The large
|
|
83
|
+
* title then stands in for the content's top padding: scroll containers start
|
|
84
|
+
* it directly under the bar, and the padding is paid below it instead.
|
|
85
|
+
*/
|
|
86
|
+
hasLargeTitle: boolean;
|
|
87
|
+
/** Reported by the header that renders the large title; false releases it. */
|
|
88
|
+
setHasLargeTitle: (value: boolean) => void;
|
|
81
89
|
/**
|
|
82
90
|
* Primary scroll container's resolved top padding in dp, including header clearance.
|
|
83
91
|
* Used to position a sticky accessory below the large title at scroll offset zero.
|
|
@@ -149,6 +157,7 @@ export function ScreenScrollProvider({
|
|
|
149
157
|
}: ScreenScrollProviderProps) {
|
|
150
158
|
const scrollY = useSharedValue(0);
|
|
151
159
|
const [headerAccessoryHeight, setAccessoryHeight] = useState(0);
|
|
160
|
+
const [hasLargeTitle, setHasLargeTitle] = useState(false);
|
|
152
161
|
const [contentTopPadding, setTopPadding] = useState(0);
|
|
153
162
|
const headerCollapseHeight = useSharedValue(0);
|
|
154
163
|
const [headerSnapOffsets, setHeaderSnapOffsets] = useState<number[]>();
|
|
@@ -191,6 +200,8 @@ export function ScreenScrollProvider({
|
|
|
191
200
|
hasHeader,
|
|
192
201
|
headerAccessoryHeight,
|
|
193
202
|
setHeaderAccessoryHeight,
|
|
203
|
+
hasLargeTitle,
|
|
204
|
+
setHasLargeTitle,
|
|
194
205
|
contentTopPadding,
|
|
195
206
|
setContentTopPadding,
|
|
196
207
|
scrollY,
|
|
@@ -210,6 +221,7 @@ export function ScreenScrollProvider({
|
|
|
210
221
|
hasHeader,
|
|
211
222
|
headerAccessoryHeight,
|
|
212
223
|
setHeaderAccessoryHeight,
|
|
224
|
+
hasLargeTitle,
|
|
213
225
|
contentTopPadding,
|
|
214
226
|
setContentTopPadding,
|
|
215
227
|
scrollY,
|
|
@@ -182,6 +182,7 @@ const ScreenListBase = forwardRef(function ScreenList(
|
|
|
182
182
|
contentPadding: screenPadding,
|
|
183
183
|
hasHeader,
|
|
184
184
|
headerAccessoryHeight,
|
|
185
|
+
hasLargeTitle,
|
|
185
186
|
setContentTopPadding,
|
|
186
187
|
contentOwnsBottomInset,
|
|
187
188
|
platformAdjustsInsets,
|
|
@@ -196,7 +197,7 @@ const ScreenListBase = forwardRef(function ScreenList(
|
|
|
196
197
|
const { y: paddingY } = resolveScreenPadding(theme, rt.breakpoint);
|
|
197
198
|
const defaultTopPadding =
|
|
198
199
|
(hasHeader ? theme.layout.header.height + headerAccessoryHeight : 0) +
|
|
199
|
-
(hasVerticalPadding(resolvedPadding) ? paddingY : 0);
|
|
200
|
+
(hasVerticalPadding(resolvedPadding) && !hasLargeTitle ? paddingY : 0);
|
|
200
201
|
const paddingTopOverride = NativeStyleSheet.flatten(contentContainerStyle)?.paddingTop;
|
|
201
202
|
const contentTopPadding = typeof paddingTopOverride === "number" ? paddingTopOverride : defaultTopPadding;
|
|
202
203
|
// ListHeaderComponent is wrapped by the list, so its local layout.y excludes
|
|
@@ -245,7 +246,14 @@ const ScreenListBase = forwardRef(function ScreenList(
|
|
|
245
246
|
automaticallyAdjustContentInsets={false}
|
|
246
247
|
automaticallyAdjustsScrollIndicatorInsets={false}
|
|
247
248
|
contentContainerStyle={[
|
|
248
|
-
styles.content(
|
|
249
|
+
styles.content(
|
|
250
|
+
resolvedPadding,
|
|
251
|
+
hasHeader,
|
|
252
|
+
hasLargeTitle,
|
|
253
|
+
bottomInset,
|
|
254
|
+
extraBottomPadding,
|
|
255
|
+
headerAccessoryHeight
|
|
256
|
+
),
|
|
249
257
|
contentContainerStyle,
|
|
250
258
|
]}
|
|
251
259
|
ListHeaderComponent={header}
|
|
@@ -285,6 +293,7 @@ const styles = StyleSheet.create((theme, runtime) => ({
|
|
|
285
293
|
content: (
|
|
286
294
|
padding: ScreenContentPadding,
|
|
287
295
|
hasHeader: boolean,
|
|
296
|
+
hasLargeTitle: boolean,
|
|
288
297
|
bottomInset: number | undefined,
|
|
289
298
|
extraBottomPadding: number,
|
|
290
299
|
accessoryHeight: number
|
|
@@ -293,9 +302,11 @@ const styles = StyleSheet.create((theme, runtime) => ({
|
|
|
293
302
|
return {
|
|
294
303
|
flexGrow: 1,
|
|
295
304
|
// The theme height covers the bar; an accessory pinned under it is extra.
|
|
305
|
+
// A large title takes the place of the top padding, which the header
|
|
306
|
+
// content host pays below it instead (see ScreenHeaderContent).
|
|
296
307
|
paddingTop:
|
|
297
308
|
(hasHeader ? theme.layout.header.height + accessoryHeight : 0) +
|
|
298
|
-
(hasVerticalPadding(padding) ? y : 0),
|
|
309
|
+
(hasVerticalPadding(padding) && !hasLargeTitle ? y : 0),
|
|
299
310
|
paddingHorizontal: hasHorizontalPadding(padding) ? x : 0,
|
|
300
311
|
paddingBottom: (bottomInset ?? (hasVerticalPadding(padding) ? y : 0)) + extraBottomPadding,
|
|
301
312
|
};
|
|
@@ -79,6 +79,7 @@ export const ScreenScrollView = forwardRef<ComponentRef<typeof ScrollView>, Scre
|
|
|
79
79
|
contentPadding: screenPadding,
|
|
80
80
|
hasHeader,
|
|
81
81
|
headerAccessoryHeight,
|
|
82
|
+
hasLargeTitle,
|
|
82
83
|
setContentTopPadding,
|
|
83
84
|
background,
|
|
84
85
|
contentOwnsBottomInset,
|
|
@@ -119,7 +120,13 @@ export const ScreenScrollView = forwardRef<ComponentRef<typeof ScrollView>, Scre
|
|
|
119
120
|
showsVerticalScrollIndicator={false}
|
|
120
121
|
scrollEventThrottle={16}
|
|
121
122
|
contentContainerStyle={[
|
|
122
|
-
styles.scrollContent(
|
|
123
|
+
styles.scrollContent(
|
|
124
|
+
hasHeader,
|
|
125
|
+
hasLargeTitle,
|
|
126
|
+
resolvedPadding,
|
|
127
|
+
extraBottomPadding,
|
|
128
|
+
headerAccessoryHeight
|
|
129
|
+
),
|
|
123
130
|
contentContainerStyle,
|
|
124
131
|
]}
|
|
125
132
|
{...rest}
|
|
@@ -140,6 +147,7 @@ ScreenScrollView.displayName = "Screen.ScrollView";
|
|
|
140
147
|
const styles = StyleSheet.create((theme, runtime) => ({
|
|
141
148
|
scrollContent: (
|
|
142
149
|
hasHeader: boolean,
|
|
150
|
+
hasLargeTitle: boolean,
|
|
143
151
|
padding: ScreenContentPadding,
|
|
144
152
|
extraBottomPadding: number,
|
|
145
153
|
accessoryHeight: number
|
|
@@ -148,9 +156,11 @@ const styles = StyleSheet.create((theme, runtime) => ({
|
|
|
148
156
|
return {
|
|
149
157
|
flexGrow: 1,
|
|
150
158
|
// The theme height covers the bar; an accessory pinned under it is extra.
|
|
159
|
+
// A large title takes the place of the top padding, which the header
|
|
160
|
+
// content host pays below it instead (see ScreenHeaderContent).
|
|
151
161
|
paddingTop:
|
|
152
162
|
(hasHeader ? theme.layout.header.height + accessoryHeight : 0) +
|
|
153
|
-
(hasVerticalPadding(padding) ? y : 0),
|
|
163
|
+
(hasVerticalPadding(padding) && !hasLargeTitle ? y : 0),
|
|
154
164
|
paddingHorizontal: hasHorizontalPadding(padding) ? x : 0,
|
|
155
165
|
paddingBottom: (hasVerticalPadding(padding) ? y : 0) + extraBottomPadding,
|
|
156
166
|
};
|
|
@@ -9,7 +9,11 @@ import { SCREEN_HEADER_PORTAL_HOST } from "../ScreenContext";
|
|
|
9
9
|
import type { ScreenContentPadding } from "../types";
|
|
10
10
|
import { hasVerticalPadding } from "../utils";
|
|
11
11
|
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Hosts the large title and sticky accessory at the top of the scroll content.
|
|
14
|
+
* While it has content it pays the content's vertical padding below itself; the
|
|
15
|
+
* scroll container skips that padding above it (see `hasLargeTitle`).
|
|
16
|
+
*/
|
|
13
17
|
export function ScreenHeaderContent({ contentPadding }: { contentPadding: ScreenContentPadding }) {
|
|
14
18
|
const [hasContent, setHasContent] = useState(false);
|
|
15
19
|
const handleLayout = useCallback((event: LayoutChangeEvent) => {
|
|
@@ -64,7 +64,7 @@ const resolveElevation = (variant: SurfaceVariant, elevation?: ElevationKey): El
|
|
|
64
64
|
* @example
|
|
65
65
|
* ```tsx
|
|
66
66
|
* <Surface variant="raised" elevation="md" p="4" radius="lg">
|
|
67
|
-
* <Text variant="
|
|
67
|
+
* <Text variant="headline">Card title</Text>
|
|
68
68
|
* </Surface>
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
ColorToken,
|
|
8
8
|
FontWeightToken,
|
|
9
9
|
HeadingTextVariant,
|
|
10
|
+
LegacyTextVariant,
|
|
10
11
|
RoleTextVariant,
|
|
11
12
|
TextVariant,
|
|
12
13
|
} from "#theme/types";
|
|
@@ -17,22 +18,40 @@ import { FONT_WEIGHT_VALUE, SYSTEM_FONT } from "#utils/typography";
|
|
|
17
18
|
export type TextAlign = "left" | "center" | "right" | "justify";
|
|
18
19
|
|
|
19
20
|
const HEADING_VARIANT_ALIAS: Record<HeadingTextVariant, RoleTextVariant> = {
|
|
20
|
-
h1: "
|
|
21
|
-
h2: "
|
|
22
|
-
h3: "
|
|
23
|
-
h4: "title-
|
|
24
|
-
h5: "
|
|
25
|
-
h6: "
|
|
21
|
+
h1: "large-title",
|
|
22
|
+
h2: "title-1",
|
|
23
|
+
h3: "title-2",
|
|
24
|
+
h4: "title-3",
|
|
25
|
+
h5: "headline",
|
|
26
|
+
h6: "callout",
|
|
26
27
|
};
|
|
27
28
|
|
|
29
|
+
// Pre-0.14 names, mapped to the nearest role on the iOS-style scale. Only the
|
|
30
|
+
// name is aliased; a legacy variant takes the new role's weight and cap.
|
|
31
|
+
const LEGACY_VARIANT_ALIAS: Record<LegacyTextVariant, RoleTextVariant> = {
|
|
32
|
+
"display-lg": "display",
|
|
33
|
+
"display-md": "display",
|
|
34
|
+
"display-sm": "large-title",
|
|
35
|
+
"headline-lg": "title-1",
|
|
36
|
+
"headline-md": "title-2",
|
|
37
|
+
"headline-sm": "title-2",
|
|
38
|
+
"title-lg": "title-3",
|
|
39
|
+
"title-md": "headline",
|
|
40
|
+
"title-sm": "headline",
|
|
41
|
+
"body-lg": "body",
|
|
42
|
+
"body-md": "body",
|
|
43
|
+
"body-sm": "subheadline",
|
|
44
|
+
caption: "caption-1",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const VARIANT_ALIAS: Record<string, RoleTextVariant> = { ...HEADING_VARIANT_ALIAS, ...LEGACY_VARIANT_ALIAS };
|
|
48
|
+
|
|
28
49
|
function resolveTextVariant(variant: TextVariant): RoleTextVariant {
|
|
29
|
-
return variant
|
|
30
|
-
? HEADING_VARIANT_ALIAS[variant as HeadingTextVariant]
|
|
31
|
-
: (variant as RoleTextVariant);
|
|
50
|
+
return VARIANT_ALIAS[variant] ?? (variant as RoleTextVariant);
|
|
32
51
|
}
|
|
33
52
|
|
|
34
53
|
export interface TextProps extends RNTextProps {
|
|
35
|
-
/** Typographic role. Defaults to `body
|
|
54
|
+
/** Typographic role. Defaults to `body`. */
|
|
36
55
|
variant?: TextVariant;
|
|
37
56
|
/** Semantic color token (e.g. `text.secondary`, `danger.soft.fg`). */
|
|
38
57
|
color?: ColorToken;
|
|
@@ -53,7 +72,7 @@ export interface TextProps extends RNTextProps {
|
|
|
53
72
|
*/
|
|
54
73
|
export const Text = forwardRef<ComponentRef<typeof RNText>, TextProps>(function Text(
|
|
55
74
|
{
|
|
56
|
-
variant = "body
|
|
75
|
+
variant = "body",
|
|
57
76
|
color,
|
|
58
77
|
weight,
|
|
59
78
|
align,
|
|
@@ -187,7 +187,7 @@ const ToastTitle = forwardRef<ComponentRef<typeof Text>, ToastTitleProps>(functi
|
|
|
187
187
|
export interface ToastDescriptionProps extends TextProps {}
|
|
188
188
|
|
|
189
189
|
const ToastDescription = forwardRef<ComponentRef<typeof Text>, ToastDescriptionProps>(
|
|
190
|
-
function ToastDescription({ color, nativeID, variant = "
|
|
190
|
+
function ToastDescription({ color, nativeID, variant = "subheadline", ...rest }, ref) {
|
|
191
191
|
const { theme } = useUnistyles();
|
|
192
192
|
const { nativeID: rootNativeID, variant: toastVariant } = useToastContext();
|
|
193
193
|
const colors = getToastColors(toastVariant, theme);
|
|
@@ -207,14 +207,14 @@ function renderLabel(children: ReactNode, tint: ColorToken): ReactNode {
|
|
|
207
207
|
return Children.map(children, (child) => {
|
|
208
208
|
if (typeof child === "string") {
|
|
209
209
|
return child.trim().length > 0 ? (
|
|
210
|
-
<Text variant={LABEL_VARIANT} color={tint} weight="
|
|
210
|
+
<Text variant={LABEL_VARIANT} color={tint} weight="medium">
|
|
211
211
|
{child}
|
|
212
212
|
</Text>
|
|
213
213
|
) : null;
|
|
214
214
|
}
|
|
215
215
|
if (typeof child === "number") {
|
|
216
216
|
return (
|
|
217
|
-
<Text variant={LABEL_VARIANT} color={tint} weight="
|
|
217
|
+
<Text variant={LABEL_VARIANT} color={tint} weight="medium">
|
|
218
218
|
{child}
|
|
219
219
|
</Text>
|
|
220
220
|
);
|