diff --git a/package.json b/package.json index 2afbfba1a..6b1a6a5be 100644 --- a/package.json +++ b/package.json @@ -41,9 +41,8 @@ "react-container-dimensions": "^1.3.3", "react-display-name": "^0.2.3", "react-dom": "^16.3.2", - "react-motion": "^0.5.2", "react-onclickout": "^2.0.8", - "react-spring": "^5.4.4", + "react-spring": "^6.1.8", "resolve-pathname": "^2.2.0", "styled-components": "^3.3.3", "underscore": "1.8.3", diff --git a/src/apps/Permissions/Permissions.js b/src/apps/Permissions/Permissions.js index 89c8eed80..25a8f2a0e 100644 --- a/src/apps/Permissions/Permissions.js +++ b/src/apps/Permissions/Permissions.js @@ -28,7 +28,9 @@ class Permissions extends React.Component { } componentDidMount() { - this.setState({ animateScreens: true }) + setTimeout(() => { + this.setState({ animateScreens: true }) + }, 0) } componentDidUpdate(prevProps) { @@ -215,39 +217,50 @@ class Permissions extends React.Component { }} /> - - {location.screen === 'home' && ( - - )} - - - - {['app', 'entity'].includes(location.screen) && ( - - {location.screen === 'app' && ( - - )} - {location.screen === 'entity' && ( - - )} - - )} - +
+ + {location.screen === 'home' && ( + + )} + + + + {['app', 'entity'].includes(location.screen) && ( + + {location.screen === 'app' && ( + + )} + {location.screen === 'entity' && ( + + )} + + )} + +
( - `translate3d(${t * 100}%, 0, 0)`), - }} - > - {children} - -) - -Main.propTypes = { - active: PropTypes.bool.isRequired, - children: PropTypes.node.isRequired, - left: PropTypes.object.isRequired, - opacity: PropTypes.object.isRequired, -} - const Screen = ({ position, children, animate }) => ( - {children && (props => )} + {children => children && (props =>
)} ) @@ -45,7 +32,24 @@ Screen.propTypes = { position: PropTypes.number.isRequired, } -const StyledMain = styled(Main)` +const Main = ({ children, opacity, left }) => ( + `translate3d(${t}%, 0, 0)`), + }} + > + {children} + +) + +Main.propTypes = { + children: PropTypes.node.isRequired, + left: PropTypes.object.isRequired, + opacity: PropTypes.object.isRequired, +} + +const StyledMain = styled(animated.div)` overflow: hidden; position: absolute; top: 0; diff --git a/src/components/App/AppLoadingProgressBar.js b/src/components/App/AppLoadingProgressBar.js index a22a7a411..b8c391b10 100644 --- a/src/components/App/AppLoadingProgressBar.js +++ b/src/components/App/AppLoadingProgressBar.js @@ -1,18 +1,21 @@ import React from 'react' import styled from 'styled-components' import ContainerDimensions from 'react-container-dimensions' -import { spring, Motion } from 'react-motion' -import { theme, spring as springConf } from '@aragon/ui' +import { Spring, animated } from 'react-spring' +import { theme } from '@aragon/ui' +import springs from '../../springs' const { accent } = theme const AppLoadingProgressBar = ({ hide, percent, ...props }) => ( - {({ opacity, percentProgress }) => ( @@ -20,7 +23,7 @@ const AppLoadingProgressBar = ({ hide, percent, ...props }) => ( `${(width * v) / 100}px`), }} {...props} > @@ -29,11 +32,11 @@ const AppLoadingProgressBar = ({ hide, percent, ...props }) => ( )} )} - + ) // Mimic nprogress with our own accent colour -const StyledProgressBar = styled.div` +const StyledProgressBar = styled(animated.div)` position: absolute; top: 0; height: 2px; diff --git a/src/components/ExpandableBox/ExpandableBox.js b/src/components/ExpandableBox/ExpandableBox.js deleted file mode 100644 index 09c1424bc..000000000 --- a/src/components/ExpandableBox/ExpandableBox.js +++ /dev/null @@ -1,145 +0,0 @@ -import React from 'react' -import { spring, Motion } from 'react-motion' -import styled from 'styled-components' -import throttle from 'lodash.throttle' -import { theme, spring as springConf, unselectable, Text } from '@aragon/ui' - -import arrow from './assets/arrow.svg' - -class ExpandableBox extends React.Component { - state = { - selfExpanded: false, - drawerHeight: 0, - } - componentDidMount() { - this.updateDrawerHeight() - window.addEventListener('resize', this.updateDrawerHeight) - } - componentWillUnmount() { - window.removeEventListener('resize', this.updateDrawerHeight) - } - - // Update the drawer height in the state - updateDrawerHeight = throttle(() => { - const rect = this.drawerElt.getBoundingClientRect() - this.setState({ drawerHeight: rect.height }) - }, 150) - - toggle = () => { - // Externally controlled toggle - if (this.props.onRequestToggle) { - this.props.onRequestToggle() - return - } - - // Self controlled toggle - this.setState({ selfExpanded: !this.state.selfExpanded }) - } - - render() { - const { title, summary, summaryFooter, expanded, children } = this.props - const { selfExpanded, drawerHeight } = this.state - const expandedFinal = expanded === undefined ? selfExpanded : expanded - return ( - - {({ openProgress }) => ( -
- - 0.1} - style={{ - boxShadow: ` - 0 7px 10px 0 rgba(101, 148, 170, ${0.08 * openProgress}) - `, - }} - > - -

