mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-26 22:28:19 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0c5c86f63 | ||
|
|
4388b6403c | ||
|
|
2cb740fbf6 | ||
|
|
ac741a703b | ||
|
|
5641b42975 |
@@ -6,6 +6,11 @@
|
||||
"development": {
|
||||
"plugins": [
|
||||
"transform-react-jsx-source"
|
||||
],
|
||||
},
|
||||
"production": {
|
||||
"plugins": [
|
||||
"transform-remove-console"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"react-navigation": "link:../.."
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-transform-remove-console": "^6.9.0",
|
||||
"babel-jest": "^21.0.0",
|
||||
"flow-bin": "^0.61.0",
|
||||
"jest": "^21.0.1",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"description": "Routing and navigation for your React Native apps",
|
||||
"main": "src/react-navigation.js",
|
||||
"repository": {
|
||||
|
||||
@@ -45,9 +45,6 @@ const navigate = createAction(NAVIGATE, payload => {
|
||||
if (payload.key) {
|
||||
action.key = payload.key;
|
||||
}
|
||||
if (payload.immediate) {
|
||||
action.immediate = payload.immediate;
|
||||
}
|
||||
return action;
|
||||
});
|
||||
|
||||
@@ -73,9 +70,6 @@ const push = createAction(PUSH, payload => {
|
||||
if (payload.action) {
|
||||
action.action = payload.action;
|
||||
}
|
||||
if (payload.immediate) {
|
||||
action.immediate = payload.immediate;
|
||||
}
|
||||
return action;
|
||||
});
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ describe('NavigationContainer', () => {
|
||||
// First dispatch
|
||||
expect(
|
||||
navigationContainer.dispatch(
|
||||
NavigationActions.navigate({ routeName: 'bar', immediate: true })
|
||||
NavigationActions.navigate({ routeName: 'bar' })
|
||||
)
|
||||
).toEqual(true);
|
||||
|
||||
@@ -119,7 +119,7 @@ describe('NavigationContainer', () => {
|
||||
// Second dispatch
|
||||
expect(
|
||||
navigationContainer.dispatch(
|
||||
NavigationActions.navigate({ routeName: 'baz', immediate: true })
|
||||
NavigationActions.navigate({ routeName: 'baz' })
|
||||
)
|
||||
).toEqual(true);
|
||||
|
||||
@@ -147,7 +147,7 @@ describe('NavigationContainer', () => {
|
||||
// First dispatch
|
||||
expect(
|
||||
navigationContainer.dispatch(
|
||||
NavigationActions.navigate({ routeName: 'bar', immediate: true })
|
||||
NavigationActions.navigate({ routeName: 'bar' })
|
||||
)
|
||||
).toEqual(true);
|
||||
|
||||
@@ -157,28 +157,28 @@ describe('NavigationContainer', () => {
|
||||
// Second dispatch
|
||||
expect(
|
||||
navigationContainer.dispatch(
|
||||
NavigationActions.navigate({ routeName: 'baz', immediate: true })
|
||||
NavigationActions.navigate({ routeName: 'baz' })
|
||||
)
|
||||
).toEqual(true);
|
||||
|
||||
// Third dispatch
|
||||
expect(
|
||||
navigationContainer.dispatch(
|
||||
NavigationActions.navigate({ routeName: 'car', immediate: true })
|
||||
NavigationActions.navigate({ routeName: 'car' })
|
||||
)
|
||||
).toEqual(true);
|
||||
|
||||
// Fourth dispatch
|
||||
expect(
|
||||
navigationContainer.dispatch(
|
||||
NavigationActions.navigate({ routeName: 'dog', immediate: true })
|
||||
NavigationActions.navigate({ routeName: 'dog' })
|
||||
)
|
||||
).toEqual(true);
|
||||
|
||||
// Fifth dispatch
|
||||
expect(
|
||||
navigationContainer.dispatch(
|
||||
NavigationActions.navigate({ routeName: 'elk', immediate: true })
|
||||
NavigationActions.navigate({ routeName: 'elk' })
|
||||
)
|
||||
).toEqual(true);
|
||||
|
||||
|
||||
@@ -148,17 +148,6 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
action.type === NavigationActions.COMPLETE_TRANSITION &&
|
||||
(action.key == null || action.key === state.key) &&
|
||||
state.isTransitioning
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
isTransitioning: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if a child scene wants to handle the action as long as it is not a reset to the root stack
|
||||
if (action.type !== NavigationActions.RESET || action.key !== null) {
|
||||
const keyIndex = action.key
|
||||
@@ -229,10 +218,6 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
behavesLikePushAction(action) &&
|
||||
childRouters[action.routeName] !== undefined
|
||||
) {
|
||||
if (state.isTransitioning) {
|
||||
return { ...state };
|
||||
}
|
||||
|
||||
const childRouter = childRouters[action.routeName];
|
||||
let route;
|
||||
|
||||
@@ -305,6 +290,17 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
action.type === NavigationActions.COMPLETE_TRANSITION &&
|
||||
(action.key == null || action.key === state.key) &&
|
||||
state.isTransitioning
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
isTransitioning: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Handle navigation to other child routers that are not yet pushed
|
||||
if (behavesLikePushAction(action)) {
|
||||
const childRouterNames = Object.keys(childRouters);
|
||||
|
||||
@@ -493,23 +493,6 @@ describe('StackRouter', () => {
|
||||
expect(poppedImmediatelyState.isTransitioning).toBe(false);
|
||||
});
|
||||
|
||||
test('Navigate does not happen while transitioning', () => {
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
bar: { screen: () => <div /> },
|
||||
});
|
||||
const initState = {
|
||||
...TestRouter.getStateForAction(NavigationActions.init()),
|
||||
isTransitioning: true,
|
||||
};
|
||||
const pushedState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'bar' }),
|
||||
initState
|
||||
);
|
||||
expect(pushedState.index).toEqual(0);
|
||||
expect(pushedState.routes.length).toEqual(1);
|
||||
});
|
||||
|
||||
test('Navigate Pushes duplicate routeName', () => {
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
@@ -517,13 +500,13 @@ describe('StackRouter', () => {
|
||||
});
|
||||
const initState = TestRouter.getStateForAction(NavigationActions.init());
|
||||
const pushedState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'bar', immediate: true }),
|
||||
NavigationActions.navigate({ routeName: 'bar' }),
|
||||
initState
|
||||
);
|
||||
expect(pushedState.index).toEqual(1);
|
||||
expect(pushedState.routes[1].routeName).toEqual('bar');
|
||||
const pushedTwiceState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'bar', immediate: true }),
|
||||
NavigationActions.navigate({ routeName: 'bar' }),
|
||||
pushedState
|
||||
);
|
||||
expect(pushedTwiceState.index).toEqual(2);
|
||||
@@ -557,7 +540,7 @@ describe('StackRouter', () => {
|
||||
});
|
||||
const initState = TestRouter.getStateForAction(NavigationActions.init());
|
||||
const pushedState = TestRouter.getStateForAction(
|
||||
NavigationActions.push({ routeName: 'bar', immediate: true }),
|
||||
NavigationActions.push({ routeName: 'bar' }),
|
||||
initState
|
||||
);
|
||||
expect(pushedState.index).toEqual(1);
|
||||
|
||||
@@ -206,132 +206,137 @@ class CardStack extends React.Component {
|
||||
const { options } = this._getScreenDetails(scene);
|
||||
const gestureDirectionInverted = options.gestureDirection === 'inverted';
|
||||
|
||||
const responder = PanResponder.create({
|
||||
onPanResponderTerminate: () => {
|
||||
this._isResponding = false;
|
||||
this._reset(index, 0);
|
||||
},
|
||||
onPanResponderGrant: () => {
|
||||
position.stopAnimation(value => {
|
||||
this._isResponding = true;
|
||||
this._gestureStartValue = value;
|
||||
});
|
||||
},
|
||||
onMoveShouldSetPanResponder: (event, gesture) => {
|
||||
if (index !== scene.index) {
|
||||
return false;
|
||||
}
|
||||
const immediateIndex =
|
||||
this._immediateIndex == null ? index : this._immediateIndex;
|
||||
const currentDragDistance = gesture[isVertical ? 'dy' : 'dx'];
|
||||
const currentDragPosition =
|
||||
event.nativeEvent[isVertical ? 'pageY' : 'pageX'];
|
||||
const axisLength = isVertical
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const axisHasBeenMeasured = !!axisLength;
|
||||
|
||||
// Measure the distance from the touch to the edge of the screen
|
||||
const screenEdgeDistance = gestureDirectionInverted
|
||||
? axisLength - (currentDragPosition - currentDragDistance)
|
||||
: currentDragPosition - currentDragDistance;
|
||||
// Compare to the gesture distance relavant to card or modal
|
||||
const {
|
||||
gestureResponseDistance: userGestureResponseDistance = {},
|
||||
} = this._getScreenDetails(scene).options;
|
||||
const gestureResponseDistance = isVertical
|
||||
? userGestureResponseDistance.vertical ||
|
||||
GESTURE_RESPONSE_DISTANCE_VERTICAL
|
||||
: userGestureResponseDistance.horizontal ||
|
||||
GESTURE_RESPONSE_DISTANCE_HORIZONTAL;
|
||||
// GESTURE_RESPONSE_DISTANCE is about 25 or 30. Or 135 for modals
|
||||
if (screenEdgeDistance > gestureResponseDistance) {
|
||||
// Reject touches that started in the middle of the screen
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasDraggedEnough =
|
||||
Math.abs(currentDragDistance) > RESPOND_THRESHOLD;
|
||||
|
||||
const isOnFirstCard = immediateIndex === 0;
|
||||
const shouldSetResponder =
|
||||
hasDraggedEnough && axisHasBeenMeasured && !isOnFirstCard;
|
||||
return shouldSetResponder;
|
||||
},
|
||||
onPanResponderMove: (event, gesture) => {
|
||||
// Handle the moving touches for our granted responder
|
||||
const startValue = this._gestureStartValue;
|
||||
const axis = isVertical ? 'dy' : 'dx';
|
||||
const axisDistance = isVertical
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const currentValue =
|
||||
(I18nManager.isRTL && axis === 'dx') !== gestureDirectionInverted
|
||||
? startValue + gesture[axis] / axisDistance
|
||||
: startValue - gesture[axis] / axisDistance;
|
||||
const value = clamp(index - 1, currentValue, index);
|
||||
position.setValue(value);
|
||||
},
|
||||
onPanResponderTerminationRequest: () =>
|
||||
// Returning false will prevent other views from becoming responder while
|
||||
// the navigation view is the responder (mid-gesture)
|
||||
false,
|
||||
onPanResponderRelease: (event, gesture) => {
|
||||
if (!this._isResponding) {
|
||||
return;
|
||||
}
|
||||
this._isResponding = false;
|
||||
|
||||
const immediateIndex =
|
||||
this._immediateIndex == null ? index : this._immediateIndex;
|
||||
|
||||
// Calculate animate duration according to gesture speed and moved distance
|
||||
const axisDistance = isVertical
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const movementDirection = gestureDirectionInverted ? -1 : 1;
|
||||
const movedDistance =
|
||||
movementDirection * gesture[isVertical ? 'dy' : 'dx'];
|
||||
const gestureVelocity =
|
||||
movementDirection * gesture[isVertical ? 'vy' : 'vx'];
|
||||
const defaultVelocity = axisDistance / ANIMATION_DURATION;
|
||||
const velocity = Math.max(Math.abs(gestureVelocity), defaultVelocity);
|
||||
const resetDuration = gestureDirectionInverted
|
||||
? (axisDistance - movedDistance) / velocity
|
||||
: movedDistance / velocity;
|
||||
const goBackDuration = gestureDirectionInverted
|
||||
? movedDistance / velocity
|
||||
: (axisDistance - movedDistance) / velocity;
|
||||
|
||||
// To asyncronously get the current animated value, we need to run stopAnimation:
|
||||
position.stopAnimation(value => {
|
||||
// If the speed of the gesture release is significant, use that as the indication
|
||||
// of intent
|
||||
if (gestureVelocity < -0.5) {
|
||||
this._reset(immediateIndex, resetDuration);
|
||||
return;
|
||||
}
|
||||
if (gestureVelocity > 0.5) {
|
||||
this._goBack(immediateIndex, goBackDuration);
|
||||
return;
|
||||
}
|
||||
|
||||
// Then filter based on the distance the screen was moved. Over a third of the way swiped,
|
||||
// and the back will happen.
|
||||
if (value <= index - POSITION_THRESHOLD) {
|
||||
this._goBack(immediateIndex, goBackDuration);
|
||||
} else {
|
||||
this._reset(immediateIndex, resetDuration);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const gesturesEnabled =
|
||||
typeof options.gesturesEnabled === 'boolean'
|
||||
? options.gesturesEnabled
|
||||
: Platform.OS === 'ios';
|
||||
|
||||
const responder = !gesturesEnabled
|
||||
? null
|
||||
: PanResponder.create({
|
||||
onPanResponderTerminate: () => {
|
||||
this._isResponding = false;
|
||||
this._reset(index, 0);
|
||||
},
|
||||
onPanResponderGrant: () => {
|
||||
position.stopAnimation(value => {
|
||||
this._isResponding = true;
|
||||
this._gestureStartValue = value;
|
||||
});
|
||||
},
|
||||
onMoveShouldSetPanResponder: (event, gesture) => {
|
||||
if (index !== scene.index) {
|
||||
return false;
|
||||
}
|
||||
const immediateIndex =
|
||||
this._immediateIndex == null ? index : this._immediateIndex;
|
||||
const currentDragDistance = gesture[isVertical ? 'dy' : 'dx'];
|
||||
const currentDragPosition =
|
||||
event.nativeEvent[isVertical ? 'pageY' : 'pageX'];
|
||||
const axisLength = isVertical
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const axisHasBeenMeasured = !!axisLength;
|
||||
|
||||
// Measure the distance from the touch to the edge of the screen
|
||||
const screenEdgeDistance = gestureDirectionInverted
|
||||
? axisLength - (currentDragPosition - currentDragDistance)
|
||||
: currentDragPosition - currentDragDistance;
|
||||
// Compare to the gesture distance relavant to card or modal
|
||||
const {
|
||||
gestureResponseDistance: userGestureResponseDistance = {},
|
||||
} = this._getScreenDetails(scene).options;
|
||||
const gestureResponseDistance = isVertical
|
||||
? userGestureResponseDistance.vertical ||
|
||||
GESTURE_RESPONSE_DISTANCE_VERTICAL
|
||||
: userGestureResponseDistance.horizontal ||
|
||||
GESTURE_RESPONSE_DISTANCE_HORIZONTAL;
|
||||
// GESTURE_RESPONSE_DISTANCE is about 25 or 30. Or 135 for modals
|
||||
if (screenEdgeDistance > gestureResponseDistance) {
|
||||
// Reject touches that started in the middle of the screen
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasDraggedEnough =
|
||||
Math.abs(currentDragDistance) > RESPOND_THRESHOLD;
|
||||
|
||||
const isOnFirstCard = immediateIndex === 0;
|
||||
const shouldSetResponder =
|
||||
hasDraggedEnough && axisHasBeenMeasured && !isOnFirstCard;
|
||||
return shouldSetResponder;
|
||||
},
|
||||
onPanResponderMove: (event, gesture) => {
|
||||
// Handle the moving touches for our granted responder
|
||||
const startValue = this._gestureStartValue;
|
||||
const axis = isVertical ? 'dy' : 'dx';
|
||||
const axisDistance = isVertical
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const currentValue =
|
||||
(I18nManager.isRTL && axis === 'dx') !== gestureDirectionInverted
|
||||
? startValue + gesture[axis] / axisDistance
|
||||
: startValue - gesture[axis] / axisDistance;
|
||||
const value = clamp(index - 1, currentValue, index);
|
||||
position.setValue(value);
|
||||
},
|
||||
onPanResponderTerminationRequest: () =>
|
||||
// Returning false will prevent other views from becoming responder while
|
||||
// the navigation view is the responder (mid-gesture)
|
||||
false,
|
||||
onPanResponderRelease: (event, gesture) => {
|
||||
if (!this._isResponding) {
|
||||
return;
|
||||
}
|
||||
this._isResponding = false;
|
||||
|
||||
const immediateIndex =
|
||||
this._immediateIndex == null ? index : this._immediateIndex;
|
||||
|
||||
// Calculate animate duration according to gesture speed and moved distance
|
||||
const axisDistance = isVertical
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const movementDirection = gestureDirectionInverted ? -1 : 1;
|
||||
const movedDistance =
|
||||
movementDirection * gesture[isVertical ? 'dy' : 'dx'];
|
||||
const gestureVelocity =
|
||||
movementDirection * gesture[isVertical ? 'vy' : 'vx'];
|
||||
const defaultVelocity = axisDistance / ANIMATION_DURATION;
|
||||
const velocity = Math.max(
|
||||
Math.abs(gestureVelocity),
|
||||
defaultVelocity
|
||||
);
|
||||
const resetDuration = gestureDirectionInverted
|
||||
? (axisDistance - movedDistance) / velocity
|
||||
: movedDistance / velocity;
|
||||
const goBackDuration = gestureDirectionInverted
|
||||
? movedDistance / velocity
|
||||
: (axisDistance - movedDistance) / velocity;
|
||||
|
||||
// To asyncronously get the current animated value, we need to run stopAnimation:
|
||||
position.stopAnimation(value => {
|
||||
// If the speed of the gesture release is significant, use that as the indication
|
||||
// of intent
|
||||
if (gestureVelocity < -0.5) {
|
||||
this._reset(immediateIndex, resetDuration);
|
||||
return;
|
||||
}
|
||||
if (gestureVelocity > 0.5) {
|
||||
this._goBack(immediateIndex, goBackDuration);
|
||||
return;
|
||||
}
|
||||
|
||||
// Then filter based on the distance the screen was moved. Over a third of the way swiped,
|
||||
// and the back will happen.
|
||||
if (value <= index - POSITION_THRESHOLD) {
|
||||
this._goBack(immediateIndex, goBackDuration);
|
||||
} else {
|
||||
this._reset(immediateIndex, resetDuration);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const handlers = gesturesEnabled ? responder.panHandlers : {};
|
||||
const containerStyle = [
|
||||
styles.container,
|
||||
|
||||
@@ -262,7 +262,11 @@ class Header extends React.PureComponent {
|
||||
let appBar;
|
||||
|
||||
if (this.props.mode === 'float') {
|
||||
const scenesProps = this.props.scenes.map(scene => ({
|
||||
const scenesByIndex = {};
|
||||
this.props.scenes.forEach(scene => {
|
||||
scenesByIndex[scene.index] = scene;
|
||||
});
|
||||
const scenesProps = Object.values(scenesByIndex).map(scene => ({
|
||||
position: this.props.position,
|
||||
progress: this.props.progress,
|
||||
scene,
|
||||
|
||||
Reference in New Issue
Block a user