@react-navigation/native-stack 8.0.0-alpha.57 → 8.0.0-alpha.59
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/README.md +1 -1
- package/lib/module/views/NativeStackView.js +105 -74
- package/lib/module/views/NativeStackView.js.map +1 -1
- package/lib/module/views/NativeStackView.native.js +98 -88
- package/lib/module/views/NativeStackView.native.js.map +1 -1
- package/lib/module/views/useHeaderConfigProps.js +21 -19
- package/lib/module/views/useHeaderConfigProps.js.map +1 -1
- package/lib/typescript/src/types.d.ts +29 -7
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/views/NativeStackView.d.ts.map +1 -1
- package/lib/typescript/src/views/NativeStackView.native.d.ts.map +1 -1
- package/lib/typescript/src/views/useHeaderConfigProps.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/types.tsx +28 -6
- package/src/views/NativeStackView.native.tsx +107 -97
- package/src/views/NativeStackView.tsx +151 -99
- package/src/views/useHeaderConfigProps.tsx +38 -20
|
@@ -52,22 +52,17 @@ type SceneViewProps = {
|
|
|
52
52
|
focused: boolean;
|
|
53
53
|
descriptor: NativeStackDescriptor;
|
|
54
54
|
previousDescriptor?: NativeStackDescriptor | undefined;
|
|
55
|
-
nextDescriptor?: NativeStackDescriptor | undefined;
|
|
56
55
|
isPresentationModal: boolean;
|
|
57
56
|
isNextScreenTransparent: boolean;
|
|
58
57
|
isNextScreenSheet: boolean;
|
|
59
58
|
isNextScreenModal: boolean;
|
|
60
59
|
isInactive: boolean;
|
|
61
60
|
isBeforeLast: boolean;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
onHeaderBackButtonClicked: ScreenProps['onHeaderBackButtonClicked'];
|
|
68
|
-
onNativeDismissCancelled: ScreenProps['onDismissed'];
|
|
69
|
-
onGestureCancel: ScreenProps['onGestureCancel'];
|
|
70
|
-
onSheetDetentChanged: ScreenProps['onSheetDetentChanged'];
|
|
61
|
+
hasNestedState: boolean;
|
|
62
|
+
stateKey: string;
|
|
63
|
+
emit: NativeStackNavigationHelpers['emit'];
|
|
64
|
+
dispatch: NativeStackNavigationHelpers['dispatch'];
|
|
65
|
+
setNextDismissedKey: (key: string) => void;
|
|
71
66
|
};
|
|
72
67
|
|
|
73
68
|
const useNativeDriver = Platform.OS !== 'web';
|
|
@@ -79,7 +74,7 @@ const TRANSPARENT_PRESENTATIONS = [
|
|
|
79
74
|
|
|
80
75
|
const SHEET_PRESENTATIONS = ['formSheet', 'pageSheet'];
|
|
81
76
|
|
|
82
|
-
const SceneView = ({
|
|
77
|
+
const SceneView = React.memo(function SceneView({
|
|
83
78
|
index,
|
|
84
79
|
focused,
|
|
85
80
|
descriptor,
|
|
@@ -90,18 +85,94 @@ const SceneView = ({
|
|
|
90
85
|
isNextScreenModal,
|
|
91
86
|
isInactive,
|
|
92
87
|
isBeforeLast,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
onNativeDismissCancelled,
|
|
100
|
-
onGestureCancel,
|
|
101
|
-
onSheetDetentChanged,
|
|
102
|
-
}: SceneViewProps) => {
|
|
88
|
+
hasNestedState,
|
|
89
|
+
stateKey,
|
|
90
|
+
emit,
|
|
91
|
+
dispatch,
|
|
92
|
+
setNextDismissedKey,
|
|
93
|
+
}: SceneViewProps) {
|
|
103
94
|
const { route, navigation, options, render } = descriptor;
|
|
104
95
|
|
|
96
|
+
const onWillDisappear = () => {
|
|
97
|
+
emit({
|
|
98
|
+
type: 'transitionStart',
|
|
99
|
+
data: { closing: true },
|
|
100
|
+
target: route.key,
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const onWillAppear = () => {
|
|
105
|
+
emit({
|
|
106
|
+
type: 'transitionStart',
|
|
107
|
+
data: { closing: false },
|
|
108
|
+
target: route.key,
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const onAppear = () => {
|
|
113
|
+
emit({
|
|
114
|
+
type: 'transitionEnd',
|
|
115
|
+
data: { closing: false },
|
|
116
|
+
target: route.key,
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const onDisappear = () => {
|
|
121
|
+
emit({
|
|
122
|
+
type: 'transitionEnd',
|
|
123
|
+
data: { closing: true },
|
|
124
|
+
target: route.key,
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const onDismissed: ScreenProps['onDismissed'] = (event) => {
|
|
129
|
+
if (isInactive) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
dispatch({
|
|
134
|
+
...StackActions.dismiss(event.nativeEvent.dismissCount),
|
|
135
|
+
source: route.key,
|
|
136
|
+
target: stateKey,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
setNextDismissedKey(route.key);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const onHeaderBackButtonClicked = () => {
|
|
143
|
+
dispatch({
|
|
144
|
+
...StackActions.pop(),
|
|
145
|
+
source: route.key,
|
|
146
|
+
target: stateKey,
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const onNativeDismissCancelled: ScreenProps['onDismissed'] = (event) => {
|
|
151
|
+
dispatch({
|
|
152
|
+
...StackActions.dismiss(event.nativeEvent.dismissCount),
|
|
153
|
+
source: route.key,
|
|
154
|
+
target: stateKey,
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const onGestureCancel = () => {
|
|
159
|
+
emit({
|
|
160
|
+
type: 'gestureCancel',
|
|
161
|
+
target: route.key,
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const onSheetDetentChanged: ScreenProps['onSheetDetentChanged'] = (event) => {
|
|
166
|
+
emit({
|
|
167
|
+
type: 'sheetDetentChange',
|
|
168
|
+
target: route.key,
|
|
169
|
+
data: {
|
|
170
|
+
index: event.nativeEvent.index,
|
|
171
|
+
stable: event.nativeEvent.isStable,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
|
|
105
176
|
const {
|
|
106
177
|
inactiveBehavior = 'pause',
|
|
107
178
|
animation,
|
|
@@ -307,6 +378,14 @@ const SceneView = ({
|
|
|
307
378
|
}
|
|
308
379
|
);
|
|
309
380
|
|
|
381
|
+
// Nested navigators clean up `route.state` when the screen is paused with `Activity`
|
|
382
|
+
// So we remember if the route had state until the screen is removed
|
|
383
|
+
const [hadNestedState, setHadNestedState] = React.useState(hasNestedState);
|
|
384
|
+
|
|
385
|
+
if (hasNestedState && !hadNestedState) {
|
|
386
|
+
setHadNestedState(true);
|
|
387
|
+
}
|
|
388
|
+
|
|
310
389
|
const activityMode =
|
|
311
390
|
// Render focused screens normally
|
|
312
391
|
// Unpause preloaded and retained screens so updates are visible
|
|
@@ -325,7 +404,7 @@ const SceneView = ({
|
|
|
325
404
|
(isNextScreenModal && !isModal) ||
|
|
326
405
|
isBeforeLast
|
|
327
406
|
? 'normal'
|
|
328
|
-
: inactiveBehavior === 'unmount' && !
|
|
407
|
+
: inactiveBehavior === 'unmount' && !hadNestedState
|
|
329
408
|
? 'unmounted'
|
|
330
409
|
: 'paused';
|
|
331
410
|
|
|
@@ -477,7 +556,7 @@ const SceneView = ({
|
|
|
477
556
|
</ScreenStackItem>
|
|
478
557
|
</NavigationProvider>
|
|
479
558
|
);
|
|
480
|
-
};
|
|
559
|
+
});
|
|
481
560
|
|
|
482
561
|
type Props = {
|
|
483
562
|
state: StackNavigationState<ParamListBase>;
|
|
@@ -534,86 +613,17 @@ export function NativeStackView({ state, navigation, descriptors }: Props) {
|
|
|
534
613
|
focused={isFocused}
|
|
535
614
|
descriptor={descriptor}
|
|
536
615
|
previousDescriptor={previousDescriptor}
|
|
537
|
-
nextDescriptor={nextDescriptor}
|
|
538
616
|
isPresentationModal={isModal}
|
|
539
617
|
isNextScreenTransparent={isNextScreenTransparent}
|
|
540
618
|
isNextScreenSheet={isNextScreenSheet}
|
|
541
619
|
isNextScreenModal={isNextScreenModal}
|
|
542
620
|
isInactive={index > state.index}
|
|
543
621
|
isBeforeLast={index === activeRoutes.length - 2}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
});
|
|
550
|
-
}}
|
|
551
|
-
onWillAppear={() => {
|
|
552
|
-
navigation.emit({
|
|
553
|
-
type: 'transitionStart',
|
|
554
|
-
data: { closing: false },
|
|
555
|
-
target: route.key,
|
|
556
|
-
});
|
|
557
|
-
}}
|
|
558
|
-
onAppear={() => {
|
|
559
|
-
navigation.emit({
|
|
560
|
-
type: 'transitionEnd',
|
|
561
|
-
data: { closing: false },
|
|
562
|
-
target: route.key,
|
|
563
|
-
});
|
|
564
|
-
}}
|
|
565
|
-
onDisappear={() => {
|
|
566
|
-
navigation.emit({
|
|
567
|
-
type: 'transitionEnd',
|
|
568
|
-
data: { closing: true },
|
|
569
|
-
target: route.key,
|
|
570
|
-
});
|
|
571
|
-
}}
|
|
572
|
-
onDismissed={(event) => {
|
|
573
|
-
navigation.dispatch({
|
|
574
|
-
...StackActions.remove(
|
|
575
|
-
route.name,
|
|
576
|
-
event.nativeEvent.dismissCount
|
|
577
|
-
),
|
|
578
|
-
source: route.key,
|
|
579
|
-
target: state.key,
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
setNextDismissedKey(route.key);
|
|
583
|
-
}}
|
|
584
|
-
onHeaderBackButtonClicked={() => {
|
|
585
|
-
navigation.dispatch({
|
|
586
|
-
...StackActions.pop(),
|
|
587
|
-
source: route.key,
|
|
588
|
-
target: state.key,
|
|
589
|
-
});
|
|
590
|
-
}}
|
|
591
|
-
onNativeDismissCancelled={(event) => {
|
|
592
|
-
navigation.dispatch({
|
|
593
|
-
...StackActions.remove(
|
|
594
|
-
route.name,
|
|
595
|
-
event.nativeEvent.dismissCount
|
|
596
|
-
),
|
|
597
|
-
source: route.key,
|
|
598
|
-
target: state.key,
|
|
599
|
-
});
|
|
600
|
-
}}
|
|
601
|
-
onGestureCancel={() => {
|
|
602
|
-
navigation.emit({
|
|
603
|
-
type: 'gestureCancel',
|
|
604
|
-
target: route.key,
|
|
605
|
-
});
|
|
606
|
-
}}
|
|
607
|
-
onSheetDetentChanged={(event) => {
|
|
608
|
-
navigation.emit({
|
|
609
|
-
type: 'sheetDetentChange',
|
|
610
|
-
target: route.key,
|
|
611
|
-
data: {
|
|
612
|
-
index: event.nativeEvent.index,
|
|
613
|
-
stable: event.nativeEvent.isStable,
|
|
614
|
-
},
|
|
615
|
-
});
|
|
616
|
-
}}
|
|
622
|
+
hasNestedState={route.state != null}
|
|
623
|
+
stateKey={state.key}
|
|
624
|
+
emit={navigation.emit}
|
|
625
|
+
dispatch={navigation.dispatch}
|
|
626
|
+
setNextDismissedKey={setNextDismissedKey}
|
|
617
627
|
/>
|
|
618
628
|
);
|
|
619
629
|
})}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
Screen,
|
|
12
12
|
} from '@react-navigation/elements/internal';
|
|
13
13
|
import {
|
|
14
|
+
type NavigationRoute,
|
|
14
15
|
type ParamListBase,
|
|
15
16
|
type StackNavigationState,
|
|
16
17
|
useLinkBuilder,
|
|
@@ -19,6 +20,7 @@ import * as React from 'react';
|
|
|
19
20
|
import { Animated, StyleSheet } from 'react-native';
|
|
20
21
|
|
|
21
22
|
import type {
|
|
23
|
+
NativeStackDescriptor,
|
|
22
24
|
NativeStackDescriptorMap,
|
|
23
25
|
NativeStackNavigationHelpers,
|
|
24
26
|
} from '../types';
|
|
@@ -43,6 +45,24 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
43
45
|
const activeRoutes = state.routes.slice(0, state.index + 1);
|
|
44
46
|
const modalRouteKeys = getModalRouteKeys(activeRoutes, descriptors);
|
|
45
47
|
|
|
48
|
+
const [savedNestedStateRouteKeys, setSavedNestedStateRouteKeys] =
|
|
49
|
+
React.useState<string[]>([]);
|
|
50
|
+
|
|
51
|
+
// Nested navigators clean up `route.state` when the screen is paused with `Activity`
|
|
52
|
+
// So we remember routes which had state until they are removed
|
|
53
|
+
const nestedStateRouteKeys = state.routes
|
|
54
|
+
.filter(
|
|
55
|
+
(route) =>
|
|
56
|
+
route.state != null || savedNestedStateRouteKeys.includes(route.key)
|
|
57
|
+
)
|
|
58
|
+
.map((route) => route.key);
|
|
59
|
+
|
|
60
|
+
if (
|
|
61
|
+
nestedStateRouteKeys.some((key) => !savedNestedStateRouteKeys.includes(key))
|
|
62
|
+
) {
|
|
63
|
+
setSavedNestedStateRouteKeys(nestedStateRouteKeys);
|
|
64
|
+
}
|
|
65
|
+
|
|
46
66
|
return (
|
|
47
67
|
<SafeAreaProviderCompat>
|
|
48
68
|
{state.routes.map((route, i) => {
|
|
@@ -61,35 +81,7 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
61
81
|
);
|
|
62
82
|
}
|
|
63
83
|
|
|
64
|
-
const {
|
|
65
|
-
|
|
66
|
-
const headerBack = previousDescriptor
|
|
67
|
-
? {
|
|
68
|
-
title: getHeaderTitle(
|
|
69
|
-
previousDescriptor.options,
|
|
70
|
-
previousDescriptor.route.name
|
|
71
|
-
),
|
|
72
|
-
href: buildHref(
|
|
73
|
-
previousDescriptor.route.name,
|
|
74
|
-
previousDescriptor.route.params
|
|
75
|
-
),
|
|
76
|
-
}
|
|
77
|
-
: parentHeaderBack;
|
|
78
|
-
|
|
79
|
-
const canGoBack = headerBack != null;
|
|
80
|
-
|
|
81
|
-
const {
|
|
82
|
-
inactiveBehavior = 'pause',
|
|
83
|
-
header,
|
|
84
|
-
headerShown,
|
|
85
|
-
headerBackIcon,
|
|
86
|
-
headerLeft,
|
|
87
|
-
headerTransparent,
|
|
88
|
-
headerBackTitle,
|
|
89
|
-
presentation,
|
|
90
|
-
contentStyle,
|
|
91
|
-
...rest
|
|
92
|
-
} = options;
|
|
84
|
+
const { inactiveBehavior = 'pause' } = descriptor.options;
|
|
93
85
|
|
|
94
86
|
const nextPresentation = nextDescriptor?.options.presentation;
|
|
95
87
|
|
|
@@ -118,7 +110,7 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
118
110
|
: inactiveBehavior === 'unmount' &&
|
|
119
111
|
!isNextScreenModal &&
|
|
120
112
|
!isBeforeLast &&
|
|
121
|
-
!route.
|
|
113
|
+
!nestedStateRouteKeys.includes(route.key)
|
|
122
114
|
? 'unmounted'
|
|
123
115
|
: 'paused';
|
|
124
116
|
|
|
@@ -126,84 +118,144 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
126
118
|
return null;
|
|
127
119
|
}
|
|
128
120
|
|
|
129
|
-
const content = (
|
|
130
|
-
<Screen
|
|
131
|
-
key={route.key}
|
|
132
|
-
focused={isFocused}
|
|
133
|
-
route={route}
|
|
134
|
-
navigation={navigation}
|
|
135
|
-
headerShown={headerShown}
|
|
136
|
-
headerTransparent={headerTransparent}
|
|
137
|
-
pageOverflowEnabled={
|
|
138
|
-
presentation == null || presentation === 'card'
|
|
139
|
-
}
|
|
140
|
-
contentStyle={contentStyle}
|
|
141
|
-
header={
|
|
142
|
-
header !== undefined ? (
|
|
143
|
-
header({
|
|
144
|
-
back: headerBack,
|
|
145
|
-
options,
|
|
146
|
-
route,
|
|
147
|
-
navigation,
|
|
148
|
-
})
|
|
149
|
-
) : (
|
|
150
|
-
<Header
|
|
151
|
-
{...rest}
|
|
152
|
-
back={headerBack}
|
|
153
|
-
title={getHeaderTitle(options, route.name)}
|
|
154
|
-
headerLeft={
|
|
155
|
-
typeof headerLeft === 'function'
|
|
156
|
-
? ({ label, ...rest }) =>
|
|
157
|
-
headerLeft({
|
|
158
|
-
...rest,
|
|
159
|
-
label: headerBackTitle ?? label,
|
|
160
|
-
})
|
|
161
|
-
: headerLeft === undefined && canGoBack
|
|
162
|
-
? ({ tintColor, label, ...rest }) => (
|
|
163
|
-
<HeaderBackButton
|
|
164
|
-
{...rest}
|
|
165
|
-
label={headerBackTitle ?? label}
|
|
166
|
-
tintColor={tintColor}
|
|
167
|
-
icon={headerBackIcon}
|
|
168
|
-
onPress={navigation.goBack}
|
|
169
|
-
/>
|
|
170
|
-
)
|
|
171
|
-
: headerLeft
|
|
172
|
-
}
|
|
173
|
-
headerTransparent={headerTransparent}
|
|
174
|
-
/>
|
|
175
|
-
)
|
|
176
|
-
}
|
|
177
|
-
style={{
|
|
178
|
-
...(presentation != null &&
|
|
179
|
-
TRANSPARENT_PRESENTATIONS.includes(presentation)
|
|
180
|
-
? { backgroundColor: 'transparent' }
|
|
181
|
-
: null),
|
|
182
|
-
}}
|
|
183
|
-
>
|
|
184
|
-
<HeaderBackContext.Provider value={headerBack}>
|
|
185
|
-
<AnimatedHeaderHeightProvider>
|
|
186
|
-
{render()}
|
|
187
|
-
</AnimatedHeaderHeightProvider>
|
|
188
|
-
</HeaderBackContext.Provider>
|
|
189
|
-
</Screen>
|
|
190
|
-
);
|
|
191
|
-
|
|
192
121
|
return (
|
|
193
|
-
<
|
|
122
|
+
<SceneView
|
|
194
123
|
key={route.key}
|
|
195
|
-
|
|
124
|
+
route={route}
|
|
125
|
+
descriptor={descriptor}
|
|
126
|
+
previousDescriptor={previousDescriptor}
|
|
127
|
+
parentHeaderBack={parentHeaderBack}
|
|
128
|
+
buildHref={buildHref}
|
|
129
|
+
focused={isFocused}
|
|
130
|
+
activityMode={activityMode}
|
|
196
131
|
visible={isFocused || isNextScreenTransparent}
|
|
197
|
-
|
|
198
|
-
>
|
|
199
|
-
{content}
|
|
200
|
-
</ActivityView>
|
|
132
|
+
/>
|
|
201
133
|
);
|
|
202
134
|
})}
|
|
203
135
|
</SafeAreaProviderCompat>
|
|
204
136
|
);
|
|
205
137
|
}
|
|
206
138
|
|
|
139
|
+
type SceneViewProps = {
|
|
140
|
+
route: NavigationRoute<ParamListBase, string>;
|
|
141
|
+
descriptor: NativeStackDescriptor;
|
|
142
|
+
previousDescriptor: NativeStackDescriptor | undefined;
|
|
143
|
+
parentHeaderBack: React.ContextType<typeof HeaderBackContext>;
|
|
144
|
+
buildHref: ReturnType<typeof useLinkBuilder>['buildHref'];
|
|
145
|
+
focused: boolean;
|
|
146
|
+
activityMode: 'normal' | 'inert' | 'paused';
|
|
147
|
+
visible: boolean;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const SceneView = React.memo(function SceneView({
|
|
151
|
+
route,
|
|
152
|
+
descriptor,
|
|
153
|
+
previousDescriptor,
|
|
154
|
+
parentHeaderBack,
|
|
155
|
+
buildHref,
|
|
156
|
+
focused,
|
|
157
|
+
activityMode,
|
|
158
|
+
visible,
|
|
159
|
+
}: SceneViewProps) {
|
|
160
|
+
const { options, navigation, render } = descriptor;
|
|
161
|
+
|
|
162
|
+
const headerBack = previousDescriptor
|
|
163
|
+
? {
|
|
164
|
+
title: getHeaderTitle(
|
|
165
|
+
previousDescriptor.options,
|
|
166
|
+
previousDescriptor.route.name
|
|
167
|
+
),
|
|
168
|
+
href: buildHref(
|
|
169
|
+
previousDescriptor.route.name,
|
|
170
|
+
previousDescriptor.route.params
|
|
171
|
+
),
|
|
172
|
+
}
|
|
173
|
+
: parentHeaderBack;
|
|
174
|
+
|
|
175
|
+
const canGoBack = headerBack != null;
|
|
176
|
+
|
|
177
|
+
const {
|
|
178
|
+
header,
|
|
179
|
+
headerShown,
|
|
180
|
+
headerBackIcon,
|
|
181
|
+
headerLeft,
|
|
182
|
+
headerTransparent,
|
|
183
|
+
headerBackTitle,
|
|
184
|
+
presentation,
|
|
185
|
+
contentStyle,
|
|
186
|
+
...rest
|
|
187
|
+
} = options;
|
|
188
|
+
|
|
189
|
+
const content = (
|
|
190
|
+
<Screen
|
|
191
|
+
key={route.key}
|
|
192
|
+
focused={focused}
|
|
193
|
+
route={route}
|
|
194
|
+
navigation={navigation}
|
|
195
|
+
headerShown={headerShown}
|
|
196
|
+
headerTransparent={headerTransparent}
|
|
197
|
+
pageOverflowEnabled={presentation == null || presentation === 'card'}
|
|
198
|
+
contentStyle={contentStyle}
|
|
199
|
+
header={
|
|
200
|
+
header !== undefined ? (
|
|
201
|
+
header({
|
|
202
|
+
back: headerBack,
|
|
203
|
+
options,
|
|
204
|
+
route,
|
|
205
|
+
navigation,
|
|
206
|
+
})
|
|
207
|
+
) : (
|
|
208
|
+
<Header
|
|
209
|
+
{...rest}
|
|
210
|
+
back={headerBack}
|
|
211
|
+
title={getHeaderTitle(options, route.name)}
|
|
212
|
+
headerLeft={
|
|
213
|
+
typeof headerLeft === 'function'
|
|
214
|
+
? ({ label, ...rest }) =>
|
|
215
|
+
headerLeft({
|
|
216
|
+
...rest,
|
|
217
|
+
label: headerBackTitle ?? label,
|
|
218
|
+
})
|
|
219
|
+
: headerLeft === undefined && canGoBack
|
|
220
|
+
? ({ tintColor, label, ...rest }) => (
|
|
221
|
+
<HeaderBackButton
|
|
222
|
+
{...rest}
|
|
223
|
+
label={headerBackTitle ?? label}
|
|
224
|
+
tintColor={tintColor}
|
|
225
|
+
icon={headerBackIcon}
|
|
226
|
+
onPress={navigation.goBack}
|
|
227
|
+
/>
|
|
228
|
+
)
|
|
229
|
+
: headerLeft
|
|
230
|
+
}
|
|
231
|
+
headerTransparent={headerTransparent}
|
|
232
|
+
/>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
style={{
|
|
236
|
+
...(presentation != null &&
|
|
237
|
+
TRANSPARENT_PRESENTATIONS.includes(presentation)
|
|
238
|
+
? { backgroundColor: 'transparent' }
|
|
239
|
+
: null),
|
|
240
|
+
}}
|
|
241
|
+
>
|
|
242
|
+
<HeaderBackContext.Provider value={headerBack}>
|
|
243
|
+
<AnimatedHeaderHeightProvider>{render()}</AnimatedHeaderHeightProvider>
|
|
244
|
+
</HeaderBackContext.Provider>
|
|
245
|
+
</Screen>
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<ActivityView
|
|
250
|
+
mode={activityMode}
|
|
251
|
+
visible={visible}
|
|
252
|
+
style={StyleSheet.absoluteFill}
|
|
253
|
+
>
|
|
254
|
+
{content}
|
|
255
|
+
</ActivityView>
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
207
259
|
const AnimatedHeaderHeightProvider = ({
|
|
208
260
|
children,
|
|
209
261
|
}: {
|
|
@@ -12,6 +12,7 @@ import { Platform, StyleSheet, type TextStyle, View } from 'react-native';
|
|
|
12
12
|
import {
|
|
13
13
|
type HeaderBarButtonItemMenuAction,
|
|
14
14
|
type HeaderBarButtonItemSubmenu,
|
|
15
|
+
type HeaderBarButtonItemVisibilityPriority,
|
|
15
16
|
type HeaderBarButtonItemWithAction,
|
|
16
17
|
type HeaderBarButtonItemWithMenu,
|
|
17
18
|
isSearchBarAvailableForCurrentPlatform,
|
|
@@ -73,7 +74,13 @@ const processBarButtonItems = (
|
|
|
73
74
|
);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
const { badge, label, labelStyle, icon, ...rest } =
|
|
77
|
+
const { badge, label, labelStyle, icon, visibilityPriority, ...rest } =
|
|
78
|
+
item;
|
|
79
|
+
|
|
80
|
+
const processedVisibilityPriority:
|
|
81
|
+
| HeaderBarButtonItemVisibilityPriority
|
|
82
|
+
| undefined =
|
|
83
|
+
visibilityPriority === 'default' ? 'standard' : visibilityPriority;
|
|
77
84
|
|
|
78
85
|
const processedItemCommon = {
|
|
79
86
|
...rest,
|
|
@@ -84,6 +91,7 @@ const processBarButtonItems = (
|
|
|
84
91
|
...labelStyle,
|
|
85
92
|
},
|
|
86
93
|
icon: transformIcon(icon),
|
|
94
|
+
visibilityPriority: processedVisibilityPriority,
|
|
87
95
|
};
|
|
88
96
|
|
|
89
97
|
let processedItem:
|
|
@@ -164,13 +172,22 @@ const getMenuItem = (
|
|
|
164
172
|
item: NativeStackHeaderItemMenuAction | NativeStackHeaderItemMenuSubmenu
|
|
165
173
|
): HeaderBarButtonItemMenuAction | HeaderBarButtonItemSubmenu => {
|
|
166
174
|
if (item.type === 'submenu') {
|
|
167
|
-
const {
|
|
168
|
-
|
|
175
|
+
const {
|
|
176
|
+
label,
|
|
177
|
+
description,
|
|
178
|
+
icon,
|
|
179
|
+
inline,
|
|
180
|
+
layout,
|
|
181
|
+
items,
|
|
182
|
+
multiselectable,
|
|
183
|
+
...rest
|
|
184
|
+
} = item;
|
|
169
185
|
|
|
170
186
|
return {
|
|
171
187
|
...rest,
|
|
172
188
|
icon: transformIcon(icon),
|
|
173
189
|
title: label,
|
|
190
|
+
subtitle: description,
|
|
174
191
|
displayAsPalette: layout === 'palette',
|
|
175
192
|
displayInline: inline,
|
|
176
193
|
singleSelection:
|
|
@@ -379,21 +396,20 @@ export function useHeaderConfigProps({
|
|
|
379
396
|
return undefined;
|
|
380
397
|
}
|
|
381
398
|
|
|
382
|
-
if (
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
399
|
+
if (
|
|
400
|
+
headerBackIcon == null &&
|
|
401
|
+
Platform.OS === 'android' &&
|
|
402
|
+
MaterialSymbol.isAvailable()
|
|
403
|
+
) {
|
|
404
|
+
// Use Material Symbol as default back icon on Android
|
|
405
|
+
// So it's consistent with other material icons
|
|
406
|
+
// Based on the available variant and weight
|
|
407
|
+
return MaterialSymbol.getImageSource({
|
|
408
|
+
name: 'arrow_back',
|
|
409
|
+
color: tintColor,
|
|
410
|
+
size: ICON_SIZE,
|
|
411
|
+
direction,
|
|
412
|
+
});
|
|
397
413
|
} else if (headerBackIcon?.type === 'image') {
|
|
398
414
|
return headerBackIcon.source;
|
|
399
415
|
} else if (headerBackIcon?.type === 'materialSymbol') {
|
|
@@ -401,13 +417,15 @@ export function useHeaderConfigProps({
|
|
|
401
417
|
name: headerBackIcon.name,
|
|
402
418
|
variant: headerBackIcon.variant,
|
|
403
419
|
weight: headerBackIcon.weight,
|
|
420
|
+
fill: headerBackIcon.fill,
|
|
404
421
|
color: tintColor,
|
|
405
422
|
size: ICON_SIZE,
|
|
423
|
+
direction,
|
|
406
424
|
});
|
|
407
425
|
}
|
|
408
426
|
|
|
409
427
|
return undefined;
|
|
410
|
-
}, [headerBackIcon, headerBackVisible, headerShown, tintColor]);
|
|
428
|
+
}, [direction, headerBackIcon, headerBackVisible, headerShown, tintColor]);
|
|
411
429
|
|
|
412
430
|
const children = (
|
|
413
431
|
<>
|
|
@@ -446,7 +464,7 @@ export function useHeaderConfigProps({
|
|
|
446
464
|
<>
|
|
447
465
|
{headerLeftElement != null || typeof headerTitle === 'function' ? (
|
|
448
466
|
// The style passed to header left, together with title element being wrapped
|
|
449
|
-
// in flex view is
|
|
467
|
+
// in flex view is required for proper header layout, in particular,
|
|
450
468
|
// for the text truncation to work.
|
|
451
469
|
<ScreenStackHeaderLeftView
|
|
452
470
|
style={!isCenterViewRenderedAndroid ? { flex: 1 } : null}
|