mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-11 22:33:32 +08:00
refactor: rewrite drawer layout with reanimated (#60)
This commit is contained in:
@@ -55,6 +55,12 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
destination: coverage
|
||||
build:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/project
|
||||
- run: yarn prepare
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
@@ -70,3 +76,6 @@ workflows:
|
||||
- unit-tests:
|
||||
requires:
|
||||
- install-dependencies
|
||||
- build:
|
||||
requires:
|
||||
- install-dependencies
|
||||
|
||||
5
packages/drawer/commitlint.config.js
Normal file
5
packages/drawer/commitlint.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
};
|
||||
@@ -1,5 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
"expo"
|
||||
],
|
||||
"plugins": [
|
||||
["module-resolver", {
|
||||
"alias": {
|
||||
"react-navigation-drawer": "../src/index"
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
{
|
||||
"extends": '../.eslintrc',
|
||||
'extends': '../.eslintrc',
|
||||
|
||||
"settings": {
|
||||
"import/core-modules": [ "react-navigation-drawer", "react-native-gesture-handler", "react-native-vector-icons" ]
|
||||
}
|
||||
'settings':
|
||||
{
|
||||
'import/core-modules':
|
||||
[
|
||||
'react-navigation-drawer',
|
||||
'react-native-gesture-handler',
|
||||
'react-native-reanimated',
|
||||
'react-native-vector-icons',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"eject": "expo eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^3.3.0",
|
||||
"@react-navigation/native": "^3.3.0",
|
||||
"@react-navigation/core": "^3.4.0",
|
||||
"@react-navigation/native": "^3.4.1",
|
||||
"expo": "32.0.6",
|
||||
"hoist-non-react-statics": "^3.3.0",
|
||||
"react": "16.5.0",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
Button,
|
||||
Dimensions,
|
||||
TextInput,
|
||||
@@ -17,6 +16,7 @@ import { createStackNavigator } from 'react-navigation-stack';
|
||||
import { SafeAreaView } from '@react-navigation/native';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
import { createDrawerNavigator } from 'react-navigation-drawer';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { KeepAwake } from 'expo';
|
||||
|
||||
const SampleText = ({ children }) => <Text>{children}</Text>;
|
||||
@@ -168,9 +168,9 @@ const DraftsStack = createStackNavigator(
|
||||
const DrawerContents = ({ drawerOpenProgress, navigation }) => {
|
||||
// `contentComponent` is passed an Animated.Value called drawerOpenProgress
|
||||
// that can be used to do interesting things like a simple parallax drawe
|
||||
const translateX = drawerOpenProgress.interpolate({
|
||||
const translateX = Animated.interpolate(drawerOpenProgress, {
|
||||
inputRange: [0, 1],
|
||||
outputRange: [-50, 0],
|
||||
outputRange: [-100, 0],
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -210,7 +210,6 @@ function createDrawerExample(options = {}) {
|
||||
{
|
||||
overlayColor: 'rgba(0,0,0,0)',
|
||||
drawerType: 'back',
|
||||
useNativeAnimations: true,
|
||||
contentContainerStyle: {
|
||||
shadowColor: '#000000',
|
||||
shadowOpacity: 0.4,
|
||||
|
||||
@@ -176,7 +176,7 @@ function createDrawerExample(options = {}) {
|
||||
},
|
||||
{
|
||||
initialRouteName: 'Drafts',
|
||||
drawerWidth: 210,
|
||||
drawerWidth: '60%',
|
||||
navigationOptions: {
|
||||
header: null,
|
||||
},
|
||||
|
||||
@@ -88,7 +88,7 @@ const DrawerExample = createDrawerNavigator(
|
||||
activeTintColor: '#e91e63',
|
||||
},
|
||||
drawerType: 'back',
|
||||
overlayColor: '#00000000',
|
||||
overlayColor: 'rgba(233, 30, 99, 0.5)',
|
||||
hideStatusBar: true,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -848,20 +848,20 @@
|
||||
pouchdb-collections "^1.0.1"
|
||||
tiny-queue "^0.2.1"
|
||||
|
||||
"@react-navigation/core@^3.3.0":
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-3.3.0.tgz#a8fa76e1c2a0da588da3d94ec9ea0956b7df753e"
|
||||
integrity sha512-jCtvNnJu6CBctIvaGzL82xedWG0IQv+URwZfKQSkoUgiFViSsUhoDWHgnoRXAlWvR8Js7au3hrC/Cwshwhi9/w==
|
||||
"@react-navigation/core@^3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-3.4.0.tgz#776845f9d4f8b2b9cb99c5d2d4433ebcef290d92"
|
||||
integrity sha512-YAnx9mK6P/zYkvn4YxZL6thaNdouSmD7FUaftFrOAbE7y7cCfH8hmk7BOLoOet6Sh2+UnrpkWX7Kg54cT2Jw+g==
|
||||
dependencies:
|
||||
hoist-non-react-statics "^2.5.5"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
path-to-regexp "^1.7.0"
|
||||
query-string "^6.2.0"
|
||||
react-is "^16.6.3"
|
||||
query-string "^6.4.2"
|
||||
react-is "^16.8.6"
|
||||
|
||||
"@react-navigation/native@^3.3.0":
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-3.3.0.tgz#def7a94ef17581a404a3de2a3200f986e999dac1"
|
||||
integrity sha512-w/+2B0qX441BpNkYb5QoPY8+Q4Q18adGTahVpc6o8Juj6odAxyIJ2RozXk7dCpN/w0dz4B+5ggqMKHVniE6K7w==
|
||||
"@react-navigation/native@^3.4.1":
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-3.4.1.tgz#e1fbf334ac834a9f10dd7d9c3af3e36939486089"
|
||||
integrity sha512-pMAPQfvwC4DvhQfsrXKAf+FiU+A5XAh216v17rEePSFcbeOEt7cvewmWxCxydN/vFjJChFiPV+xnjJyJBdPLOg==
|
||||
dependencies:
|
||||
hoist-non-react-statics "^3.0.1"
|
||||
react-native-safe-area-view "^0.13.0"
|
||||
@@ -3157,7 +3157,7 @@ has-values@^1.0.0:
|
||||
is-number "^3.0.0"
|
||||
kind-of "^4.0.0"
|
||||
|
||||
hoist-non-react-statics@2.5.0, hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.5, hoist-non-react-statics@^3.0.1, hoist-non-react-statics@^3.1.0:
|
||||
hoist-non-react-statics@2.5.0, hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^3.0.1, hoist-non-react-statics@^3.1.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40"
|
||||
integrity sha512-6Bl6XsDT1ntE0lHbIhr4Kp2PGcleGZ66qu5Jqk8lc0Xc/IeG6gVLmwUGs/K0Us+L8VWoKgj0uWdPMataOsm31w==
|
||||
@@ -4831,7 +4831,7 @@ qs@^6.5.0:
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
query-string@^6.2.0:
|
||||
query-string@^6.4.2:
|
||||
version "6.4.2"
|
||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.4.2.tgz#8be1dbd105306aebf86022144f575a29d516b713"
|
||||
integrity sha512-DfJqAen17LfLA3rQ+H5S4uXphrF+ANU1lT2ijds4V/Tj4gZxA3gx5/tg1bz7kYCmwna7LyJNCYqO7jNRzo3aLw==
|
||||
@@ -4882,7 +4882,7 @@ react-devtools-core@3.3.4:
|
||||
shell-quote "^1.6.1"
|
||||
ws "^3.3.1"
|
||||
|
||||
react-is@^16.6.3, react-is@^16.7.0:
|
||||
react-is@^16.7.0, react-is@^16.8.6:
|
||||
version "16.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
|
||||
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
|
||||
@@ -4906,7 +4906,7 @@ react-native-gesture-handler@~1.0.14:
|
||||
invariant "^2.2.2"
|
||||
prop-types "^15.5.10"
|
||||
|
||||
"react-native-maps@github:expo/react-native-maps#v0.22.1-exp.0":
|
||||
react-native-maps@expo/react-native-maps#v0.22.1-exp.0:
|
||||
version "0.22.1"
|
||||
resolved "https://codeload.github.com/expo/react-native-maps/tar.gz/e6f98ff7272e5d0a7fe974a41f28593af2d77bb2"
|
||||
|
||||
|
||||
@@ -1,16 +1,64 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
jest.mock('react-native-gesture-handler/DrawerLayout', () => {
|
||||
const React = require('react');
|
||||
const View = require.requireActual('View');
|
||||
const DrawerLayout = React.forwardRef((props, ref) => (
|
||||
<View {...props} ref={ref} />
|
||||
));
|
||||
import NativeModules from 'NativeModules';
|
||||
|
||||
DrawerLayout.positions = {
|
||||
Left: 'left',
|
||||
Right: 'right',
|
||||
};
|
||||
|
||||
return DrawerLayout;
|
||||
Object.assign(NativeModules, {
|
||||
RNGestureHandlerModule: {
|
||||
attachGestureHandler: jest.fn(),
|
||||
createGestureHandler: jest.fn(),
|
||||
dropGestureHandler: jest.fn(),
|
||||
updateGestureHandler: jest.fn(),
|
||||
State: {},
|
||||
Directions: {},
|
||||
},
|
||||
ReanimatedModule: {
|
||||
createNode: jest.fn(),
|
||||
configureProps: jest.fn(),
|
||||
configureNativeProps: jest.fn(),
|
||||
connectNodes: jest.fn(),
|
||||
disconnectNodes: jest.fn(),
|
||||
addListener: jest.fn(),
|
||||
removeListeners: jest.fn(),
|
||||
},
|
||||
PlatformConstants: {
|
||||
forceTouchAvailable: false,
|
||||
},
|
||||
});
|
||||
|
||||
jest.mock('react-native-reanimated', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
View: require('react-native').Animated.View,
|
||||
Text: require('react-native').Animated.Text,
|
||||
Clock: jest.fn(),
|
||||
Value: jest.fn(),
|
||||
onChange: jest.fn(),
|
||||
interpolate: jest.fn(),
|
||||
abs: jest.fn(),
|
||||
add: jest.fn(),
|
||||
sub: jest.fn(),
|
||||
and: jest.fn(),
|
||||
block: jest.fn(),
|
||||
call: jest.fn(),
|
||||
clockRunning: jest.fn(),
|
||||
cond: jest.fn(),
|
||||
divide: jest.fn(),
|
||||
eq: jest.fn(),
|
||||
event: jest.fn(),
|
||||
greaterThan: jest.fn(),
|
||||
lessThan: jest.fn(),
|
||||
max: jest.fn(),
|
||||
min: jest.fn(),
|
||||
multiply: jest.fn(),
|
||||
neq: jest.fn(),
|
||||
or: jest.fn(),
|
||||
set: jest.fn(),
|
||||
spring: jest.fn(),
|
||||
startClock: jest.fn(),
|
||||
stopClock: jest.fn(),
|
||||
timing: jest.fn(),
|
||||
},
|
||||
Easing: {
|
||||
out: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
"test": "jest",
|
||||
"lint": "eslint --ext .js,.ts,.tsx .",
|
||||
"typescript": "tsc --noEmit",
|
||||
"bootstrap": "yarn && yarn --cwd example",
|
||||
"example": "yarn --cwd example",
|
||||
"bootstrap": "yarn && yarn example",
|
||||
"prepare": "bob build"
|
||||
},
|
||||
"keywords": [
|
||||
@@ -38,30 +39,34 @@
|
||||
"homepage": "https://github.com/react-navigation/react-navigation-drawer#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.3",
|
||||
"@commitlint/config-conventional": "^7.5.0",
|
||||
"@expo/vector-icons": "^10.0.1",
|
||||
"@react-native-community/bob": "^0.3.3",
|
||||
"@react-navigation/core": "^3.3.0",
|
||||
"@react-navigation/native": "^3.3.0",
|
||||
"@react-native-community/bob": "^0.3.4",
|
||||
"@react-navigation/core": "^3.4.0",
|
||||
"@react-navigation/native": "^3.4.1",
|
||||
"@types/jest": "^24.0.11",
|
||||
"@types/react": "^16.8.13",
|
||||
"@types/react-native": "^0.57.43",
|
||||
"@types/react-native": "^0.57.49",
|
||||
"@types/react-test-renderer": "^16.8.1",
|
||||
"babel-jest": "^24.7.1",
|
||||
"commitlint": "^7.5.2",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-satya164": "^2.4.1",
|
||||
"eslint-plugin-react-native-globals": "^0.1.0",
|
||||
"husky": "^1.3.1",
|
||||
"jest": "^24.7.1",
|
||||
"prettier": "^1.16.4",
|
||||
"prettier": "^1.17.0",
|
||||
"react": "16.5.0",
|
||||
"react-dom": "16.5.0",
|
||||
"react-lifecycles-compat": "^3.0.4",
|
||||
"react-native": "~0.57.1",
|
||||
"react-native-gesture-handler": "^1.1.0",
|
||||
"react-native-reanimated": "^1.0.1",
|
||||
"react-native-screens": "^1.0.0-alpha.22",
|
||||
"react-native-testing-library": "^1.7.0",
|
||||
"react-test-renderer": "16.8.6",
|
||||
"typescript": "^3.4.3"
|
||||
"typescript": "^3.4.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/core": "^3.0.0",
|
||||
@@ -69,6 +74,7 @@
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": "^1.0.12",
|
||||
"react-native-reanimated": "^1.0.0",
|
||||
"react-native-screens": "^1.0.0 || ^1.0.0-alpha"
|
||||
},
|
||||
"jest": {
|
||||
@@ -97,7 +103,8 @@
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "yarn lint && yarn typescript && yarn test"
|
||||
"pre-commit": "yarn lint && yarn typescript && yarn test",
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
@@ -105,7 +112,8 @@
|
||||
"output": "lib",
|
||||
"targets": [
|
||||
"commonjs",
|
||||
"module"
|
||||
"module",
|
||||
"typescript"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import * as DrawerAcions from './routers/DrawerActions';
|
||||
|
||||
/**
|
||||
* Navigators
|
||||
*/
|
||||
/**
|
||||
* Router
|
||||
*/
|
||||
import * as DrawerAcions from './routers/DrawerActions';
|
||||
|
||||
export {
|
||||
default as createDrawerNavigator,
|
||||
} from './navigators/createDrawerNavigator';
|
||||
|
||||
/**
|
||||
* Router
|
||||
*/
|
||||
export { DrawerAcions };
|
||||
export { default as DrawerRouter } from './routers/DrawerRouter';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { render } from 'react-native-testing-library';
|
||||
import { createAppContainer } from '@react-navigation/native';
|
||||
|
||||
import createDrawerNavigator from '../createDrawerNavigator';
|
||||
@@ -21,62 +21,63 @@ class HomeScreen extends React.Component {
|
||||
it('renders successfully', () => {
|
||||
const MyDrawerNavigator = createDrawerNavigator({ Home: HomeScreen });
|
||||
const App = createAppContainer(MyDrawerNavigator);
|
||||
const rendered = renderer.create(<App />).toJSON();
|
||||
const rendered = render(<App />).toJSON();
|
||||
|
||||
expect(rendered).toMatchInlineSnapshot(`
|
||||
<RCTView
|
||||
drawerBackgroundColor="white"
|
||||
drawerPosition="left"
|
||||
drawerType="front"
|
||||
drawerWidth={320}
|
||||
hideStatusBar={false}
|
||||
keyboardDismissMode="on-drag"
|
||||
onDrawerClose={[Function]}
|
||||
onDrawerOpen={[Function]}
|
||||
onDrawerStateChanged={[Function]}
|
||||
onGestureRef={[Function]}
|
||||
overlayColor="black"
|
||||
renderNavigationView={[Function]}
|
||||
statusBarAnimation="slide"
|
||||
useNativeAnimations={true}
|
||||
<View
|
||||
collapsable={false}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"transform": Array [
|
||||
Object {
|
||||
"translateX": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
pointerEvents="auto"
|
||||
removeClippedSubviews={false}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
},
|
||||
Object {
|
||||
"opacity": 1,
|
||||
},
|
||||
],
|
||||
]
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
pointerEvents="auto"
|
||||
removeClippedSubviews={false}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
},
|
||||
Object {
|
||||
"opacity": 1,
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
@@ -85,10 +86,150 @@ it('renders successfully', () => {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
collapsable={false}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(0, 0, 0, 0.5)",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"opacity": undefined,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
"zIndex": undefined,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</RCTView>
|
||||
<View
|
||||
accessibilityViewIsModal={false}
|
||||
onLayout={[Function]}
|
||||
removeClippedSubviews={false}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "white",
|
||||
"bottom": 0,
|
||||
"left": undefined,
|
||||
"maxWidth": "100%",
|
||||
"opacity": Object {},
|
||||
"position": "absolute",
|
||||
"top": 0,
|
||||
"transform": Array [
|
||||
Object {
|
||||
"translateX": undefined,
|
||||
},
|
||||
],
|
||||
"width": 320,
|
||||
"zIndex": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<RCTScrollView
|
||||
alwaysBounceVertical={false}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"paddingVertical": 4,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessibilityLabel="Welcome anonymous"
|
||||
accessible={true}
|
||||
isTVSelectable={true}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "rgba(0, 0, 0, .04)",
|
||||
"flexDirection": "row",
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontWeight": "bold",
|
||||
"margin": 16,
|
||||
},
|
||||
Object {
|
||||
"color": "#2196f3",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
Welcome anonymous
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RCTScrollView>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Dimensions, Platform, ScrollView } from 'react-native';
|
||||
import { Dimensions, Platform, ScrollView, I18nManager } from 'react-native';
|
||||
import { createNavigator } from '@react-navigation/core';
|
||||
import { SafeAreaView } from '@react-navigation/native';
|
||||
import DrawerRouter from '../routers/DrawerRouter';
|
||||
@@ -35,14 +35,12 @@ const DefaultDrawerConfig = {
|
||||
return Math.min(smallerAxisSize - appBarHeight, maxWidth);
|
||||
},
|
||||
contentComponent: defaultContentComponent,
|
||||
drawerPosition: 'left',
|
||||
drawerPosition: I18nManager.isRTL ? 'right' : 'left',
|
||||
keyboardDismissMode: 'on-drag',
|
||||
drawerBackgroundColor: 'white',
|
||||
useNativeAnimations: true,
|
||||
drawerType: 'front',
|
||||
hideStatusBar: false,
|
||||
statusBarAnimation: 'slide',
|
||||
overlayColor: 'black',
|
||||
};
|
||||
|
||||
const DrawerNavigator = (routeConfigs: object, config: any = {}) => {
|
||||
|
||||
@@ -10,12 +10,7 @@ export const MARK_DRAWER_IDLE = 'Navigation/MARK_DRAWER_IDLE';
|
||||
export type DrawerActionType =
|
||||
| typeof OPEN_DRAWER
|
||||
| typeof CLOSE_DRAWER
|
||||
| typeof TOGGLE_DRAWER
|
||||
| typeof DRAWER_OPENED
|
||||
| typeof DRAWER_CLOSED
|
||||
| typeof MARK_DRAWER_ACTIVE
|
||||
| typeof MARK_DRAWER_SETTLING
|
||||
| typeof MARK_DRAWER_IDLE;
|
||||
| typeof TOGGLE_DRAWER;
|
||||
|
||||
export const openDrawer = (payload?: any) => ({
|
||||
type: OPEN_DRAWER,
|
||||
@@ -27,21 +22,6 @@ export const closeDrawer = (payload?: any) => ({
|
||||
...payload,
|
||||
});
|
||||
|
||||
export const markDrawerActive = (payload?: any) => ({
|
||||
type: MARK_DRAWER_ACTIVE,
|
||||
...payload,
|
||||
});
|
||||
|
||||
export const markDrawerIdle = (payload?: any) => ({
|
||||
type: MARK_DRAWER_IDLE,
|
||||
...payload,
|
||||
});
|
||||
|
||||
export const markDrawerSettling = (payload?: any) => ({
|
||||
type: MARK_DRAWER_SETTLING,
|
||||
...payload,
|
||||
});
|
||||
|
||||
export const toggleDrawer = (payload?: any) => ({
|
||||
type: TOGGLE_DRAWER,
|
||||
...payload,
|
||||
|
||||
@@ -15,8 +15,6 @@ type Action = {
|
||||
|
||||
type State = Route & {
|
||||
isDrawerOpen?: any;
|
||||
isDrawerIdle?: any;
|
||||
drawerMovementDirection?: any;
|
||||
};
|
||||
|
||||
function withDefaultValue(obj: object, key: string, defaultValue: any): any {
|
||||
@@ -60,12 +58,6 @@ export default (
|
||||
|
||||
const switchRouter = SwitchRouter(routeConfigs, config);
|
||||
|
||||
let __id = -1;
|
||||
const genId = () => {
|
||||
__id++;
|
||||
return __id;
|
||||
};
|
||||
|
||||
return {
|
||||
...switchRouter,
|
||||
|
||||
@@ -84,11 +76,6 @@ export default (
|
||||
return {
|
||||
...switchRouter.getStateForAction(action, undefined),
|
||||
isDrawerOpen: false,
|
||||
isDrawerIdle: true,
|
||||
drawerMovementDirection: null,
|
||||
openId: genId(),
|
||||
closeId: genId(),
|
||||
toggleId: genId(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -96,78 +83,27 @@ export default (
|
||||
|
||||
if (isRouterTargeted) {
|
||||
// Only handle actions that are meant for this drawer, as specified by action.key.
|
||||
|
||||
if (action.type === DrawerActions.DRAWER_CLOSED) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerOpen: false,
|
||||
isDrawerIdle: true,
|
||||
drawerMovementDirection: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === DrawerActions.DRAWER_OPENED) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerOpen: true,
|
||||
isDrawerIdle: true,
|
||||
drawerMovementDirection: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === DrawerActions.CLOSE_DRAWER) {
|
||||
return {
|
||||
...state,
|
||||
closeId: genId(),
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === DrawerActions.MARK_DRAWER_SETTLING) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerIdle: false,
|
||||
drawerMovementDirection: action.willShow ? 'opening' : 'closing',
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === DrawerActions.MARK_DRAWER_ACTIVE) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerIdle: false,
|
||||
drawerMovementDirection: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === DrawerActions.MARK_DRAWER_IDLE) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerIdle: true,
|
||||
drawerMovementDirection: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
action.type === NavigationActions.BACK &&
|
||||
(state.isDrawerOpen || !state.isDrawerIdle) &&
|
||||
state.drawerMovementDirection !== 'closing'
|
||||
action.type === DrawerActions.CLOSE_DRAWER ||
|
||||
(action.type === NavigationActions.BACK && state.isDrawerOpen)
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
closeId: genId(),
|
||||
isDrawerOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === DrawerActions.OPEN_DRAWER) {
|
||||
return {
|
||||
...state,
|
||||
openId: genId(),
|
||||
isDrawerOpen: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === DrawerActions.TOGGLE_DRAWER) {
|
||||
return {
|
||||
...state,
|
||||
toggleId: genId(),
|
||||
isDrawerOpen: !state.isDrawerOpen,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -185,11 +121,11 @@ export default (
|
||||
// If any navigation has happened, and the drawer is maybe open, make sure to close it
|
||||
if (
|
||||
getActiveRouteKey(switchedState) !== getActiveRouteKey(state) &&
|
||||
(state.isDrawerOpen || state.drawerMovementDirection !== 'closing')
|
||||
state.isDrawerOpen
|
||||
) {
|
||||
return {
|
||||
...switchedState,
|
||||
closeId: genId(),
|
||||
isDrawerOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,143 +12,136 @@ import * as DrawerActions from '../../routers/DrawerActions';
|
||||
|
||||
const INIT_ACTION = { type: NavigationActions.INIT };
|
||||
|
||||
describe('DrawerRouter', () => {
|
||||
it('Handles basic drawer logic and fires close on switch', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
const router = DrawerRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
Bar: { screen: ScreenB },
|
||||
});
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
const expectedState = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo', params: undefined },
|
||||
{ key: 'Bar', routeName: 'Bar', params: undefined },
|
||||
],
|
||||
isDrawerOpen: false,
|
||||
isDrawerIdle: true,
|
||||
drawerMovementDirection: null,
|
||||
openId: 0,
|
||||
closeId: 1,
|
||||
toggleId: 2,
|
||||
};
|
||||
expect(state).toEqual(expectedState);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
state
|
||||
);
|
||||
const expectedState2 = {
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo', params: undefined },
|
||||
{ key: 'Bar', routeName: 'Bar', params: undefined },
|
||||
],
|
||||
isDrawerOpen: false,
|
||||
isDrawerIdle: true,
|
||||
drawerMovementDirection: null,
|
||||
openId: 0,
|
||||
closeId: 3,
|
||||
toggleId: 2,
|
||||
};
|
||||
expect(state2).toEqual(expectedState2);
|
||||
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
|
||||
expect(router.getComponentForState(expectedState2)).toEqual(ScreenB);
|
||||
it('handles basic drawer logic and fires close on switch', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
const router = DrawerRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
Bar: { screen: ScreenB },
|
||||
});
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
const expectedState = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo', params: undefined },
|
||||
{ key: 'Bar', routeName: 'Bar', params: undefined },
|
||||
],
|
||||
isDrawerOpen: false,
|
||||
};
|
||||
expect(state).toEqual(expectedState);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
state
|
||||
);
|
||||
const expectedState2 = {
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo', params: undefined },
|
||||
{ key: 'Bar', routeName: 'Bar', params: undefined },
|
||||
],
|
||||
isDrawerOpen: false,
|
||||
};
|
||||
expect(state2).toEqual(expectedState2);
|
||||
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
|
||||
expect(router.getComponentForState(expectedState2)).toEqual(ScreenB);
|
||||
});
|
||||
|
||||
it('Handles initial route navigation', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = () => <div />;
|
||||
const router = DrawerRouter(
|
||||
{
|
||||
Foo: {
|
||||
screen: FooScreen,
|
||||
},
|
||||
Bar: {
|
||||
screen: BarScreen,
|
||||
},
|
||||
it('handles initial route navigation', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = () => <div />;
|
||||
const router = DrawerRouter(
|
||||
{
|
||||
Foo: {
|
||||
screen: FooScreen,
|
||||
},
|
||||
{ initialRouteName: 'Bar' }
|
||||
);
|
||||
const state = router.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
});
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isDrawerOpen: false,
|
||||
isDrawerIdle: true,
|
||||
drawerMovementDirection: null,
|
||||
isTransitioning: false,
|
||||
openId: 0,
|
||||
closeId: 1,
|
||||
toggleId: 2,
|
||||
routes: [
|
||||
{
|
||||
key: 'Foo',
|
||||
params: undefined,
|
||||
routeName: 'Foo',
|
||||
},
|
||||
{
|
||||
key: 'Bar',
|
||||
params: undefined,
|
||||
routeName: 'Bar',
|
||||
},
|
||||
],
|
||||
});
|
||||
Bar: {
|
||||
screen: BarScreen,
|
||||
},
|
||||
},
|
||||
{ initialRouteName: 'Bar' }
|
||||
);
|
||||
const state = router.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
});
|
||||
|
||||
it('Drawer opens closes and toggles', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
const router = DrawerRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
Bar: { screen: ScreenB },
|
||||
});
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
expect(state.toggleId).toEqual(2);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: DrawerActions.OPEN_DRAWER },
|
||||
state
|
||||
);
|
||||
expect(state2.openId).toEqual(3);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: DrawerActions.CLOSE_DRAWER },
|
||||
state2
|
||||
);
|
||||
expect(state3.closeId).toEqual(4);
|
||||
const state4 = router.getStateForAction(
|
||||
{ type: DrawerActions.TOGGLE_DRAWER },
|
||||
state3
|
||||
);
|
||||
expect(state4.toggleId).toEqual(5);
|
||||
});
|
||||
|
||||
it('Drawer opens closes with key targeted', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
const router = DrawerRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
Bar: { screen: ScreenB },
|
||||
});
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: DrawerActions.OPEN_DRAWER, key: 'wrong' },
|
||||
state
|
||||
);
|
||||
expect(state2.openId).toEqual(0);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: DrawerActions.OPEN_DRAWER, key: state.key },
|
||||
state2
|
||||
);
|
||||
expect(state3.openId).toEqual(3);
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isDrawerOpen: false,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
key: 'Foo',
|
||||
params: undefined,
|
||||
routeName: 'Foo',
|
||||
},
|
||||
{
|
||||
key: 'Bar',
|
||||
params: undefined,
|
||||
routeName: 'Bar',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('Nested routers bubble up blocked actions', () => {
|
||||
it('drawer opens, closes and toggles', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
const router = DrawerRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
Bar: { screen: ScreenB },
|
||||
});
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
|
||||
expect(state.isDrawerOpen).toEqual(false);
|
||||
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: DrawerActions.OPEN_DRAWER },
|
||||
state
|
||||
);
|
||||
|
||||
expect(state2.isDrawerOpen).toEqual(true);
|
||||
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: DrawerActions.CLOSE_DRAWER },
|
||||
state2
|
||||
);
|
||||
|
||||
expect(state3.isDrawerOpen).toEqual(false);
|
||||
|
||||
const state4 = router.getStateForAction(
|
||||
{ type: DrawerActions.TOGGLE_DRAWER },
|
||||
state3
|
||||
);
|
||||
|
||||
expect(state4.isDrawerOpen).toEqual(true);
|
||||
});
|
||||
|
||||
it('drawer opens, closes with key targeted', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
const router = DrawerRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
Bar: { screen: ScreenB },
|
||||
});
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: DrawerActions.OPEN_DRAWER, key: 'wrong' },
|
||||
state
|
||||
);
|
||||
|
||||
expect(state2.isDrawerOpen).toEqual(false);
|
||||
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: DrawerActions.OPEN_DRAWER, key: state.key },
|
||||
state2
|
||||
);
|
||||
|
||||
expect(state3.isDrawerOpen).toEqual(true);
|
||||
});
|
||||
|
||||
it('nested routers bubble up blocked actions', () => {
|
||||
const ScreenA = () => <div />;
|
||||
ScreenA.router = {
|
||||
getStateForAction(action: { type: string }, lastState: any) {
|
||||
@@ -167,7 +160,7 @@ it('Nested routers bubble up blocked actions', () => {
|
||||
expect(state2).toEqual(null);
|
||||
});
|
||||
|
||||
it('Drawer does not fire close when child routers return new state', () => {
|
||||
it('drawer does not fire close when child routers return new state', () => {
|
||||
const ScreenA = () => <div />;
|
||||
ScreenA.router = {
|
||||
getStateForAction(
|
||||
@@ -184,14 +177,14 @@ it('Drawer does not fire close when child routers return new state', () => {
|
||||
});
|
||||
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
expect(state.closeId).toEqual(1);
|
||||
expect(state.isDrawerOpen).toEqual(false);
|
||||
|
||||
const state2 = router.getStateForAction({ type: 'CHILD_ACTION' }, state);
|
||||
expect(state2.closeId).toEqual(1);
|
||||
expect(state2.isDrawerOpen).toEqual(false);
|
||||
expect(state2.routes[0].changed).toEqual(true);
|
||||
});
|
||||
|
||||
it('DrawerRouter will close drawer on child navigaton, not on child param changes', () => {
|
||||
it('drawerRouter will close drawer on child navigaton, not on child param changes', () => {
|
||||
class FooView extends React.Component {
|
||||
render() {
|
||||
return <div />;
|
||||
@@ -217,13 +210,13 @@ it('DrawerRouter will close drawer on child navigaton, not on child param change
|
||||
DrawerActions.openDrawer(),
|
||||
emptyState
|
||||
);
|
||||
expect(initState.openId).toBe(3);
|
||||
expect(initState.isDrawerOpen).toBe(true);
|
||||
|
||||
const state0 = router.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'Quo' }),
|
||||
initState
|
||||
);
|
||||
expect(state0.closeId).toBe(4);
|
||||
expect(state0.isDrawerOpen).toBe(false);
|
||||
|
||||
const initSwitchState = initState.routes[initState.index];
|
||||
const initQuxState = initSwitchState.routes[initSwitchState.index];
|
||||
@@ -237,7 +230,7 @@ it('DrawerRouter will close drawer on child navigaton, not on child param change
|
||||
);
|
||||
const state1switchState = state1.routes[state1.index];
|
||||
const state1quxState = state1switchState.routes[state1switchState.index];
|
||||
expect(state1.closeId).toBe(1); // don't fire close
|
||||
expect(state1.isDrawerOpen).toBe(true); // don't fire close
|
||||
expect(state1quxState.params.foo).toEqual('bar');
|
||||
});
|
||||
|
||||
@@ -266,12 +259,6 @@ it('goBack closes drawer when inside of stack', () => {
|
||||
);
|
||||
expect(state3.index).toEqual(1);
|
||||
expect(state3.routes[1].isDrawerOpen).toEqual(true);
|
||||
expect(state3.routes[1].closeId).toEqual(1); // changed
|
||||
const state4 = router.getStateForAction(NavigationActions.back(), state3);
|
||||
expect(state4.routes[1].closeId).toEqual(4);
|
||||
const state5 = router.getStateForAction(
|
||||
{ type: DrawerActions.DRAWER_CLOSED },
|
||||
state4
|
||||
);
|
||||
expect(state5.routes[1].isDrawerOpen).toEqual(false);
|
||||
expect(state4.routes[1].isDrawerOpen).toEqual(false);
|
||||
});
|
||||
|
||||
@@ -17,10 +17,6 @@ export type Navigation = {
|
||||
key: string;
|
||||
index: number;
|
||||
routes: Route[];
|
||||
openId: string;
|
||||
closeId: string;
|
||||
toggleId: string;
|
||||
isDrawerIdle: boolean;
|
||||
isDrawerOpen: boolean;
|
||||
};
|
||||
openDrawer: () => void;
|
||||
|
||||
586
packages/drawer/src/views/Drawer.tsx
Normal file
586
packages/drawer/src/views/Drawer.tsx
Normal file
@@ -0,0 +1,586 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
ViewStyle,
|
||||
LayoutChangeEvent,
|
||||
I18nManager,
|
||||
Platform,
|
||||
Keyboard,
|
||||
StatusBar,
|
||||
} from 'react-native';
|
||||
import {
|
||||
PanGestureHandler,
|
||||
TapGestureHandler,
|
||||
State,
|
||||
TapGestureHandlerStateChangeEvent,
|
||||
} from 'react-native-gesture-handler';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
const {
|
||||
Clock,
|
||||
Value,
|
||||
onChange,
|
||||
clockRunning,
|
||||
startClock,
|
||||
stopClock,
|
||||
interpolate,
|
||||
spring,
|
||||
abs,
|
||||
add,
|
||||
and,
|
||||
block,
|
||||
call,
|
||||
cond,
|
||||
divide,
|
||||
eq,
|
||||
event,
|
||||
greaterThan,
|
||||
lessThan,
|
||||
max,
|
||||
min,
|
||||
multiply,
|
||||
neq,
|
||||
or,
|
||||
set,
|
||||
sub,
|
||||
} = Animated;
|
||||
|
||||
const TRUE = 1;
|
||||
const FALSE = 0;
|
||||
const NOOP = 0;
|
||||
const UNSET = -1;
|
||||
|
||||
const PROGRESS_EPSILON = 0.05;
|
||||
|
||||
const DIRECTION_LEFT = 1;
|
||||
const DIRECTION_RIGHT = -1;
|
||||
|
||||
const SWIPE_DISTANCE_THRESHOLD_DEFAULT = 60;
|
||||
|
||||
const SWIPE_DISTANCE_MINIMUM = 5;
|
||||
|
||||
const SPRING_CONFIG = {
|
||||
damping: 30,
|
||||
mass: 0.5,
|
||||
stiffness: 150,
|
||||
overshootClamping: true,
|
||||
restSpeedThreshold: 0.001,
|
||||
restDisplacementThreshold: 0.001,
|
||||
};
|
||||
|
||||
type Binary = 0 | 1;
|
||||
|
||||
type Renderer = (props: { progress: Animated.Node<number> }) => React.ReactNode;
|
||||
|
||||
type Props = {
|
||||
open: boolean;
|
||||
onOpen: () => void;
|
||||
onClose: () => void;
|
||||
onGestureRef?: (ref: PanGestureHandler | null) => void;
|
||||
locked: boolean;
|
||||
drawerPosition: 'left' | 'right';
|
||||
drawerType: 'front' | 'back' | 'slide';
|
||||
keyboardDismissMode: 'none' | 'on-drag';
|
||||
swipeEdgeWidth: number;
|
||||
swipeDistanceThreshold?: number;
|
||||
swipeVelocityThreshold: number;
|
||||
hideStatusBar: boolean;
|
||||
statusBarAnimation: 'slide' | 'none' | 'fade';
|
||||
overlayStyle?: ViewStyle;
|
||||
drawerStyle?: ViewStyle;
|
||||
contentContainerStyle?: ViewStyle;
|
||||
renderDrawerContent: Renderer;
|
||||
renderSceneContent: Renderer;
|
||||
};
|
||||
|
||||
export default class DrawerView extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
locked: false,
|
||||
drawerPostion: I18nManager.isRTL ? 'left' : 'right',
|
||||
drawerType: 'front',
|
||||
swipeEdgeWidth: 32,
|
||||
swipeVelocityThreshold: 500,
|
||||
keyboardDismissMode: 'on-drag',
|
||||
hideStatusBar: false,
|
||||
statusBarAnimation: 'slide',
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const {
|
||||
open,
|
||||
drawerPosition,
|
||||
drawerType,
|
||||
swipeDistanceThreshold,
|
||||
swipeVelocityThreshold,
|
||||
hideStatusBar,
|
||||
} = this.props;
|
||||
|
||||
if (
|
||||
// If we're not in the middle of a transition, sync the drawer's open state
|
||||
typeof this.pendingOpenValue !== 'boolean' ||
|
||||
open !== this.pendingOpenValue
|
||||
) {
|
||||
this.toggleDrawer(open);
|
||||
}
|
||||
|
||||
this.pendingOpenValue = undefined;
|
||||
|
||||
if (open !== prevProps.open && hideStatusBar) {
|
||||
this.toggleStatusBar(open);
|
||||
}
|
||||
|
||||
if (prevProps.drawerPosition !== drawerPosition) {
|
||||
this.drawerPosition.setValue(
|
||||
drawerPosition === 'right' ? DIRECTION_RIGHT : DIRECTION_LEFT
|
||||
);
|
||||
}
|
||||
|
||||
if (prevProps.drawerType !== drawerType) {
|
||||
this.isDrawerTypeFront.setValue(drawerType === 'front' ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
if (prevProps.swipeDistanceThreshold !== swipeDistanceThreshold) {
|
||||
this.swipeDistanceThreshold.setValue(
|
||||
swipeDistanceThreshold !== undefined
|
||||
? swipeDistanceThreshold
|
||||
: SWIPE_DISTANCE_THRESHOLD_DEFAULT
|
||||
);
|
||||
}
|
||||
|
||||
if (prevProps.swipeVelocityThreshold !== swipeVelocityThreshold) {
|
||||
this.swipeVelocityThreshold.setValue(swipeVelocityThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.toggleStatusBar(false);
|
||||
}
|
||||
|
||||
private clock = new Clock();
|
||||
|
||||
private isDrawerTypeFront = new Value<Binary>(
|
||||
this.props.drawerType === 'front' ? TRUE : FALSE
|
||||
);
|
||||
|
||||
private isOpen = new Value<Binary>(this.props.open ? TRUE : FALSE);
|
||||
private nextIsOpen = new Value<Binary | -1>(UNSET);
|
||||
private isSwiping = new Value<Binary>(FALSE);
|
||||
|
||||
private gestureState = new Value<number>(State.UNDETERMINED);
|
||||
private touchX = new Value<number>(0);
|
||||
private velocityX = new Value<number>(0);
|
||||
private gestureX = new Value<number>(0);
|
||||
private offsetX = new Value<number>(0);
|
||||
private position = new Value<number>(0);
|
||||
|
||||
private containerWidth = new Value<number>(0);
|
||||
private drawerWidth = new Value<number>(0);
|
||||
private drawerOpacity = new Value<number>(0);
|
||||
private drawerPosition = new Value<number>(
|
||||
this.props.drawerPosition === 'right' ? DIRECTION_RIGHT : DIRECTION_LEFT
|
||||
);
|
||||
|
||||
// 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 dragX. 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"
|
||||
private touchDistanceFromDrawer = cond(
|
||||
this.isDrawerTypeFront,
|
||||
cond(
|
||||
eq(this.drawerPosition, DIRECTION_LEFT),
|
||||
max(
|
||||
// Distance of touch start from left screen edge - Drawer width
|
||||
sub(sub(this.touchX, this.gestureX), this.drawerWidth),
|
||||
0
|
||||
),
|
||||
min(
|
||||
multiply(
|
||||
// Distance of drawer from left screen edge - Touch start point
|
||||
sub(
|
||||
sub(this.containerWidth, this.drawerWidth),
|
||||
sub(this.touchX, this.gestureX)
|
||||
),
|
||||
DIRECTION_RIGHT
|
||||
),
|
||||
0
|
||||
)
|
||||
),
|
||||
0
|
||||
);
|
||||
|
||||
private swipeDistanceThreshold = new Value<number>(
|
||||
this.props.swipeDistanceThreshold !== undefined
|
||||
? this.props.swipeDistanceThreshold
|
||||
: SWIPE_DISTANCE_THRESHOLD_DEFAULT
|
||||
);
|
||||
private swipeVelocityThreshold = new Value<number>(
|
||||
this.props.swipeVelocityThreshold
|
||||
);
|
||||
|
||||
private currentOpenValue: boolean = this.props.open;
|
||||
private pendingOpenValue: boolean | undefined;
|
||||
|
||||
private isStatusBarHidden: boolean = false;
|
||||
|
||||
private transitionTo = (isOpen: number | Animated.Node<number>) => {
|
||||
const toValue = new Value(0);
|
||||
const frameTime = new Value(0);
|
||||
|
||||
const state = {
|
||||
position: this.position,
|
||||
time: new Value(0),
|
||||
finished: new Value(FALSE),
|
||||
};
|
||||
|
||||
return block([
|
||||
cond(clockRunning(this.clock), NOOP, [
|
||||
// Animation wasn't running before
|
||||
// Set the initial values and start the clock
|
||||
set(toValue, multiply(isOpen, this.drawerWidth, this.drawerPosition)),
|
||||
set(frameTime, 0),
|
||||
set(state.time, 0),
|
||||
set(state.finished, FALSE),
|
||||
set(this.isOpen, isOpen),
|
||||
startClock(this.clock),
|
||||
]),
|
||||
spring(
|
||||
this.clock,
|
||||
{ ...state, velocity: this.velocityX },
|
||||
{ ...SPRING_CONFIG, toValue }
|
||||
),
|
||||
cond(state.finished, [
|
||||
// Reset gesture and velocity from previous gesture
|
||||
set(this.touchX, 0),
|
||||
set(this.gestureX, 0),
|
||||
set(this.velocityX, 0),
|
||||
set(this.offsetX, 0),
|
||||
// When the animation finishes, stop the clock
|
||||
stopClock(this.clock),
|
||||
call([this.isOpen], ([value]: ReadonlyArray<Binary>) => {
|
||||
const open = Boolean(value);
|
||||
|
||||
if (open !== this.props.open) {
|
||||
// Sync drawer's state after animation finished
|
||||
// This shouldn't be necessary, but there seems to be an issue on iOS
|
||||
this.toggleDrawer(this.props.open);
|
||||
}
|
||||
}),
|
||||
]),
|
||||
]);
|
||||
};
|
||||
|
||||
private dragX = block([
|
||||
onChange(
|
||||
this.isOpen,
|
||||
call([this.isOpen], ([value]: ReadonlyArray<Binary>) => {
|
||||
const open = Boolean(value);
|
||||
|
||||
this.currentOpenValue = open;
|
||||
|
||||
// Without this check, the drawer can go to an infinite update <-> animate loop for sync updates
|
||||
if (open !== this.props.open) {
|
||||
// If the mode changed, update state
|
||||
if (open) {
|
||||
this.props.onOpen();
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
this.pendingOpenValue = open;
|
||||
|
||||
// Force componentDidUpdate to fire, whether user does a setState or not
|
||||
// This allows us to detect when the user drops the update and revert back
|
||||
// It's necessary to make sure that the state stays in sync
|
||||
this.forceUpdate();
|
||||
}
|
||||
})
|
||||
),
|
||||
onChange(
|
||||
this.nextIsOpen,
|
||||
cond(neq(this.nextIsOpen, UNSET), [
|
||||
// Stop any running animations
|
||||
cond(clockRunning(this.clock), stopClock(this.clock)),
|
||||
// Update the open value to trigger the transition
|
||||
set(this.isOpen, this.nextIsOpen),
|
||||
set(this.nextIsOpen, UNSET),
|
||||
])
|
||||
),
|
||||
// This block must be after the this.isOpen listener since we check for current value
|
||||
onChange(
|
||||
this.isSwiping,
|
||||
// Listen to updates for this value only when it changes
|
||||
// Without `onChange`, this will fire even if the value didn't change
|
||||
// We don't want to call the listeners if the value didn't change
|
||||
call([this.isSwiping], ([value]: ReadonlyArray<Binary>) => {
|
||||
const { keyboardDismissMode } = this.props;
|
||||
|
||||
if (value === TRUE) {
|
||||
if (keyboardDismissMode === 'on-drag') {
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
|
||||
this.toggleStatusBar(true);
|
||||
} else {
|
||||
this.toggleStatusBar(this.currentOpenValue);
|
||||
}
|
||||
})
|
||||
),
|
||||
cond(
|
||||
eq(this.gestureState, State.ACTIVE),
|
||||
[
|
||||
cond(this.isSwiping, NOOP, [
|
||||
// We weren't dragging before, set it to true
|
||||
set(this.isSwiping, TRUE),
|
||||
// Also update the drag offset to the last position
|
||||
set(this.offsetX, this.position),
|
||||
]),
|
||||
// Update position with previous offset + gesture distance
|
||||
set(
|
||||
this.position,
|
||||
add(this.offsetX, this.gestureX, this.touchDistanceFromDrawer)
|
||||
),
|
||||
// Stop animations while we're dragging
|
||||
stopClock(this.clock),
|
||||
],
|
||||
[
|
||||
set(this.isSwiping, FALSE),
|
||||
set(this.touchX, 0),
|
||||
this.transitionTo(
|
||||
cond(
|
||||
or(
|
||||
and(
|
||||
greaterThan(abs(this.gestureX), SWIPE_DISTANCE_MINIMUM),
|
||||
greaterThan(abs(this.velocityX), this.swipeVelocityThreshold)
|
||||
),
|
||||
greaterThan(abs(this.gestureX), this.swipeDistanceThreshold)
|
||||
),
|
||||
cond(
|
||||
eq(this.drawerPosition, DIRECTION_LEFT),
|
||||
// If swiped to right, open the drawer, otherwise close it
|
||||
greaterThan(
|
||||
cond(eq(this.velocityX, 0), this.gestureX, this.velocityX),
|
||||
0
|
||||
),
|
||||
// If swiped to left, open the drawer, otherwise close it
|
||||
lessThan(
|
||||
cond(eq(this.velocityX, 0), this.gestureX, this.velocityX),
|
||||
0
|
||||
)
|
||||
),
|
||||
this.isOpen
|
||||
)
|
||||
),
|
||||
]
|
||||
),
|
||||
this.position,
|
||||
]);
|
||||
|
||||
private translateX = cond(
|
||||
eq(this.drawerPosition, DIRECTION_RIGHT),
|
||||
min(max(multiply(this.drawerWidth, -1), this.dragX), 0),
|
||||
max(min(this.drawerWidth, this.dragX), 0)
|
||||
);
|
||||
|
||||
private progress = cond(
|
||||
// Check if the drawer width is available to avoid division by zero
|
||||
eq(this.drawerWidth, 0),
|
||||
0,
|
||||
abs(divide(this.translateX, this.drawerWidth))
|
||||
);
|
||||
|
||||
private handleGestureEvent = event([
|
||||
{
|
||||
nativeEvent: {
|
||||
x: this.touchX,
|
||||
translationX: this.gestureX,
|
||||
velocityX: this.velocityX,
|
||||
state: this.gestureState,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
private handleTapStateChange = ({
|
||||
nativeEvent,
|
||||
}: TapGestureHandlerStateChangeEvent) => {
|
||||
if (nativeEvent.oldState === State.ACTIVE && !this.props.locked) {
|
||||
this.toggleDrawer(false);
|
||||
}
|
||||
};
|
||||
|
||||
private handleContainerLayout = (e: LayoutChangeEvent) =>
|
||||
this.containerWidth.setValue(e.nativeEvent.layout.width);
|
||||
|
||||
private handleDrawerLayout = (e: LayoutChangeEvent) => {
|
||||
this.drawerWidth.setValue(e.nativeEvent.layout.width);
|
||||
this.toggleDrawer(this.props.open);
|
||||
|
||||
// Until layout is available, drawer is hidden with opacity: 0 by default
|
||||
// Show it in the next frame when layout is available
|
||||
// If we don't delay it until the next frame, there's a visible flicker
|
||||
requestAnimationFrame(() => this.drawerOpacity.setValue(1));
|
||||
};
|
||||
|
||||
private toggleDrawer = (open: boolean) => {
|
||||
this.nextIsOpen.setValue(open ? TRUE : FALSE);
|
||||
|
||||
// This value will also be set shortly after as changing this.nextIsOpen changes this.isOpen
|
||||
// However, there's a race condition on Android, so we need to set a bit earlier
|
||||
this.currentOpenValue = open;
|
||||
};
|
||||
|
||||
private toggleStatusBar = (hidden: boolean) => {
|
||||
const { hideStatusBar, statusBarAnimation } = this.props;
|
||||
|
||||
if (hideStatusBar && this.isStatusBarHidden !== hidden) {
|
||||
this.isStatusBarHidden = hidden;
|
||||
StatusBar.setHidden(hidden, statusBarAnimation);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
open,
|
||||
locked,
|
||||
drawerPosition,
|
||||
drawerType,
|
||||
swipeEdgeWidth,
|
||||
contentContainerStyle,
|
||||
drawerStyle,
|
||||
overlayStyle,
|
||||
onGestureRef,
|
||||
renderDrawerContent,
|
||||
renderSceneContent,
|
||||
} = this.props;
|
||||
|
||||
const right = drawerPosition === 'right';
|
||||
|
||||
const contentTranslateX = drawerType === 'front' ? 0 : this.translateX;
|
||||
const drawerTranslateX =
|
||||
drawerType === 'back'
|
||||
? I18nManager.isRTL
|
||||
? multiply(this.drawerWidth, DIRECTION_RIGHT)
|
||||
: this.drawerWidth
|
||||
: this.translateX;
|
||||
|
||||
const offset = I18nManager.isRTL ? '100%' : multiply(this.drawerWidth, -1);
|
||||
|
||||
// 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 = right
|
||||
? // 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: open ? undefined : swipeEdgeWidth }
|
||||
: { left: 0, width: open ? undefined : swipeEdgeWidth };
|
||||
|
||||
return (
|
||||
<PanGestureHandler
|
||||
ref={onGestureRef}
|
||||
activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
|
||||
failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
|
||||
onGestureEvent={this.handleGestureEvent}
|
||||
onHandlerStateChange={this.handleGestureEvent}
|
||||
hitSlop={hitSlop}
|
||||
enabled={!locked}
|
||||
>
|
||||
<Animated.View
|
||||
onLayout={this.handleContainerLayout}
|
||||
style={styles.main}
|
||||
>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.content,
|
||||
{
|
||||
transform: [{ translateX: contentTranslateX }],
|
||||
},
|
||||
contentContainerStyle as any,
|
||||
]}
|
||||
>
|
||||
{renderSceneContent({ progress: this.progress })}
|
||||
<TapGestureHandler onHandlerStateChange={this.handleTapStateChange}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.overlay,
|
||||
{
|
||||
opacity: interpolate(this.progress, {
|
||||
inputRange: [PROGRESS_EPSILON, 1],
|
||||
outputRange: [0, 1],
|
||||
}),
|
||||
// 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, which works, and is much less code
|
||||
zIndex: cond(
|
||||
greaterThan(this.progress, PROGRESS_EPSILON),
|
||||
0,
|
||||
-1
|
||||
),
|
||||
},
|
||||
overlayStyle,
|
||||
]}
|
||||
/>
|
||||
</TapGestureHandler>
|
||||
</Animated.View>
|
||||
<Animated.View
|
||||
accessibilityViewIsModal={open}
|
||||
removeClippedSubviews={Platform.OS !== 'ios'}
|
||||
onLayout={this.handleDrawerLayout}
|
||||
style={[
|
||||
styles.container,
|
||||
right ? { right: offset } : { left: offset },
|
||||
{
|
||||
transform: [{ translateX: drawerTranslateX }],
|
||||
opacity: this.drawerOpacity,
|
||||
zIndex: drawerType === 'back' ? -1 : 0,
|
||||
},
|
||||
drawerStyle as any,
|
||||
]}
|
||||
>
|
||||
{renderDrawerContent({ progress: this.progress })}
|
||||
</Animated.View>
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: 'white',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: '80%',
|
||||
maxWidth: '100%',
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
},
|
||||
main: {
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
@@ -1,30 +1,28 @@
|
||||
import * as React from 'react';
|
||||
import { Dimensions, StyleSheet, ViewStyle, Animated } from 'react-native';
|
||||
import { Dimensions, StyleSheet, ViewStyle } from 'react-native';
|
||||
import { SceneView } from '@react-navigation/core';
|
||||
import DrawerLayout from 'react-native-gesture-handler/DrawerLayout';
|
||||
import { ScreenContainer } from 'react-native-screens';
|
||||
|
||||
import * as DrawerActions from '../routers/DrawerActions';
|
||||
import DrawerSidebar, { ContentComponentProps } from './DrawerSidebar';
|
||||
import DrawerGestureContext from '../utils/DrawerGestureContext';
|
||||
import ResourceSavingScene from '../views/ResourceSavingScene';
|
||||
import ResourceSavingScene from './ResourceSavingScene';
|
||||
import Drawer from './Drawer';
|
||||
import { Navigation } from '../types';
|
||||
import { PanGestureHandler } from 'react-native-gesture-handler';
|
||||
|
||||
type DrawerOptions = {
|
||||
drawerBackgroundColor?: string;
|
||||
overlayColor?: string;
|
||||
minSwipeDistance?: number;
|
||||
drawerPosition: 'left' | 'right';
|
||||
drawerType: 'front' | 'back' | 'slide';
|
||||
drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open';
|
||||
keyboardDismissMode?: 'on-drag' | 'none';
|
||||
drawerType: 'front' | 'back' | 'slide';
|
||||
drawerWidth: number | (() => number);
|
||||
statusBarAnimation: 'slide' | 'none' | 'fade';
|
||||
useNativeAnimations?: boolean;
|
||||
onDrawerClose?: () => void;
|
||||
onDrawerOpen?: () => void;
|
||||
onDrawerStateChanged?: () => void;
|
||||
drawerContainerStyle?: ViewStyle;
|
||||
contentContainerStyle?: ViewStyle;
|
||||
edgeWidth: number;
|
||||
hideStatusBar?: boolean;
|
||||
@@ -82,90 +80,36 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Dimensions.addEventListener('change', this._updateWidth);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const {
|
||||
openId,
|
||||
closeId,
|
||||
toggleId,
|
||||
isDrawerOpen,
|
||||
} = this.props.navigation.state;
|
||||
const {
|
||||
openId: prevOpenId,
|
||||
closeId: prevCloseId,
|
||||
toggleId: prevToggleId,
|
||||
} = prevProps.navigation.state;
|
||||
|
||||
let prevIds = [prevOpenId, prevCloseId, prevToggleId];
|
||||
let changedIds = [openId, closeId, toggleId]
|
||||
.filter(id => !prevIds.includes(id))
|
||||
// @ts-ignore
|
||||
.sort((a, b) => a > b);
|
||||
|
||||
changedIds.forEach(id => {
|
||||
if (id === openId) {
|
||||
this._drawer.openDrawer();
|
||||
} else if (id === closeId) {
|
||||
this._drawer.closeDrawer();
|
||||
} else if (id === toggleId) {
|
||||
if (isDrawerOpen) {
|
||||
this._drawer.closeDrawer();
|
||||
} else {
|
||||
this._drawer.openDrawer();
|
||||
}
|
||||
}
|
||||
});
|
||||
Dimensions.addEventListener('change', this.updateWidth);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Dimensions.removeEventListener('change', this._updateWidth);
|
||||
Dimensions.removeEventListener('change', this.updateWidth);
|
||||
}
|
||||
|
||||
_drawer: typeof DrawerLayout;
|
||||
private drawerGestureRef = React.createRef<PanGestureHandler>();
|
||||
|
||||
drawerGestureRef = React.createRef();
|
||||
private handleDrawerOpen = () => {
|
||||
const { navigation } = this.props;
|
||||
|
||||
_handleDrawerStateChange = (newState: string, willShow: boolean) => {
|
||||
if (newState === 'Idle') {
|
||||
if (!this.props.navigation.state.isDrawerIdle) {
|
||||
this.props.navigation.dispatch({
|
||||
type: DrawerActions.MARK_DRAWER_IDLE,
|
||||
key: this.props.navigation.state.key,
|
||||
});
|
||||
}
|
||||
} else if (newState === 'Settling') {
|
||||
this.props.navigation.dispatch({
|
||||
type: DrawerActions.MARK_DRAWER_SETTLING,
|
||||
key: this.props.navigation.state.key,
|
||||
willShow,
|
||||
});
|
||||
} else {
|
||||
if (this.props.navigation.state.isDrawerIdle) {
|
||||
this.props.navigation.dispatch({
|
||||
type: DrawerActions.MARK_DRAWER_ACTIVE,
|
||||
key: this.props.navigation.state.key,
|
||||
});
|
||||
}
|
||||
}
|
||||
navigation.dispatch(
|
||||
DrawerActions.openDrawer({
|
||||
key: navigation.state.key,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
_handleDrawerOpen = () => {
|
||||
this.props.navigation.dispatch({
|
||||
type: DrawerActions.DRAWER_OPENED,
|
||||
key: this.props.navigation.state.key,
|
||||
});
|
||||
private handleDrawerClose = () => {
|
||||
const { navigation } = this.props;
|
||||
|
||||
navigation.dispatch(
|
||||
DrawerActions.closeDrawer({
|
||||
key: navigation.state.key,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
_handleDrawerClose = () => {
|
||||
this.props.navigation.dispatch({
|
||||
type: DrawerActions.DRAWER_CLOSED,
|
||||
key: this.props.navigation.state.key,
|
||||
});
|
||||
};
|
||||
|
||||
_updateWidth = () => {
|
||||
private updateWidth = () => {
|
||||
const drawerWidth =
|
||||
typeof this.props.navigationConfig.drawerWidth === 'function'
|
||||
? this.props.navigationConfig.drawerWidth()
|
||||
@@ -176,27 +120,23 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
_renderNavigationView = (
|
||||
drawerOpenProgress: Animated.AnimatedInterpolation
|
||||
) => {
|
||||
private renderNavigationView = ({ progress }: any) => {
|
||||
return (
|
||||
<DrawerGestureContext.Provider value={this.drawerGestureRef}>
|
||||
<DrawerSidebar
|
||||
screenProps={this.props.screenProps}
|
||||
drawerOpenProgress={drawerOpenProgress}
|
||||
navigation={this.props.navigation}
|
||||
descriptors={this.props.descriptors}
|
||||
contentComponent={this.props.navigationConfig.contentComponent}
|
||||
contentOptions={this.props.navigationConfig.contentOptions}
|
||||
drawerPosition={this.props.navigationConfig.drawerPosition}
|
||||
style={this.props.navigationConfig.style}
|
||||
{...this.props.navigationConfig}
|
||||
/>
|
||||
</DrawerGestureContext.Provider>
|
||||
<DrawerSidebar
|
||||
screenProps={this.props.screenProps}
|
||||
drawerOpenProgress={progress}
|
||||
navigation={this.props.navigation}
|
||||
descriptors={this.props.descriptors}
|
||||
contentComponent={this.props.navigationConfig.contentComponent}
|
||||
contentOptions={this.props.navigationConfig.contentOptions}
|
||||
drawerPosition={this.props.navigationConfig.drawerPosition}
|
||||
style={this.props.navigationConfig.style}
|
||||
{...this.props.navigationConfig}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
_renderContent = () => {
|
||||
private renderContent = () => {
|
||||
let { lazy, navigation } = this.props;
|
||||
let { loaded } = this.state;
|
||||
let { routes } = navigation.state;
|
||||
@@ -214,7 +154,7 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<ScreenContainer style={styles.pages}>
|
||||
<ScreenContainer style={styles.content}>
|
||||
{routes.map((route, index) => {
|
||||
if (lazy && !loaded.includes(index)) {
|
||||
// Don't render a screen if we've never navigated to it
|
||||
@@ -246,67 +186,68 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
_setDrawerGestureRef = (ref: any) => {
|
||||
private setDrawerGestureRef = (ref: PanGestureHandler | null) => {
|
||||
// @ts-ignore
|
||||
this.drawerGestureRef.current = ref;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation, screenProps } = this.props;
|
||||
const { navigation } = this.props;
|
||||
const {
|
||||
drawerType,
|
||||
drawerBackgroundColor,
|
||||
overlayColor,
|
||||
contentContainerStyle,
|
||||
edgeWidth,
|
||||
minSwipeDistance,
|
||||
hideStatusBar,
|
||||
statusBarAnimation,
|
||||
} = this.props.navigationConfig;
|
||||
const activeKey = navigation.state.routes[navigation.state.index].key;
|
||||
const { drawerLockMode } = this.props.descriptors[activeKey].options;
|
||||
|
||||
const isOpen =
|
||||
drawerLockMode === 'locked-closed'
|
||||
? false
|
||||
: drawerLockMode === 'locked-open'
|
||||
? true
|
||||
: this.props.navigation.state.isDrawerOpen;
|
||||
|
||||
return (
|
||||
<DrawerLayout
|
||||
ref={(c: any) => {
|
||||
this._drawer = c;
|
||||
}}
|
||||
onGestureRef={this._setDrawerGestureRef}
|
||||
drawerLockMode={
|
||||
drawerLockMode ||
|
||||
(typeof screenProps === 'object' &&
|
||||
screenProps != null &&
|
||||
// @ts-ignore
|
||||
screenProps.drawerLockMode) ||
|
||||
this.props.navigationConfig.drawerLockMode
|
||||
}
|
||||
drawerBackgroundColor={
|
||||
this.props.navigationConfig.drawerBackgroundColor
|
||||
}
|
||||
keyboardDismissMode={this.props.navigationConfig.keyboardDismissMode}
|
||||
drawerWidth={this.state.drawerWidth}
|
||||
onDrawerOpen={this._handleDrawerOpen}
|
||||
onDrawerClose={this._handleDrawerClose}
|
||||
onDrawerStateChanged={this._handleDrawerStateChange}
|
||||
useNativeAnimations={this.props.navigationConfig.useNativeAnimations}
|
||||
renderNavigationView={this._renderNavigationView}
|
||||
drawerPosition={
|
||||
this.props.navigationConfig.drawerPosition === 'right'
|
||||
? DrawerLayout.positions.Right
|
||||
: DrawerLayout.positions.Left
|
||||
}
|
||||
/* props specific to react-native-gesture-handler/DrawerLayout */
|
||||
drawerType={this.props.navigationConfig.drawerType}
|
||||
edgeWidth={this.props.navigationConfig.edgeWidth}
|
||||
hideStatusBar={this.props.navigationConfig.hideStatusBar}
|
||||
statusBarAnimation={this.props.navigationConfig.statusBarAnimation}
|
||||
minSwipeDistance={this.props.navigationConfig.minSwipeDistance}
|
||||
overlayColor={this.props.navigationConfig.overlayColor}
|
||||
drawerContainerStyle={this.props.navigationConfig.drawerContainerStyle}
|
||||
contentContainerStyle={
|
||||
this.props.navigationConfig.contentContainerStyle
|
||||
}
|
||||
>
|
||||
<DrawerGestureContext.Provider value={this.drawerGestureRef}>
|
||||
{this._renderContent()}
|
||||
</DrawerGestureContext.Provider>
|
||||
</DrawerLayout>
|
||||
<DrawerGestureContext.Provider value={this.drawerGestureRef}>
|
||||
<Drawer
|
||||
open={isOpen}
|
||||
locked={
|
||||
drawerLockMode === 'locked-open' ||
|
||||
drawerLockMode === 'locked-closed'
|
||||
}
|
||||
onOpen={this.handleDrawerOpen}
|
||||
onClose={this.handleDrawerClose}
|
||||
onGestureRef={this.setDrawerGestureRef}
|
||||
drawerType={drawerType}
|
||||
drawerPosition={this.props.navigationConfig.drawerPosition}
|
||||
contentContainerStyle={contentContainerStyle}
|
||||
drawerStyle={{
|
||||
backgroundColor: drawerBackgroundColor || 'white',
|
||||
width: this.state.drawerWidth,
|
||||
}}
|
||||
overlayStyle={{
|
||||
backgroundColor: overlayColor || 'rgba(0, 0, 0, 0.5)',
|
||||
}}
|
||||
swipeEdgeWidth={edgeWidth}
|
||||
swipeDistanceThreshold={minSwipeDistance}
|
||||
hideStatusBar={hideStatusBar}
|
||||
statusBarAnimation={statusBarAnimation}
|
||||
renderDrawerContent={this.renderNavigationView}
|
||||
renderSceneContent={this.renderContent}
|
||||
/>
|
||||
</DrawerGestureContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
pages: {
|
||||
content: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
declare module 'react-native-gesture-handler/DrawerLayout';
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user