summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoranderklander <anderklander@gmail.com>2018-02-21 14:46:07 +0100
committerAndrej Mihajlov <and@mullvad.net>2018-02-26 16:15:52 +0100
commit608029ca1c403101a88ec45d52e44dc1f22e434f (patch)
treef2f7bb91c19f6d01d259e785242dbd967bc6392f
parent47a78129cd2f953fe4dca33a04c138ac2093a52e (diff)
downloadmullvadvpn-608029ca1c403101a88ec45d52e44dc1f22e434f.tar.xz
mullvadvpn-608029ca1c403101a88ec45d52e44dc1f22e434f.zip
Add TransitionContainer using ReactXP animations
-rw-r--r--app/assets/css/style.css1
-rw-r--r--app/assets/css/transitions.css91
-rw-r--r--app/components/TransitionContainer.js201
-rw-r--r--app/routes.js6
-rw-r--r--app/transitions.js48
-rw-r--r--flow-typed/npm/react-transition-group_vx.x.x.js80
-rw-r--r--package.json1
-rw-r--r--yarn.lock20
8 files changed, 223 insertions, 225 deletions
diff --git a/app/assets/css/style.css b/app/assets/css/style.css
index 5021d7d1a9..7d667f67e3 100644
--- a/app/assets/css/style.css
+++ b/app/assets/css/style.css
@@ -3,7 +3,6 @@
@import 'fonts.css';
@import 'global.css';
@import 'buttons.css';
-@import 'transitions.css';
/* app */
@import '../../components/PlatformWindow.css';
diff --git a/app/assets/css/transitions.css b/app/assets/css/transitions.css
deleted file mode 100644
index 239be5b63b..0000000000
--- a/app/assets/css/transitions.css
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * CSS rules for transitions using React-router and CSSTransitionGroup
- */
-
-div[class*="-transition-leave"], div[class*="-transition-enter"] {
- /* keep animated .layout divs pinned to viewport boundaries */
- position: absolute;
- top: 0;
- left: 0;
- width: 100vw;
-
- /* disable UI interaction during transitions */
- pointer-events: none;
-}
-
-.transition-container {
- position: relative;
- width: 100vw;
- height: 100vh;
- overflow: hidden;
-}
-
-/* New view slides bottom top */
-
-.slide-up-transition-leave { z-index: 0; }
-.slide-up-transition-enter {
- transform: translateY(100vh);
- z-index: 1;
-}
-
-.slide-up-transition-enter.slide-up-transition-enter-active {
- transform: translateY(0);
- transition: transform 450ms ease;
-}
-
-/* New view slides top bottom */
-
-.slide-down-transition-enter { z-index: 0; }
-.slide-down-transition-leave {
- transform: translateY(0);
- z-index: 1;
-}
-
-.slide-down-transition-leave.slide-down-transition-leave-active {
- transform: translateY(100vh);
- transition: transform 450ms ease;
-}
-
-/* New view slids right to left */
-
-.push-transition-leave {
- transform: translateX(0vw);
- z-index: 0;
-}
-
-.push-transition-enter {
- transform: translateX(100vw);
- z-index: 1;
-}
-
-.push-transition-leave.push-transition-leave-active {
- transform: translateX(-50vw);
- transition: transform 450ms ease;
-}
-
-.push-transition-enter.push-transition-enter-active {
- transform: translateX(0);
- transition: transform 450ms ease;
-}
-
-/* New view slides left to right */
-
-.pop-transition-enter {
- transform: translateX(-50vw);
- z-index: 0;
-}
-
-.pop-transition-leave {
- transform: translateX(0);
- z-index: 1;
-}
-
-.pop-transition-enter.pop-transition-enter-active {
- transform: translateX(0vw);
- transition: transform 450ms ease;
-}
-
-.pop-transition-leave.pop-transition-leave-active {
- transform: translateX(100vw);
- transition: transform 450ms ease;
-} \ No newline at end of file
diff --git a/app/components/TransitionContainer.js b/app/components/TransitionContainer.js
new file mode 100644
index 0000000000..c4a011a978
--- /dev/null
+++ b/app/components/TransitionContainer.js
@@ -0,0 +1,201 @@
+// @flow
+import * as React from 'react';
+import { Styles, Component, Animated, View, Types, UserInterface } from 'reactxp';
+import type { TransitionGroupProps } from '../transitions';
+
+type TransitionContainerProps = {
+ children: React.Node,
+ ...TransitionGroupProps
+};
+
+type State = {
+ previousChildren: ?React.Node,
+ childrenAnimation: Types.AnimatedViewStyleRuleSet,
+ previousChildrenAnimation: Types.AnimatedViewStyleRuleSet,
+ animationStyles: Types.AnimatedViewStyleRuleSet,
+ dimensions: Types.Dimensions,
+};
+
+export default class TransitionContainer extends Component<TransitionContainerProps, State> {
+
+ constructor(props: TransitionContainerProps) {
+ super(props);
+
+ const dimensions = UserInterface.measureWindow();
+
+ this.state = {
+ dimensions,
+ animationStyles: {
+ style: Styles.createAnimatedViewStyle({
+ position: 'absolute',
+ width: dimensions.width,
+ height: dimensions.height,
+ }),
+ allowPointerEvents: Styles.createAnimatedViewStyle({
+ pointerEvents: 'auto',
+ })
+ }
+ };
+ }
+
+ componentWillReceiveProps(nextProps: TransitionContainerProps) {
+ switch (nextProps.name){
+ case 'slide-up':
+ this.slideUpTransition(nextProps);
+ break;
+ case 'slide-down':
+ this.slideDownTransition(nextProps);
+ break;
+ case 'push':
+ this.pushTransition(nextProps);
+ break;
+ case 'pop':
+ this.popTransition(nextProps);
+ break;
+ default:
+ break;
+ }
+ }
+
+ onFinishedAnimation() {
+ this.setState({
+ childrenAnimation: this.state.animationStyles.allowPointerEvents,
+ previousChildren: null
+ });
+ }
+
+ slideUpTransition(nextProps: TransitionContainerProps) {
+ const currentTranslationValue = Animated.createValue(this.state.dimensions.height);
+ this.setState({
+ previousChildren: this.props.children,
+ childrenAnimation: Styles.createAnimatedViewStyle({
+ pointerEvents: 'none',
+ zIndex: 1,
+ transform: [{ translateY: currentTranslationValue }]
+ }),
+ previousChildrenAnimation: Styles.createAnimatedViewStyle({
+ pointerEvents: 'none',
+ zIndex: 0,
+ transform: [{ translateY: 0 }]
+ }),
+ }, () => {
+ Animated.timing(currentTranslationValue, {
+ toValue: 0,
+ easing: Animated.Easing.InOut(),
+ duration: nextProps.duration,
+ useNativeDriver: true,
+ }).start(() => this.onFinishedAnimation());
+ });
+ }
+
+ slideDownTransition(nextProps: TransitionContainerProps) {
+ const previousTranslationValue = Animated.createValue(0);
+ this.setState({
+ previousChildren: this.props.children,
+ childrenAnimation: Styles.createAnimatedViewStyle({
+ pointerEvents: 'none',
+ zIndex: 0,
+ transform: [{ translateY: 0 }]
+ }),
+ previousChildrenAnimation: Styles.createAnimatedViewStyle({
+ pointerEvents: 'none',
+ zIndex: 1,
+ transform: [{ translateY: previousTranslationValue }]
+ }),
+ }, () => {
+ Animated.timing(previousTranslationValue, {
+ toValue: this.state.dimensions.height,
+ easing: Animated.Easing.InOut(),
+ duration: nextProps.duration,
+ useNativeDriver: true,
+ }).start(() => this.onFinishedAnimation());
+ });
+ }
+
+ pushTransition(nextProps: TransitionContainerProps) {
+ const currentTranslationValue = Animated.createValue(this.state.dimensions.width);
+ const previousTranslationValue = Animated.createValue(0);
+ this.setState({
+ previousChildren: this.props.children,
+ childrenAnimation: Styles.createAnimatedViewStyle({
+ pointerEvents: 'none',
+ zIndex: 1,
+ transform: [{ translateX: currentTranslationValue }]
+ }),
+ previousChildrenAnimation: Styles.createAnimatedViewStyle({
+ pointerEvents: 'none',
+ zIndex: 0,
+ transform: [{ translateX: previousTranslationValue }]
+ }),
+ }, () => {
+ const compositeAnimation = Animated.parallel([
+ Animated.timing(currentTranslationValue, {
+ toValue: 0,
+ easing: Animated.Easing.InOut(),
+ duration: nextProps.duration,
+ useNativeDriver: true,
+ }),
+ Animated.timing(previousTranslationValue, {
+ toValue: - this.state.dimensions.width / 2,
+ easing: Animated.Easing.InOut(),
+ duration: nextProps.duration,
+ useNativeDriver: true,
+ })
+ ]);
+ compositeAnimation.start(() => this.onFinishedAnimation());
+ });
+ }
+
+ popTransition(nextProps: TransitionContainerProps) {
+ const currentTranslationValue = Animated.createValue(- this.state.dimensions.width / 2 );
+ const previousTranslationValue = Animated.createValue(0);
+ this.setState({
+ previousChildren: this.props.children,
+ childrenAnimation: Styles.createAnimatedViewStyle({
+ pointerEvents: 'none',
+ zIndex: 0,
+ transform: [{ translateX: currentTranslationValue }]
+ }),
+ previousChildrenAnimation: Styles.createAnimatedViewStyle({
+ pointerEvents: 'none',
+ zIndex: 1,
+ transform: [{ translateX: previousTranslationValue }]
+ }),
+ }, () => {
+ const compositeAnimation = Animated.parallel([
+ Animated.timing(currentTranslationValue, {
+ toValue: 0,
+ easing: Animated.Easing.InOut(),
+ duration: nextProps.duration,
+ useNativeDriver: true,
+ }),
+ Animated.timing(previousTranslationValue, {
+ toValue: this.state.dimensions.width,
+ easing: Animated.Easing.InOut(),
+ duration: nextProps.duration,
+ useNativeDriver: true,
+ })
+ ]);
+ compositeAnimation.start(() => this.onFinishedAnimation());
+ });
+ }
+
+ render() {
+ const { children } = this.props;
+ const { previousChildren, childrenAnimation, previousChildrenAnimation } = this.state;
+ return (
+ <View style={{flex:1}}>
+
+ { previousChildren &&
+ (<Animated.View style={[this.state.animationStyles.style, previousChildrenAnimation]}>
+ { previousChildren }
+ </Animated.View>) }
+
+ <Animated.View style={[this.state.animationStyles.style, childrenAnimation]}>
+ { children }
+ </Animated.View>
+
+ </View>
+ );
+ }
+} \ No newline at end of file
diff --git a/app/routes.js b/app/routes.js
index f9917f17bf..b5e1f8aa07 100644
--- a/app/routes.js
+++ b/app/routes.js
@@ -2,7 +2,7 @@
import * as React from 'react';
import { Switch, Route, Redirect } from 'react-router';
-import { CSSTransitionGroup } from 'react-transition-group';
+import TransitionContainer from './components/TransitionContainer';
import PlatformWindow from './components/PlatformWindow';
import LoginPage from './containers/LoginPage';
import ConnectPage from './containers/ConnectPage';
@@ -92,7 +92,7 @@ export default function makeRoutes(getState: ReduxGetState, componentProps: Shar
return (
<PlatformWindow>
- <CSSTransitionGroup component="div" className="transition-container" { ...transitionProps }>
+ <TransitionContainer { ...transitionProps }>
<Switch key={ location.key } location={ location }>
<LoginRoute exact path="/" component={ LoginPage } />
<PrivateRoute exact path="/connect" component={ ConnectPage } />
@@ -103,7 +103,7 @@ export default function makeRoutes(getState: ReduxGetState, componentProps: Shar
<PublicRoute exact path="/settings/support" component={ SupportPage } />
<PrivateRoute exact path="/select-location" component={ SelectLocationPage } />
</Switch>
- </CSSTransitionGroup>
+ </TransitionContainer>
</PlatformWindow>
);
}} />
diff --git a/app/transitions.js b/app/transitions.js
index 58e692f99c..3e825c2968 100644
--- a/app/transitions.js
+++ b/app/transitions.js
@@ -1,16 +1,10 @@
// @flow
-
import TransitionRule from './lib/transition-rule';
import type { TransitionFork, TransitionDescriptor } from './lib/transition-rule';
-export type CSSTransitionGroupProps = {
- transitionName: string,
- transitionEnterTimeout: number,
- transitionLeaveTimeout: number,
- transitionEnter: boolean,
- transitionLeave: boolean,
- transitionAppear?: boolean,
- transitionAppearTimeout?: number
+export type TransitionGroupProps = {
+ name: string,
+ duration: number,
};
type TransitionMap = {
@@ -23,21 +17,21 @@ type TransitionMap = {
const transitions: TransitionMap = {
slide: {
forward: {
- name: 'slide-up-transition',
+ name: 'slide-up',
duration: 450
},
backward: {
- name: 'slide-down-transition',
+ name: 'slide-down',
duration: 450
}
},
push: {
forward: {
- name: 'push-transition',
+ name: 'push',
duration: 450
},
backward: {
- name: 'pop-transition',
+ name: 'pop',
duration: 450
}
}
@@ -57,12 +51,12 @@ const transitionRules = [
];
/**
- * Calculate CSSTransitionGroup props.
+ * Calculate TransitionGroup props.
*
* @param {string} [fromRoute] - source route
* @param {string} toRoute - target route
*/
-export function getTransitionProps(fromRoute: ?string, toRoute: string): CSSTransitionGroupProps {
+export function getTransitionProps(fromRoute: ?string, toRoute: string): TransitionGroupProps {
// ignore initial transition and transition between the same routes
if(!fromRoute || fromRoute === toRoute) {
return noTransitionProps();
@@ -71,7 +65,7 @@ export function getTransitionProps(fromRoute: ?string, toRoute: string): CSSTran
for(const rule of transitionRules) {
const match = rule.match(fromRoute, toRoute);
if(match) {
- return toCSSTransitionGroupProps(match.descriptor);
+ return toTransitionGroupProps(match.descriptor);
}
}
@@ -79,30 +73,24 @@ export function getTransitionProps(fromRoute: ?string, toRoute: string): CSSTran
}
/**
- * Integrate TransitionDescriptor into CSSTransitionGroupProps
+ * Integrate TransitionDescriptor into TransitionGroupProps
* @param {TransitionDescriptor} descriptor
*/
-function toCSSTransitionGroupProps(descriptor: TransitionDescriptor): CSSTransitionGroupProps {
+function toTransitionGroupProps(descriptor: TransitionDescriptor): TransitionGroupProps {
const {name, duration} = descriptor;
return {
- transitionName: name,
- transitionEnterTimeout: duration,
- transitionLeaveTimeout: duration,
- transitionEnter: true,
- transitionLeave: true
+ name: name,
+ duration: duration,
};
}
/**
- * Returns default props with animations disabled
+ * Returns default props with no animation
*/
-function noTransitionProps(): CSSTransitionGroupProps {
+function noTransitionProps(): TransitionGroupProps {
return {
- transitionName: '',
- transitionEnterTimeout: 0,
- transitionLeaveTimeout: 0,
- transitionEnter: false,
- transitionLeave: false
+ name: '',
+ duration: 0,
};
}
diff --git a/flow-typed/npm/react-transition-group_vx.x.x.js b/flow-typed/npm/react-transition-group_vx.x.x.js
deleted file mode 100644
index 3de6d4279a..0000000000
--- a/flow-typed/npm/react-transition-group_vx.x.x.js
+++ /dev/null
@@ -1,80 +0,0 @@
-// flow-typed signature: 29dbe0ae1689d8d245ff83cf4c3ce647
-// flow-typed version: <<STUB>>/react-transition-group_v^1.2.0/flow_v0.65.0
-
-/**
- * This is an autogenerated libdef stub for:
- *
- * 'react-transition-group'
- *
- * Fill this stub out by replacing all the `any` types.
- *
- * Once filled out, we encourage you to share your work with the
- * community by sending a pull request to:
- * https://github.com/flowtype/flow-typed
- */
-
-declare module 'react-transition-group' {
- declare module.exports: any;
-}
-
-/**
- * We include stubs for each file inside this npm package in case you need to
- * require those files directly. Feel free to delete any files that aren't
- * needed.
- */
-declare module 'react-transition-group/CSSTransitionGroup' {
- declare module.exports: any;
-}
-
-declare module 'react-transition-group/CSSTransitionGroupChild' {
- declare module.exports: any;
-}
-
-declare module 'react-transition-group/dist/react-transition-group' {
- declare module.exports: any;
-}
-
-declare module 'react-transition-group/dist/react-transition-group.min' {
- declare module.exports: any;
-}
-
-declare module 'react-transition-group/TransitionGroup' {
- declare module.exports: any;
-}
-
-declare module 'react-transition-group/utils/ChildMapping' {
- declare module.exports: any;
-}
-
-declare module 'react-transition-group/utils/PropTypes' {
- declare module.exports: any;
-}
-
-// Filename aliases
-declare module 'react-transition-group/CSSTransitionGroup.js' {
- declare module.exports: $Exports<'react-transition-group/CSSTransitionGroup'>;
-}
-declare module 'react-transition-group/CSSTransitionGroupChild.js' {
- declare module.exports: $Exports<'react-transition-group/CSSTransitionGroupChild'>;
-}
-declare module 'react-transition-group/dist/react-transition-group.js' {
- declare module.exports: $Exports<'react-transition-group/dist/react-transition-group'>;
-}
-declare module 'react-transition-group/dist/react-transition-group.min.js' {
- declare module.exports: $Exports<'react-transition-group/dist/react-transition-group.min'>;
-}
-declare module 'react-transition-group/index' {
- declare module.exports: $Exports<'react-transition-group'>;
-}
-declare module 'react-transition-group/index.js' {
- declare module.exports: $Exports<'react-transition-group'>;
-}
-declare module 'react-transition-group/TransitionGroup.js' {
- declare module.exports: $Exports<'react-transition-group/TransitionGroup'>;
-}
-declare module 'react-transition-group/utils/ChildMapping.js' {
- declare module.exports: $Exports<'react-transition-group/utils/ChildMapping'>;
-}
-declare module 'react-transition-group/utils/PropTypes.js' {
- declare module.exports: $Exports<'react-transition-group/utils/PropTypes'>;
-}
diff --git a/package.json b/package.json
index 01045db15b..c9a8f45144 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,6 @@
"react-router": "^4.2.0",
"react-router-redux": "^5.0.0-alpha.9",
"react-simple-maps": "^0.10.1",
- "react-transition-group": "^1.2.0",
"reactxp": "^0.51.8",
"redux": "^3.0.0",
"redux-thunk": "^2.2.0",
diff --git a/yarn.lock b/yarn.lock
index b4bf86e758..27d84f9dc0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1641,10 +1641,6 @@ chai@^4.1.0:
pathval "^1.0.0"
type-detect "^4.0.0"
-chain-function@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc"
-
chainsaw@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98"
@@ -2476,10 +2472,6 @@ doctrine@^2.0.2, doctrine@^2.1.0:
dependencies:
esutils "^2.0.2"
-dom-helpers@^3.2.0:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"
-
dom-serializer@0, dom-serializer@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@@ -5989,7 +5981,7 @@ promised-io@*:
version "0.3.5"
resolved "https://registry.yarnpkg.com/promised-io/-/promised-io-0.3.5.tgz#4ad217bb3658bcaae9946b17a8668ecd851e1356"
-prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0:
+prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies:
@@ -6289,16 +6281,6 @@ react-transform-hmr@^1.0.4:
global "^4.3.0"
react-proxy "^1.1.7"
-react-transition-group@^1.2.0:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-1.2.1.tgz#e11f72b257f921b213229a774df46612346c7ca6"
- dependencies:
- chain-function "^1.0.0"
- dom-helpers "^3.2.0"
- loose-envify "^1.3.1"
- prop-types "^15.5.6"
- warning "^3.0.0"
-
react@^16.0.0:
version "16.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"