@react-navigation/elements 3.0.0-alpha.2 → 3.0.0-alpha.20
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/lib/module/ActivityView.js +90 -0
- package/lib/module/ActivityView.js.map +1 -0
- package/lib/module/ActivityView.native.js +67 -0
- package/lib/module/ActivityView.native.js.map +1 -0
- package/lib/module/Badge.js +5 -4
- package/lib/module/Badge.js.map +1 -1
- package/lib/module/Button.js +7 -2
- package/lib/module/Button.js.map +1 -1
- package/lib/module/Color.js +95 -0
- package/lib/module/Color.js.map +1 -1
- package/lib/module/Container.js +11 -1
- package/lib/module/Container.js.map +1 -1
- package/lib/module/Header/Header.js +26 -11
- package/lib/module/Header/Header.js.map +1 -1
- package/lib/module/Header/HeaderBackButton.js +27 -13
- package/lib/module/Header/HeaderBackButton.js.map +1 -1
- package/lib/module/Header/HeaderIcon.js +29 -10
- package/lib/module/Header/HeaderIcon.js.map +1 -1
- package/lib/module/Header/HeaderSearchBar.js +28 -21
- package/lib/module/Header/HeaderSearchBar.js.map +1 -1
- package/lib/module/Header/getHeaderTitle.js.map +1 -1
- package/lib/module/Header/useHeaderHeight.js +1 -1
- package/lib/module/Header/useHeaderHeight.js.map +1 -1
- package/lib/module/Label/getLabel.js.map +1 -1
- package/lib/module/PlatformColor.js +1 -0
- package/lib/module/PlatformColor.js.map +1 -1
- package/lib/module/PlatformColor.native.js +1 -1
- package/lib/module/PlatformColor.native.js.map +1 -1
- package/lib/module/PlatformPressable.js.map +1 -1
- package/lib/module/SafeAreaProviderCompat.js +1 -1
- package/lib/module/SafeAreaProviderCompat.js.map +1 -1
- package/lib/module/Screen.js +13 -9
- package/lib/module/Screen.js.map +1 -1
- package/lib/module/index.js +1 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal.js +1 -1
- package/lib/module/internal.js.map +1 -1
- package/lib/module/useFrameSize.js +1 -1
- package/lib/module/useFrameSize.js.map +1 -1
- package/lib/typescript/src/ActivityView.d.ts +31 -0
- package/lib/typescript/src/ActivityView.d.ts.map +1 -0
- package/lib/typescript/src/ActivityView.native.d.ts +3 -0
- package/lib/typescript/src/ActivityView.native.d.ts.map +1 -0
- package/lib/typescript/src/Badge.d.ts +3 -3
- package/lib/typescript/src/Badge.d.ts.map +1 -1
- package/lib/typescript/src/Button.d.ts +3 -3
- package/lib/typescript/src/Button.d.ts.map +1 -1
- package/lib/typescript/src/Color.d.ts +4 -3
- package/lib/typescript/src/Color.d.ts.map +1 -1
- package/lib/typescript/src/Container.d.ts +7 -4
- package/lib/typescript/src/Container.d.ts.map +1 -1
- package/lib/typescript/src/Header/Header.d.ts +2 -2
- package/lib/typescript/src/Header/Header.d.ts.map +1 -1
- package/lib/typescript/src/Header/HeaderBackButton.d.ts +1 -1
- package/lib/typescript/src/Header/HeaderBackButton.d.ts.map +1 -1
- package/lib/typescript/src/Header/HeaderBackground.d.ts +3 -3
- package/lib/typescript/src/Header/HeaderBackground.d.ts.map +1 -1
- package/lib/typescript/src/Header/HeaderIcon.d.ts +8 -4
- package/lib/typescript/src/Header/HeaderIcon.d.ts.map +1 -1
- package/lib/typescript/src/Header/HeaderSearchBar.d.ts +4 -4
- package/lib/typescript/src/Header/HeaderSearchBar.d.ts.map +1 -1
- package/lib/typescript/src/Header/HeaderTitle.d.ts +3 -3
- package/lib/typescript/src/Header/HeaderTitle.d.ts.map +1 -1
- package/lib/typescript/src/Header/getHeaderTitle.d.ts +1 -1
- package/lib/typescript/src/Header/getHeaderTitle.d.ts.map +1 -1
- package/lib/typescript/src/Label/getLabel.d.ts +2 -2
- package/lib/typescript/src/Label/getLabel.d.ts.map +1 -1
- package/lib/typescript/src/PlatformColor.d.ts +1 -0
- package/lib/typescript/src/PlatformColor.d.ts.map +1 -1
- package/lib/typescript/src/PlatformColor.native.d.ts +1 -1
- package/lib/typescript/src/PlatformColor.native.d.ts.map +1 -1
- package/lib/typescript/src/PlatformPressable.d.ts +12 -12
- package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
- package/lib/typescript/src/Screen.d.ts +6 -6
- package/lib/typescript/src/Screen.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/internal.d.ts +1 -1
- package/lib/typescript/src/internal.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +119 -69
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +14 -13
- package/src/ActivityView.native.tsx +90 -0
- package/src/ActivityView.tsx +129 -0
- package/src/Badge.tsx +9 -7
- package/src/Button.tsx +15 -7
- package/src/Color.tsx +133 -3
- package/src/Container.tsx +20 -4
- package/src/Header/Header.tsx +43 -20
- package/src/Header/HeaderBackButton.tsx +31 -17
- package/src/Header/HeaderBackground.tsx +3 -3
- package/src/Header/HeaderIcon.tsx +49 -9
- package/src/Header/HeaderSearchBar.tsx +31 -22
- package/src/Header/HeaderTitle.tsx +3 -3
- package/src/Header/getHeaderTitle.tsx +4 -1
- package/src/Header/useHeaderHeight.tsx +1 -1
- package/src/Label/getLabel.tsx +1 -1
- package/src/PlatformColor.native.tsx +1 -1
- package/src/PlatformColor.tsx +4 -0
- package/src/PlatformPressable.tsx +12 -8
- package/src/SafeAreaProviderCompat.tsx +1 -1
- package/src/Screen.tsx +19 -19
- package/src/index.tsx +1 -16
- package/src/internal.tsx +1 -1
- package/src/types.tsx +153 -76
- package/src/useFrameSize.tsx +1 -1
- package/lib/module/Lazy.js +0 -42
- package/lib/module/Lazy.js.map +0 -1
- package/lib/module/assets/back-icon.ios.svg +0 -4
- package/lib/module/assets/back-icon@1x.android.png +0 -0
- package/lib/module/assets/back-icon@1x.ios.png +0 -0
- package/lib/module/assets/back-icon@2x.android.png +0 -0
- package/lib/module/assets/back-icon@2x.ios.png +0 -0
- package/lib/module/assets/back-icon@3x.android.png +0 -0
- package/lib/module/assets/back-icon@3x.ios.png +0 -0
- package/lib/module/assets/back-icon@4x.android.png +0 -0
- package/lib/module/assets/back-icon@4x.ios.png +0 -0
- package/lib/module/assets/clear-icon@1x.png +0 -0
- package/lib/module/assets/clear-icon@2x.png +0 -0
- package/lib/module/assets/clear-icon@3x.png +0 -0
- package/lib/module/assets/clear-icon@4x.png +0 -0
- package/lib/module/assets/close-icon@1x.png +0 -0
- package/lib/module/assets/close-icon@2x.png +0 -0
- package/lib/module/assets/close-icon@3x.png +0 -0
- package/lib/module/assets/close-icon@4x.png +0 -0
- package/lib/module/assets/search-icon-legacy@1x.ios.png +0 -0
- package/lib/module/assets/search-icon-legacy@2x.ios.png +0 -0
- package/lib/module/assets/search-icon-legacy@3x.ios.png +0 -0
- package/lib/module/assets/search-icon-legacy@4x.ios.png +0 -0
- package/lib/module/assets/search-icon.ios.svg +0 -4
- package/lib/module/assets/search-icon@1x.android.png +0 -0
- package/lib/module/assets/search-icon@1x.ios.png +0 -0
- package/lib/module/assets/search-icon@2x.android.png +0 -0
- package/lib/module/assets/search-icon@2x.ios.png +0 -0
- package/lib/module/assets/search-icon@3x.android.png +0 -0
- package/lib/module/assets/search-icon@3x.ios.png +0 -0
- package/lib/module/assets/search-icon@4x.android.png +0 -0
- package/lib/module/assets/search-icon@4x.ios.png +0 -0
- package/lib/typescript/src/Lazy.d.ts +0 -31
- package/lib/typescript/src/Lazy.d.ts.map +0 -1
- package/src/Lazy.tsx +0 -59
- package/src/assets/back-icon.ios.svg +0 -4
- package/src/assets/back-icon@1x.android.png +0 -0
- package/src/assets/back-icon@1x.ios.png +0 -0
- package/src/assets/back-icon@2x.android.png +0 -0
- package/src/assets/back-icon@2x.ios.png +0 -0
- package/src/assets/back-icon@3x.android.png +0 -0
- package/src/assets/back-icon@3x.ios.png +0 -0
- package/src/assets/back-icon@4x.android.png +0 -0
- package/src/assets/back-icon@4x.ios.png +0 -0
- package/src/assets/clear-icon@1x.png +0 -0
- package/src/assets/clear-icon@2x.png +0 -0
- package/src/assets/clear-icon@3x.png +0 -0
- package/src/assets/clear-icon@4x.png +0 -0
- package/src/assets/close-icon@1x.png +0 -0
- package/src/assets/close-icon@2x.png +0 -0
- package/src/assets/close-icon@3x.png +0 -0
- package/src/assets/close-icon@4x.png +0 -0
- package/src/assets/search-icon-legacy@1x.ios.png +0 -0
- package/src/assets/search-icon-legacy@2x.ios.png +0 -0
- package/src/assets/search-icon-legacy@3x.ios.png +0 -0
- package/src/assets/search-icon-legacy@4x.ios.png +0 -0
- package/src/assets/search-icon.ios.svg +0 -4
- package/src/assets/search-icon@1x.android.png +0 -0
- package/src/assets/search-icon@1x.ios.png +0 -0
- package/src/assets/search-icon@2x.android.png +0 -0
- package/src/assets/search-icon@2x.ios.png +0 -0
- package/src/assets/search-icon@3x.android.png +0 -0
- package/src/assets/search-icon@3x.ios.png +0 -0
- package/src/assets/search-icon@4x.android.png +0 -0
- package/src/assets/search-icon@4x.ios.png +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Activity, useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { Platform, View, type ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { Container } from './Container';
|
|
5
|
+
|
|
6
|
+
export type Props = {
|
|
7
|
+
/**
|
|
8
|
+
* Mode of the activity view
|
|
9
|
+
* - `normal`: The view renders normally
|
|
10
|
+
* - `inert`: Content is not interactive
|
|
11
|
+
* - `paused`: Effects are unmounted and content is not interactive
|
|
12
|
+
*/
|
|
13
|
+
mode: 'normal' | 'inert' | 'paused';
|
|
14
|
+
/**
|
|
15
|
+
* Whether the content is visible or not
|
|
16
|
+
*/
|
|
17
|
+
visible: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Delay before pausing effects.
|
|
20
|
+
* So pending animations have time to finish.
|
|
21
|
+
*
|
|
22
|
+
* Defaults to 500ms.
|
|
23
|
+
*/
|
|
24
|
+
delay?: number | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The style for the container view
|
|
27
|
+
*/
|
|
28
|
+
style?: Omit<React.CSSProperties & ViewStyle, 'display'> | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* The content of the activity view
|
|
31
|
+
*/
|
|
32
|
+
children: React.ReactNode;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function ActivityView({
|
|
36
|
+
mode,
|
|
37
|
+
visible,
|
|
38
|
+
delay = 500,
|
|
39
|
+
style,
|
|
40
|
+
children,
|
|
41
|
+
}: Props) {
|
|
42
|
+
const [delayedMode, setDelayedMode] = useState(mode);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!delay) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const timer = setTimeout(() => {
|
|
50
|
+
setDelayedMode(mode);
|
|
51
|
+
}, delay);
|
|
52
|
+
|
|
53
|
+
return () => clearTimeout(timer);
|
|
54
|
+
}, [delay, mode]);
|
|
55
|
+
|
|
56
|
+
const display = visible ? 'flex' : 'none';
|
|
57
|
+
const activityMode =
|
|
58
|
+
mode !== 'paused' || (delay && delayedMode !== 'paused')
|
|
59
|
+
? 'visible'
|
|
60
|
+
: 'hidden';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Activity has 2 modes, visible and hidden - hidden unmounts effects
|
|
64
|
+
* But what we want is to unmount effects, without hiding content
|
|
65
|
+
* So we use hidden mode, but unset display: none to make content visible
|
|
66
|
+
*/
|
|
67
|
+
const onRef = useCallback(
|
|
68
|
+
(node: HTMLDivElement | View | null) => {
|
|
69
|
+
if (Platform.OS !== 'web' || !(node && node instanceof HTMLElement)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const observers: MutationObserver[] = [];
|
|
74
|
+
|
|
75
|
+
const observe = () => {
|
|
76
|
+
// Remove previous observers
|
|
77
|
+
observers.forEach((o) => o.disconnect());
|
|
78
|
+
observers.length = 0;
|
|
79
|
+
|
|
80
|
+
const children = node.childNodes;
|
|
81
|
+
|
|
82
|
+
// When the style attribute for children is updated by React
|
|
83
|
+
// We observe it and update display to make content visible
|
|
84
|
+
children.forEach((child) => {
|
|
85
|
+
if (child instanceof HTMLElement) {
|
|
86
|
+
child.style.display = display;
|
|
87
|
+
|
|
88
|
+
const o = new MutationObserver(() => {
|
|
89
|
+
child.style.display = display;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
o.observe(child, {
|
|
93
|
+
attributes: true,
|
|
94
|
+
attributeFilter: ['style'],
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
observers.push(o);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
observe();
|
|
103
|
+
|
|
104
|
+
// React removes refs when `Activity` is hidden
|
|
105
|
+
// So we render outside of the `Activity` and observer child list
|
|
106
|
+
const observer = new MutationObserver(observe);
|
|
107
|
+
|
|
108
|
+
observer.observe(node, {
|
|
109
|
+
childList: true,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return () => {
|
|
113
|
+
observer.disconnect();
|
|
114
|
+
observers.forEach((o) => o.disconnect());
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
[display]
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<Container ref={onRef} style={{ display: 'contents' }}>
|
|
122
|
+
<Activity mode={activityMode}>
|
|
123
|
+
<Container inert={mode !== 'normal'} style={{ ...style, display }}>
|
|
124
|
+
{children}
|
|
125
|
+
</Container>
|
|
126
|
+
</Activity>
|
|
127
|
+
</Container>
|
|
128
|
+
);
|
|
129
|
+
}
|
package/src/Badge.tsx
CHANGED
|
@@ -19,15 +19,15 @@ type Props = TextProps & {
|
|
|
19
19
|
/**
|
|
20
20
|
* Content of the `Badge`.
|
|
21
21
|
*/
|
|
22
|
-
children?: string | number;
|
|
22
|
+
children?: string | number | undefined;
|
|
23
23
|
/**
|
|
24
24
|
* Size of the `Badge`.
|
|
25
25
|
*/
|
|
26
|
-
size?: number;
|
|
26
|
+
size?: number | undefined;
|
|
27
27
|
/**
|
|
28
28
|
* Style object for the tab bar container.
|
|
29
29
|
*/
|
|
30
|
-
style?: Animated.WithAnimatedValue<StyleProp<TextStyle
|
|
30
|
+
style?: Animated.WithAnimatedValue<StyleProp<TextStyle>> | undefined;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const useNativeDriver = Platform.OS !== 'web';
|
|
@@ -49,17 +49,19 @@ export function Badge({
|
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
Animated.timing(opacity, {
|
|
52
|
+
const animation = Animated.timing(opacity, {
|
|
53
53
|
toValue: visible ? 1 : 0,
|
|
54
54
|
duration: 150,
|
|
55
55
|
useNativeDriver,
|
|
56
|
-
})
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
animation.start(({ finished }) => {
|
|
57
59
|
if (finished && !visible) {
|
|
58
60
|
setRendered(false);
|
|
59
61
|
}
|
|
60
62
|
});
|
|
61
63
|
|
|
62
|
-
return () =>
|
|
64
|
+
return () => animation.stop();
|
|
63
65
|
}, [opacity, rendered, visible]);
|
|
64
66
|
|
|
65
67
|
if (!rendered) {
|
|
@@ -73,7 +75,7 @@ export function Badge({
|
|
|
73
75
|
// @ts-expect-error: backgroundColor definitely exists
|
|
74
76
|
const { backgroundColor = colors.notification, ...restStyle } =
|
|
75
77
|
StyleSheet.flatten(style) || {};
|
|
76
|
-
const textColor = Color(backgroundColor)
|
|
78
|
+
const textColor = Color.foreground(backgroundColor);
|
|
77
79
|
|
|
78
80
|
const borderRadius = size / 2;
|
|
79
81
|
const fontSize = Math.floor((size * 3) / 4);
|
package/src/Button.tsx
CHANGED
|
@@ -15,15 +15,15 @@ import {
|
|
|
15
15
|
import { Text } from './Text';
|
|
16
16
|
|
|
17
17
|
type ButtonBaseProps = Omit<PlatformPressableProps, 'children'> & {
|
|
18
|
-
variant?: 'plain' | 'tinted' | 'filled';
|
|
19
|
-
color?: ColorValue;
|
|
18
|
+
variant?: 'plain' | 'tinted' | 'filled' | undefined;
|
|
19
|
+
color?: ColorValue | undefined;
|
|
20
20
|
children: string | string[];
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
type ButtonLinkProps<
|
|
24
24
|
ParamList extends {} = RootParamList,
|
|
25
25
|
RouteName extends keyof ParamList = keyof ParamList,
|
|
26
|
-
> = LinkProps<ParamList, RouteName> &
|
|
26
|
+
> = LinkProps<ParamList, RouteName> & ButtonBaseProps;
|
|
27
27
|
|
|
28
28
|
const BUTTON_RADIUS = 40;
|
|
29
29
|
|
|
@@ -54,12 +54,22 @@ function ButtonLink<
|
|
|
54
54
|
params,
|
|
55
55
|
action,
|
|
56
56
|
href,
|
|
57
|
+
onPress,
|
|
57
58
|
...rest
|
|
58
59
|
}: ButtonLinkProps<ParamList, RouteName>) {
|
|
59
60
|
// @ts-expect-error: This is already type-checked by the prop types
|
|
60
61
|
const props = useLinkProps({ screen, params, action, href });
|
|
61
62
|
|
|
62
|
-
return
|
|
63
|
+
return (
|
|
64
|
+
<ButtonBase
|
|
65
|
+
{...rest}
|
|
66
|
+
{...props}
|
|
67
|
+
onPress={(e) => {
|
|
68
|
+
onPress?.(e);
|
|
69
|
+
props.onPress?.(e);
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
function ButtonBase({
|
|
@@ -90,9 +100,7 @@ function ButtonBase({
|
|
|
90
100
|
break;
|
|
91
101
|
case 'filled':
|
|
92
102
|
backgroundColor = color;
|
|
93
|
-
textColor = Color(
|
|
94
|
-
? 'white'
|
|
95
|
-
: (Color(color)?.darken(0.71).string() ?? '#fff');
|
|
103
|
+
textColor = Color.foreground(backgroundColor);
|
|
96
104
|
break;
|
|
97
105
|
}
|
|
98
106
|
|
package/src/Color.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// eslint-disable-next-line no-restricted-imports
|
|
2
2
|
import OriginalColor from 'color';
|
|
3
|
-
import type
|
|
3
|
+
import { type ColorValue, Platform } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { DynamicColorIOS, PlatformColor } from './PlatformColor';
|
|
4
6
|
|
|
5
7
|
type ColorType = {
|
|
6
|
-
isLight(): boolean;
|
|
7
|
-
isDark(): boolean;
|
|
8
8
|
alpha(amount: number): ColorType;
|
|
9
9
|
alpha(): number;
|
|
10
10
|
fade(amount: number): ColorType;
|
|
@@ -19,3 +19,133 @@ export function Color(value: ColorValue): ColorType | undefined {
|
|
|
19
19
|
|
|
20
20
|
return undefined;
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
Color.foreground = (color: ColorValue): ColorValue => {
|
|
24
|
+
const value = color as unknown;
|
|
25
|
+
|
|
26
|
+
if (typeof value === 'object' && value != null) {
|
|
27
|
+
// Special case for Android platform colors
|
|
28
|
+
// Available colors: https://developer.android.com/reference/android/R.color
|
|
29
|
+
if (
|
|
30
|
+
Platform.OS === 'android' &&
|
|
31
|
+
PlatformColor &&
|
|
32
|
+
'resource_paths' in value &&
|
|
33
|
+
Array.isArray(value.resource_paths) &&
|
|
34
|
+
typeof value.resource_paths[0] === 'string'
|
|
35
|
+
) {
|
|
36
|
+
const name = value.resource_paths[0].replace('@android:color/', '');
|
|
37
|
+
|
|
38
|
+
if (name in ANDROID_COLOR_MAP) {
|
|
39
|
+
return PlatformColor(`@android:color/${ANDROID_COLOR_MAP[name]}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Special case for iOS platform colors
|
|
44
|
+
if (
|
|
45
|
+
Platform.OS === 'ios' &&
|
|
46
|
+
PlatformColor &&
|
|
47
|
+
'semantic' in value &&
|
|
48
|
+
Array.isArray(value.semantic) &&
|
|
49
|
+
typeof value.semantic[0] === 'string'
|
|
50
|
+
) {
|
|
51
|
+
const name = value.semantic[0];
|
|
52
|
+
|
|
53
|
+
if (name in IOS_COLOR_MAP) {
|
|
54
|
+
const foreground = IOS_COLOR_MAP[name];
|
|
55
|
+
return foreground === 'white' || foreground === 'black'
|
|
56
|
+
? foreground
|
|
57
|
+
: PlatformColor(foreground);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Special case for iOS dynamic colors
|
|
62
|
+
if (
|
|
63
|
+
Platform.OS === 'ios' &&
|
|
64
|
+
DynamicColorIOS &&
|
|
65
|
+
'dynamic' in value &&
|
|
66
|
+
typeof value.dynamic === 'object' &&
|
|
67
|
+
value.dynamic != null &&
|
|
68
|
+
'light' in value.dynamic &&
|
|
69
|
+
typeof value.dynamic.light === 'string' &&
|
|
70
|
+
'dark' in value.dynamic &&
|
|
71
|
+
typeof value.dynamic.dark === 'string'
|
|
72
|
+
) {
|
|
73
|
+
const lightForeground = Color.foreground(value.dynamic.light);
|
|
74
|
+
const darkForeground = Color.foreground(value.dynamic.dark);
|
|
75
|
+
|
|
76
|
+
if (lightForeground && darkForeground) {
|
|
77
|
+
return DynamicColorIOS({
|
|
78
|
+
light: lightForeground,
|
|
79
|
+
dark: darkForeground,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} else if (typeof color === 'string' && !color.startsWith('var(')) {
|
|
84
|
+
const processed = OriginalColor(color);
|
|
85
|
+
|
|
86
|
+
if (processed.isLight()) {
|
|
87
|
+
return processed.darken(0.71).string();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return '#fff';
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const ANDROID_COLOR_MAP: Record<string, string> = {
|
|
95
|
+
system_background_dark: 'system_on_background_dark',
|
|
96
|
+
system_background_light: 'system_on_background_light',
|
|
97
|
+
system_error_container_dark: 'system_on_error_container_dark',
|
|
98
|
+
system_error_container_light: 'system_on_error_container_light',
|
|
99
|
+
system_error_dark: 'system_on_error_dark',
|
|
100
|
+
system_error_light: 'system_on_error_light',
|
|
101
|
+
system_primary_container_dark: 'system_on_primary_container_dark',
|
|
102
|
+
system_primary_container_light: 'system_on_primary_container_light',
|
|
103
|
+
system_primary_dark: 'system_on_primary_dark',
|
|
104
|
+
system_primary_fixed: 'system_on_primary_fixed',
|
|
105
|
+
system_primary_light: 'system_on_primary_light',
|
|
106
|
+
system_secondary_container_dark: 'system_on_secondary_container_dark',
|
|
107
|
+
system_secondary_container_light: 'system_on_secondary_container_light',
|
|
108
|
+
system_secondary_dark: 'system_on_secondary_dark',
|
|
109
|
+
system_secondary_fixed: 'system_on_secondary_fixed',
|
|
110
|
+
system_secondary_light: 'system_on_secondary_light',
|
|
111
|
+
system_surface_dark: 'system_on_surface_dark',
|
|
112
|
+
system_surface_disabled: 'system_on_surface_disabled',
|
|
113
|
+
system_surface_light: 'system_on_surface_light',
|
|
114
|
+
system_surface_variant_dark: 'system_on_surface_variant_dark',
|
|
115
|
+
system_surface_variant_light: 'system_on_surface_variant_light',
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const IOS_COLOR_MAP: Record<string, string> = {
|
|
119
|
+
systemBackground: 'label',
|
|
120
|
+
secondarySystemBackground: 'label',
|
|
121
|
+
tertiarySystemBackground: 'label',
|
|
122
|
+
systemGroupedBackground: 'label',
|
|
123
|
+
secondarySystemGroupedBackground: 'label',
|
|
124
|
+
tertiarySystemGroupedBackground: 'label',
|
|
125
|
+
|
|
126
|
+
systemFill: 'label',
|
|
127
|
+
secondarySystemFill: 'label',
|
|
128
|
+
tertiarySystemFill: 'label',
|
|
129
|
+
quaternarySystemFill: 'label',
|
|
130
|
+
|
|
131
|
+
systemRed: 'white',
|
|
132
|
+
systemGreen: 'white',
|
|
133
|
+
systemBlue: 'white',
|
|
134
|
+
systemIndigo: 'white',
|
|
135
|
+
systemPurple: 'white',
|
|
136
|
+
systemBrown: 'white',
|
|
137
|
+
|
|
138
|
+
systemOrange: 'black',
|
|
139
|
+
systemYellow: 'black',
|
|
140
|
+
systemMint: 'black',
|
|
141
|
+
systemTeal: 'black',
|
|
142
|
+
systemCyan: 'black',
|
|
143
|
+
systemPink: 'black',
|
|
144
|
+
|
|
145
|
+
systemGray: 'label',
|
|
146
|
+
systemGray2: 'label',
|
|
147
|
+
systemGray3: 'label',
|
|
148
|
+
systemGray4: 'label',
|
|
149
|
+
systemGray5: 'label',
|
|
150
|
+
systemGray6: 'label',
|
|
151
|
+
};
|
package/src/Container.tsx
CHANGED
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
import { Platform, View, type ViewStyle } from 'react-native';
|
|
2
2
|
|
|
3
3
|
export type Props = {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
ref?: React.Ref<HTMLDivElement | View> | undefined;
|
|
5
|
+
inert?: boolean | undefined;
|
|
6
|
+
style?:
|
|
7
|
+
| (ViewStyle &
|
|
8
|
+
Omit<React.CSSProperties, 'backgroundColor'> & {
|
|
9
|
+
backgroundColor?: ViewStyle['backgroundColor'] | undefined;
|
|
10
|
+
})
|
|
11
|
+
| undefined;
|
|
6
12
|
children: React.ReactNode;
|
|
7
13
|
};
|
|
8
14
|
|
|
9
|
-
export function Container({ inert, children, style }: Props) {
|
|
15
|
+
export function Container({ ref, inert, children, style }: Props) {
|
|
10
16
|
if (Platform.OS === 'web') {
|
|
17
|
+
const { backgroundColor, ...rest } = style ?? {};
|
|
18
|
+
|
|
11
19
|
return (
|
|
12
20
|
<div
|
|
21
|
+
ref={ref as React.Ref<HTMLDivElement> | undefined}
|
|
13
22
|
inert={inert}
|
|
14
23
|
aria-hidden={inert}
|
|
15
|
-
style={{
|
|
24
|
+
style={{
|
|
25
|
+
...DEFAULT_STYLE,
|
|
26
|
+
...rest,
|
|
27
|
+
backgroundColor:
|
|
28
|
+
// In practice we only get string on web instead of OpaqueValue
|
|
29
|
+
typeof backgroundColor === 'string' ? backgroundColor : undefined,
|
|
30
|
+
}}
|
|
16
31
|
>
|
|
17
32
|
{children}
|
|
18
33
|
</div>
|
|
@@ -21,6 +36,7 @@ export function Container({ inert, children, style }: Props) {
|
|
|
21
36
|
|
|
22
37
|
return (
|
|
23
38
|
<View
|
|
39
|
+
ref={ref as React.Ref<View> | undefined}
|
|
24
40
|
aria-hidden={inert}
|
|
25
41
|
style={[{ pointerEvents: inert ? 'none' : 'box-none' }, style]}
|
|
26
42
|
collapsable={false}
|
package/src/Header/Header.tsx
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
UNSTABLE_CornerInset,
|
|
3
|
+
useNavigation,
|
|
4
|
+
useTheme,
|
|
5
|
+
} from '@react-navigation/native';
|
|
2
6
|
import * as React from 'react';
|
|
3
7
|
import {
|
|
4
8
|
Animated,
|
|
@@ -30,20 +34,22 @@ type Props = HeaderOptions & {
|
|
|
30
34
|
/**
|
|
31
35
|
* Options for the back button.
|
|
32
36
|
*/
|
|
33
|
-
back?:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
back?:
|
|
38
|
+
| {
|
|
39
|
+
/**
|
|
40
|
+
* Title of the previous screen.
|
|
41
|
+
*/
|
|
42
|
+
title: string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* The `href` to use for the anchor tag on web
|
|
45
|
+
*/
|
|
46
|
+
href: string | undefined;
|
|
47
|
+
}
|
|
48
|
+
| undefined;
|
|
43
49
|
/**
|
|
44
50
|
* Whether the header is in a modal
|
|
45
51
|
*/
|
|
46
|
-
modal?: boolean;
|
|
52
|
+
modal?: boolean | undefined;
|
|
47
53
|
/**
|
|
48
54
|
* Title text for the header.
|
|
49
55
|
*/
|
|
@@ -79,7 +85,7 @@ export function Header(props: Props) {
|
|
|
79
85
|
const { colors } = useTheme();
|
|
80
86
|
|
|
81
87
|
const navigation = useNavigation();
|
|
82
|
-
const isParentHeaderShown = React.
|
|
88
|
+
const isParentHeaderShown = React.use(HeaderShownContext);
|
|
83
89
|
|
|
84
90
|
const [searchBarVisible, setSearchBarVisible] = React.useState(false);
|
|
85
91
|
|
|
@@ -321,21 +327,21 @@ export function Header(props: Props) {
|
|
|
321
327
|
return;
|
|
322
328
|
}
|
|
323
329
|
|
|
324
|
-
Animated.timing(searchBarVisibleAnim, {
|
|
330
|
+
const animation = Animated.timing(searchBarVisibleAnim, {
|
|
325
331
|
toValue: searchBarVisible ? 1 : 0,
|
|
326
332
|
duration: 150,
|
|
327
333
|
useNativeDriver,
|
|
328
334
|
easing: Easing.in(Easing.linear),
|
|
329
|
-
})
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
animation.start(({ finished }) => {
|
|
330
338
|
if (finished) {
|
|
331
339
|
setSearchBarRendered(searchBarVisible);
|
|
332
340
|
searchBarVisibleRef.current = searchBarVisible;
|
|
333
341
|
}
|
|
334
342
|
});
|
|
335
343
|
|
|
336
|
-
return () =>
|
|
337
|
-
searchBarVisibleAnim.stopAnimation();
|
|
338
|
-
};
|
|
344
|
+
return () => animation.stop();
|
|
339
345
|
}, [searchBarVisible, searchBarVisibleAnim]);
|
|
340
346
|
|
|
341
347
|
const headerOpacity = searchBarVisibleAnim.interpolate({
|
|
@@ -402,6 +408,7 @@ export function Header(props: Props) {
|
|
|
402
408
|
},
|
|
403
409
|
]}
|
|
404
410
|
>
|
|
411
|
+
<UNSTABLE_CornerInset direction="horizontal" edge="left" />
|
|
405
412
|
<View
|
|
406
413
|
style={[
|
|
407
414
|
styles.start,
|
|
@@ -458,7 +465,23 @@ export function Header(props: Props) {
|
|
|
458
465
|
headerSearchBarOptions?.onOpen?.();
|
|
459
466
|
}}
|
|
460
467
|
>
|
|
461
|
-
<HeaderIcon
|
|
468
|
+
<HeaderIcon
|
|
469
|
+
icon={Platform.select({
|
|
470
|
+
android: {
|
|
471
|
+
type: 'materialSymbol',
|
|
472
|
+
name: 'search',
|
|
473
|
+
},
|
|
474
|
+
ios: {
|
|
475
|
+
type: 'sfSymbol',
|
|
476
|
+
name: 'magnifyingglass',
|
|
477
|
+
},
|
|
478
|
+
default: {
|
|
479
|
+
type: 'image',
|
|
480
|
+
source: searchIcon,
|
|
481
|
+
},
|
|
482
|
+
})}
|
|
483
|
+
color={iconTintColor}
|
|
484
|
+
/>
|
|
462
485
|
</HeaderButton>
|
|
463
486
|
) : null}
|
|
464
487
|
</HeaderButtonBackground>
|
|
@@ -540,7 +563,7 @@ const styles = StyleSheet.create({
|
|
|
540
563
|
maxWidth: '50%',
|
|
541
564
|
},
|
|
542
565
|
background: {
|
|
543
|
-
...StyleSheet.
|
|
566
|
+
...StyleSheet.absoluteFill,
|
|
544
567
|
pointerEvents: 'box-none',
|
|
545
568
|
},
|
|
546
569
|
});
|
|
@@ -12,11 +12,12 @@ import {
|
|
|
12
12
|
View,
|
|
13
13
|
} from 'react-native';
|
|
14
14
|
|
|
15
|
-
import
|
|
15
|
+
import backIconImage from '../assets/back-icon.png';
|
|
16
16
|
import { isLiquidGlassSupported } from '../LiquidGlassView';
|
|
17
17
|
import type {
|
|
18
18
|
HeaderBackButtonDisplayMode,
|
|
19
19
|
HeaderBackButtonProps,
|
|
20
|
+
Icon,
|
|
20
21
|
} from '../types';
|
|
21
22
|
import { BUTTON_SIZE, HeaderButton } from './HeaderButton';
|
|
22
23
|
import { HeaderIcon } from './HeaderIcon';
|
|
@@ -24,7 +25,7 @@ import { HeaderIcon } from './HeaderIcon';
|
|
|
24
25
|
export function HeaderBackButton({
|
|
25
26
|
disabled,
|
|
26
27
|
allowFontScaling,
|
|
27
|
-
|
|
28
|
+
icon,
|
|
28
29
|
label,
|
|
29
30
|
labelStyle,
|
|
30
31
|
displayMode = 'minimal',
|
|
@@ -48,17 +49,30 @@ export function HeaderBackButton({
|
|
|
48
49
|
const isMinimal = displayMode === 'minimal' || measuredMinimal;
|
|
49
50
|
|
|
50
51
|
const renderBackImage = () => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<HeaderIcon
|
|
56
|
-
source={backIcon}
|
|
57
|
-
tintColor={tintColor ?? colors.text}
|
|
58
|
-
style={styles.icon}
|
|
59
|
-
/>
|
|
60
|
-
);
|
|
52
|
+
const color = tintColor ?? colors.text;
|
|
53
|
+
|
|
54
|
+
if (typeof icon === 'function') {
|
|
55
|
+
return icon({ tintColor: color });
|
|
61
56
|
}
|
|
57
|
+
|
|
58
|
+
const backIcon =
|
|
59
|
+
icon ??
|
|
60
|
+
Platform.select<Icon>({
|
|
61
|
+
ios: {
|
|
62
|
+
type: 'sfSymbol',
|
|
63
|
+
name: 'chevron.left',
|
|
64
|
+
},
|
|
65
|
+
android: {
|
|
66
|
+
type: 'materialSymbol',
|
|
67
|
+
name: 'arrow_back',
|
|
68
|
+
},
|
|
69
|
+
default: {
|
|
70
|
+
type: 'image',
|
|
71
|
+
source: backIconImage,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return <HeaderIcon icon={backIcon} color={color} style={styles.icon} />;
|
|
62
76
|
};
|
|
63
77
|
|
|
64
78
|
const handlePress = () => {
|
|
@@ -105,11 +119,11 @@ function HeaderBackLabel({
|
|
|
105
119
|
truncatedLabel,
|
|
106
120
|
onMeasureMinimal,
|
|
107
121
|
}: {
|
|
108
|
-
allowFontScaling?: boolean;
|
|
122
|
+
allowFontScaling?: boolean | undefined;
|
|
109
123
|
displayMode: HeaderBackButtonDisplayMode;
|
|
110
124
|
label: string | undefined;
|
|
111
|
-
labelStyle?: Animated.WithAnimatedValue<StyleProp<TextStyle
|
|
112
|
-
tintColor?: ColorValue;
|
|
125
|
+
labelStyle?: Animated.WithAnimatedValue<StyleProp<TextStyle>> | undefined;
|
|
126
|
+
tintColor?: ColorValue | undefined;
|
|
113
127
|
truncatedLabel: string | undefined;
|
|
114
128
|
onMeasureMinimal: () => void;
|
|
115
129
|
}) {
|
|
@@ -231,8 +245,8 @@ function HeaderBackLabel({
|
|
|
231
245
|
// iOS uses a smaller chevron, Android uses a larger arrow
|
|
232
246
|
const ICON_WIDTH = Platform.OS === 'ios' ? 13 : 24;
|
|
233
247
|
const ICON_SPACING_START = isLiquidGlassSupported
|
|
234
|
-
?
|
|
235
|
-
:
|
|
248
|
+
? 15 // Standard distance of chevron from left edge in liquid glass
|
|
249
|
+
: 2; // Otherwise icon is aligned to the start of the button
|
|
236
250
|
|
|
237
251
|
// Standard distance between chevron and label
|
|
238
252
|
const ICON_LABEL_SPACING = 9;
|
|
@@ -12,9 +12,9 @@ import { BlurEffectBackground } from '../BlurEffectBackground';
|
|
|
12
12
|
import { type BlurEffectType } from '../getBlurBackgroundColor';
|
|
13
13
|
|
|
14
14
|
type Props = Omit<ViewProps, 'style'> & {
|
|
15
|
-
blurEffect?: BlurEffectType | 'none';
|
|
16
|
-
style?: StyleProp<ViewStyle
|
|
17
|
-
children?: React.ReactNode;
|
|
15
|
+
blurEffect?: BlurEffectType | 'none' | undefined;
|
|
16
|
+
style?: StyleProp<ViewStyle> | undefined;
|
|
17
|
+
children?: React.ReactNode | undefined;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export function HeaderBackground({
|