reactxp-navigation
Version:
Plugin for ReactXP that provides a navigation framework
286 lines (285 loc) • 13.1 kB
JavaScript
"use strict";
/**
* NavigatorExperimentalDelegate.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Delegate which encapsulates experimental react-native Navigator experience.
* The main difference of Experimental Navigator is that it uses Animated for navigation animation
* so we can enable useNativeDriver options for those animations.
*
* Currently, Android support on NativeAnimations is more stable and performant than iOS.
* That's why we need to have the ability to pick different implementations for different platforms.
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var React = require("react");
var RN = require("react-native");
var Navigation = require("reactxp-experimental-navigation");
var assert_1 = require("../common/assert");
var _ = require("../common/lodashMini");
var Types_1 = require("../common/Types");
var StateUtils = Navigation.StateUtils;
var NavigatorExperimentalDelegate = /** @class */ (function (_super) {
__extends(NavigatorExperimentalDelegate, _super);
function NavigatorExperimentalDelegate(navigator) {
var _this = _super.call(this, navigator) || this;
_this._onTransitionEnd = function () {
_this._transitionSpec = _this._buildTransitionSpec(_this._state);
_this._owner.setState({ state: _this._state });
if (_this._owner.props.transitionCompleted) {
_this._owner.props.transitionCompleted();
}
};
_this._onTransitionStart = function (transitionProps, prevTransitionProps) {
if (_this._owner.props.transitionStarted) {
var fromIndex = prevTransitionProps && prevTransitionProps.scene ? prevTransitionProps.scene.index : undefined;
var toIndex = transitionProps.scene ? transitionProps.scene.index : undefined;
var fromRouteId = prevTransitionProps && prevTransitionProps.scene ? prevTransitionProps.scene.route.key : undefined;
var toRouteId = transitionProps.scene ? transitionProps.scene.route.key : undefined;
_this._owner.props.transitionStarted(transitionProps.position, toRouteId, fromRouteId, toIndex, fromIndex);
}
};
// Callback from Navigator.js to RX.Navigator
_this._renderScene = function (props) {
var parentState = props.navigationState;
var sceneState = parentState.routes[props.scene.index];
// Does the route exist?
if (sceneState && sceneState.route) {
// Call the renderScene callback.
return _this._owner.props.renderScene(sceneState.route);
}
// No route? Return empty scene.
return React.createElement(RN.View, null);
};
/**
* This method is going to be deprecated in later releases
*/
_this._onNavigateBack = function () {
_this.onBackPress();
};
var route = { key: '0', route: { routeId: 0, sceneConfigType: 0 } };
_this._state = { index: 0, routes: [route] };
_this._transitionSpec = _this._buildTransitionSpec(_this._state);
return _this;
}
NavigatorExperimentalDelegate.prototype.getRoutes = function () {
return _.map(this._state.routes, function (element) {
var routeState = element;
return routeState.route;
});
};
// Reset route stack with default route stack
NavigatorExperimentalDelegate.prototype.immediatelyResetRouteStack = function (nextRouteStack) {
var prevState = this._state;
this._state = this._createParentState(nextRouteStack, prevState);
this._transitionSpec = this._buildTransitionSpec(this._state);
this._owner.setState({ state: this._state });
};
// Render without initial route to get a reference for Navigator object
NavigatorExperimentalDelegate.prototype.render = function () {
return (React.createElement(Navigation.CardStack, { direction: this._transitionSpec.direction, customTransitionConfig: this._transitionSpec.customTransitionConfig, navigationState: this._state, onNavigateBack: this._onNavigateBack, onTransitionStart: this._onTransitionStart, onTransitionEnd: this._onTransitionEnd, renderScene: this._renderScene, cardStyle: this._transitionSpec.cardStyle || this._owner.props.cardStyle, hideShadow: this._transitionSpec.hideShadow, enableGestures: this._transitionSpec.enableGesture, gestureResponseDistance: this._transitionSpec.gestureResponseDistance }));
};
NavigatorExperimentalDelegate.prototype._convertCustomTransitionConfig = function (config) {
if (!config) {
return undefined;
}
var nativeConfig = {
transitionStyle: config.transitionStyle,
presentBelowPrevious: config.presentBelowPrevious
};
if (config.transitionSpec) {
var transitionSpec = {};
if (config.transitionSpec.duration) {
transitionSpec.duration = config.transitionSpec.duration;
}
if (config.transitionSpec.easing) {
transitionSpec.easing = config.transitionSpec.easing.function;
}
nativeConfig.transitionSpec = transitionSpec;
}
return nativeConfig;
};
NavigatorExperimentalDelegate.prototype._buildTransitionSpec = function (state) {
var route = state.routes[state.index].route;
var direction = 'horizontal';
var customSceneConfig = undefined;
var enableGesture = false;
var responseDistance = 0;
var hideShadow = route && route.customSceneConfig && route.customSceneConfig.hideShadow;
var cardStyle = route && route.customSceneConfig
? route.customSceneConfig.cardStyle
: undefined;
var gestureDistanceSet = false;
if (route) {
// If defined, use the gestureResponseDistance override
if (route.gestureResponseDistance !== undefined && route.gestureResponseDistance !== null) {
responseDistance = route.gestureResponseDistance;
gestureDistanceSet = true;
}
customSceneConfig = this._convertCustomTransitionConfig(route.customSceneConfig);
switch (route.sceneConfigType) {
case Types_1.NavigatorSceneConfigType.FloatFromBottom:
direction = 'vertical';
if (!gestureDistanceSet) {
responseDistance = 150;
gestureDistanceSet = true;
}
break;
case Types_1.NavigatorSceneConfigType.Fade:
case Types_1.NavigatorSceneConfigType.FadeWithSlide:
direction = 'fade';
if (!gestureDistanceSet) {
responseDistance = 0;
gestureDistanceSet = true;
}
break;
// Currently we support only right to left animation
//case NavigatorSceneConfigType.FloatFromRight:
//case NavigatorSceneConfigType.FloatFromLeft:
default:
break;
}
}
// Fall back to 30 as a default for responseDistance
if (!gestureDistanceSet) {
responseDistance = 30;
}
// Conditionally enable gestures
enableGesture = responseDistance > 0;
return {
enableGesture: enableGesture,
gestureResponseDistance: responseDistance,
direction: direction,
customTransitionConfig: customSceneConfig,
cardStyle: cardStyle,
hideShadow: hideShadow,
};
};
NavigatorExperimentalDelegate.prototype.handleBackPress = function () {
this._owner.pop();
};
NavigatorExperimentalDelegate.prototype.processCommand = function (commandQueue) {
// Return if nothing to process
if (!commandQueue.length) {
return;
}
var previousState = this._state;
var useNewStateAsScene = false;
var command = commandQueue.shift();
var route = command.param.route;
var value = command.param.value;
switch (command.type) {
case Types_1.CommandType.Push:
useNewStateAsScene = true;
this._state = StateUtils.push(this._state, this._createState(route));
break;
case Types_1.CommandType.Pop:
if (route !== undefined) {
this._state = this._popToRoute(this._state, route);
}
else if (value !== undefined) {
if (value > 0) {
this._state = this._popN(this._state, value);
}
else {
this._state = this._popToTop(this._state);
}
}
else {
this._state = StateUtils.pop(this._state);
}
break;
case Types_1.CommandType.Replace:
if (value === -1) {
this._state = StateUtils.replaceAtIndex(this._state, this._state.routes.length - 2, this._createState(route));
}
else {
this._state = StateUtils.replaceAtIndex(this._state, this._state.routes.length - 1, this._createState(route));
}
break;
default:
console.error('Undefined Navigation command: ', command.type);
break;
}
if (previousState !== this._state) {
if (useNewStateAsScene) {
this._transitionSpec = this._buildTransitionSpec(this._state);
}
else {
this._transitionSpec = this._buildTransitionSpec(previousState);
}
this._owner.setState({ state: this._state });
}
};
NavigatorExperimentalDelegate.prototype._createState = function (route) {
return { key: route.routeId.toString(), route: route };
};
NavigatorExperimentalDelegate.prototype._createParentState = function (routes, prevState) {
var _this = this;
var prevRoutes = prevState.routes;
var children = _.map(routes, function (element, index) {
if (prevRoutes.length > index) {
var prevRoute = prevRoutes[index];
// Navigator state reducer is a little bit naive,
// let's make sure it's scene rendering caching would work properly
if (prevRoute.route.routeId === element.routeId) {
return prevRoute;
}
}
return _this._createState(element);
});
return { routes: children, index: routes.length - 1 };
};
NavigatorExperimentalDelegate.prototype._popToTop = function (state) {
var popCount = state.routes.length - 1;
if (popCount > 0) {
return this._popN(state, popCount);
}
return state;
};
NavigatorExperimentalDelegate.prototype._popN = function (state, n) {
assert_1.default(n > 0, 'n < 0 please pass positive value');
var initialRoutes = state.routes;
var initialLength = initialRoutes.length;
assert_1.default(initialLength >= n, 'navigation stack underflow');
var result = _.clone(state);
result.routes = initialRoutes.slice(0, initialLength - n);
result.index = initialLength - n - 1;
return result;
};
NavigatorExperimentalDelegate.prototype._popToRoute = function (state, route) {
var popCount = 0;
for (var i = state.routes.length - 1; i >= 0; i--) {
var child = state.routes[i];
if (route.routeId === child.route.routeId) {
break;
}
else {
popCount++;
}
}
if (popCount > 0) {
return this._popN(state, popCount);
}
else {
return state;
}
};
return NavigatorExperimentalDelegate;
}(Types_1.NavigatorDelegate));
exports.NavigatorExperimentalDelegate = NavigatorExperimentalDelegate;
exports.default = NavigatorExperimentalDelegate;