feat: refactor animation code

This commit is contained in:
fragosti
2018-10-30 15:21:58 -07:00
parent d0a0af5130
commit a49bf353f8
10 changed files with 130 additions and 74 deletions

View File

@@ -0,0 +1,70 @@
import * as React from 'react';
import { Keyframes } from 'styled-components';
import { css, keyframes, styled } from '../../style/theme';
const generateTransitionInfoCss = (
key: keyof TransitionInfo,
top?: TransitionInfo,
bottom?: TransitionInfo,
left?: TransitionInfo,
right?: TransitionInfo,
): string => {
const topStringIfExists = top ? `top: ${top[key]};` : '';
const bottomStringIfExists = bottom ? `bottom: ${bottom[key]};` : '';
const leftStringIfExists = left ? `left: ${left[key]};` : '';
const rightStringIfExists = right ? `right: ${right[key]};` : '';
return `
${topStringIfExists}
${bottomStringIfExists}
${leftStringIfExists}
${rightStringIfExists}
`;
};
const slideKeyframeGenerator = (
top?: TransitionInfo,
bottom?: TransitionInfo,
left?: TransitionInfo,
right?: TransitionInfo,
) => keyframes`
from {
position: relative;
${generateTransitionInfoCss('from', top, bottom, left, right)}
}
to {
position: relative;
${generateTransitionInfoCss('to', top, bottom, left, right)}
}
`;
export interface TransitionInfo {
from: string;
to: string;
}
export interface PositionAnimationProps {
top?: TransitionInfo;
bottom?: TransitionInfo;
left?: TransitionInfo;
right?: TransitionInfo;
timingFunction: string;
direction?: string;
}
export const PositionAnimation =
styled.div <
PositionAnimationProps >
`
animation-name: ${props =>
css`
${slideKeyframeGenerator(props.top, props.bottom, props.left, props.right)};
`};
animation-duration: 0.3s;
animation-timing-function: ${props => props.timingFunction};
animation-delay: 0s;
animation-iteration-count: 1;
animation-fill-mode: ${props => props.direction || 'none'};
position: relative;
`;

View File

@@ -3,56 +3,16 @@ import { Keyframes } from 'styled-components';
import { css, keyframes, styled } from '../../style/theme';
const slideKeyframeGenerator = (fromY: string, toY: string) => keyframes`
from {
position: relative;
top: ${fromY};
}
to {
position: relative;
top: ${toY};
}
`;
import { PositionAnimation, PositionAnimationProps } from './position_animation';
export type SlideAnimationPhase = 'slideIn' | 'slideOut';
export interface SlideAnimationProps {
keyframes: Keyframes;
animationType: string;
animationDirection?: string;
phase: SlideAnimationPhase;
slideIn: PositionAnimationProps;
slideOut: PositionAnimationProps;
}
export const SlideAnimation =
styled.div <
SlideAnimationProps >
`
animation-name: ${props =>
css`
${props.keyframes};
`};
animation-duration: 0.3s;
animation-timing-function: ${props => props.animationType};
animation-delay: 0s;
animation-iteration-count: 1;
animation-fill-mode: ${props => props.animationDirection || 'none'};
position: relative;
`;
export interface SlideAnimationComponentProps {
downY: string;
}
export const SlideUpAnimation: React.StatelessComponent<SlideAnimationComponentProps> = props => (
<SlideAnimation animationType="ease-in" keyframes={slideKeyframeGenerator(props.downY, '0px')}>
{props.children}
</SlideAnimation>
);
export const SlideDownAnimation: React.StatelessComponent<SlideAnimationComponentProps> = props => (
<SlideAnimation
animationDirection="forwards"
animationType="cubic-bezier(0.25, 0.1, 0.25, 1)"
keyframes={slideKeyframeGenerator('0px', props.downY)}
>
{props.children}
</SlideAnimation>
);
export const SlideAnimation: React.StatelessComponent<SlideAnimationProps> = props => {
const propsToUse = props.phase === 'slideIn' ? props.slideIn : props.slideOut;
return <PositionAnimation {...propsToUse}>{props.children}</PositionAnimation>;
};

View File

