UNPKG

reactxp-navigation

Version:

Plugin for ReactXP that provides a navigation framework

286 lines (285 loc) 13.1 kB
"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;