Compare commits

..

1 Commits

Author SHA1 Message Date
Satyajit Sahoo
41736641c1 chore: publish
- @react-navigation/bottom-tabs@6.0.0-next.0
 - @react-navigation/core@6.0.0-next.0
 - @react-navigation/devtools@6.0.0-next.0
 - @react-navigation/drawer@6.0.0-next.0
 - @react-navigation/elements@1.0.0-next.0
 - @react-navigation/material-bottom-tabs@6.0.0-next.0
 - @react-navigation/material-top-tabs@6.0.0-next.0
 - @react-navigation/native@6.0.0-next.0
 - @react-navigation/routers@6.0.0-next.0
 - @react-navigation/stack@6.0.0-next.0
2021-03-09 17:44:47 +01:00
84 changed files with 3489 additions and 3649 deletions

View File

@@ -23,54 +23,26 @@ jobs:
'gm'
);
if (regex.test(body)) {
await github.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['repro provided'],
});
try {
await github.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: 'needs repro',
});
} catch (error) {
if (!/Label does not exist/.test(error.message)) {
throw error;
}
}
} else {
if (context.eventName !== 'issues') {
return;
}
const body = "Hey! Thanks for opening the issue. The issue doesn't seem to contain a link to a repro (a [snack.expo.io](https://snack.expo.io) link or link to a GitHub repo under your username).\n\nCan you provide a [minimal repro](https://stackoverflow.com/help/minimal-reproducible-example) which demonstrates the issue? A repro will help us debug the issue faster. Please try to keep the repro as small as possible and make sure that we can run it without additional setup.";
const comments = await github.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});
if (comments.data.some(comment => comment.body === body)) {
return;
}
await github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body,
});
await github.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['needs repro'],
});
if (!regex.test(body)) {
return;
}
await github.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['repro provided'],
});
try {
await github.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: 'needs repro',
});
} catch (error) {
if (!/Label does not exist/.test(error.message)) {
throw error;
}
}

View File

@@ -18,7 +18,9 @@ jobs:
- name: Setup Expo
uses: expo/expo-github-action@v5
with:
expo-token: ${{ secrets.EXPO_TOKEN }}
expo-version: 3.x
expo-username: ${{ secrets.EXPO_CLI_USERNAME }}
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
expo-cache: true
- name: Restore yarn cache
@@ -34,7 +36,7 @@ jobs:
- name: Publish Expo app
working-directory: ./example
run: yarn expo publish --release-channel=pr-${{ github.event.number }}
run: expo publish --release-channel=pr-${{ github.event.number }}
env:
EXPO_USE_DEV_SERVER: true

View File

@@ -21,7 +21,9 @@ jobs:
- name: Setup Expo
uses: expo/expo-github-action@v5
with:
expo-token: ${{ secrets.EXPO_TOKEN }}
expo-version: 3.x
expo-username: ${{ secrets.EXPO_CLI_USERNAME }}
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
expo-cache: true
- name: Restore yarn cache
@@ -37,4 +39,4 @@ jobs:
- name: Publish Expo app
working-directory: ./example
run: yarn expo publish
run: expo publish

View File

@@ -31,7 +31,7 @@ jobs:
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: "Hey! Thanks for opening the issue. Can you provide a [minimal repro](https://stackoverflow.com/help/minimal-reproducible-example) which demonstrates the issue? Posting a snippet of your code in the issue is useful, but it's not usually straightforward to run. A repro will help us debug the issue faster. Please try to keep the repro as small as possible and make sure that we can run it without additional setup.\n\nThe easiest way to provide a repro is on [snack.expo.io](https://snack.expo.io). If it's not possible to repro it on [snack.expo.io](https://snack.expo.io), then please provide the repro in a GitHub repository."
body: "Hey! Thanks for opening the issue. Can you provide a [minimal repro](https://stackoverflow.com/help/minimal-reproducible-example) which demonstrates the issue? Posting a snippet of your code in the issue is useful, but it's not usually straightforward to run. A repro will help us debug the issue faster. Please try to keep the repro as small as possible.\n\nThe easiest way to provide a repro is on [snack.expo.io](https://snack.expo.io). If it's not possible to repro it on [snack.expo.io](https://snack.expo.io), then please provide the repro in a GitHub repository."
})
question:

View File

@@ -2,6 +2,5 @@ module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin'],
};
};

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -13,51 +13,50 @@
"test": "jest"
},
"dependencies": {
"@expo/vector-icons": "^12.0.0",
"@react-native-async-storage/async-storage": "^1.13.0",
"@react-native-masked-view/masked-view": "0.2.3",
"@expo/vector-icons": "^12.0.3",
"@react-native-masked-view/masked-view": "0.2.0",
"color": "^3.1.3",
"expo": "^41.0.0-beta.2",
"expo-asset": "~8.3.1",
"expo-blur": "~9.0.3",
"expo-linking": "~2.2.1",
"expo-updates": "~0.5.3",
"expo": "^40.0.1",
"expo-asset": "~8.2.2",
"expo-blur": "~9.0.0",
"expo-linking": "~2.1.1",
"expo-updates": "~0.4.2",
"koa": "^2.13.0",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz",
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.0.tar.gz",
"react-native-appearance": "~0.3.3",
"react-native-gesture-handler": "~1.10.2",
"react-native-pager-view": "~5.0.12",
"react-native-gesture-handler": "~1.8.0",
"react-native-pager-view": "^4.2.4",
"react-native-paper": "^4.7.2",
"react-native-reanimated": "~2.1.0",
"react-native-safe-area-context": "~3.2.0",
"react-native-screens": "~3.0.0",
"react-native-tab-view": "^3.0.1",
"react-native-reanimated": "~1.13.0",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "~2.15.0",
"react-native-tab-view": "^3.0.0",
"react-native-vector-icons": "^8.1.0",
"react-native-web": "~0.15.0"
},
"devDependencies": {
"@babel/node": "^7.13.13",
"@expo/webpack-config": "~0.12.63",
"@types/cheerio": "^0.22.28",
"@babel/node": "^7.13.0",
"@expo/webpack-config": "~0.12.60",
"@types/cheerio": "^0.22.24",
"@types/jest-dev-server": "^4.2.0",
"@types/koa": "^2.13.1",
"@types/node-fetch": "^2.5.9",
"@types/node-fetch": "^2.5.8",
"@types/react": "~16.9.35",
"@types/react-dom": "~16.9.8",
"@types/react-native": "~0.63.2",
"@types/react-native": "~0.63.51",
"babel-loader": "^8.2.2",
"babel-plugin-module-resolver": "^4.0.0",
"babel-preset-expo": "8.3.0",
"cheerio": "^1.0.0-rc.3",
"expo-cli": "^4.3.4",
"expo-cli": "^4.2.1",
"jest": "^26.6.3",
"jest-dev-server": "^4.4.0",
"mock-require-assets": "^0.0.1",
"node-fetch": "^2.6.1",
"nodemon": "^2.0.6",
"playwright": "^1.10.0",
"playwright": "^1.9.1",
"serve": "^11.3.0",
"typescript": "~4.2.3"
}

View File

@@ -0,0 +1,2 @@
import * as Linking from 'expo-linking';
export default [Linking.makeUrl('/')];

View File

@@ -0,0 +1 @@
export default ['rne://127.0.0.1:19000/--/'];

View File

@@ -1,159 +0,0 @@
import * as React from 'react';
import { View, Platform, StyleSheet, ScrollView } from 'react-native';
import { Button } from 'react-native-paper';
import type { ParamListBase } from '@react-navigation/native';
import {
createStackNavigator,
StackScreenProps,
TransitionPresets,
HeaderStyleInterpolators,
} from '@react-navigation/stack';
import Article from '../Shared/Article';
import Albums from '../Shared/Albums';
import NewsFeed from '../Shared/NewsFeed';
export type SimpleStackParams = {
Article: { author: string } | undefined;
NewsFeed: { date: number };
Albums: undefined;
};
const scrollEnabled = Platform.select({ web: true, default: false });
const ArticleScreen = ({
navigation,
route,
}: StackScreenProps<SimpleStackParams, 'Article'>) => {
return (
<ScrollView>
<View style={styles.buttons}>
<Button
mode="contained"
onPress={() => navigation.push('NewsFeed', { date: Date.now() })}
style={styles.button}
>
Push feed
</Button>
<Button
mode="outlined"
onPress={() => navigation.pop()}
style={styles.button}
>
Pop screen
</Button>
</View>
<Article
author={{ name: route.params?.author ?? 'Unknown' }}
scrollEnabled={scrollEnabled}
/>
</ScrollView>
);
};
const NewsFeedScreen = ({
route,
navigation,
}: StackScreenProps<SimpleStackParams, 'NewsFeed'>) => {
return (
<ScrollView>
<View style={styles.buttons}>
<Button
mode="contained"
onPress={() => navigation.push('Albums')}
style={styles.button}
>
Navigate to album
</Button>
<Button
mode="outlined"
onPress={() => navigation.pop()}
style={styles.button}
>
Pop screen
</Button>
</View>
<NewsFeed scrollEnabled={scrollEnabled} date={route.params.date} />
</ScrollView>
);
};
const AlbumsScreen = ({
navigation,
}: StackScreenProps<SimpleStackParams, 'Albums'>) => {
return (
<ScrollView>
<View style={styles.buttons}>
<Button
mode="contained"
onPress={() => navigation.push('Article', { author: 'Babel fish' })}
style={styles.button}
>
Push article
</Button>
<Button
mode="outlined"
onPress={() => navigation.pop()}
style={styles.button}
>
Pop screen
</Button>
</View>
<Albums scrollEnabled={scrollEnabled} />
</ScrollView>
);
};
const SimpleStack = createStackNavigator<SimpleStackParams>();
export default function SimpleStackScreen({
navigation,
}: StackScreenProps<ParamListBase>) {
React.useLayoutEffect(() => {
navigation.setOptions({
headerShown: false,
});
}, [navigation]);
return (
<SimpleStack.Navigator
screenOptions={{
...TransitionPresets.SlideFromRightIOS,
headerMode: 'float',
headerStyleInterpolator: HeaderStyleInterpolators.forUIKit,
}}
>
<SimpleStack.Screen
name="Article"
component={ArticleScreen}
options={({ route }) => ({
title: `Article by ${route.params?.author ?? 'Unknown'}`,
})}
initialParams={{ author: 'Gandalf' }}
/>
<SimpleStack.Screen
name="NewsFeed"
component={NewsFeedScreen}
options={{ title: 'Feed' }}
/>
<SimpleStack.Screen
name="Albums"
component={AlbumsScreen}
options={{
...TransitionPresets.ModalSlideFromBottomIOS,
headerMode: 'screen',
title: 'Albums',
}}
/>
</SimpleStack.Navigator>
);
}
const styles = StyleSheet.create({
buttons: {
flexDirection: 'row',
padding: 8,
},
button: {
margin: 8,
},
});

View File