@@ -15,7 +15,7 @@ export interface ERC20AssetAmountInputProps {
asset?: ERC20Asset;
value?: BigNumberInput;
onChange: (value?: BigNumberInput, asset?: ERC20Asset) => void;
onSymbolClick: (asset?: ERC20Asset) => void;
onSymbolClick?: (asset?: ERC20Asset) => void;
startingFontSizePx: number;
fontColor?: ColorOption;
isDisabled: boolean;
@@ -28,7 +28,6 @@ export interface ERC20AssetAmountInputState {
export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInputProps, ERC20AssetAmountInputState> {
public static defaultProps = {
onChange: util.boundNoop,
onSymbolClick: util.boundNoop,
isDisabled: false,
};
constructor(props: ERC20AssetAmountInputProps) {
@@ -112,7 +111,9 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput
});
};
private readonly _handleSymbolClick = () => {
this.props.onSymbolClick(this.props.asset);
if (this.props.onSymbolClick) {
this.props.onSymbolClick(this.props.asset);
}
};
// For assets with symbols of different length,
// start scaling the input at different character lengths

View File

@@ -60,8 +60,8 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
private _renderAmountsSection(): React.ReactNode {
return (
<Container>
<Container marginBottom="5px">{this._placeholderOrAmount(this._ethAmount)}</Container>
<Container opacity={0.7}>{this._placeholderOrAmount(this._dollarAmount)}</Container>
<Container marginBottom="5px">{this._renderPlaceholderOrAmount(this._renderEthAmount)}</Container>
<Container opacity={0.7}>{this._renderPlaceholderOrAmount(this._renderDollarAmount)}</Container>
</Container>
);
}
@@ -92,7 +92,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
return 'I want to buy';
}
private _placeholderOrAmount(amountFunction: () => React.ReactNode): React.ReactNode {
private _renderPlaceholderOrAmount(amountFunction: () => React.ReactNode): React.ReactNode {
if (this.props.quoteRequestState === AsyncProcessState.PENDING) {
return <AmountPlaceholder isPulsating={true} color={PLACEHOLDER_COLOR} />;
}
@@ -102,7 +102,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
return amountFunction();
}
private readonly _ethAmount = (): React.ReactNode => {
private readonly _renderEthAmount = (): React.ReactNode => {
return (
<Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}>
{format.ethBaseAmount(
@@ -114,7 +114,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
);
};
private readonly _dollarAmount = (): React.ReactNode => {
private readonly _renderDollarAmount = (): React.ReactNode => {
return (
<Text fontSize="16px" fontColor={ColorOption.white}>
{format.ethBaseAmountInUsd(

View File

@@ -12,10 +12,11 @@ export interface PanelProps {
export const Panel: React.StatelessComponent<PanelProps> = ({ children, onClose }) => (
<Container
backgroundColor={ColorOption.white}
position="absolute"
top="0px"
left="0px"
width="100%"
// position="absolute"
// left="0px"
// bottom="0px"
// width="100%"
// height="100%"
height="100%"
zIndex={zIndex.panel}
>

View File

@@ -2,7 +2,8 @@ import * as React from 'react';
import { ColorOption } from '../style/theme';
import { SlideDownAnimation, SlideUpAnimation } from './animations/slide_animations';
import { PositionAnimationProps } from './animations/position_animation';
import { SlideAnimation, SlideAnimationPhase } from './animations/slide_animations';
import { Container, Flex, Text } from './ui';
@@ -31,16 +32,29 @@ export const Error: React.StatelessComponent<ErrorProps> = props => (
</Container>
);
export type SlidingDirection = 'up' | 'down';
export interface SlidingErrorProps extends ErrorProps {
direction: SlidingDirection;
phase: SlideAnimationPhase;
}
export const SlidingError: React.StatelessComponent<SlidingErrorProps> = props => {
const AnimationComponent = props.direction === 'up' ? SlideUpAnimation : SlideDownAnimation;
const slideAmount = '120px';
const slideUp: PositionAnimationProps = {
timingFunction: 'ease-in',
top: {
from: slideAmount,
to: '0px',
},
};
const slideDown: PositionAnimationProps = {
timingFunction: 'cubic-bezier(0.25, 0.1, 0.25, 1)',
top: {
from: '0px',
to: slideAmount,
},
direction: 'forwards',
};
return (
<AnimationComponent downY="120px">
<SlideAnimation slideIn={slideUp} slideOut={slideDown} phase={props.phase}>
<Error icon={props.icon} message={props.message} />
</AnimationComponent>
</SlideAnimation>
);
};

View File

@@ -29,6 +29,7 @@ export interface ContainerProps {
whiteSpace?: string;
opacity?: number;
cursor?: string;
overflow?: string;
}
export const Container =
@@ -59,6 +60,7 @@ export const Container =
${props => cssRuleIfExists(props, 'white-space')}
${props => cssRuleIfExists(props, 'opacity')}
${props => cssRuleIfExists(props, 'cursor')}
${props => cssRuleIfExists(props, 'overflow')}
${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')};
background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')};

View File

@@ -4,16 +4,16 @@ import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order
import { LatestError } from '../containers/latest_error';
import { SelectedAssetBuyOrderStateButtons } from '../containers/selected_asset_buy_order_state_buttons';
import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading';
import { ColorOption } from '../style/theme';
import { zIndex } from '../style/z_index';
import { Panel } from './panel';
import { Container, Flex } from './ui';
export interface ZeroExInstantContainerProps {}
export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantContainerProps> = props => (
<Container width="350px">
<Container width="350px" position="relative">
<Container zIndex={zIndex.errorPopup} position="relative">
<LatestError />
</Container>
@@ -23,6 +23,7 @@ export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantConta
backgroundColor={ColorOption.white}
borderRadius="3px"
hasBoxShadow={true}
overflow="hidden"
>
<Flex direction="column" justify="flex-start">
<SelectedAssetInstantHeading />
@@ -31,6 +32,11 @@ export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantConta
<SelectedAssetBuyOrderStateButtons />
</Container>
</Flex>
{/* <Container position="absolute" left="0px" bottom="0px" width="100%" height="100%">
<SlideAnimationHelper direction="up" downY="200px">
<Panel> Hey </Panel>
</SlideAnimationHelper>
</Container> */}
</Container>
</Container>
);

View File

@@ -2,6 +2,7 @@ import * as React from 'react';
import { connect } from 'react-redux';
import { SlideAnimationPhase } from '../components/animations/slide_animations';
import { SlidingError } from '../components/sliding_error';
import { State } from '../redux/reducer';
import { Asset, DisplayStatus } from '../types';
@@ -9,26 +10,26 @@ import { Asset, DisplayStatus } from '../types';
export interface LatestErrorComponentProps {
asset?: Asset;
latestErrorMessage?: string;
slidingDirection: 'down' | 'up';
slidingPhase: SlideAnimationPhase;
}
export const LatestErrorComponent: React.StatelessComponent<LatestErrorComponentProps> = props => {
if (!props.latestErrorMessage) {
return <div />;
}
return <SlidingError direction={props.slidingDirection} icon="😢" message={props.latestErrorMessage} />;
return <SlidingError phase={props.slidingPhase} icon="😢" message={props.latestErrorMessage} />;
};
interface ConnectedState {
asset?: Asset;
latestErrorMessage?: string;
slidingDirection: 'down' | 'up';
slidingPhase: SlideAnimationPhase;
}
export interface LatestErrorProps {}
const mapStateToProps = (state: State, _ownProps: LatestErrorProps): ConnectedState => ({
asset: state.selectedAsset,
latestErrorMessage: state.latestErrorMessage,
slidingDirection: state.latestErrorDisplayStatus === DisplayStatus.Present ? 'up' : 'down',
slidingPhase: state.latestErrorDisplayStatus === DisplayStatus.Present ? 'slideIn' : 'slideOut',
});
export const LatestError = connect(mapStateToProps)(LatestErrorComponent);

View File

@@ -19,6 +19,7 @@ import { errorFlasher } from '../util/error_flasher';
export interface SelectedERC20AssetAmountInputProps {
fontColor?: ColorOption;
startingFontSizePx: number;
onSymbolClick?: (asset?: ERC20Asset) => void;
}
interface ConnectedState {