mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-20 19:08:15 +08:00
Compare commits
1 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41736641c1 |
72
.github/workflows/check-repro.yml
vendored
72
.github/workflows/check-repro.yml
vendored
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
6
.github/workflows/expo-preview.yml
vendored
6
.github/workflows/expo-preview.yml
vendored
@@ -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
|
||||
|
||||
|
||||
6
.github/workflows/expo.yml
vendored
6
.github/workflows/expo.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/triage.yml
vendored
2
.github/workflows/triage.yml
vendored
@@ -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:
|
||||
|
||||
@@ -2,6 +2,5 @@ module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ['babel-preset-expo'],
|
||||
plugins: ['react-native-reanimated/plugin'],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
2
example/src/LinkingPrefixes.expo.ts
Normal file
2
example/src/LinkingPrefixes.expo.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import * as Linking from 'expo-linking';
|
||||
export default [Linking.makeUrl('/')];
|
||||
1
example/src/LinkingPrefixes.ts
Normal file
1
example/src/LinkingPrefixes.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default ['rne://127.0.0.1:19000/--/'];
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
"version": "independent",
|
||||
"command": {
|
||||
"publish": {
|
||||
"allowBranch": "main",
|
||||
"allowBranch": "6.x",
|
||||
"conventionalCommits": true,
|
||||
"createRelease": "github",
|
||||
"distTag": "next",
|
||||
"message": "chore: publish",
|
||||
"ignoreChanges": [
|
||||
"**/__fixtures__/**",
|
||||
|
||||
13
package.json
13
package.json
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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'] : [],
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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' },
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -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 }) => [
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 & {
|
||||
|
||||
@@ -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' })}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 }],
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
})}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user