@@ -86,7 +86,8 @@ const AlbumsScreen = ({ navigation }: StackScreenProps<SimpleStackParams>) => {
const SimpleStack = createStackNavigator<SimpleStackParams>();
type Props = StackScreenProps<ParamListBase>;
type Props = Partial<React.ComponentProps<typeof SimpleStack.Navigator>> &
StackScreenProps<ParamListBase>;
function CustomHeader(props: StackHeaderProps) {
const { current, next } = props.progress;
@@ -107,7 +108,7 @@ function CustomHeader(props: StackHeaderProps) {
);
}
export default function HeaderCustomizationScreen({ navigation }: Props) {
export default function SimpleStackScreen({ navigation, ...rest }: Props) {
React.useLayoutEffect(() => {
navigation.setOptions({
headerShown: false,
@@ -118,13 +119,13 @@ export default function HeaderCustomizationScreen({ navigation }: Props) {
const [headerTitleCentered, setHeaderTitleCentered] = React.useState(true);
return (
<SimpleStack.Navigator screenOptions={{ headerMode: 'float' }}>
<SimpleStack.Navigator {...rest}>
<SimpleStack.Screen
name="Article"
component={ArticleScreen}
options={({ route }) => ({
title: `Article by ${route.params?.author}`,
header: (props) => <CustomHeader {...props} />,
header: CustomHeader,
headerTintColor: '#fff',
headerStyle: { backgroundColor: '#ff005d' },
headerBackTitleVisible: false,

View File

@@ -1,5 +1,6 @@
import * as React from 'react';
import {
AsyncStorage,
ScrollView,
Platform,
StatusBar,
@@ -20,8 +21,6 @@ import {
Divider,
Text,
} from 'react-native-paper';
import { createURL } from 'expo-linking';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {
InitialState,
NavigationContainer,
@@ -39,10 +38,10 @@ import {
import { useReduxDevToolsExtension } from '@react-navigation/devtools';
import { restartApp } from './Restart';
import LinkingPrefixes from './LinkingPrefixes';
import SettingsItem from './Shared/SettingsItem';
import SimpleStack from './Screens/SimpleStack';
import ModalStack from './Screens/ModalStack';
import MixedHeaderMode from './Screens/MixedHeaderMode';
import StackTransparent from './Screens/StackTransparent';
import StackHeaderCustomization from './Screens/StackHeaderCustomization';
import BottomTabs from './Screens/BottomTabs';
@@ -71,10 +70,6 @@ const SCREENS = {
title: 'Modal Stack',
component: ModalStack,
},
MixedHeaderMode: {
title: 'Float + Screen Header Stack',
component: MixedHeaderMode,
},
StackTransparent: {
title: 'Transparent Stack',
component: StackTransparent,
@@ -225,7 +220,7 @@ export default function App() {
// Android (bare): adb shell am start -a android.intent.action.VIEW -d "rne://127.0.0.1:19000/--/simple-stack"
// iOS (bare): xcrun simctl openurl booted rne://127.0.0.1:19000/--/simple-stack
// The first segment of the link is the the scheme + host (returned by `Linking.makeUrl`)
prefixes: [createURL('/')],
prefixes: LinkingPrefixes,
config: {
initialRouteName: 'Home',
screens: Object.keys(SCREENS).reduce<PathConfigMap>(

View File

@@ -8,10 +8,9 @@
"version": "independent",
"command": {
"publish": {
"allowBranch": "main",
"allowBranch": "6.x",
"conventionalCommits": true,
"createRelease": "github",
"distTag": "next",
"message": "chore: publish",
"ignoreChanges": [
"**/__fixtures__/**",

View File

@@ -8,7 +8,8 @@
]
},
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"license": "MIT",
"repository": {
@@ -25,13 +26,13 @@
"example": "yarn --cwd example"
},
"devDependencies": {
"@commitlint/config-conventional": "^12.1.1",
"@types/jest": "^26.0.22",
"@commitlint/config-conventional": "^12.0.1",
"@types/jest": "^26.0.19",
"babel-jest": "^26.6.3",
"codecov": "^3.8.1",
"commitlint": "^12.1.1",
"eslint": "^7.23.0",
"eslint-config-satya164": "^3.1.10",
"commitlint": "^12.0.1",
"eslint": "^7.21.0",
"eslint-config-satya164": "^3.1.9",
"husky": "^4.3.6",
"jest": "^26.6.3",
"lerna": "^4.0.0",

View File

@@ -3,63 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [6.0.0-next.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@6.0.0-next.4...@react-navigation/bottom-tabs@6.0.0-next.5) (2021-04-16)
### Bug Fixes
* update tab bar height correctly. fixes [#9296](https://github.com/react-navigation/react-navigation/issues/9296) ([338ed6f](https://github.com/react-navigation/react-navigation/commit/338ed6ff07bd2d6efa1abdb369612ea72f540502))
# [6.0.0-next.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@6.0.0-next.3...@react-navigation/bottom-tabs@6.0.0-next.4) (2021-04-08)
### Bug Fixes
* remove calls to removed Keyboard.removeListener in useIsKeyboardShown ([#9457](https://github.com/react-navigation/react-navigation/issues/9457)) ([d87857e](https://github.com/react-navigation/react-navigation/commit/d87857e5d93c19ebee2fd84eb4910e36001ec2a3))
# [6.0.0-next.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@6.0.0-next.2...@react-navigation/bottom-tabs@6.0.0-next.3) (2021-03-22)
### Bug Fixes
* use tab role on Android for accessibility ([de805a3](https://github.com/react-navigation/react-navigation/commit/de805a3ebf35db81cb7b7bcbf5cfd4a03e69c567))
### Features
* add a Background component ([cbaabc1](https://github.com/react-navigation/react-navigation/commit/cbaabc1288e780698e499a00b9ca06ab9746a0da))
# [6.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@6.0.0-next.1...@react-navigation/bottom-tabs@6.0.0-next.2) (2021-03-12)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [6.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@6.0.0...@react-navigation/bottom-tabs@6.0.0-next.1) (2021-03-10)
### Bug Fixes
* fix peer dep versions ([72f90b5](https://github.com/react-navigation/react-navigation/commit/72f90b50d27eda1315bb750beca8a36f26dafe17))
# [6.0.0-next.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.1...@react-navigation/bottom-tabs@6.0.0-next.0) (2021-03-09)

View File

@@ -2,4 +2,4 @@
Bottom tab navigator for React Navigation following iOS design guidelines.
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/6.x/bottom-tab-navigator/).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/bottom-tab-navigator/).

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/bottom-tabs",
"description": "Bottom tab navigator following iOS design guidelines",
"version": "6.0.0-next.5",
"version": "6.0.0-next.0",
"keywords": [
"react-native-component",
"react-component",
@@ -29,37 +29,38 @@
],
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/elements": "^1.0.0-next.4",
"@react-navigation/elements": "^1.0.0",
"color": "^3.1.3",
"warn-once": "^0.0.1"
},
"devDependencies": {
"@react-navigation/native": "^6.0.0-next.2",
"@react-navigation/native": "^6.0.0-next.0",
"@testing-library/react-native": "^7.2.0",
"@types/color": "^3.0.1",
"@types/react": "^16.9.53",
"@types/react-native": "~0.63.51",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.4",
"react-native": "~0.63.2",
"react-native-builder-bob": "^0.18.1",
"react-native-safe-area-context": "~3.2.0",
"react-native-screens": "~3.0.0",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "~2.15.0",
"typescript": "^4.2.3"
},
"peerDependencies": {
"@react-navigation/native": "^6.0.0",
"@react-navigation/native": "^5.0.5",
"react": "*",
"react-native": "*",
"react-native-safe-area-context": ">= 3.0.0",
"react-native-screens": ">= 3.0.0"
"react-native-screens": ">= 2.15.0"
},
"react-native-builder-bob": {
"source": "src",

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { Keyboard, Platform, EmitterSubscription } from 'react-native';
import { Keyboard, Platform } from 'react-native';
export default function useIsKeyboardShown() {
const [isKeyboardShown, setIsKeyboardShown] = React.useState(false);
@@ -8,22 +8,22 @@ export default function useIsKeyboardShown() {
const handleKeyboardShow = () => setIsKeyboardShown(true);
const handleKeyboardHide = () => setIsKeyboardShown(false);
let subscriptions: EmitterSubscription[];
if (Platform.OS === 'ios') {
subscriptions = [
Keyboard.addListener('keyboardWillShow', handleKeyboardShow),
Keyboard.addListener('keyboardWillHide', handleKeyboardHide),
];
Keyboard.addListener('keyboardWillShow', handleKeyboardShow);
Keyboard.addListener('keyboardWillHide', handleKeyboardHide);
} else {
subscriptions = [
Keyboard.addListener('keyboardDidShow', handleKeyboardShow),
Keyboard.addListener('keyboardDidHide', handleKeyboardHide),
];
Keyboard.addListener('keyboardDidShow', handleKeyboardShow);
Keyboard.addListener('keyboardDidHide', handleKeyboardHide);
}
return () => {
subscriptions.forEach((s) => s.remove());
if (Platform.OS === 'ios') {
Keyboard.removeListener('keyboardWillShow', handleKeyboardShow);
Keyboard.removeListener('keyboardWillHide', handleKeyboardHide);
} else {
Keyboard.removeListener('keyboardDidShow', handleKeyboardShow);
Keyboard.removeListener('keyboardDidHide', handleKeyboardHide);
}
};
}, []);

View File

@@ -284,9 +284,8 @@ export default function BottomTabBar({
tabBarStyle,
]}
pointerEvents={isTabBarHidden ? 'none' : 'auto'}
onLayout={handleLayout}
>
<View accessibilityRole="tablist" style={styles.content}>
<View style={styles.content} onLayout={handleLayout}>
{routes.map((route, index) => {
const focused = index === state.index;
const { options } = descriptors[route.key];
@@ -323,7 +322,7 @@ export default function BottomTabBar({
const accessibilityLabel =
options.tabBarAccessibilityLabel !== undefined
? options.tabBarAccessibilityLabel
: typeof label === 'string' && Platform.OS === 'ios'
: typeof label === 'string'
? `${label}, tab, ${index + 1} of ${routes.length}`
: undefined;

View File

@@ -263,8 +263,7 @@ export default function BottomTabBarItem({
onLongPress,
testID,
accessibilityLabel,
// FIXME: accessibilityRole: 'tab' doesn't seem to work as expected on iOS
accessibilityRole: Platform.select({ ios: 'button', default: 'tab' }),
accessibilityRole: 'button',
accessibilityState: { selected: focused },
// @ts-expect-error: keep for compatibility with older React Native versions
accessibilityStates: focused ? ['selected'] : [],

View File

@@ -1,11 +1,12 @@
import * as React from 'react';
import { StyleSheet } from 'react-native';
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
import { ScreenContainer } from 'react-native-screens';
import { SafeAreaInsetsContext } from 'react-native-safe-area-context';
import {
NavigationHelpersContext,
ParamListBase,
TabNavigationState,
useTheme,
} from '@react-navigation/native';
import {
Header,
@@ -33,6 +34,28 @@ type Props = BottomTabNavigationConfig & {
descriptors: BottomTabDescriptorMap;
};
function SceneContent({
isFocused,
children,
style,
}: {
isFocused: boolean;
children: React.ReactNode;
style?: StyleProp<ViewStyle>;
}) {
const { colors } = useTheme();
return (
<View
accessibilityElementsHidden={!isFocused}
importantForAccessibility={isFocused ? 'auto' : 'no-hide-descendants'}
style={[styles.content, { backgroundColor: colors.background }, style]}
>
{children}
</View>
);
}
export default function BottomTabView(props: Props) {
const {
tabBar = (props: BottomTabBarProps) => <BottomTabBar {...props} />,
@@ -127,26 +150,26 @@ export default function BottomTabView(props: Props) {
visible={isFocused}
enabled={detachInactiveScreens}
>
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
<Screen
focused={isFocused}
route={descriptor.route}
navigation={descriptor.navigation}
headerShown={descriptor.options.headerShown}
headerStatusBarHeight={
descriptor.options.headerStatusBarHeight
}
header={header({
layout: dimensions,
route: descriptor.route,
navigation: descriptor.navigation as BottomTabNavigationProp<ParamListBase>,
options: descriptor.options,
})}
style={sceneContainerStyle}
>
{descriptor.render()}
</Screen>
</BottomTabBarHeightContext.Provider>
<SceneContent isFocused={isFocused} style={sceneContainerStyle}>
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
<Screen
route={descriptor.route}
navigation={descriptor.navigation}
headerShown={descriptor.options.headerShown}
headerStatusBarHeight={
descriptor.options.headerStatusBarHeight
}
header={header({
layout: dimensions,
route: descriptor.route,
navigation: descriptor.navigation as BottomTabNavigationProp<ParamListBase>,
options: descriptor.options,
})}
>
{descriptor.render()}
</Screen>
</BottomTabBarHeightContext.Provider>
</SceneContent>
</ScreenFallback>
);
})}
@@ -164,4 +187,7 @@ const styles = StyleSheet.create({
flex: 1,
overflow: 'hidden',
},
content: {
flex: 1,
},
});

View File

@@ -6,7 +6,7 @@ import {
// @ts-ignore
shouldUseActivityState,
} from 'react-native-screens';
import { ResourceSavingView } from '@react-navigation/elements';
import { ResourceSavingScene } from '@react-navigation/elements';
type Props = {
visible: boolean;
@@ -34,8 +34,8 @@ export default function ScreenFallback({ visible, children, ...rest }: Props) {
}
return (
<ResourceSavingView visible={visible} {...rest}>
<ResourceSavingScene visible={visible} {...rest}>
{children}
</ResourceSavingView>
</ResourceSavingScene>
);
}

View File

@@ -3,30 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [6.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@6.0.0-next.1...@react-navigation/core@6.0.0-next.2) (2021-04-08)
### Bug Fixes
* properly resolve initialRouteNames ([c38906a](https://github.com/react-navigation/react-navigation/commit/c38906a7a09b997f37ce56734ea823c75ea744db))
### Features
* improve useNavigationState typing ([#9464](https://github.com/react-navigation/react-navigation/issues/9464)) ([84020a0](https://github.com/react-navigation/react-navigation/commit/84020a0b27ebae50d3037438a51d95eb31b02424))
# [6.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@6.0.0...@react-navigation/core@6.0.0-next.1) (2021-03-10)
**Note:** Version bump only for package @react-navigation/core
# [6.0.0-next.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.14.3...@react-navigation/core@6.0.0-next.0) (2021-03-09)

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/core",
"description": "Core utilities for building navigators",
"version": "6.0.0-next.2",
"version": "6.0.0-next.0",
"keywords": [
"react",
"react-native",
@@ -28,17 +28,18 @@
"!**/__tests__"
],
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^6.0.0-next.2",
"@react-navigation/routers": "^6.0.0-next.0",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.22",
"query-string": "^7.0.0",
"nanoid": "^3.1.20",
"query-string": "^6.14.1",
"react-is": "^16.13.0"
},
"devDependencies": {
@@ -46,7 +47,7 @@
"@types/react": "^16.9.53",
"@types/react-is": "^16.7.1",
"del-cli": "^3.0.1",
"immer": "^9.0.1",
"immer": "^8.0.1",
"react": "~16.13.1",
"react-native-builder-bob": "^0.18.1",
"react-test-renderer": "~16.13.1",

View File

@@ -2303,119 +2303,3 @@ it('throws if two screens map to the same pattern', () => {
})
).not.toThrow();
});
it('correctly applies initialRouteName for config with similar route names', () => {
const path = '/weekly-earnings';
const config = {
screens: {
RootTabs: {
screens: {
HomeTab: {
screens: {
Home: '',
WeeklyEarnings: 'weekly-earnings',
EventDetails: 'event-details/:eventId',
},
},
EarningsTab: {
initialRouteName: 'Earnings',
path: 'earnings',
screens: {
Earnings: '',
WeeklyEarnings: 'weekly-earnings',
},
},
},
},
},
};
const state = {
routes: [
{
name: 'RootTabs',
state: {
routes: [
{
name: 'HomeTab',
state: {
routes: [
{
name: 'WeeklyEarnings',
path,
},
],
},
},
],
},
},
],
};
expect(getStateFromPath(path, config)).toEqual(state);
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
state
);
});
it('correctly applies initialRouteName for config with similar route names v2', () => {
const path = '/earnings/weekly-earnings';
const config = {
screens: {
RootTabs: {
screens: {
HomeTab: {
initialRouteName: 'Home',
screens: {
Home: '',
WeeklyEarnings: 'weekly-earnings',
},
},
EarningsTab: {
initialRouteName: 'Earnings',
path: 'earnings',
screens: {
Earnings: '',
WeeklyEarnings: 'weekly-earnings',
},
},
},
},
},
};
const state = {
routes: [
{
name: 'RootTabs',
state: {
routes: [
{
name: 'EarningsTab',
state: {
index: 1,
routes: [
{
name: 'Earnings',
},
{
name: 'WeeklyEarnings',
path,
},
],
},
},
],
},
},
],
};
expect(getStateFromPath(path, config)).toEqual(state);
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
state
);
});

View File

@@ -26,7 +26,7 @@ type RouteConfig = {
type InitialRouteConfig = {
initialRouteName: string;
parentScreens: string[];
connectedRoutes: string[];
};
type ResultState = PartialState<NavigationState> & {
@@ -69,7 +69,7 @@ export default function getStateFromPath(
if (options?.initialRouteName) {
initialRoutes.push({
initialRouteName: options.initialRouteName,
parentScreens: [],
connectedRoutes: Object.keys(options.screens),
});
}
@@ -108,8 +108,7 @@ export default function getStateFromPath(
key,
screens as PathConfigMap,
[],
initialRoutes,
[]
initialRoutes
)
)
)
@@ -310,15 +309,12 @@ const createNormalizedConfigs = (
routeConfig: PathConfigMap,
routeNames: string[] = [],
initials: InitialRouteConfig[],
parentScreens: string[],
parentPattern?: string
): RouteConfig[] => {
const configs: RouteConfig[] = [];
routeNames.push(screen);
parentScreens.push(screen);
const config = routeConfig[screen];
if (typeof config === 'string') {
@@ -354,7 +350,7 @@ const createNormalizedConfigs = (
if (config.initialRouteName) {
initials.push({
initialRouteName: config.initialRouteName,
parentScreens,
connectedRoutes: Object.keys(config.screens),
});
}
@@ -364,7 +360,6 @@ const createNormalizedConfigs = (
config.screens as PathConfigMap,
routeNames,
initials,
[...parentScreens],
pattern ?? parentPattern
);
@@ -430,23 +425,13 @@ const findParseConfigForRoute = (
// Try to find an initial route connected with the one passed
const findInitialRoute = (
routeName: string,
parentScreens: string[],
initialRoutes: InitialRouteConfig[]
): string | undefined => {
for (const config of initialRoutes) {
if (parentScreens.length === config.parentScreens.length) {
let sameParents = true;
for (let i = 0; i < parentScreens.length; i++) {
if (parentScreens[i].localeCompare(config.parentScreens[i]) !== 0) {
sameParents = false;
break;
}
}
if (sameParents) {
return routeName !== config.initialRouteName
? config.initialRouteName
: undefined;
}
if (config.connectedRoutes.includes(routeName)) {
return config.initialRouteName === routeName
? undefined
: config.initialRouteName;
}
}
return undefined;
@@ -492,11 +477,7 @@ const createNestedStateObject = (
) => {
let state: InitialState;
let route = routes.shift() as ParsedRoute;
const parentScreens: string[] = [];
let initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
parentScreens.push(route.name);
let initialRoute = findInitialRoute(route.name, initialRoutes);
state = createStateObject(initialRoute, route, routes.length === 0);
@@ -504,7 +485,7 @@ const createNestedStateObject = (
let nestedState = state;
while ((route = routes.shift() as ParsedRoute)) {
initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
initialRoute = findInitialRoute(route.name, initialRoutes);
const nestedStateIndex =
nestedState.index || nestedState.routes.length - 1;
@@ -519,8 +500,6 @@ const createNestedStateObject = (
nestedState = nestedState.routes[nestedStateIndex]
.state as InitialState;
}
parentScreens.push(route.name);
}
}

View File

@@ -1,21 +1,16 @@
import * as React from 'react';
import type { NavigationState, ParamListBase } from '@react-navigation/routers';
import type { NavigationState } from '@react-navigation/routers';
import useNavigation from './useNavigation';
import type { NavigationProp } from './types';
type Selector<ParamList extends ParamListBase, T> = (
state: NavigationState<ParamList>
) => T;
type Selector<T> = (state: NavigationState) => T;
/**
* Hook to get a value from the current navigation state using a selector.
*
* @param selector Selector function to get a value from the state.
*/
export default function useNavigationState<ParamList extends ParamListBase, T>(
selector: Selector<ParamList, T>
): T {
const navigation = useNavigation<NavigationProp<ParamList>>();
export default function useNavigationState<T>(selector: Selector<T>): T {
const navigation = useNavigation();
// We don't care about the state value, we run the selector again at the end
// The state is only to make sure that there's a re-render when we have a new value

View File

@@ -3,22 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [6.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@6.0.0-next.1...@react-navigation/devtools@6.0.0-next.2) (2021-04-08)
**Note:** Version bump only for package @react-navigation/devtools
# [6.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@6.0.0...@react-navigation/devtools@6.0.0-next.1) (2021-03-10)
**Note:** Version bump only for package @react-navigation/devtools
# [6.0.0-next.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.17...@react-navigation/devtools@6.0.0-next.0) (2021-03-09)
**Note:** Version bump only for package @react-navigation/devtools

View File

@@ -2,7 +2,7 @@
Developer tools for React Navigation.
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/6.x/devtools).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/devtools).
## Installation

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/devtools",
"description": "Developer tools for React Navigation",
"version": "6.0.0-next.2",
"version": "6.0.0-next.0",
"keywords": [
"react",
"react-native",
@@ -29,14 +29,15 @@
],
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/core": "^6.0.0-next.2",
"@react-navigation/core": "^6.0.0-next.0",
"deep-equal": "^2.0.5"
},
"devDependencies": {

View File

@@ -3,52 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [6.0.0-next.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@6.0.0-next.3...@react-navigation/drawer@6.0.0-next.4) (2021-04-08)
### Bug Fixes
* don't handle back button with permanent drawer ([b893968](https://github.com/react-navigation/react-navigation/commit/b89396888f46ba79af3cfd84be55fba79d8387d2))
* fix drawer overlay on web ([3241190](https://github.com/react-navigation/react-navigation/commit/3241190b19946c1cd0a744fb09a19d79ba683d74))
* only handle back button in drawer when focused ([5ae0bad](https://github.com/react-navigation/react-navigation/commit/5ae0badc44b576d464f8841822a911b18a698403))
# [6.0.0-next.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@6.0.0-next.2...@react-navigation/drawer@6.0.0-next.3) (2021-03-22)
### Features
* add a Background component ([cbaabc1](https://github.com/react-navigation/react-navigation/commit/cbaabc1288e780698e499a00b9ca06ab9746a0da))
# [6.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@6.0.0-next.1...@react-navigation/drawer@6.0.0-next.2) (2021-03-12)
### Features
* export drawer button ([2c8401d](https://github.com/react-navigation/react-navigation/commit/2c8401d5cb347d37c96e5b30f8ad05c17fd22ea4))
# [6.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@6.0.0...@react-navigation/drawer@6.0.0-next.1) (2021-03-10)
### Bug Fixes
* fix peer dep versions ([72f90b5](https://github.com/react-navigation/react-navigation/commit/72f90b50d27eda1315bb750beca8a36f26dafe17))
# [6.0.0-next.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.11.2...@react-navigation/drawer@6.0.0-next.0) (2021-03-09)

View File

@@ -1,5 +1,5 @@
# `@react-navigation/drawer`
Drawer navigator for React Navigation following Material Design guidelines.
Drawer navigator for React Navigation following Material Design guidelines.
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/6.x/drawer-navigator/).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/drawer-navigator/).

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/drawer",
"description": "Drawer navigator component with animated transitions and gesturess",
"version": "6.0.0-next.4",
"version": "6.0.0-next.0",
"keywords": [
"react-native-component",
"react-component",
@@ -34,40 +34,41 @@
],
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/elements": "^1.0.0-next.4",
"@react-navigation/elements": "^1.0.0",
"color": "^3.1.3",
"warn-once": "^0.0.1"
},
"devDependencies": {
"@react-navigation/native": "^6.0.0-next.2",
"@react-navigation/native": "^6.0.0-next.0",
"@testing-library/react-native": "^7.2.0",
"@types/react": "^16.9.53",
"@types/react-native": "~0.63.51",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.4",
"react-native": "~0.63.2",
"react-native-builder-bob": "^0.18.1",
"react-native-gesture-handler": "~1.10.2",
"react-native-reanimated": "~2.1.0",
"react-native-safe-area-context": "~3.2.0",
"react-native-screens": "~3.0.0",
"react-native-gesture-handler": "~1.8.0",
"react-native-reanimated": "~1.13.0",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "~2.15.0",
"typescript": "^4.2.3"
},
"peerDependencies": {
"@react-navigation/native": "^6.0.0",
"@react-navigation/native": "^5.0.5",
"react": "*",
"react-native": "*",
"react-native-gesture-handler": ">= 1.0.0",
"react-native-reanimated": ">= 1.0.0",
"react-native-safe-area-context": ">= 3.0.0",
"react-native-screens": ">= 3.0.0"
"react-native-screens": ">= 2.15.0"
},
"react-native-builder-bob": {
"source": "src",

View File

@@ -11,16 +11,12 @@ export { default as DrawerItem } from './views/DrawerItem';
export { default as DrawerItemList } from './views/DrawerItemList';
export { default as DrawerContent } from './views/DrawerContent';
export { default as DrawerContentScrollView } from './views/DrawerContentScrollView';
export { default as DrawerToggleButton } from './views/DrawerToggleButton';
/**
* Utilities
*/
export { default as DrawerGestureContext } from './utils/DrawerGestureContext';
export { default as DrawerProgressContext } from './utils/DrawerProgressContext';
export { default as useDrawerProgress } from './utils/useDrawerProgress';
export { default as getDrawerStatusFromState } from './utils/getDrawerStatusFromState';
export { default as useDrawerStatus } from './utils/useDrawerStatus';

View File

@@ -1,8 +1,6 @@
import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
import type {
PanGestureHandler,
PanGestureHandlerProperties,
} from 'react-native-gesture-handler';
import type Animated from 'react-native-reanimated';
import type { PanGestureHandlerProperties } from 'react-native-gesture-handler';
import type {
Route,
ParamListBase,
@@ -35,18 +33,6 @@ export type DrawerNavigationConfig = {
* Defaults to `true`.
*/
detachInactiveScreens?: boolean;
/**
* Whether to use the legacy implementation based on Reanimated 1.
* The new implementation based on Reanimated 2 will perform better,
* but you need additional configuration and need to use Hermes with Flipper to debug.
*
* This defaults to `true` in following cases:
* - Reanimated 2 is not configured
* - App is connected to Chrome debugger (Reanimated 2 cannot be used with Chrome debugger)
*
* Otherwise, it defaults to `false`
*/
useLegacyImplementation?: boolean;
};
export type DrawerNavigationOptions = HeaderOptions & {
@@ -221,6 +207,11 @@ export type DrawerContentComponentProps = {
state: DrawerNavigationState<ParamListBase>;
navigation: DrawerNavigationHelpers;
descriptors: DrawerDescriptorMap;
/**
* Animated node which represents the current progress of the drawer's open state.
* `0` is closed, `1` is open.
*/
progress: Animated.Node<number>;
};
export type DrawerHeaderProps = {
@@ -277,24 +268,3 @@ export type DrawerDescriptor = Descriptor<
>;
export type DrawerDescriptorMap = Record<string, DrawerDescriptor>;
export type DrawerProps = {
dimensions: { width: number; height: number };
drawerPosition: 'left' | 'right';
drawerStyle?: StyleProp<ViewStyle>;
drawerType: 'front' | 'back' | 'slide' | 'permanent';
gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
hideStatusBarOnOpen: boolean;
keyboardDismissMode: 'none' | 'on-drag';
onClose: () => void;
onOpen: () => void;
open: boolean;
overlayStyle?: StyleProp<ViewStyle>;
renderDrawerContent: () => React.ReactNode;
renderSceneContent: () => React.ReactNode;
statusBarAnimation: 'slide' | 'none' | 'fade';
swipeDistanceThreshold: number;
swipeEdgeWidth: number;
swipeEnabled: boolean;
swipeVelocityThreshold: number;
};

View File

@@ -1,6 +0,0 @@
import * as React from 'react';
import type Animated from 'react-native-reanimated';
export default React.createContext<
Readonly<Animated.SharedValue<number>> | Animated.Node<number> | undefined
>(undefined);

View File

@@ -1,17 +0,0 @@
import * as React from 'react';
import type Animated from 'react-native-reanimated';
import DrawerProgressContext from './DrawerProgressContext';
export default function useDrawerProgress():
| Readonly<Animated.SharedValue<number>>
| Animated.Node<number> {
const progress = React.useContext(DrawerProgressContext);
if (progress === undefined) {
throw new Error(
"Couldn't find a drawer. Is your component inside a drawer navigator?"
);
}
return progress;
}

View File

@@ -1,19 +1,24 @@
import * as React from 'react';
import {
StyleSheet,
ViewStyle,
LayoutChangeEvent,
I18nManager,
Platform,
Keyboard,
StatusBar,
StyleProp,
View,
InteractionManager,
Pressable,
} from 'react-native';
import Animated from 'react-native-reanimated';
import { PanGestureHandler, GestureState } from '../GestureHandler';
import {
PanGestureHandler,
TapGestureHandler,
GestureState,
} from './GestureHandler';
import Overlay from './Overlay';
import DrawerProgressContext from '../../utils/DrawerProgressContext';
import type { DrawerProps } from '../../types';
const {
Clock,
@@ -51,6 +56,7 @@ const UNSET = -1;
const DIRECTION_LEFT = 1;
const DIRECTION_RIGHT = -1;
const SWIPE_DISTANCE_THRESHOLD_DEFAULT = 60;
const SWIPE_DISTANCE_MINIMUM = 5;
const DEFAULT_DRAWER_WIDTH = '80%';
@@ -69,8 +75,54 @@ const ANIMATED_ONE = new Animated.Value(1);
type Binary = 0 | 1;
export default class DrawerView extends React.Component<DrawerProps> {
componentDidUpdate(prevProps: DrawerProps) {
type Renderer = (props: { progress: Animated.Node<number> }) => React.ReactNode;
type Props = {
open: boolean;
onOpen: () => void;
onClose: () => void;
gestureEnabled: boolean;
swipeEnabled: boolean;
drawerPosition: 'left' | 'right';
drawerType: 'front' | 'back' | 'slide' | 'permanent';
keyboardDismissMode: 'none' | 'on-drag';
swipeEdgeWidth: number;
swipeDistanceThreshold?: number;
swipeVelocityThreshold: number;
hideStatusBarOnOpen: boolean;
statusBarAnimation: 'slide' | 'none' | 'fade';
overlayStyle?: StyleProp<ViewStyle>;
drawerStyle?: StyleProp<ViewStyle>;
sceneContainerStyle?: StyleProp<ViewStyle>;
renderDrawerContent: Renderer;
renderSceneContent: Renderer;
gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
dimensions: { width: number; height: number };
};
export default class DrawerView extends React.Component<Props> {
static defaultProps = {
drawerPosition: I18nManager.isRTL ? 'left' : 'right',
drawerType: 'front',
gestureEnabled: true,
swipeEnabled:
Platform.OS !== 'web' &&
Platform.OS !== 'windows' &&
Platform.OS !== 'macos',
swipeEdgeWidth: 32,
swipeVelocityThreshold: 500,
keyboardDismissMode: 'on-drag',
hideStatusBarOnOpen: false,
statusBarAnimation: 'slide',
};
componentDidMount() {
if (Platform.OS === 'web') {
document?.body?.addEventListener?.('keyup', this.handleEscape);
}
}
componentDidUpdate(prevProps: Props) {
const {
open,
drawerPosition,
@@ -105,7 +157,11 @@ export default class DrawerView extends React.Component<DrawerProps> {
}
if (prevProps.swipeDistanceThreshold !== swipeDistanceThreshold) {
this.swipeDistanceThreshold.setValue(swipeDistanceThreshold);
this.swipeDistanceThreshold.setValue(
swipeDistanceThreshold !== undefined
? swipeDistanceThreshold
: SWIPE_DISTANCE_THRESHOLD_DEFAULT
);
}
if (prevProps.swipeVelocityThreshold !== swipeVelocityThreshold) {
@@ -116,8 +172,22 @@ export default class DrawerView extends React.Component<DrawerProps> {
componentWillUnmount() {
this.toggleStatusBar(false);
this.handleEndInteraction();
if (Platform.OS === 'web') {
document?.body?.removeEventListener?.('keyup', this.handleEscape);
}
}
private handleEscape = (e: KeyboardEvent) => {
const { open, onClose } = this.props;
if (e.key === 'Escape') {
if (open) {
onClose();
}
}
};
private handleEndInteraction = () => {
if (this.interactionHandle !== undefined) {
InteractionManager.clearInteractionHandle(this.interactionHandle);
@@ -234,7 +304,9 @@ export default class DrawerView extends React.Component<DrawerProps> {
);
private swipeDistanceThreshold = new Value<number>(
this.props.swipeDistanceThreshold
this.props.swipeDistanceThreshold !== undefined
? this.props.swipeDistanceThreshold
: SWIPE_DISTANCE_THRESHOLD_DEFAULT
);
private swipeVelocityThreshold = new Value<number>(
this.props.swipeVelocityThreshold
@@ -444,6 +516,18 @@ export default class DrawerView extends React.Component<DrawerProps> {
},
]);
private handleTapStateChange = event([
{
nativeEvent: {
oldState: (s: Animated.Value<number>) =>
cond(
eq(s, GestureState.ACTIVE),
set(this.manuallyTriggerSpring, TRUE)
),
},
},
]);
private handleContainerLayout = (e: LayoutChangeEvent) =>
this.containerWidth.setValue(e.nativeEvent.layout.width);
@@ -484,10 +568,12 @@ export default class DrawerView extends React.Component<DrawerProps> {
render() {
const {
open,
gestureEnabled,
swipeEnabled,
drawerPosition,
drawerType,
swipeEdgeWidth,
sceneContainerStyle,
drawerStyle,
overlayStyle,
renderDrawerContent,
@@ -533,103 +619,109 @@ export default class DrawerView extends React.Component<DrawerProps> {
const progress = drawerType === 'permanent' ? ANIMATED_ONE : this.progress;
return (
<DrawerProgressContext.Provider value={progress}>
<PanGestureHandler
activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
onGestureEvent={this.handleGestureEvent}
onHandlerStateChange={this.handleGestureStateChange}
hitSlop={hitSlop}
enabled={drawerType !== 'permanent' && swipeEnabled}
{...gestureHandlerProps}
<PanGestureHandler
activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
onGestureEvent={this.handleGestureEvent}
onHandlerStateChange={this.handleGestureStateChange}
hitSlop={hitSlop}
enabled={drawerType !== 'permanent' && gestureEnabled && swipeEnabled}
{...gestureHandlerProps}
>
<Animated.View
onLayout={this.handleContainerLayout}
style={[
styles.main,
{
flexDirection:
drawerType === 'permanent' && !isRight ? 'row-reverse' : 'row',
},
]}
>
<Animated.View
onLayout={this.handleContainerLayout}
style={[
styles.main,
{
flexDirection:
drawerType === 'permanent' && !isRight
? 'row-reverse'
: 'row',
},
styles.content,
{ transform: [{ translateX: contentTranslateX }] },
sceneContainerStyle as any,
]}
>
<Animated.View
style={[
styles.content,
{ transform: [{ translateX: contentTranslateX }] },
]}
>
<View
accessibilityElementsHidden={
isOpen && drawerType !== 'permanent'
}
importantForAccessibility={
isOpen && drawerType !== 'permanent'
? 'no-hide-descendants'
: 'auto'
}
style={styles.content}
>
{renderSceneContent()}
</View>
{
// Disable overlay if sidebar is permanent
drawerType === 'permanent' ? null : (
<Overlay
progress={progress}
onPress={() => this.toggleDrawer(false)}
style={overlayStyle as any}
/>
)
<View
accessibilityElementsHidden={isOpen && drawerType !== 'permanent'}
importantForAccessibility={
isOpen && drawerType !== 'permanent'
? 'no-hide-descendants'
: 'auto'
}
</Animated.View>
<Animated.Code
// This is needed to make sure that container width updates with `setValue`
// Without this, it won't update when not used in styles
exec={this.containerWidth}
/>
{drawerType === 'permanent' ? null : (
<Animated.Code
exec={block([
onChange(this.manuallyTriggerSpring, [
cond(eq(this.manuallyTriggerSpring, TRUE), [
set(this.nextIsOpen, FALSE),
call([], () => (this.currentOpenValue = false)),
]),
]),
])}
/>
)}
<Animated.View
accessibilityViewIsModal={isOpen && drawerType !== 'permanent'}
removeClippedSubviews={Platform.OS !== 'ios'}
onLayout={this.handleDrawerLayout}
style={[
styles.container,
{
transform: [{ translateX: drawerTranslateX }],
opacity: this.drawerOpacity,
},
drawerType === 'permanent'
? // Without this, the `left`/`right` values don't get reset
isRight
? { right: 0 }
: { left: 0 }
: [
styles.nonPermanent,
isRight ? { right: offset } : { left: offset },
{ zIndex: drawerType === 'back' ? -1 : 0 },
],
drawerStyle as any,
]}
style={styles.content}
>
{renderDrawerContent()}
</Animated.View>
{renderSceneContent({ progress })}
</View>
{
// Disable overlay if sidebar is permanent
drawerType === 'permanent' ? null : Platform.OS === 'web' ||
Platform.OS === 'windows' ||
Platform.OS === 'macos' ? (
<Pressable
onPress={
gestureEnabled ? () => this.toggleDrawer(false) : undefined
}
>
<Overlay progress={progress} style={overlayStyle as any} />
</Pressable>
) : (
<TapGestureHandler
enabled={gestureEnabled}
onHandlerStateChange={this.handleTapStateChange}
>
<Overlay progress={progress} style={overlayStyle as any} />
</TapGestureHandler>
)
}
</Animated.View>
</PanGestureHandler>
</DrawerProgressContext.Provider>
<Animated.Code
// This is needed to make sure that container width updates with `setValue`
// Without this, it won't update when not used in styles
exec={this.containerWidth}
/>
{drawerType === 'permanent' ? null : (
<Animated.Code
exec={block([
onChange(this.manuallyTriggerSpring, [
cond(eq(this.manuallyTriggerSpring, TRUE), [
set(this.nextIsOpen, FALSE),
call([], () => (this.currentOpenValue = false)),
]),
]),
])}
/>
)}
<Animated.View
accessibilityViewIsModal={isOpen && drawerType !== 'permanent'}
removeClippedSubviews={Platform.OS !== 'ios'}
onLayout={this.handleDrawerLayout}
style={[
styles.container,
{
transform: [{ translateX: drawerTranslateX }],
opacity: this.drawerOpacity,
},
drawerType === 'permanent'
? // Without this, the `left`/`right` values don't get reset
isRight
? { right: 0 }
: { left: 0 }
: [
styles.nonPermanent,
isRight ? { right: offset } : { left: offset },
{ zIndex: drawerType === 'back' ? -1 : 0 },
],
drawerStyle as any,
]}
>
{renderDrawerContent({ progress })}
</Animated.View>
</Animated.View>
</PanGestureHandler>
);
}
}

View File

@@ -5,10 +5,10 @@ import {
I18nManager,
Platform,
BackHandler,
NativeEventSubscription,
} from 'react-native';
import { ScreenContainer } from 'react-native-screens';
import { useSafeAreaFrame } from 'react-native-safe-area-context';
import Animated from 'react-native-reanimated';
import {
NavigationHelpersContext,
DrawerNavigationState,
@@ -27,6 +27,7 @@ import { GestureHandlerRootView } from './GestureHandler';
import ScreenFallback from './ScreenFallback';
import DrawerToggleButton from './DrawerToggleButton';
import DrawerContent from './DrawerContent';
import Drawer from './Drawer';
import DrawerStatusContext from '../utils/DrawerStatusContext';
import DrawerPositionContext from '../utils/DrawerPositionContext';
import getDrawerStatusFromState from '../utils/getDrawerStatusFromState';
@@ -37,7 +38,6 @@ import type {
DrawerContentComponentProps,
DrawerHeaderProps,
DrawerNavigationProp,
DrawerProps,
} from '../types';
type Props = DrawerNavigationConfig & {
@@ -77,17 +77,7 @@ function DrawerViewBase({
<DrawerContent {...props} />
),
detachInactiveScreens = true,
// Running in chrome debugger
// @ts-expect-error
useLegacyImplementation = !global.nativeCallSyncHook ||
// Reanimated 2 is not configured
// @ts-expect-error: the type definitions are incomplete
!Animated.isConfigured?.(),
}: Props) {
const Drawer: React.ComponentType<DrawerProps> = useLegacyImplementation
? require('./legacy/Drawer').default
: require('./modern/Drawer').default;
const focusedRouteKey = state.routes[state.index].key;
const {
drawerHideStatusBarOnOpen = false,
@@ -95,14 +85,14 @@ function DrawerViewBase({
drawerStatusBarAnimation = 'slide',
drawerStyle,
drawerType = Platform.select({ ios: 'slide', default: 'front' }),
gestureEnabled,
gestureHandlerProps,
keyboardDismissMode = 'on-drag',
overlayColor = 'rgba(0, 0, 0, 0.5)',
swipeEdgeWidth = 32,
swipeEnabled = Platform.OS !== 'web' &&
Platform.OS !== 'windows' &&
Platform.OS !== 'macos',
swipeMinDistance = 60,
sceneContainerStyle,
swipeEdgeWidth,
swipeEnabled,
swipeMinDistance,
} = descriptors[focusedRouteKey].options;
const [loaded, setLoaded] = React.useState([focusedRouteKey]);
@@ -132,53 +122,27 @@ function DrawerViewBase({
}, [navigation, state.key]);
React.useEffect(() => {
if (drawerStatus !== 'open' || drawerType === 'permanent') {
return;
let subscription: NativeEventSubscription | undefined;
if (drawerStatus === 'open') {
// We only add the subscription when drawer opens
// This way we can make sure that the subscription is added as late as possible
// This will make sure that our handler will run first when back button is pressed
subscription = BackHandler.addEventListener('hardwareBackPress', () => {
handleDrawerClose();
return true;
});
}
const handleClose = () => {
// We shouldn't handle the back button if the parent screen isn't focused
// This will avoid the drawer overriding event listeners from a focused screen
if (!navigation.isFocused()) {
return false;
}
return () => subscription?.remove();
}, [handleDrawerClose, drawerStatus, navigation, state.key]);
handleDrawerClose();
return true;
};
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
handleClose();
}
};
// We only add the listeners when drawer opens
// This way we can make sure that the listener is added as late as possible
// This will make sure that our handler will run first when back button is pressed
const subscription = BackHandler.addEventListener(
'hardwareBackPress',
handleClose
);
if (Platform.OS === 'web') {
document?.body?.addEventListener?.('keyup', handleEscape);
}
return () => {
subscription.remove();
if (Platform.OS === 'web') {
document?.body?.removeEventListener?.('keyup', handleEscape);
}
};
}, [drawerStatus, drawerType, handleDrawerClose, navigation]);
const renderDrawerContent = () => {
const renderDrawerContent = ({ progress }: any) => {
return (
<DrawerPositionContext.Provider value={drawerPosition}>
{drawerContent({
progress: progress,
state: state,
navigation: navigation,
descriptors: descriptors,
@@ -217,7 +181,6 @@ function DrawerViewBase({
}
/>
),
sceneContainerStyle,
} = descriptor.options;
return (
@@ -228,7 +191,6 @@ function DrawerViewBase({
enabled={detachInactiveScreens}
>
<Screen
focused={isFocused}
route={descriptor.route}
navigation={descriptor.navigation}
headerShown={descriptor.options.headerShown}
@@ -239,7 +201,6 @@ function DrawerViewBase({
navigation: descriptor.navigation as DrawerNavigationProp<ParamListBase>,
options: descriptor.options,
})}
style={sceneContainerStyle}
>
{descriptor.render()}
</Screen>
@@ -254,18 +215,17 @@ function DrawerViewBase({
<DrawerStatusContext.Provider value={drawerStatus}>
<Drawer
open={drawerStatus !== 'closed'}
gestureEnabled={gestureEnabled}
swipeEnabled={swipeEnabled}
onOpen={handleDrawerOpen}
onClose={handleDrawerClose}
gestureHandlerProps={gestureHandlerProps}
swipeEnabled={swipeEnabled}
swipeEdgeWidth={swipeEdgeWidth}
swipeVelocityThreshold={500}
swipeDistanceThreshold={swipeMinDistance}
hideStatusBarOnOpen={drawerHideStatusBarOnOpen}
statusBarAnimation={drawerStatusBarAnimation}
keyboardDismissMode={keyboardDismissMode}
drawerType={drawerType}
drawerPosition={drawerPosition}
sceneContainerStyle={[
{ backgroundColor: colors.background },
sceneContainerStyle,
]}
drawerStyle={[
{
width: getDefaultDrawerWidth(dimensions),
@@ -284,8 +244,13 @@ function DrawerViewBase({
drawerStyle,
]}
overlayStyle={{ backgroundColor: overlayColor }}
swipeEdgeWidth={swipeEdgeWidth}
swipeDistanceThreshold={swipeMinDistance}
hideStatusBarOnOpen={drawerHideStatusBarOnOpen}
statusBarAnimation={drawerStatusBarAnimation}
renderDrawerContent={renderDrawerContent}
renderSceneContent={renderSceneContent}
keyboardDismissMode={keyboardDismissMode}
dimensions={dimensions}
/>
</DrawerStatusContext.Provider>

View File

@@ -1,26 +1,26 @@
import * as React from 'react';
import { Pressable, Platform, StyleSheet } from 'react-native';
import { Platform, StyleSheet } from 'react-native';
import Animated from 'react-native-reanimated';
const {
interpolate: interpolateDeprecated,
// @ts-expect-error: this property is only present in Reanimated 2
interpolateNode,
cond,
greaterThan,
} = Animated;
const interpolate: typeof interpolateNode =
const interpolate: typeof interpolateDeprecated =
interpolateNode ?? interpolateDeprecated;
const PROGRESS_EPSILON = 0.05;
type Props = React.ComponentProps<typeof Animated.View> & {
progress: Animated.Node<number>;
onPress: () => void;
};
const Overlay = React.forwardRef(function Overlay(
{ progress, onPress, style, ...props }: Props,
{ progress, style, ...props }: Props,
ref: React.Ref<Animated.View>
) {
const animatedStyle = {
@@ -46,9 +46,7 @@ const Overlay = React.forwardRef(function Overlay(
{...props}
ref={ref}
style={[styles.overlay, overlayStyle, animatedStyle, style]}
>
<Pressable onPress={onPress} style={styles.pressable} />
</Animated.View>
/>
);
});
@@ -66,9 +64,6 @@ const styles = StyleSheet.create({
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
pressable: {
flex: 1,
},
});
export default Overlay;

View File

@@ -6,7 +6,7 @@ import {
// @ts-ignore
shouldUseActivityState,
} from 'react-native-screens';
import { ResourceSavingView } from '@react-navigation/elements';
import { ResourceSavingScene } from '@react-navigation/elements';
type Props = {
visible: boolean;
@@ -34,8 +34,8 @@ export default function ScreenFallback({ visible, children, ...rest }: Props) {
}
return (
<ResourceSavingView visible={visible} {...rest}>
<ResourceSavingScene visible={visible} {...rest}>
{children}
</ResourceSavingView>
</ResourceSavingScene>
);
}

View File

@@ -1,379 +0,0 @@
import * as React from 'react';
import {
InteractionManager,
Keyboard,
Platform,
StatusBar,
StyleSheet,
View,
} from 'react-native';
import {
PanGestureHandler,
PanGestureHandlerGestureEvent,
State as GestureState,
} from 'react-native-gesture-handler';
import Animated, {
interpolate,
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useDerivedValue,
useSharedValue,
withSpring,
} from 'react-native-reanimated';
import type { DrawerProps } from '../../types';
import DrawerProgressContext from '../../utils/DrawerProgressContext';
import Overlay from './Overlay';
const SWIPE_DISTANCE_MINIMUM = 5;
const DEFAULT_DRAWER_WIDTH = '80%';
const minmax = (value: number, start: number, end: number) => {
'worklet';
return Math.min(Math.max(value, start), end);
};
export default function Drawer({
dimensions,
drawerPosition,
drawerStyle,
drawerType,
gestureHandlerProps,
hideStatusBarOnOpen,
keyboardDismissMode,
onClose,
onOpen,
open,
overlayStyle,
renderDrawerContent,
renderSceneContent,
statusBarAnimation,
swipeDistanceThreshold,
swipeEdgeWidth,
swipeEnabled,
swipeVelocityThreshold,
}: DrawerProps) {
const getDrawerWidth = (): number => {
const { width = DEFAULT_DRAWER_WIDTH } =
StyleSheet.flatten(drawerStyle) || {};
if (typeof width === 'string' && width.endsWith('%')) {
// Try to calculate width if a percentage is given
const percentage = Number(width.replace(/%$/, ''));
if (Number.isFinite(percentage)) {
return dimensions.width * (percentage / 100);
}
}
return typeof width === 'number' ? width : 0;
};
const drawerWidth = getDrawerWidth();
const isOpen = drawerType === 'permanent' ? true : open;
const isRight = drawerPosition === 'right';
const getDrawerTranslationX = React.useCallback(
(open: boolean) => {
'worklet';
if (drawerPosition === 'left') {
return open ? 0 : -drawerWidth;
}
return open ? 0 : drawerWidth;
},
[drawerPosition, drawerWidth]
);
const hideStatusBar = React.useCallback(
(hide: boolean) => {
if (hideStatusBarOnOpen) {
StatusBar.setHidden(hide, statusBarAnimation);
}
},
[hideStatusBarOnOpen, statusBarAnimation]
);
React.useEffect(() => {
hideStatusBar(isOpen);
return () => hideStatusBar(false);
}, [isOpen, hideStatusBarOnOpen, statusBarAnimation, hideStatusBar]);
const interactionHandleRef = React.useRef<number | null>(null);
const startInteraction = () => {
interactionHandleRef.current = InteractionManager.createInteractionHandle();
};
const endInteraction = () => {
if (interactionHandleRef.current != null) {
InteractionManager.clearInteractionHandle(interactionHandleRef.current);
interactionHandleRef.current = null;
}
};
const hideKeyboard = () => {
if (keyboardDismissMode === 'on-drag') {
Keyboard.dismiss();
}
};
const onGestureStart = () => {
startInteraction();
hideKeyboard();
hideStatusBar(true);
};
const onGestureEnd = () => {
endInteraction();
};
// FIXME: Currently hitSlop is broken when on Android when drawer is on right
// https://github.com/kmagiera/react-native-gesture-handler/issues/569
const hitSlop = isRight
? // Extend hitSlop to the side of the screen when drawer is closed
// This lets the user drag the drawer from the side of the screen
{ right: 0, width: isOpen ? undefined : swipeEdgeWidth }
: { left: 0, width: isOpen ? undefined : swipeEdgeWidth };
const touchStartX = useSharedValue(0);
const touchX = useSharedValue(0);
const translationX = useSharedValue(getDrawerTranslationX(open));
const gestureState = useSharedValue<GestureState>(GestureState.UNDETERMINED);
const toggleDrawer = React.useCallback(
(open: boolean, velocity?: number) => {
'worklet';
const translateX = getDrawerTranslationX(open);
touchStartX.value = 0;
touchX.value = 0;
translationX.value = withSpring(
translateX,
{
velocity,
stiffness: 1000,
damping: 500,
mass: 3,
overshootClamping: true,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 0.01,
},
() => {
if (translationX.value === getDrawerTranslationX(true)) {
runOnJS(onOpen)();
} else if (translationX.value === getDrawerTranslationX(false)) {
runOnJS(onClose)();
}
}
);
},
[getDrawerTranslationX, onClose, onOpen, touchStartX, touchX, translationX]
);
React.useEffect(() => toggleDrawer(open), [open, toggleDrawer]);
const onGestureEvent = useAnimatedGestureHandler<
PanGestureHandlerGestureEvent,
{ startX: number }
>({
onStart: (event, ctx) => {
ctx.startX = translationX.value;
gestureState.value = event.state;
touchStartX.value = event.x;
runOnJS(onGestureStart)();
},
onActive: (event, ctx) => {
touchX.value = event.x;
translationX.value = ctx.startX + event.translationX;
gestureState.value = event.state;
},
onEnd: (event) => {
gestureState.value = event.state;
const nextOpen =
(Math.abs(event.translationX) > SWIPE_DISTANCE_MINIMUM &&
Math.abs(event.translationX) > swipeVelocityThreshold) ||
Math.abs(event.translationX) > swipeDistanceThreshold
? drawerPosition === 'left'
? // If swiped to right, open the drawer, otherwise close it
(event.velocityX === 0 ? event.translationX : event.velocityX) > 0
: // If swiped to left, open the drawer, otherwise close it
(event.velocityX === 0 ? event.translationX : event.velocityX) < 0
: open;
toggleDrawer(nextOpen, event.velocityX);
runOnJS(onGestureEnd)();
},
});
const translateX = useDerivedValue(() => {
// Comment stolen from react-native-gesture-handler/DrawerLayout
//
// While closing the drawer when user starts gesture outside of its area (in greyed
// out part of the window), we want the drawer to follow only once finger reaches the
// edge of the drawer.
// E.g. on the diagram below drawer is illustrate by X signs and the greyed out area by
// dots. The touch gesture starts at '*' and moves left, touch path is indicated by
// an arrow pointing left
// 1) +---------------+ 2) +---------------+ 3) +---------------+ 4) +---------------+
// |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
// |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
// |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
// |XXXXXXXX|......| |XXXXXXXX|.<-*..| |XXXXXXXX|<--*..| |XXXXX|<-----*..|
// |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
// |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
// |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
// +---------------+ +---------------+ +---------------+ +---------------+
//
// For the above to work properly we define animated value that will keep start position
// of the gesture. Then we use that value to calculate how much we need to subtract from
// the translationX. If the gesture started on the greyed out area we take the distance from the
// edge of the drawer to the start position. Otherwise we don't subtract at all and the
// drawer be pulled back as soon as you start the pan.
//
// This is used only when drawerType is "front"
const touchDistance =
drawerType === 'front' && gestureState.value === GestureState.ACTIVE
? minmax(
drawerPosition === 'left'
? touchStartX.value - drawerWidth
: dimensions.width - drawerWidth - touchStartX.value,
0,
dimensions.width
)
: 0;
const translateX =
drawerPosition === 'left'
? minmax(translationX.value + touchDistance, -drawerWidth, 0)
: minmax(translationX.value - touchDistance, 0, drawerWidth);
return translateX;
});
const drawerAnimatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX:
drawerType === 'permanent' || drawerType === 'back'
? 0
: translateX.value,
},
],
};
});
const contentAnimatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX:
drawerType === 'permanent' || drawerType === 'front'
? 0
: drawerPosition === 'left'
? drawerWidth + translateX.value
: translateX.value - drawerWidth,
},
],
};
});
const progress = useDerivedValue(() => {
return drawerType === 'permanent'
? 1
: interpolate(
translateX.value,
[getDrawerTranslationX(false), getDrawerTranslationX(true)],
[0, 1]
);
});
return (
<DrawerProgressContext.Provider value={progress}>
<PanGestureHandler
activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
hitSlop={hitSlop}
enabled={drawerType !== 'permanent' && swipeEnabled}
onGestureEvent={onGestureEvent}
{...gestureHandlerProps}
>
{/* Immediate child of gesture handler needs to be an Animated.View */}
<Animated.View
style={[
styles.main,
{
flexDirection:
drawerType === 'permanent' && !isRight ? 'row-reverse' : 'row',
},
]}
>
<Animated.View style={[styles.content, contentAnimatedStyle]}>
<View
accessibilityElementsHidden={isOpen && drawerType !== 'permanent'}
importantForAccessibility={
isOpen && drawerType !== 'permanent'
? 'no-hide-descendants'
: 'auto'
}
style={styles.content}
>
{renderSceneContent()}
</View>
{drawerType !== 'permanent' ? (
<Overlay
progress={progress}
onPress={() => toggleDrawer(false)}
style={overlayStyle}
/>
) : null}
</Animated.View>
<Animated.View
accessibilityViewIsModal={isOpen && drawerType !== 'permanent'}
removeClippedSubviews={Platform.OS !== 'ios'}
style={[
styles.container,
{
position: drawerType === 'permanent' ? 'relative' : 'absolute',
zIndex: drawerType === 'back' ? -1 : 0,
},
drawerAnimatedStyle,
drawerStyle as any,
]}
>
{renderDrawerContent()}
</Animated.View>
</Animated.View>
</PanGestureHandler>
</DrawerProgressContext.Provider>
);
}
const styles = StyleSheet.create({
container: {
top: 0,
bottom: 0,
maxWidth: '100%',
width: DEFAULT_DRAWER_WIDTH,
},
content: {
flex: 1,
},
main: {
flex: 1,
...Platform.select({
// FIXME: We need to hide `overflowX` on Web so the translated content doesn't show offscreen.
// But adding `overflowX: 'hidden'` prevents content from collapsing the URL bar.
web: null,
default: { overflow: 'hidden' },
}),
},
});

View File

@@ -1,56 +0,0 @@
import * as React from 'react';
import { Pressable, Platform, StyleSheet } from 'react-native';
import Animated, { useAnimatedStyle } from 'react-native-reanimated';
const PROGRESS_EPSILON = 0.05;
type Props = React.ComponentProps<typeof Animated.View> & {
progress: Animated.SharedValue<number>;
onPress: () => void;
};
const Overlay = React.forwardRef(function Overlay(
{ progress, onPress, style, ...props }: Props,
ref: React.Ref<Animated.View>
) {
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: progress.value,
// We don't want the user to be able to press through the overlay when drawer is open
// One approach is to adjust the pointerEvents based on the progress
// But we can also send the overlay behind the screen
zIndex: progress.value > PROGRESS_EPSILON ? 0 : -1,
};
});
return (
<Animated.View
{...props}
ref={ref}
style={[styles.overlay, overlayStyle, animatedStyle, style]}
>
<Pressable onPress={onPress} style={styles.pressable} />
</Animated.View>
);
});
const overlayStyle = Platform.select<Record<string, string>>({
web: {
// Disable touch highlight on mobile Safari.
// WebkitTapHighlightColor must be used outside of StyleSheet.create because react-native-web will omit the property.
WebkitTapHighlightColor: 'transparent',
},
default: {},
});
const styles = StyleSheet.create({
overlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
pressable: {
flex: 1,
},
});
export default Overlay;

View File

@@ -3,52 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.0.0-next.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/elements@1.0.0-next.3...@react-navigation/elements@1.0.0-next.4) (2021-04-08)
**Note:** Version bump only for package @react-navigation/elements
# [1.0.0-next.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/elements@1.0.0-next.2...@react-navigation/elements@1.0.0-next.3) (2021-03-22)
### Features
* add a Background component ([cbaabc1](https://github.com/react-navigation/react-navigation/commit/cbaabc1288e780698e499a00b9ca06ab9746a0da))
# [1.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/elements@1.0.0-next.1...@react-navigation/elements@1.0.0-next.2) (2021-03-12)
### Bug Fixes
* use theme in PlatformPressable ([40439cc](https://github.com/react-navigation/react-navigation/commit/40439ccb420825a1aa480648526a816f2422ea6e))
### Features
* return nearest parent header height for useHeaderHeight ([24b3f73](https://github.com/react-navigation/react-navigation/commit/24b3f739da4b8af8dca77d92c72cfdaa762e564a))
# [1.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/elements@1.0.0...@react-navigation/elements@1.0.0-next.1) (2021-03-10)
### Bug Fixes
* fix peer dep versions ([72f90b5](https://github.com/react-navigation/react-navigation/commit/72f90b50d27eda1315bb750beca8a36f26dafe17))
# 1.0.0-next.0 (2021-03-09)

View File

@@ -2,4 +2,4 @@
UI Components for React Navigation.
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/6.x/elements/).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/elements/).

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/elements",
"description": "UI Components for React Navigation",
"version": "1.0.0-next.4",
"version": "1.0.0-next.0",
"keywords": [
"react-native",
"react-navigation",
@@ -30,26 +30,27 @@
],
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"devDependencies": {
"@react-native-masked-view/masked-view": "^0.2.3",
"@react-navigation/native": "^6.0.0-next.2",
"@react-native-masked-view/masked-view": "^0.2.2",
"@react-navigation/native": "^6.0.0-next.0",
"@testing-library/react-native": "^7.2.0",
"@types/react": "^16.9.53",
"@types/react-native": "~0.63.51",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.4",
"react-native": "~0.63.2",
"react-native-builder-bob": "^0.18.1",
"typescript": "^4.2.3"
},
"peerDependencies": {
"@react-navigation/native": "^6.0.0",
"@react-navigation/native": "^5.0.5",
"react": "*",
"react-native": "*",
"react-native-safe-area-context": ">= 3.0.0"

View File

@@ -1,18 +0,0 @@
import * as React from 'react';
import { View, ViewProps } from 'react-native';
import { useTheme } from '@react-navigation/native';
type Props = ViewProps & {
children: React.ReactNode;
};
export default function Background({ style, ...rest }: Props) {
const { colors } = useTheme();
return (
<View
{...rest}
style={[{ flex: 1, backgroundColor: colors.background }, style]}
/>
);
}

View File

@@ -22,8 +22,7 @@ export default function HeaderBackButton({
labelVisible = Platform.OS === 'ios',
onLabelLayout,
onPress,
pressColor,
pressOpacity,
pressColorAndroid: customPressColorAndroid,
screenLayout,
tintColor: customTintColor,
titleLayout,
@@ -32,7 +31,7 @@ export default function HeaderBackButton({
testID,
style,
}: HeaderBackButtonProps) {
const { colors } = useTheme();
const { dark, colors } = useTheme();
const [initialLabelWidth, setInitialLabelWidth] = React.useState<
undefined | number
@@ -46,6 +45,13 @@ export default function HeaderBackButton({
default: colors.text,
});
const pressColorAndroid =
customPressColorAndroid !== undefined
? customPressColorAndroid
: dark
? 'rgba(255, 255, 255, .32)'
: 'rgba(0, 0, 0, .32)';
const handleLabelLayout = (e: LayoutChangeEvent) => {
onLabelLayout?.(e);
@@ -150,8 +156,7 @@ export default function HeaderBackButton({
accessibilityLabel={accessibilityLabel}
testID={testID}
onPress={disabled ? undefined : handlePress}
pressColor={pressColor}
pressOpacity={pressOpacity}
pressColor={pressColorAndroid}
android_ripple={{ borderless: true }}
style={[styles.container, disabled && styles.disabled, style]}
hitSlop={Platform.select({

View File

@@ -1,6 +1,5 @@
import * as React from 'react';
import { Platform, Pressable, PressableProps } from 'react-native';
import { useTheme } from '@react-navigation/native';
export type Props = PressableProps & {
pressColor?: string;
@@ -13,30 +12,24 @@ const ANDROID_SUPPORTS_RIPPLE =
Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP;
/**
* PlatformPressable provides an abstraction on top of Pressable to handle platform differences.
* PlatformPressable provides an abstraction on top of TouchableNativeFeedback and
* TouchableOpacity to handle platform differences.
*
* On Android, you can pass the props of TouchableNativeFeedback.
* On other platforms, you can pass the props of TouchableOpacity.
*/
export default function PlatformPressable({
android_ripple,
pressColor,
pressColor = 'rgba(0, 0, 0, .32)',
pressOpacity,
style,
...rest
}: Props) {
const { dark } = useTheme();
return (
<Pressable
android_ripple={
ANDROID_SUPPORTS_RIPPLE
? {
color:
pressColor !== undefined
? pressColor
: dark
? 'rgba(255, 255, 255, .32)'
: 'rgba(0, 0, 0, .32)',
...android_ripple,
}
? { color: pressColor, ...android_ripple }
: undefined
}
style={({ pressed }) => [

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
import { View, StyleSheet } from 'react-native';
import {
useSafeAreaFrame,
useSafeAreaInsets,
@@ -12,19 +12,16 @@ import {
ParamListBase,
} from '@react-navigation/native';
import Background from './Background';
import HeaderShownContext from './Header/HeaderShownContext';
import HeaderHeightContext from './Header/HeaderHeightContext';
import getDefaultHeaderHeight from './Header/getDefaultHeaderHeight';
type Props = {
focused: boolean;
navigation: NavigationProp<ParamListBase>;
route: RouteProp<ParamListBase, string>;
header: React.ReactNode;
headerShown?: boolean;
headerStatusBarHeight?: number;
style?: StyleProp<ViewStyle>;
children: React.ReactNode;
};
@@ -33,17 +30,14 @@ export default function Screen(props: Props) {
const insets = useSafeAreaInsets();
const isParentHeaderShown = React.useContext(HeaderShownContext);
const parentHeaderHeight = React.useContext(HeaderHeightContext);
const {
focused,
header,
headerShown = true,
headerStatusBarHeight = isParentHeaderShown ? 0 : insets.top,
children,
navigation,
route,
children,
style,
} = props;
const [headerHeight, setHeaderHeight] = React.useState(() =>
@@ -51,18 +45,12 @@ export default function Screen(props: Props) {
);
return (
<Background
accessibilityElementsHidden={!focused}
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
style={[styles.container, style]}
>
<View style={styles.container}>
<View style={styles.content}>
<HeaderShownContext.Provider
value={isParentHeaderShown || headerShown !== false}
>
<HeaderHeightContext.Provider
value={headerShown ? headerHeight : parentHeaderHeight}
>
<HeaderHeightContext.Provider value={headerShown ? headerHeight : 0}>
{children}
</HeaderHeightContext.Provider>
</HeaderShownContext.Provider>
@@ -82,7 +70,7 @@ export default function Screen(props: Props) {
</NavigationRouteContext.Provider>
</NavigationContext.Provider>
) : null}
</Background>
</View>
);
}

View File

@@ -11,10 +11,9 @@ export { default as getHeaderTitle } from './Header/getHeaderTitle';
export { default as MissingIcon } from './MissingIcon';
export { default as PlatformPressable } from './PlatformPressable';
export { default as ResourceSavingView } from './ResourceSavingView';
export { default as ResourceSavingScene } from './ResourceSavingScene';
export { default as SafeAreaProviderCompat } from './SafeAreaProviderCompat';
export { default as Screen } from './Screen';
export { default as Background } from './Background';
export const Assets = [
// eslint-disable-next-line import/no-commonjs

View File

@@ -127,16 +127,13 @@ export type HeaderBackButtonProps = {
disabled?: boolean;
/**
* Callback to call when the button is pressed.
* By default, this triggers `goBack`.
*/
onPress?: () => void;
/**
* Color for material ripple (Android >= 5.0 only).
*/
pressColor?: string;
/**
* Opacity when the button is pressed, used when ripple is not supported.
*/
pressOpacity?: number;
pressColorAndroid?: string;
/**
* Function which returns a React Element to display custom image in header's back button.
*/

View File

@@ -3,25 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [6.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@6.0.0-next.1...@react-navigation/material-bottom-tabs@6.0.0-next.2) (2021-04-08)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
# [6.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@6.0.0...@react-navigation/material-bottom-tabs@6.0.0-next.1) (2021-03-10)
### Bug Fixes
* fix peer dep versions ([72f90b5](https://github.com/react-navigation/react-navigation/commit/72f90b50d27eda1315bb750beca8a36f26dafe17))
# [6.0.0-next.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.9...@react-navigation/material-bottom-tabs@6.0.0-next.0) (2021-03-09)

View File

@@ -2,4 +2,4 @@
React Navigation integration for [bottom navigation](https://material.io/components/bottom-navigation) component from [`react-native-paper`](https://callstack.github.io/react-native-paper/bottom-navigation.html).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/6.x/material-bottom-tab-navigator/).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/material-bottom-tab-navigator/).

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/material-bottom-tabs",
"description": "Integration for bottom navigation component from react-native-paper",
"version": "6.0.0-next.2",
"version": "6.0.0-next.0",
"keywords": [
"react-native-component",
"react-component",
@@ -34,28 +34,29 @@
],
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"devDependencies": {
"@react-navigation/native": "^6.0.0-next.2",
"@react-navigation/native": "^6.0.0-next.0",
"@testing-library/react-native": "^7.2.0",
"@types/react": "^16.9.53",
"@types/react-native": "~0.63.51",
"@types/react-native-vector-icons": "^6.4.6",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.4",
"react-native": "~0.63.2",
"react-native-builder-bob": "^0.18.1",
"react-native-paper": "^4.7.2",
"react-native-vector-icons": "^8.1.0",
"typescript": "^4.2.3"
},
"peerDependencies": {
"@react-navigation/native": "^6.0.0",
"@react-navigation/native": "^5.0.5",
"react": "*",
"react-native": "*",
"react-native-paper": ">= 3.0.0",

View File

@@ -3,33 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [6.0.0-next.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@6.0.0-next.2...@react-navigation/material-top-tabs@6.0.0-next.3) (2021-04-08)
**Note:** Version bump only for package @react-navigation/material-top-tabs
# [6.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@6.0.0-next.1...@react-navigation/material-top-tabs@6.0.0-next.2) (2021-04-08)
**Note:** Version bump only for package @react-navigation/material-top-tabs
# [6.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@6.0.0...@react-navigation/material-top-tabs@6.0.0-next.1) (2021-03-10)
### Bug Fixes
* fix peer dep versions ([72f90b5](https://github.com/react-navigation/react-navigation/commit/72f90b50d27eda1315bb750beca8a36f26dafe17))
# [6.0.0-next.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.9...@react-navigation/material-top-tabs@6.0.0-next.0) (2021-03-09)

View File

@@ -1,5 +1,5 @@
# `@react-navigation/material-top-tabs`
React Navigation integration for animated tab view component from [`react-native-tab-view`](https://github.com/satya164/react-native-tab-view).
React Navigation integration for animated tab view component from [`react-native-tab-view`](https://github.com/react-native-community/react-native-tab-view).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/6.x/material-top-tab-navigator/).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/material-top-tab-navigator/).

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/material-top-tabs",
"description": "Integration for the animated tab view component from react-native-tab-view",
"version": "6.0.0-next.3",
"version": "6.0.0-next.0",
"keywords": [
"react-native-component",
"react-component",
@@ -34,7 +34,8 @@
],
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
@@ -45,23 +46,23 @@
"warn-once": "^0.0.1"
},
"devDependencies": {
"@react-navigation/native": "^6.0.0-next.2",
"@react-navigation/native": "^6.0.0-next.0",
"@testing-library/react-native": "^7.2.0",
"@types/react": "^16.9.53",
"@types/react-native": "~0.63.51",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.4",
"react-native": "~0.63.2",
"react-native-builder-bob": "^0.18.1",
"react-native-pager-view": "^5.0.12",
"react-native-tab-view": "^3.0.1",
"react-native-pager-view": "^4.2.4",
"react-native-tab-view": "^3.0.0",
"typescript": "^4.2.3"
},
"peerDependencies": {
"@react-navigation/native": "^6.0.0",
"@react-navigation/native": "^5.0.5",
"react": "*",
"react-native": "*",
"react-native-pager-view": ">= 4.0.0",
"react-native-pager-view": ">= 1.0.0",
"react-native-tab-view": ">= 3.0.0"
},
"react-native-builder-bob": {

View File

@@ -26,7 +26,9 @@ function MaterialTopTabNavigator({
backBehavior,
children,
screenOptions,
// @ts-expect-error: lazy is deprecated
lazy,
// @ts-expect-error: tabBarOptions is deprecated
tabBarOptions,
...rest
}: Props) {

View File

@@ -1,5 +1,5 @@
import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
import type { SceneRendererProps, TabViewProps } from 'react-native-tab-view';
import type { SceneRendererProps, TabView } from 'react-native-tab-view';
import type {
ParamListBase,
Descriptor,
@@ -67,16 +67,6 @@ export type MaterialTopTabNavigationOptions = {
*/
lazy?: boolean;
/**
* Function that returns a React element to render if this screen hasn't been rendered yet.
* The `lazy` option also needs to be enabled for this to work.
*
* This view is usually only shown for a split second. Keep it lightweight.
*
* By default, this renders null.
*/
lazyPlaceholder?: () => React.ReactNode;
/**
* Title string of a tab displayed in the tab bar
* or a function that given { focused: boolean, color: string } returns a React.Node, to display in tab bar.
@@ -197,21 +187,37 @@ export type MaterialTopTabDescriptorMap = Record<
MaterialTopTabDescriptor
>;
export type MaterialTopTabNavigationConfig = Omit<
TabViewProps<Route<string>>,
| 'navigationState'
| 'onIndexChange'
| 'onSwipeStart'
| 'onSwipeEnd'
| 'renderScene'
| 'renderTabBar'
| 'renderLazyPlaceholder'
| 'lazy'
export type MaterialTopTabNavigationConfig = Partial<
Omit<
React.ComponentProps<typeof TabView>,
| 'navigationState'
| 'onIndexChange'
| 'onSwipeStart'
| 'onSwipeEnd'
| 'renderScene'
| 'renderTabBar'
| 'renderLazyPlaceholder'
| 'lazy'
>
> & {
/**
* Function that returns a React element to render for routes that haven't been rendered yet.
* Receives an object containing the route as the prop.
* The lazy prop also needs to be enabled.
*
* This view is usually only shown for a split second. Keep it lightweight.
*
* By default, this renders null.
*/
lazyPlaceholder?: (props: { route: Route<string> }) => React.ReactNode;
/**
* Function that returns a React element to display as the tab bar.
*/
tabBar?: (props: MaterialTopTabBarProps) => React.ReactNode;
/**
* Position of the tab bar. Defaults to `top`.
*/
tabBarPosition?: 'top' | 'bottom';
};
export type MaterialTopTabBarProps = SceneRendererProps & {

View File

@@ -21,9 +21,11 @@ type Props = MaterialTopTabNavigationConfig & {
state: TabNavigationState<ParamListBase>;
navigation: MaterialTopTabNavigationHelpers;
descriptors: MaterialTopTabDescriptorMap;
tabBarPosition?: 'top' | 'bottom';
};
export default function MaterialTopTabView({
lazyPlaceholder,
tabBar = (props: MaterialTopTabBarProps) => <MaterialTopTabBar {...props} />,
state,
navigation,
@@ -55,9 +57,7 @@ export default function MaterialTopTabView({
renderScene={({ route }) => descriptors[route.key].render()}
navigationState={state}
renderTabBar={renderTabBar}
renderLazyPlaceholder={({ route }) =>
descriptors[route.key].options.lazyPlaceholder?.() ?? null
}
renderLazyPlaceholder={lazyPlaceholder}
lazy={({ route }) => descriptors[route.key].options.lazy === true}
onSwipeStart={() => navigation.emit({ type: 'swipeStart' })}
onSwipeEnd={() => navigation.emit({ type: 'swipeEnd' })}

View File

@@ -3,22 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [6.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@6.0.0-next.1...@react-navigation/native@6.0.0-next.2) (2021-04-08)
**Note:** Version bump only for package @react-navigation/native
# [6.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@6.0.0...@react-navigation/native@6.0.0-next.1) (2021-03-10)
**Note:** Version bump only for package @react-navigation/native
# [6.0.0-next.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.9...@react-navigation/native@6.0.0-next.0) (2021-03-09)

View File

@@ -2,4 +2,4 @@
React Native integration for React Navigation.
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/6.x/getting-started/).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/getting-started/).

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/native",
"description": "React Native integration for React Navigation",
"version": "6.0.0-next.2",
"version": "6.0.0-next.0",
"keywords": [
"react-native",
"react-navigation",
@@ -30,16 +30,17 @@
],
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/core": "^6.0.0-next.2",
"@react-navigation/core": "^6.0.0-next.0",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.22"
"nanoid": "^3.1.20"
},
"devDependencies": {
"@testing-library/react-native": "^7.2.0",
@@ -49,7 +50,7 @@
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-dom": "^16.13.1",
"react-native": "~0.63.4",
"react-native": "~0.63.2",
"react-native-builder-bob": "^0.18.1",
"typescript": "^4.2.3"
},

View File

@@ -3,22 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [6.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@6.0.0-next.1...@react-navigation/routers@6.0.0-next.2) (2021-04-08)
**Note:** Version bump only for package @react-navigation/routers
# [6.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@6.0.0...@react-navigation/routers@6.0.0-next.1) (2021-03-10)
**Note:** Version bump only for package @react-navigation/routers
# [6.0.0-next.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.6.2...@react-navigation/routers@6.0.0-next.0) (2021-03-09)

View File

@@ -14,4 +14,4 @@ yarn add @react-navigation/routers
## Usage
Documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/6.x/custom-routers/).
Documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/custom-routers/).

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/routers",
"description": "Routers to help build custom navigators",
"version": "6.0.0-next.2",
"version": "6.0.0-next.0",
"keywords": [
"react",
"react-native",
@@ -29,14 +29,15 @@
],
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"nanoid": "^3.1.22"
"nanoid": "^3.1.20"
},
"devDependencies": {
"del-cli": "^3.0.1",

View File

@@ -3,117 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [6.0.0-next.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.8...@react-navigation/stack@6.0.0-next.9) (2021-04-08)
**Note:** Version bump only for package @react-navigation/stack
# [6.0.0-next.8](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.7...@react-navigation/stack@6.0.0-next.8) (2021-03-22)
### Features
* add a Background component ([cbaabc1](https://github.com/react-navigation/react-navigation/commit/cbaabc1288e780698e499a00b9ca06ab9746a0da))
# [6.0.0-next.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.6...@react-navigation/stack@6.0.0-next.7) (2021-03-22)
### Code Refactoring
* make gestureResponseDistance a number ([48851c9](https://github.com/react-navigation/react-navigation/commit/48851c9ebdcf1b835bbcb673adeb88e56b989443))
### BREAKING CHANGES
* now we need to pass a number instead of an object
# [6.0.0-next.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.5...@react-navigation/stack@6.0.0-next.6) (2021-03-14)
**Note:** Version bump only for package @react-navigation/stack
# [6.0.0-next.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.4...@react-navigation/stack@6.0.0-next.5) (2021-03-14)
### Bug Fixes
* consider header colors when managing statusbar ([3ad2bcb](https://github.com/react-navigation/react-navigation/commit/3ad2bcbaf85996ce0d5e1e961081978a32448899))
* consider header colors when managing statusbar ([faee245](https://github.com/react-navigation/react-navigation/commit/faee245d2ec8c59f9e9033d96ae21c5e60d95ba6))
### Code Refactoring
* move headerMode to options ([aacc1b5](https://github.com/react-navigation/react-navigation/commit/aacc1b525d86f0e0b1bad8016fd85e82024f16e9))
### BREAKING CHANGES
* headerMode is now moved to options instead of props
# [6.0.0-next.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.3...@react-navigation/stack@6.0.0-next.4) (2021-03-12)
### Bug Fixes
* add special statusbar handling to modal presentation ([a204edd](https://github.com/react-navigation/react-navigation/commit/a204edd012060f0816eddee7a093183aa379d049))
# [6.0.0-next.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.2...@react-navigation/stack@6.0.0-next.3) (2021-03-12)
### Features
* export drawer button ([2c8401d](https://github.com/react-navigation/react-navigation/commit/2c8401d5cb347d37c96e5b30f8ad05c17fd22ea4))
* return nearest parent header height for useHeaderHeight ([24b3f73](https://github.com/react-navigation/react-navigation/commit/24b3f739da4b8af8dca77d92c72cfdaa762e564a))
# [6.0.0-next.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.1...@react-navigation/stack@6.0.0-next.2) (2021-03-11)
### Bug Fixes
* respect headerStatusBarHeight option in Stack ([#9405](https://github.com/react-navigation/react-navigation/issues/9405)) ([8a6511c](https://github.com/react-navigation/react-navigation/commit/8a6511c491b2affbe378d720e613a3e3041ca9c2))
# [6.0.0-next.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0...@react-navigation/stack@6.0.0-next.1) (2021-03-10)
### Bug Fixes
* fix peer dep versions ([72f90b5](https://github.com/react-navigation/react-navigation/commit/72f90b50d27eda1315bb750beca8a36f26dafe17))
* remove use of deprecated currentlyFocusedField ([038eb87](https://github.com/react-navigation/react-navigation/commit/038eb87c42564f9d733e6870826726d3fb0adaee))
# [6.0.0-next.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.6...@react-navigation/stack@6.0.0-next.0) (2021-03-09)

View File

@@ -2,4 +2,4 @@
Stack navigator for React Navigation.
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/6.x/stack-navigator/).
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/stack-navigator/).

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/stack",
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
"version": "6.0.0-next.9",
"version": "6.0.0-next.0",
"keywords": [
"react-native-component",
"react-component",
@@ -33,40 +33,41 @@
],
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "alpha"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/elements": "^1.0.0-next.4",
"@react-navigation/elements": "^1.0.0",
"color": "^3.1.3",
"react-native-iphone-x-helper": "^1.3.0",
"warn-once": "^0.0.1"
},
"devDependencies": {
"@react-navigation/native": "^6.0.0-next.2",
"@react-navigation/native": "^6.0.0-next.0",
"@testing-library/react-native": "^7.2.0",
"@types/color": "^3.0.1",
"@types/react": "^16.9.53",
"@types/react-native": "~0.63.51",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.4",
"react-native": "~0.63.2",
"react-native-builder-bob": "^0.18.1",
"react-native-gesture-handler": "~1.10.2",
"react-native-safe-area-context": "~3.2.0",
"react-native-screens": "~3.0.0",
"react-native-gesture-handler": "~1.8.0",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "~2.15.0",
"typescript": "^4.2.3"
},
"peerDependencies": {
"@react-navigation/native": "^6.0.0",
"@react-navigation/native": "^5.0.5",
"react": "*",
"react-native": "*",
"react-native-gesture-handler": ">= 1.0.0",
"react-native-safe-area-context": ">= 3.0.0",
"react-native-screens": ">= 3.0.0"
"react-native-screens": ">= 2.15.0"
},
"react-native-builder-bob": {
"source": "src",

View File

@@ -160,10 +160,6 @@ export function forModalPresentationIOS({
overflow: 'hidden',
borderTopLeftRadius: borderRadius,
borderTopRightRadius: borderRadius,
// We don't need these for the animation
// But different border radius for corners improves animation perf
borderBottomLeftRadius: isIphoneX() ? borderRadius : 0,
borderBottomRightRadius: isIphoneX() ? borderRadius : 0,
marginTop: index === 0 ? 0 : statusBarHeight,
marginBottom: index === 0 ? 0 : topOffset,
transform: [{ translateY }, { scale }],

View File

@@ -18,7 +18,6 @@ import type {
StackNavigationConfig,
StackNavigationOptions,
StackNavigationEventMap,
StackHeaderMode,
} from '../types';
type Props = DefaultNavigatorOptions<StackNavigationOptions> &
@@ -32,18 +31,13 @@ function StackNavigator({
...rest
}: Props) {
// @ts-expect-error: headerMode='none' is deprecated
const headerMode = rest.headerMode as StackHeaderMode | 'none' | undefined;
const isHeaderModeNone = rest.headerMode === 'none';
warnOnce(
headerMode === 'none',
isHeaderModeNone,
`Stack Navigator: 'headerMode="none"' is deprecated. Use 'headerShown: false' in 'screenOptions' instead.`
);
warnOnce(
headerMode && headerMode !== 'none',
`Stack Navigator: 'headerMode' is moved to 'options'. Moved it to 'screenOptions' to keep current behavior.`
);
const { state, descriptors, navigation } = useNavigationBuilder<
StackNavigationState<ParamListBase>,
StackRouterOptions,
@@ -54,22 +48,14 @@ function StackNavigator({
initialRouteName,
children,
screenOptions,
defaultScreenOptions: ({ options }) => ({
headerShown: headerMode ? headerMode !== 'none' : true,
headerMode:
headerMode && headerMode !== 'none'
? headerMode
: rest.mode !== 'modal' &&
Platform.OS === 'ios' &&
options.header === undefined
? 'float'
: 'screen',
defaultScreenOptions: {
headerShown: !isHeaderModeNone,
gestureEnabled: Platform.OS === 'ios',
animationEnabled:
Platform.OS !== 'web' &&
Platform.OS !== 'windows' &&
Platform.OS !== 'macos',
}),
},
});
React.useEffect(

View File

@@ -1,5 +1,6 @@
import type * as React from 'react';
import type { Animated, StyleProp, TextStyle, ViewStyle } from 'react-native';
import type { EdgeInsets } from 'react-native-safe-area-context';
import type {
NavigationProp,
ParamListBase,
@@ -148,6 +149,10 @@ export type StackHeaderProps = {
* Layout of the screen.
*/
layout: Layout;
/**
* Safe area insets to use in the header, e.g. to apply extra spacing for statusbar and notch.
*/
insets: EdgeInsets;
/**
* Options for the back button.
*/
@@ -198,12 +203,7 @@ export type StackNavigationOptions = StackHeaderOptions &
*/
header?: (props: StackHeaderProps) => React.ReactNode;
/**
* Whether the header floats above the screen or part of the screen.
* Defaults to `float` on iOS for non-modals, and `screen` for the rest.
*/
headerMode?: StackHeaderMode;
/**
* Whether to show the header. The header is shown by default.
* Whether to show the header. The header is shown by default unless `headerMode` was set to `none`.
* Setting this to `false` hides the header.
*/
headerShown?: boolean;
@@ -249,15 +249,35 @@ export type StackNavigationOptions = StackHeaderOptions &
*/
gestureEnabled?: boolean;
/**
* Distance of touch start from the edge of the screen to recognize gestures.
* Object to override the distance of touch start from the edge of the screen to recognize gestures.
* Not supported on Web.
*/
gestureResponseDistance?: number;
gestureResponseDistance?: {
/**
* Distance for vertical direction. Defaults to 135.
*/
vertical?: number;
/**
* Distance for horizontal direction. Defaults to 25.
*/
horizontal?: number;
};
/**
* Number which determines the relevance of velocity for the gesture. Defaults to 0.3.
* Not supported on Web.
*/
gestureVelocityImpact?: number;
/**
* Safe area insets for the screen. This is used to avoid elements like notch and status bar.
* By default, the device's safe area insets are automatically detected. You can override the behavior with this option.
* For example, to remove the extra spacing for status bar, pass `safeAreaInsets: { top: 0 }`.
*/
safeAreaInsets?: {
top?: number;
right?: number;
bottom?: number;
left?: number;
};
/**
* Whether to detach the previous screen from the view hierarchy to save memory.
* Set it to `false` if you need the previous screen to be seen through the active screen.
@@ -269,6 +289,7 @@ export type StackNavigationOptions = StackHeaderOptions &
export type StackNavigationConfig = {
mode?: StackCardMode;
headerMode?: StackHeaderMode;
/**
* If `false`, the keyboard will NOT automatically dismiss when navigating to a new screen.
* Defaults to `true`.

View File

@@ -1,5 +1,4 @@
import * as React from 'react';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { StackActions, useNavigationState } from '@react-navigation/native';
import { getHeaderTitle, HeaderShownContext } from '@react-navigation/elements';
@@ -11,14 +10,13 @@ import type { StackHeaderProps } from '../../types';
export default React.memo(function Header({
back,
layout,
insets,
progress,
options,
route,
navigation,
styleInterpolator,
}: StackHeaderProps) {
const insets = useSafeAreaInsets();
let previousTitle;
// The label for the left back button shows the title of the previous screen
@@ -49,11 +47,7 @@ export default React.memo(function Header({
);
const statusBarHeight =
options.headerStatusBarHeight !== undefined
? options.headerStatusBarHeight
: (isModal && !isFirstRouteInParent) || isParentHeaderShown
? 0
: insets.top;
(isModal && !isFirstRouteInParent) || isParentHeaderShown ? 0 : insets.top;
return (
<HeaderSegment

View File

@@ -7,6 +7,7 @@ import {
ParamListBase,
} from '@react-navigation/native';
import { HeaderBackContext, getHeaderTitle } from '@react-navigation/elements';
import type { EdgeInsets } from 'react-native-safe-area-context';
import Header from './Header';
import {
@@ -21,11 +22,13 @@ import type {
StackHeaderStyleInterpolator,
StackNavigationProp,
StackHeaderProps,
GestureDirection,
} from '../../types';
export type Props = {
mode: 'float' | 'screen';
layout: Layout;
insets: EdgeInsets;
scenes: (Scene | undefined)[];
getPreviousScene: (props: { route: Route<string> }) => Scene | undefined;
getFocusedRoute: () => Route<string>;
@@ -34,6 +37,7 @@ export type Props = {
height: number;
}) => void;
styleInterpolator: StackHeaderStyleInterpolator;
gestureDirection: GestureDirection;
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
};
@@ -41,9 +45,11 @@ export default function HeaderContainer({
mode,
scenes,
layout,
insets,
getPreviousScene,
getFocusedRoute,
onContentHeightChange,
gestureDirection,
styleInterpolator,
style,
}: Props) {
@@ -57,10 +63,10 @@ export default function HeaderContainer({
return null;
}
const { header, headerMode, headerShown = true, headerTransparent } =
const { header, headerShown = true, headerTransparent } =
scene.descriptor.options || {};
if (headerMode !== mode || !headerShown) {
if (!headerShown) {
return null;
}
@@ -84,27 +90,22 @@ export default function HeaderContainer({
const previousDescriptor = self[i - 1]?.descriptor;
const nextDescriptor = self[i + 1]?.descriptor;
const {
headerShown: previousHeaderShown = true,
headerMode: previousHeaderMode,
} = previousDescriptor?.options || {};
const { headerShown: previousHeaderShown = true } =
previousDescriptor?.options || {};
const {
headerShown: nextHeaderShown = true,
headerMode: nextHeaderMode,
gestureDirection: nextGestureDirection,
} = nextDescriptor?.options || {};
const { headerShown: nextHeaderShown = true } =
nextDescriptor?.options || {};
const isHeaderStatic =
((previousHeaderShown === false || previousHeaderMode === 'screen') &&
(previousHeaderShown === false &&
// We still need to animate when coming back from next scene
// A hacky way to check this is if the next scene exists
!nextDescriptor) ||
nextHeaderShown === false ||
nextHeaderMode === 'screen';
nextHeaderShown === false;
const props: StackHeaderProps = {
layout,
insets,
back: headerBack,
progress: scene.progress,
options: scene.descriptor.options,
@@ -114,10 +115,10 @@ export default function HeaderContainer({
styleInterpolator:
mode === 'float'
? isHeaderStatic
? nextGestureDirection === 'vertical' ||
nextGestureDirection === 'vertical-inverted'
? gestureDirection === 'vertical' ||
gestureDirection === 'vertical-inverted'
? forSlideUp
: nextGestureDirection === 'horizontal-inverted'
: gestureDirection === 'horizontal-inverted'
? forSlideRight
: forSlideLeft
: styleInterpolator

View File

@@ -111,10 +111,6 @@ export default function HeaderSegment(props: Props) {
headerBackTestID,
headerBackAllowFontScaling,
headerBackTitleStyle,
headerTitleContainerStyle,
headerLeftContainerStyle,
headerRightContainerStyle,
headerBackgroundContainerStyle,
headerStyle: customHeaderStyle,
headerStatusBarHeight = isParentHeaderShown ? 0 : insets.top,
styleInterpolator,
@@ -176,13 +172,10 @@ export default function HeaderSegment(props: Props) {
layout={layout}
headerTitle={headerTitle}
headerLeft={headerLeft}
headerTitleContainerStyle={[titleStyle, headerTitleContainerStyle]}
headerLeftContainerStyle={[leftButtonStyle, headerLeftContainerStyle]}
headerRightContainerStyle={[rightButtonStyle, headerRightContainerStyle]}
headerBackgroundContainerStyle={[
backgroundStyle,
headerBackgroundContainerStyle,
]}
headerTitleContainerStyle={titleStyle}
headerLeftContainerStyle={leftButtonStyle}
headerRightContainerStyle={rightButtonStyle}
headerBackgroundContainerStyle={backgroundStyle}
headerStyle={customHeaderStyle}
headerStatusBarHeight={headerStatusBarHeight}
{...rest}

View File

@@ -37,7 +37,8 @@ export default class KeyboardManager extends React.Component<Props> {
this.clearKeyboardTimeout();
const input: InputRef = TextInput.State.currentlyFocusedInput();
// @ts-expect-error: blurTextInput accepts both number and ref, but types say only ref
const input: InputRef = TextInput.State.currentlyFocusedField();
// When a page change begins, blur the currently focused input
input?.blur();

View File

@@ -1,58 +0,0 @@
import * as React from 'react';
import { StatusBar, StyleSheet } from 'react-native';
import { useTheme } from '@react-navigation/native';
import type { EdgeInsets } from 'react-native-safe-area-context';
import type { Layout } from '../types';
type Props = {
dark: boolean | undefined;
layout: Layout;
insets: EdgeInsets;
style: any;
};
export default function ModalStatusBarManager({
dark,
layout,
insets,
style,
}: Props) {
const { dark: darkTheme } = useTheme();
const [overlapping, setOverlapping] = React.useState(true);
const enabled = layout.width && layout.height > layout.width;
const scale = 1 - 20 / layout.width;
const offset = (insets.top - 34) * scale;
const flattenedStyle = StyleSheet.flatten(style);
const translateY = flattenedStyle?.transform?.find(
(s: any) => s.translateY !== undefined
)?.translateY;
React.useEffect(() => {
if (!enabled) {
return;
}
const listener = ({ value }: { value: number }) => {
setOverlapping(value < offset);
};
const sub = translateY?.addListener(listener);
return () => translateY?.removeListener(sub);
}, [enabled, offset, translateY]);
if (!enabled) {
return null;
}
const darkContent = dark ?? !darkTheme;
return (
<StatusBar
animated
barStyle={overlapping && darkContent ? 'dark-content' : 'light-content'}
/>
);
}

View File

@@ -18,8 +18,6 @@ import {
GestureState,
PanGestureHandlerGestureEvent,
} from '../GestureHandler';
import ModalStatusBarManager from '../ModalStatusBarManager';
import { forModalPresentationIOS } from '../../TransitionConfigs/CardStyleInterpolators';
import CardAnimationContext from '../../utils/CardAnimationContext';
import getDistanceForDirection from '../../utils/getDistanceForDirection';
import getInvertedMultiplier from '../../utils/getInvertedMultiplier';
@@ -39,7 +37,6 @@ type Props = ViewProps & {
gesture: Animated.Value;
layout: Layout;
insets: EdgeInsets;
headerDarkContent: boolean | undefined;
pageOverflowEnabled: boolean;
gestureDirection: GestureDirection;
onOpen: () => void;
@@ -55,7 +52,10 @@ type Props = ViewProps & {
overlayEnabled: boolean;
shadowEnabled: boolean;
gestureEnabled: boolean;
gestureResponseDistance?: number;
gestureResponseDistance?: {
vertical?: number;
horizontal?: number;
};
gestureVelocityImpact: number;
transitionSpec: {
open: TransitionSpec;
@@ -406,11 +406,13 @@ export default class Card extends React.Component<Props> {
const { layout, gestureDirection, gestureResponseDistance } = this.props;
const distance =
gestureResponseDistance !== undefined
? gestureResponseDistance
: gestureDirection === 'vertical' ||
gestureDirection === 'vertical-inverted'
? GESTURE_RESPONSE_DISTANCE_VERTICAL
gestureDirection === 'vertical' ||
gestureDirection === 'vertical-inverted'
? gestureResponseDistance?.vertical !== undefined
? gestureResponseDistance.vertical
: GESTURE_RESPONSE_DISTANCE_VERTICAL
: gestureResponseDistance?.horizontal !== undefined
? gestureResponseDistance.horizontal
: GESTURE_RESPONSE_DISTANCE_HORIZONTAL;
if (gestureDirection === 'vertical') {
@@ -462,7 +464,6 @@ export default class Card extends React.Component<Props> {
gestureEnabled,
gestureDirection,
pageOverflowEnabled,
headerDarkContent,
children,
containerStyle: customContainerStyle,
contentStyle,
@@ -522,22 +523,6 @@ export default class Card extends React.Component<Props> {
return (
<CardAnimationContext.Provider value={animationContext}>
{
// StatusBar messes with translucent status bar on Android
// So we should only enable it on iOS
Platform.OS === 'ios' &&
overlayEnabled &&
index === 0 &&
next &&
styleInterpolator === forModalPresentationIOS ? (
<ModalStatusBarManager
dark={headerDarkContent}
layout={layout}
insets={insets}
style={cardStyle}
/>
) : null
}
<Animated.View
style={{
// This is a dummy style that doesn't actually change anything visually.

View File

@@ -27,7 +27,6 @@ type Props = TransitionPreset & {
layout: Layout;
gesture: Animated.Value;
scene: Scene;
headerDarkContent: boolean | undefined;
safeAreaInsetTop: number;
safeAreaInsetRight: number;
safeAreaInsetBottom: number;
@@ -56,12 +55,15 @@ type Props = TransitionPreset & {
onGestureEnd?: (props: { route: Route<string> }) => void;
onGestureCancel?: (props: { route: Route<string> }) => void;
gestureEnabled?: boolean;
gestureResponseDistance?: number;
gestureResponseDistance?: {
vertical?: number;
horizontal?: number;
};
gestureVelocityImpact?: number;
mode: StackCardMode;
headerMode: StackHeaderMode;
headerShown: boolean;
hasAbsoluteFloatHeader: boolean;
hasAbsoluteHeader: boolean;
headerHeight: number;
onHeaderHeightChange: (props: {
route: Route<string>;
@@ -89,11 +91,10 @@ function CardContainer({
getPreviousScene,
getFocusedRoute,
mode,
headerDarkContent,
headerMode,
headerShown,
headerStyleInterpolator,
hasAbsoluteFloatHeader,
hasAbsoluteHeader,
headerHeight,
onHeaderHeightChange,
isParentHeaderShown,
@@ -118,8 +119,6 @@ function CardContainer({
scene,
transitionSpec,
}: Props) {
const parentHeaderHeight = React.useContext(HeaderHeightContext);
const handleOpen = () => {
const { route } = scene.descriptor;
@@ -247,12 +246,7 @@ function CardContainer({
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
pointerEvents={active ? 'box-none' : pointerEvents}
pageOverflowEnabled={headerMode !== 'float' && mode === 'card'}
headerDarkContent={headerDarkContent}
containerStyle={
hasAbsoluteFloatHeader && headerMode !== 'screen'
? { marginTop: headerHeight }
: null
}
containerStyle={hasAbsoluteHeader ? { marginTop: headerHeight } : null}
contentStyle={[{ backgroundColor: colors.background }, cardStyle]}
style={[
{
@@ -269,9 +263,7 @@ function CardContainer({
<HeaderShownContext.Provider
value={isParentHeaderShown || headerShown !== false}
>
<HeaderHeightContext.Provider
value={headerShown ? headerHeight : parentHeaderHeight}
>
<HeaderHeightContext.Provider value={headerHeight}>
{renderScene({ route: scene.descriptor.route })}
</HeaderHeightContext.Provider>
</HeaderShownContext.Provider>
@@ -282,9 +274,11 @@ function CardContainer({
{renderHeader({
mode: 'screen',
layout,
insets,
scenes: [previousScene, scene],
getPreviousScene,
getFocusedRoute,
gestureDirection,
styleInterpolator: headerStyleInterpolator,
onContentHeightChange: onHeaderHeightChange,
})}

View File

@@ -6,7 +6,6 @@ import {
Platform,
} from 'react-native';
import type { EdgeInsets } from 'react-native-safe-area-context';
import Color from 'color';
import type {
ParamListBase,
Route,
@@ -15,7 +14,6 @@ import type {
import {
getDefaultHeaderHeight,
SafeAreaProviderCompat,
Background,
} from '@react-navigation/elements';
import {
@@ -29,10 +27,12 @@ import {
DefaultTransition,
ModalTransition,
} from '../../TransitionConfigs/TransitionPresets';
import { forNoAnimation as forNoAnimationHeader } from '../../TransitionConfigs/HeaderStyleInterpolators';
import { forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators';
import getDistanceForDirection from '../../utils/getDistanceForDirection';
import type {
Layout,
StackHeaderMode,
StackCardMode,
StackDescriptorMap,
StackNavigationOptions,
@@ -60,6 +60,7 @@ type Props = {
getGesturesEnabled: (props: { route: Route<string> }) => boolean;
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
renderScene: (props: { route: Route<string> }) => React.ReactNode;
headerMode: StackHeaderMode;
isParentHeaderShown: boolean;
onTransitionStart: (
props: { route: Route<string> },
@@ -107,8 +108,13 @@ const getHeaderHeights = (
const height =
typeof style.height === 'number' ? style.height : previous[curr.key];
const safeAreaInsets = {
...insets,
...options.safeAreaInsets,
};
const {
headerStatusBarHeight = isParentHeaderShown ? 0 : insets.top,
headerStatusBarHeight = isParentHeaderShown ? 0 : safeAreaInsets.top,
} = options;
acc[curr.key] =
@@ -380,6 +386,7 @@ export default class CardStack extends React.Component<Props, State> {
getGesturesEnabled,
renderHeader,
renderScene,
headerMode,
isParentHeaderShown,
onTransitionStart,
onTransitionEnd,
@@ -406,6 +413,20 @@ export default class CardStack extends React.Component<Props, State> {
let defaultTransitionPreset =
mode === 'modal' ? ModalTransition : DefaultTransition;
if (headerMode !== 'float') {
defaultTransitionPreset = {
...defaultTransitionPreset,
headerStyleInterpolator: forNoAnimationHeader,
};
}
const {
top = insets.top,
right = insets.right,
bottom = insets.bottom,
left = insets.left,
} = focusedOptions.safeAreaInsets || {};
let activeScreensLimit = 1;
for (let i = scenes.length - 1; i >= 0; i--) {
@@ -423,52 +444,54 @@ export default class CardStack extends React.Component<Props, State> {
}
}
const isFloatHeaderAbsolute = this.state.scenes.slice(-2).some((scene) => {
const options = scene.descriptor.options ?? {};
const {
headerMode = 'screen',
headerTransparent,
headerShown = true,
} = options;
const isFloatHeaderAbsolute =
headerMode === 'float'
? this.state.scenes.slice(-2).some((scene) => {
const { descriptor } = scene;
const options = descriptor ? descriptor.options : {};
const { headerTransparent, headerShown = true } = options;
if (
headerTransparent ||
headerShown === false ||
headerMode === 'screen'
) {
return true;
}
if (headerTransparent || headerShown === false) {
return true;
}
return false;
});
return false;
})
: false;
const floatingHeader = (
<React.Fragment key="header">
{renderHeader({
mode: 'float',
layout,
scenes,
getPreviousScene: this.getPreviousScene,
getFocusedRoute: this.getFocusedRoute,
onContentHeightChange: this.handleHeaderLayout,
styleInterpolator:
focusedOptions.headerStyleInterpolator !== undefined
? focusedOptions.headerStyleInterpolator
: defaultTransitionPreset.headerStyleInterpolator,
style: [
styles.floating,
isFloatHeaderAbsolute && [
// Without this, the header buttons won't be touchable on Android when headerTransparent: true
{ height: focusedHeaderHeight },
styles.absolute,
const floatingHeader =
headerMode === 'float' ? (
<React.Fragment key="header">
{renderHeader({
mode: 'float',
layout,
insets: { top, right, bottom, left },
scenes,
getPreviousScene: this.getPreviousScene,
getFocusedRoute: this.getFocusedRoute,
onContentHeightChange: this.handleHeaderLayout,
gestureDirection:
focusedOptions.gestureDirection !== undefined
? focusedOptions.gestureDirection
: defaultTransitionPreset.gestureDirection,
styleInterpolator:
focusedOptions.headerStyleInterpolator !== undefined
? focusedOptions.headerStyleInterpolator
: defaultTransitionPreset.headerStyleInterpolator,
style: [
styles.floating,
isFloatHeaderAbsolute && [
// Without this, the header buttons won't be touchable on Android when headerTransparent: true
{ height: focusedHeaderHeight },
styles.absolute,
],
],
],
})}
</React.Fragment>
);
})}
</React.Fragment>
) : null;
return (
<Background>
<React.Fragment>
{isFloatHeaderAbsolute ? null : floatingHeader}
<MaybeScreenContainer
enabled={detachInactiveScreens}
@@ -517,11 +540,9 @@ export default class CardStack extends React.Component<Props, State> {
}
const {
safeAreaInsets,
headerShown = true,
headerMode = 'screen',
headerTransparent,
headerStyle,
headerTintColor,
cardShadowEnabled,
cardOverlayEnabled = Platform.OS !== 'ios' || mode === 'modal',
cardOverlay,
@@ -577,27 +598,16 @@ export default class CardStack extends React.Component<Props, State> {
}
}
const safeAreaInsetTop = insets.top;
const safeAreaInsetRight = insets.right;
const safeAreaInsetBottom = insets.bottom;
const safeAreaInsetLeft = insets.left;
const {
top: safeAreaInsetTop = insets.top,
right: safeAreaInsetRight = insets.right,
bottom: safeAreaInsetBottom = insets.bottom,
left: safeAreaInsetLeft = insets.left,
} = safeAreaInsets || {};
const headerHeight =
headerShown !== false ? headerHeights[route.key] : 0;
const { backgroundColor: headerBackgroundColor } =
StyleSheet.flatten(headerStyle) || {};
let headerDarkContent: boolean | undefined;
if (headerShown) {
if (headerTintColor) {
headerDarkContent = Color(headerTintColor).isDark();
} else if (typeof headerBackgroundColor === 'string') {
headerDarkContent = !Color(headerBackgroundColor).isDark();
}
}
return (
<MaybeScreen
key={route.key}
@@ -637,8 +647,7 @@ export default class CardStack extends React.Component<Props, State> {
mode={mode}
headerMode={headerMode}
headerShown={headerShown}
headerDarkContent={headerDarkContent}
hasAbsoluteFloatHeader={
hasAbsoluteHeader={
isFloatHeaderAbsolute && !headerTransparent
}
renderHeader={renderHeader}
@@ -656,7 +665,7 @@ export default class CardStack extends React.Component<Props, State> {
})}
</MaybeScreenContainer>
{isFloatHeaderAbsolute ? floatingHeader : null}
</Background>
</React.Fragment>
);
}
}

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import { View, Platform, StyleSheet } from 'react-native';
import {
SafeAreaInsetsContext,
EdgeInsets,
@@ -439,6 +439,9 @@ export default class StackView extends React.Component<Props, State> {
navigation,
keyboardHandlingEnabled,
mode = 'card',
headerMode = mode === 'card' && Platform.OS === 'ios'
? 'float'
: 'screen',
// eslint-disable-next-line @typescript-eslint/no-unused-vars
descriptors: _,
...rest
@@ -476,6 +479,7 @@ export default class StackView extends React.Component<Props, State> {
onTransitionEnd={this.handleTransitionEnd}
renderHeader={this.renderHeader}
renderScene={this.renderScene}
headerMode={headerMode}
state={state}
descriptors={descriptors}
onGestureStart={this.handleGestureStart}

4534
yarn.lock

File diff suppressed because it is too large Load Diff