- - {title} - -

-

{summary}

-
- {summaryFooter && {summaryFooter}} -
- - (this.drawerElt = elt)} - style={{ - marginTop: `${-drawerHeight * (1 - openProgress)}px`, - }} - > - {children} - - -
- )} -
- ) - } -} - -const Main = styled.section` - position: relative; - border: 1px solid ${theme.contentBorder}; - border-radius: 3px; - background: ${theme.contentBackground}; - ${unselectable}; -` - -const Arrow = styled.img` - position: absolute; - top: 30px; - right: 20px; -` - -const Summary = styled.div` - position: relative; - z-index: 1; - border-bottom: 1px solid - ${({ opened }) => (opened ? theme.contentBorder : theme.contentBackground)}; -` - -const SummaryTopArea = styled.div` - position: relative; - z-index: 1; - cursor: pointer; - padding: 20px; - border-bottom: 1px solid - ${({ opened }) => (opened ? theme.contentBorder : theme.contentBackground)}; - - h1 { - margin-bottom: 10px; - } -` - -const SummaryFooter = styled.div` - padding: 0 20px 20px; -` - -const DrawerWrapper = styled.div` - overflow: hidden; - position: relative; -` - -const Drawer = styled.div` - overflow: hidden; - position: relative; - padding: 30px 20px; - background: #edf3f6; -` - -const DrawerContent = styled.div`` - -export default ExpandableBox diff --git a/src/components/Home/Home.js b/src/components/Home/Home.js index 4586e33ac..333267f45 100644 --- a/src/components/Home/Home.js +++ b/src/components/Home/Home.js @@ -1,10 +1,11 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import { spring, Motion } from 'react-motion' -import { theme, AppBar, Text } from '@aragon/ui' +import { Spring, animated } from 'react-spring' +import { theme, AppView, Text } from '@aragon/ui' import HomeCard from './HomeCard' import { lerp } from '../../math-utils' +import springs from '../../springs' import logo from './assets/logo-background.svg' @@ -13,12 +14,10 @@ import imgFinance from './assets/finance.svg' import imgPayment from './assets/payment.svg' import imgVote from './assets/vote.svg' -const CARD_WIDTH = 220 -const CARD_HEIGHT = 200 +const CARD_WIDTH = 200 +const CARD_HEIGHT = 180 const CARD_MARGIN = 30 -const SPRING = { stiffness: 120, damping: 17, precision: 0.005 } - const actions = [ { id: 'assign-tokens', @@ -87,7 +86,7 @@ class Home extends React.Component { } } render() { - const { connected, apps, locator, appsLoading } = this.props + const { connected, apps, locator } = this.props const { showApps } = this.state const appActions = actions.filter(({ appName }) => @@ -95,13 +94,11 @@ class Home extends React.Component { ) return (
- - - - - - + + {({ showAppsProgress }) => ( @@ -125,27 +122,16 @@ class Home extends React.Component { : 'You are using Aragon 0.6 — Alba'} - {appsLoading || - (appActions.length > 0 && ( -

- - {showApps - ? 'What do you want to do?' - : 'Loading apps…'} - -

- ))} -
+ + {showApps ? 'What do you want to do?' : 'Loading apps…'} + +

+ @@ -160,12 +146,12 @@ class Home extends React.Component { ))} -
+
)} -
-
-
+ + + @@ -181,8 +167,6 @@ const Main = styled.div` display: flex; height: 100%; flex-direction: column; - align-items: stretch; - justify-content: stretch; background-color: ${theme.mainBackground}; background-image: url(${logo}); @@ -190,24 +174,11 @@ const Main = styled.div` background-repeat: no-repeat; ` -const AppBarWrapper = styled.div` - flex-shrink: 0; -` - -const ScrollWrapper = styled.div` - display: flex; - flex-direction: column; - justify-content: stretch; - overflow-y: scroll; +const AppContent = styled.div` flex-grow: 1; -` - -const AppWrapper = styled.div` - flex-grow: 1; - min-height: min-content; - display: flex; - align-items: stretch; - justify-content: space-between; + flex-shrink: 1; + min-height: 0; + height: calc(100% - 54px); ` const AppFooter = styled.div` @@ -220,24 +191,16 @@ const AppFooter = styled.div` border-top: 1px solid ${theme.contentBorder}; ` -const ConnectionBullet = styled.span` - width: 8px; - height: 8px; - margin-top: -2px; - margin-right: 8px; - border-radius: 50%; - background: ${({ connected }) => - connected ? theme.positive : theme.negative}; -` - const Content = styled.div` display: flex; + width: 100%; + flex-grow: 1; flex-direction: column; justify-content: center; align-items: center; - width: 100%; + padding-top: 40px; + padding-bottom: 40px; text-align: center; - padding: 40px; ` const Title = styled.h1` @@ -257,8 +220,18 @@ const CardWrap = styled.div` flex-grow: 0; width: ${CARD_WIDTH}px; height: ${CARD_HEIGHT}px; - margin-bottom: ${CARD_MARGIN}px; + margin-top: ${CARD_MARGIN}px; margin-left: ${CARD_MARGIN}px; ` +const ConnectionBullet = styled.span` + width: 8px; + height: 8px; + margin-top: -2px; + margin-right: 8px; + border-radius: 50%; + background: ${({ connected }) => + connected ? theme.positive : theme.negative}; +` + export default Home diff --git a/src/components/MenuPanel/MenuPanel.js b/src/components/MenuPanel/MenuPanel.js index 4d236e9b1..ec2c20374 100644 --- a/src/components/MenuPanel/MenuPanel.js +++ b/src/components/MenuPanel/MenuPanel.js @@ -1,17 +1,8 @@ import React from 'react' import PropTypes from 'prop-types' -// import { spring, Motion } from 'react-motion' import styled from 'styled-components' -import { - theme, - unselectable, - // spring as springConf, - // IconWallet, - // IconNotifications, -} from '@aragon/ui' -// import ClickOutHandler from 'react-onclickout' -// import NotificationsPanel from '../NotificationsPanel/NotificationsPanel' -// import { lerp } from '../../math-utils' +import { theme, unselectable } from '@aragon/ui' +import memoize from 'lodash.memoize' import { appIconUrl } from '../../utils' import { staticApps } from '../../static-apps' import MenuPanelAppGroup from './MenuPanelAppGroup' @@ -70,6 +61,7 @@ class MenuPanel extends React.PureComponent { state = { notificationsOpened: false, } + handleNotificationsClick = event => { // Prevent clickout events to trigger event.nativeEvent.stopImmediatePropagation() @@ -80,16 +72,13 @@ class MenuPanel extends React.PureComponent { handleCloseNotifications = () => { this.setState({ notificationsOpened: false }) } + + getAppGroups = memoize(apps => prepareAppGroups(apps)) + render() { - const { - apps, - // notificationsObservable, - // onClearAllNotifications, - // onOpenNotification, - } = this.props - // const { notificationsOpened } = this.state + const { apps } = this.props + const appGroups = this.getAppGroups(apps) - const appGroups = prepareAppGroups(apps) const menuApps = [ APP_HOME, appGroups, @@ -103,13 +92,6 @@ class MenuPanel extends React.PureComponent {
Aragon - {/* -
- - - -
- */}
@@ -126,38 +108,11 @@ class MenuPanel extends React.PureComponent {
- - {/* - - {({ openProgress }) => ( - - - - )} - - */}
) } - renderAppGroup = (app, readyToExpand) => { + + renderAppGroup(app) { const { activeInstanceId, onOpenApp } = this.props const { appId, name, icon, instances = [] } = app @@ -173,25 +128,39 @@ class MenuPanel extends React.PureComponent { icon={icon} instances={instances} active={isActive} - expand={isActive && readyToExpand} + expand={isActive} activeInstanceId={activeInstanceId} onActivate={onOpenApp} /> ) } - renderLoadedAppGroup = apps => { - const { appsStatus, onRequestAppsReload } = this.props + + renderLoadedAppGroup(appGroups) { + const { appsStatus, activeInstanceId, onRequestAppsReload } = this.props + + // Used by the menu transition + const expandedInstancesCount = appGroups.reduce( + (height, { instances }) => + instances.length > 1 && + instances.findIndex( + ({ instanceId }) => instanceId === activeInstanceId + ) > -1 + ? height + instances.length + : height, + 0 + ) // Wrap the DAO apps in the loader return ( - {done => apps.map(app => this.renderAppGroup(app, done))} + {() => appGroups.map(app => this.renderAppGroup(app))} ) } @@ -220,18 +189,6 @@ const In = styled.div` box-shadow: 1px 0 15px rgba(0, 0, 0, 0.1); ` -// const IconButton = styled.span` -// cursor: pointer; -// ` - -// const NotificationsWrapper = styled.div` -// position: fixed; -// z-index: 1; -// top: 0; -// bottom: 0; -// left: 220px; -// ` - const Header = styled.div` flex-shrink: 0; display: flex; diff --git a/src/components/MenuPanel/MenuPanelAppGroup.js b/src/components/MenuPanel/MenuPanelAppGroup.js index bf98e4865..ee8880373 100644 --- a/src/components/MenuPanel/MenuPanelAppGroup.js +++ b/src/components/MenuPanel/MenuPanelAppGroup.js @@ -1,15 +1,16 @@ import React from 'react' import PropTypes from 'prop-types' -import { spring, Motion } from 'react-motion' import styled from 'styled-components' -import { theme, spring as springConf, IconBlank } from '@aragon/ui' -import MenuPanelInstance from './MenuPanelInstance' +import { Spring, animated } from 'react-spring' +import { theme, IconBlank } from '@aragon/ui' import color from 'onecolor' +import MenuPanelInstance from './MenuPanelInstance' +import springs from '../../springs' class MenuPanelAppGroup extends React.PureComponent { static propTypes = { active: PropTypes.bool.isRequired, - activeInstanceId: PropTypes.string.isRequired, + activeInstanceId: PropTypes.string, expand: PropTypes.bool.isRequired, icon: PropTypes.object.isRequired, instances: PropTypes.array.isRequired, @@ -40,13 +41,10 @@ class MenuPanelAppGroup extends React.PureComponent { } = this.props const singleInstance = instances.length === 1 return ( - {({ openProgress }) => (
@@ -55,7 +53,9 @@ class MenuPanelAppGroup extends React.PureComponent { `translate3d(-${(1 - v) * 100}%, 0, 0)` + ), }} /> @@ -71,11 +71,13 @@ class MenuPanelAppGroup extends React.PureComponent { {instances.length > 1 && ( -
    `${(instances.length * 30 + 5) * v}px` + ), + paddingBottom: openProgress.interpolate(v => `${5 * v}px`), }} > {instances.map(({ name, instanceId, identifier }) => { @@ -91,11 +93,11 @@ class MenuPanelAppGroup extends React.PureComponent { ) : null })} -
+ )}
)} -
+ ) } } @@ -165,7 +167,7 @@ const ActiveBackground = styled.div` .cssa()}; ` -const MenuItemBar = styled.div` +const MenuItemBar = styled(animated.div)` position: absolute; width: 4px; height: 100%; diff --git a/src/components/MenuPanel/MenuPanelAppsLoader.js b/src/components/MenuPanel/MenuPanelAppsLoader.js index a583d9157..379988bce 100644 --- a/src/components/MenuPanel/MenuPanelAppsLoader.js +++ b/src/components/MenuPanel/MenuPanelAppsLoader.js @@ -2,10 +2,10 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' import { Spring, animated } from 'react-spring' -import { IconError, Button, theme, springs } from '@aragon/ui' +import { IconError, Button, theme } from '@aragon/ui' import color from 'onecolor' -import { lerp } from '../../math-utils' import { noop } from '../../utils' +import springs from '../../springs' import LoadingRing from '../LoadingRing' import { APPS_STATUS_ERROR, @@ -21,54 +21,94 @@ class MenuPanelAppsLoader extends React.Component { APPS_STATUS_LOADING, ]).isRequired, children: PropTypes.func.isRequired, - itemsCount: PropTypes.number.isRequired, + expandedInstancesCount: PropTypes.number.isRequired, + appsCount: PropTypes.number.isRequired, onRetry: PropTypes.func.isRequired, } static defaultProps = { children: noop, - itemsCount: 0, appStatus: APPS_STATUS_LOADING, onRetry: noop, } state = { showApps: false, + transitionDone: false, } componentWillReceiveProps({ appsStatus }) { if (appsStatus !== this.props.appsStatus) { // Always set this to false to reinitialize the animation - this.setState({ showApps: false }) + this.setState({ showApps: false, transitionDone: false }) } } handleRest = () => { + const { showApps } = this.state + if (this.props.appsStatus === APPS_STATUS_READY) { - setTimeout(() => { - this.setState({ showApps: true }) - }, 0) + this.setState({ + // `showApps: true` triggers the transition showing the apps. + showApps: true, + + // The second time handleRest() is called, showApps is `true`, which + // means that we reached the end of the transition. + transitionDone: showApps, + }) + } + } + + getInstancesHeight() { + const { appsStatus, appsCount, expandedInstancesCount } = this.props + const { showApps, transitionDone } = this.state + if (transitionDone) { + return -1 } + if (appsStatus === APPS_STATUS_READY || showApps) { + return ( + appsCount * 40 + + expandedInstancesCount * 30 + + (expandedInstancesCount > 0 ? 5 : 0) + ) + } + return appsStatus === APPS_STATUS_ERROR ? 80 : 40 } render() { - const { children, itemsCount, appsStatus, onRetry } = this.props - const { showApps } = this.state + const { children, appsStatus, onRetry } = this.props + const { showApps, transitionDone } = this.state return ( - {({ afterLoadingMessageProgress, showAppsProgress }) => ( -
+ {({ + afterLoadingMessageProgress, + showAppsProgress, + instancesHeight, + }) => ( +
(v === -1 ? 'auto' : `${v}px`) + ), + }} + > - + 1 - v), + transform: showAppsProgress.interpolate( + v => ` + translate3d(${v * 40}px, 0, 0) + ` + ), + }} + > @@ -120,22 +169,15 @@ class MenuPanelAppsLoader extends React.Component { - `${lerp( - v, - 40, - (appsStatus === APPS_STATUS_READY ? itemsCount : 2) * 40 - )}px` - ), + opacity: showAppsProgress, transform: showAppsProgress.interpolate( v => ` - translate3d(-${(1 - Math.min(1, v)) * 100}%, 0, 0) + translate3d(-${(1 - v) * 60}px, 0, 0) ` ), }} > - {children(showApps)} + {children()}
)} @@ -154,7 +196,7 @@ const StatusIndicatorWrapper = styled.div` margin-right: 15px; ` -const Main = styled.div` +const Main = styled(animated.div)` position: relative; overflow: hidden; ` @@ -193,7 +235,7 @@ const IconErrorWrapper = styled(animated.div)` justify-content: center; ` -const StatusContent = styled.div` +const StatusContent = styled(animated.div)` display: flex; align-items: center; position: relative; diff --git a/src/components/ModalManager/ModalManager.js b/src/components/ModalManager/ModalManager.js index 0011224b9..411d920e3 100644 --- a/src/components/ModalManager/ModalManager.js +++ b/src/components/ModalManager/ModalManager.js @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components' import { Transition, animated } from 'react-spring' -import { springs } from '@aragon/ui' +import springs from '../../springs' const ModalContext = React.createContext({ modalComponent: null, @@ -43,14 +43,15 @@ const ModalView = () => ( {({ modalComponent: ModalComponent, modalComponentProps, hideModal }) => ( - {ModalComponent && + {ModalComponent => + ModalComponent && (({ opacity, enterProgress, blocking }) => ( v * 0.5) }} /> @@ -68,7 +69,8 @@ const ModalView = () => ( - ))} + )) + } )} diff --git a/src/components/SignerPanel/SignerPanel.js b/src/components/SignerPanel/SignerPanel.js index 6443ee740..11f6ffbd6 100644 --- a/src/components/SignerPanel/SignerPanel.js +++ b/src/components/SignerPanel/SignerPanel.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import { SidePanel, springs } from '@aragon/ui' +import { SidePanel } from '@aragon/ui' import { Transition, animated } from 'react-spring' import { addressesEqual } from '../../web3-utils' import ConfirmTransaction from './ConfirmTransaction' @@ -14,6 +14,8 @@ import { STATUS_ERROR, } from './signer-statuses' +import springs from '../../springs' + const INITIAL_STATE = { panelOpened: false, intent: {}, @@ -167,57 +169,57 @@ class SignerPanel extends React.Component { >
- {status === STATUS_CONFIRMING - ? ({ enterProgress, signingEnabled }) => ( - `translate3d(${-100 * (1 - v)}%, 0, 0)` - ), - }} - > - - - - - ) - : ({ enterProgress, signError }) => ( - `translate3d(${100 * (1 - v)}%, 0, 0)` - ), - }} - > - - - - - )} + {confirming => + confirming + ? ({ enterProgress }) => ( + + + + + + ) + : ({ enterProgress }) => ( + + + + + + ) + }
diff --git a/src/onboarding/Domain.js b/src/onboarding/Domain.js index 542edfc5e..6ff9aa4e6 100644 --- a/src/onboarding/Domain.js +++ b/src/onboarding/Domain.js @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components' import { theme, Text, TextInput, IconCheck, IconCross } from '@aragon/ui' -import { lerp } from '../math-utils' +import { animated } from 'react-spring' import { noop } from '../utils' import LoadingRing from '../components/LoadingRing' @@ -14,17 +14,14 @@ import { class Domain extends React.Component { static defaultProps = { - positionProgress: 0, + screenPosition: 0, domain: '', domainCheckStatus: DomainCheckNone, onDomainChange: noop, onSubmit: noop, } - componentWillReceiveProps({ positionProgress }) { - if ( - positionProgress === 0 && - positionProgress !== this.props.positionProgress - ) { + componentWillReceiveProps({ forceFocus }) { + if (forceFocus && forceFocus !== this.props.forceFocus) { this.focusEl.focus() } } @@ -33,22 +30,16 @@ class Domain extends React.Component { } handleSubmit = event => { event.preventDefault() - this.focusEl.blur() this.props.onSubmit() } handleFocusElRef = el => { this.focusEl = el } render() { - const { positionProgress, domain, domainCheckStatus } = this.props + const { domain, domainCheckStatus, screenTransitionStyles } = this.props return (
- + +
) @@ -39,7 +31,7 @@ class LaunchContent extends React.PureComponent { } } -const Main = styled.div` +const Main = styled(animated.div)` display: flex; align-items: center; justify-content: center; diff --git a/src/onboarding/Onboarding.js b/src/onboarding/Onboarding.js index 3bb2cb3db..a80f3067b 100644 --- a/src/onboarding/Onboarding.js +++ b/src/onboarding/Onboarding.js @@ -1,11 +1,11 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import { Motion, spring } from 'react-motion' -import { spring as springConf } from '@aragon/ui' +import { Spring, animated } from 'react-spring' import { noop } from '../utils' import { getUnknownBalance } from '../web3-utils' import { isNameAvailable } from '../aragonjs-wrapper' +import springs from '../springs' import * as Steps from './steps' import Templates from './templates' @@ -32,18 +32,6 @@ import { DAO_CREATION_STATUS_ERROR, } from '../symbols' -const SPRING_SHOW = { - stiffness: 120, - damping: 17, - precision: 0.001, -} -const SPRING_HIDE = { - stiffness: 70, - damping: 15, - precision: 0.001, -} -const SPRING_SCREEN = springConf('slow') - const initialState = { template: null, templateData: {}, @@ -404,29 +392,30 @@ class Onboarding extends React.PureComponent { const step = this.currentStep() const steps = this.getSteps() return ( - {({ showProgress }) => (
`translate3d(0, ${110 * (1 - v)}%, 0)` + ), + opacity: showProgress, }} > {banner} - {({ screenProgress }) => ( @@ -436,8 +425,9 @@ class Onboarding extends React.PureComponent { {this.renderScreen( screen, - i - stepIndex, - i - screenProgress + i, + screenProgress, + screen === step.screen )} ))} @@ -453,15 +443,15 @@ class Onboarding extends React.PureComponent { /> )} - +
)} -
+ ) } - renderScreen(screen, position, positionProgress) { + renderScreen(screen, screenIndex, positionProgress, isActive) { const { template, domain, @@ -480,9 +470,23 @@ class Onboarding extends React.PureComponent { } = this.props // No need to move the screens farther than one step - positionProgress = Math.min(1, Math.max(-1, positionProgress)) + const getPositionProgress = progress => + Math.min(1, Math.max(-1, screenIndex - progress)) + + // used by every screen + const screenTransitionStyles = { + opacity: positionProgress.interpolate( + v => 1 - Math.abs(getPositionProgress(v)) + ), + transform: positionProgress.interpolate( + v => `translateX(${getPositionProgress(v) * 50}%)` + ), + } - const sharedProps = { positionProgress } + const sharedProps = { + screenTransitionStyles, + forceFocus: isActive, + } if (screen === 'start') { return ( @@ -558,7 +562,7 @@ class Onboarding extends React.PureComponent { } } -const Main = styled.div` +const Main = styled(animated.div)` position: fixed; z-index: 2; top: 0; diff --git a/src/onboarding/PrevNext.js b/src/onboarding/PrevNext.js index 59c11e804..ebc8ad752 100644 --- a/src/onboarding/PrevNext.js +++ b/src/onboarding/PrevNext.js @@ -1,8 +1,9 @@ import React from 'react' import styled from 'styled-components' -import { Motion, spring } from 'react-motion' -import { spring as springConf, Button } from '@aragon/ui' +import { Spring, animated } from 'react-spring' +import { Button } from '@aragon/ui' import { lerp } from '../math-utils' +import springs from '../springs' class PrevNext extends React.Component { render() { @@ -16,10 +17,10 @@ class PrevNext extends React.Component { isSigningNext, } = this.props return ( - {({ showProgress }) => (
`translateY(${lerp(v, 40, 0)}px)` + ) : 'none', opacity: showProgress, }} @@ -41,7 +44,7 @@ class PrevNext extends React.Component { />
)} -
+ ) } } @@ -69,7 +72,7 @@ class PrevNextContent extends React.PureComponent { } } -const Main = styled.div` +const Main = styled(animated.div)` position: absolute; left: 0; right: 0; diff --git a/src/onboarding/Sign.js b/src/onboarding/Sign.js index 321944a20..63569cef0 100644 --- a/src/onboarding/Sign.js +++ b/src/onboarding/Sign.js @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components' import { theme, Text, Button } from '@aragon/ui' -import { lerp } from '../math-utils' +import { animated } from 'react-spring' import { noop } from '../utils' import imgPending from '../assets/transaction-pending.svg' @@ -15,20 +15,14 @@ import { class Sign extends React.Component { static defaultProps = { - positionProgress: 0, daoCreationStatus: DAO_CREATION_STATUS_NONE, onTryAgain: noop, } render() { - const { positionProgress, daoCreationStatus, onTryAgain } = this.props + const { daoCreationStatus, onTryAgain, screenTransitionStyles } = this.props return (
- + - +
+ -1 return ( - 0 ? spring(stepIndex, springConf('fast')) : 0, + 0 ? stepIndex : 0, }} > {({ showProgress, stepProgress }) => ( @@ -61,7 +62,7 @@ class ProgressBar extends React.Component {
)} - + ) } } diff --git a/src/onboarding/Template.js b/src/onboarding/Template.js index 1ae1bf0b2..2ec14a02f 100644 --- a/src/onboarding/Template.js +++ b/src/onboarding/Template.js @@ -1,28 +1,22 @@ import React from 'react' import styled from 'styled-components' import { theme, Text } from '@aragon/ui' +import { animated } from 'react-spring' import { noop } from '../utils' -import { lerp } from '../math-utils' import TemplateCard from './TemplateCard' class Template extends React.Component { static defaultProps = { - positionProgress: 0, onSelect: noop, } handleTemplateSelect = template => { this.props.onSelect(template) } render() { - const { positionProgress, templates, activeTemplate } = this.props + const { templates, activeTemplate, screenTransitionStyles } = this.props return (
- + { event.preventDefault() - this.formEl.elements[0].blur() this.props.onSubmit() } handleFormRef = el => { this.formEl = el } render() { - const { positionProgress, fields } = this.props + const { fields, screenTransitionStyles } = this.props return ( -
+
( ) -const Main = styled.div` +const Main = styled(animated.div)` display: flex; align-items: flex-start; justify-content: center; diff --git a/src/onboarding/templates/democracy/ConfigureVotingDefaults.js b/src/onboarding/templates/democracy/ConfigureVotingDefaults.js index 7f745644d..7cea0da4c 100644 --- a/src/onboarding/templates/democracy/ConfigureVotingDefaults.js +++ b/src/onboarding/templates/democracy/ConfigureVotingDefaults.js @@ -1,12 +1,11 @@ import React from 'react' import styled from 'styled-components' import { Field, TextInput, Text, theme } from '@aragon/ui' -import { lerp } from '../../../math-utils' +import { animated } from 'react-spring' import { noop } from '../../../utils' class ConfigureVotingDefaults extends React.Component { static defaultProps = { - positionProgress: 0, onFieldUpdate: noop, onSubmit: noop, fields: {}, @@ -17,11 +16,8 @@ class ConfigureVotingDefaults extends React.Component { this.handleMinQuorumChange = this.createChangeHandler('minQuorum') this.handleVoteDurationChange = this.createChangeHandler('voteDuration') } - componentWillReceiveProps({ positionProgress }) { - if ( - positionProgress === 0 && - positionProgress !== this.props.positionProgress - ) { + componentWillReceiveProps({ forceFocus }) { + if (forceFocus && forceFocus !== this.props.forceFocus) { this.formEl.elements[0].focus() } } @@ -33,21 +29,15 @@ class ConfigureVotingDefaults extends React.Component { } handleSubmit = event => { event.preventDefault() - this.formEl.elements[0].blur() this.props.onSubmit() } handleFormRef = el => { this.formEl = el } render() { - const { positionProgress, fields } = this.props + const { fields, screenTransitionStyles } = this.props return ( -
+
( ) -const Main = styled.div` +const Main = styled(animated.div)` display: flex; align-items: flex-start; justify-content: center; diff --git a/src/onboarding/templates/multisig/ConfigureMultisigAddresses.js b/src/onboarding/templates/multisig/ConfigureMultisigAddresses.js index 2333dd2d1..c70476c3d 100644 --- a/src/onboarding/templates/multisig/ConfigureMultisigAddresses.js +++ b/src/onboarding/templates/multisig/ConfigureMultisigAddresses.js @@ -1,21 +1,17 @@ import React from 'react' import styled, { css } from 'styled-components' import { Field, Button, TextInput, Text, DropDown, theme } from '@aragon/ui' -import { lerp } from '../../../math-utils' +import { animated } from 'react-spring' import { noop } from '../../../utils' class ConfigureMultisigAddresses extends React.Component { static defaultProps = { - positionProgress: 0, onFieldUpdate: noop, onSubmit: noop, fields: {}, } - componentWillReceiveProps({ positionProgress }) { - if ( - positionProgress === 0 && - positionProgress !== this.props.positionProgress - ) { + componentWillReceiveProps({ forceFocus }) { + if (forceFocus && forceFocus !== this.props.forceFocus) { this.formEl.elements[0].focus() } } @@ -47,21 +43,15 @@ class ConfigureMultisigAddresses extends React.Component { } handleSubmit = event => { event.preventDefault() - this.formEl.elements[0].blur() this.props.onSubmit() } handleFormRef = el => { this.formEl = el } render() { - const { positionProgress, fields } = this.props + const { fields, screenTransitionStyles } = this.props return ( -
+
( ) -const Main = styled.div` +const Main = styled(animated.div)` display: flex; align-items: flex-start; justify-content: center; diff --git a/src/onboarding/templates/multisig/ConfigureMultisigToken.js b/src/onboarding/templates/multisig/ConfigureMultisigToken.js index 1b4745dff..218316f3a 100644 --- a/src/onboarding/templates/multisig/ConfigureMultisigToken.js +++ b/src/onboarding/templates/multisig/ConfigureMultisigToken.js @@ -1,12 +1,11 @@ import React from 'react' import styled from 'styled-components' import { Field, TextInput, Text, theme } from '@aragon/ui' -import { lerp } from '../../../math-utils' +import { animated } from 'react-spring' import { noop } from '../../../utils' class ConfigureTokenName extends React.Component { static defaultProps = { - positionProgress: 0, onFieldUpdate: noop, onSubmit: noop, fields: {}, @@ -16,11 +15,8 @@ class ConfigureTokenName extends React.Component { this.handleTokenNameChange = this.createChangeHandler('tokenName') this.handleTokenSymbolChange = this.createChangeHandler('tokenSymbol') } - componentWillReceiveProps({ positionProgress }) { - if ( - positionProgress === 0 && - positionProgress !== this.props.positionProgress - ) { + componentWillReceiveProps({ forceFocus }) { + if (forceFocus && forceFocus !== this.props.forceFocus) { this.formEl.elements[0].focus() } } @@ -32,21 +28,15 @@ class ConfigureTokenName extends React.Component { } handleSubmit = event => { event.preventDefault() - this.formEl.elements[0].blur() this.props.onSubmit() } handleFormRef = el => { this.formEl = el } render() { - const { positionProgress, fields } = this.props + const { fields, screenTransitionStyles } = this.props return ( -
+
( ) -const Main = styled.div` +const Main = styled(animated.div)` display: flex; align-items: flex-start; justify-content: center; diff --git a/src/springs.js b/src/springs.js new file mode 100644 index 000000000..63c77a9b0 --- /dev/null +++ b/src/springs.js @@ -0,0 +1,13 @@ +// TODO: remove when upgrading to the next version of Aragon UI. +// From https://github.com/aragon/aragon-ui/blob/ec45bb4fc5d764879d473434719bea8517ff06c1/src/utils/styles/spring.js + +export default { + // Slow spring, can be used to move large things (e.g. a side panel). + lazy: { mass: 1, tension: 120, friction: 20, precision: 0.01 }, + + // Medium speed spring, can be used to move small objects. + smooth: { mass: 0.7, tension: 300, friction: 25, precision: 0.01 }, + + // Fast speed spring, for actions that need to feel almost instant. + swift: { mass: 0.5, tension: 400, friction: 30, precision: 0.01 }, +}