Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

[instant] Viewport specific errors #1228

Merged
merged 20 commits into from
Nov 8, 2018
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 45 additions & 19 deletions packages/instant/src/components/animations/position_animation.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Keyframes } from 'styled-components';
import { InterpolationValue } from 'styled-components';

import { media, OptionallyScreenSpecific, stylesForMedia } from '../../style/media';
import { css, keyframes, styled } from '../../style/theme';

export interface TransitionInfo {
Expand Down Expand Up @@ -51,30 +52,55 @@ export interface PositionAnimationSettings {
right?: TransitionInfo;
timingFunction: string;
duration?: string;
position?: string;
}

export interface PositionAnimationProps extends PositionAnimationSettings {
position: string;
const generatePositionAnimationCss = (positionSettings: PositionAnimationSettings) => {
return css`
animation-name: ${slideKeyframeGenerator(
positionSettings.position || 'relative',
positionSettings.top,
positionSettings.bottom,
positionSettings.left,
positionSettings.right,
)};
animation-duration: ${positionSettings.duration || '0.3s'};
animation-timing-function: ${positionSettings.timingFunction};
animation-delay: 0s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
position: ${positionSettings.position || 'relative'};
width: 100%;
`;
};

export interface PositionAnimationProps {
positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
zIndex?: OptionallyScreenSpecific<number>;
}

const defaultAnimation = (positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>) => {
const bestDefault = 'default' in positionSettings ? positionSettings.default : positionSettings;
return generatePositionAnimationCss(bestDefault);
};
const animationForSize = (
positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>,
sizeKey: 'sm' | 'md' | 'lg',
mediaFn: (...args: any[]) => InterpolationValue[],
) => {
// checking default makes sure we have a PositionAnimationSettings object
// and then we check to see if we have a setting for the specific `sizeKey`
const animationSettingsForSize = 'default' in positionSettings && positionSettings[sizeKey];
return animationSettingsForSize && mediaFn`${generatePositionAnimationCss(animationSettingsForSize)}`;
};

export const PositionAnimation =
styled.div <
PositionAnimationProps >
`
animation-name: ${props =>
css`
${slideKeyframeGenerator(props.position, props.top, props.bottom, props.left, props.right)};
`};
animation-duration: ${props => props.duration || '0.3s'};
animation-timing-function: ${props => props.timingFunction};
animation-delay: 0s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
position: ${props => props.position};
height: 100%;
width: 100%;
${props => props.zIndex && stylesForMedia<number>('z-index', props.zIndex)}
${props => defaultAnimation(props.positionSettings)}
${props => animationForSize(props.positionSettings, 'sm', media.small)}
${props => animationForSize(props.positionSettings, 'md', media.medium)}
${props => animationForSize(props.positionSettings, 'lg', media.large)}
`;

PositionAnimation.defaultProps = {
position: 'relative',
};
12 changes: 7 additions & 5 deletions packages/instant/src/components/animations/slide_animation.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import * as React from 'react';

import { OptionallyScreenSpecific } from '../../style/media';

import { PositionAnimation, PositionAnimationSettings } from './position_animation';

export type SlideAnimationState = 'slidIn' | 'slidOut' | 'none';
export interface SlideAnimationProps {
position: string;
animationState: SlideAnimationState;
slideInSettings: PositionAnimationSettings;
slideOutSettings: PositionAnimationSettings;
slideInSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
slideOutSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
zIndex?: OptionallyScreenSpecific<number>;
}

export const SlideAnimation: React.StatelessComponent<SlideAnimationProps> = props => {
if (props.animationState === 'none') {
return <React.Fragment>{props.children}</React.Fragment>;
}
const propsToUse = props.animationState === 'slidIn' ? props.slideInSettings : props.slideOutSettings;
const positionSettings = props.animationState === 'slidIn' ? props.slideInSettings : props.slideOutSettings;
return (
<PositionAnimation position={props.position} {...propsToUse}>
<PositionAnimation positionSettings={positionSettings} zIndex={props.zIndex}>
{props.children}
</PositionAnimation>
);
Expand Down
37 changes: 33 additions & 4 deletions packages/instant/src/components/sliding_error.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as React from 'react';

import { ScreenSpecification } from '../style/media';
import { ColorOption } from '../style/theme';
import { zIndex } from '../style/z_index';

import { PositionAnimationSettings } from './animations/position_animation';
import { SlideAnimation, SlideAnimationState } from './animations/slide_animation';
Expand All @@ -21,6 +23,7 @@ export const Error: React.StatelessComponent<ErrorProps> = props => (
backgroundColor={ColorOption.lightOrange}
width="100%"
borderRadius="6px"
marginTop="10px"
marginBottom="10px"
>
<Flex justify="flex-start">
Expand All @@ -39,25 +42,51 @@ export interface SlidingErrorProps extends ErrorProps {
}
export const SlidingError: React.StatelessComponent<SlidingErrorProps> = props => {
const slideAmount = '120px';
const slideUpSettings: PositionAnimationSettings = {

const desktopSlideIn: PositionAnimationSettings = {
timingFunction: 'ease-in',
top: {
from: slideAmount,
to: '0px',
},
position: 'relative',
};
const slideDownSettings: PositionAnimationSettings = {
const desktopSlideOut: PositionAnimationSettings = {
timingFunction: 'cubic-bezier(0.25, 0.1, 0.25, 1)',
top: {
from: '0px',
to: slideAmount,
},
position: 'relative',
};

const mobileSlideIn: PositionAnimationSettings = {
duration: '0.5s',
timingFunction: 'ease-in',
top: { from: '-120px', to: '0px' },
position: 'fixed',
};
const moblieSlideOut: PositionAnimationSettings = {
duration: '0.5s',
timingFunction: 'ease-in',
top: { from: '0px', to: '-120px' },
position: 'fixed',
};

const slideUpSettings: ScreenSpecification<PositionAnimationSettings> = {
default: desktopSlideIn,
sm: mobileSlideIn,
};
const slideOutSettings: ScreenSpecification<PositionAnimationSettings> = {
default: desktopSlideOut,
sm: moblieSlideOut,
};

return (
<SlideAnimation
position="relative"
slideInSettings={slideUpSettings}
slideOutSettings={slideDownSettings}
slideOutSettings={slideOutSettings}
zIndex={{ sm: zIndex.errorPopUp, default: zIndex.errorPopBehind }}
animationState={props.animationState}
>
<Error icon={props.icon} message={props.message} />
Expand Down
3 changes: 2 additions & 1 deletion packages/instant/src/components/sliding_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const SlidingPanel: React.StatelessComponent<SlidingPanelProps> = props =
from: slideAmount,
to: '0px',
},
position: 'absolute',
};
const slideDownSettings: PositionAnimationSettings = {
duration: '0.3s',
Expand All @@ -59,10 +60,10 @@ export const SlidingPanel: React.StatelessComponent<SlidingPanelProps> = props =
from: '0px',
to: slideAmount,
},
position: 'absolute',
};
return (
<SlideAnimation
position="absolute"
slideInSettings={slideUpSettings}
slideOutSettings={slideDownSettings}
animationState={animationState}
Expand Down
6 changes: 3 additions & 3 deletions packages/instant/src/components/ui/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ export const Container =
${props => cssRuleIfExists(props, 'cursor')}
${props => cssRuleIfExists(props, 'overflow')}
${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')};
${props => props.display && stylesForMedia('display', props.display)}
${props => stylesForMedia('width', props.width || 'auto')}
${props => stylesForMedia('height', props.height || 'auto')}
${props => props.display && stylesForMedia<string>('display', props.display)}
${props => props.width && stylesForMedia<string>('width', props.width)}
${props => props.height && stylesForMedia<string>('height', props.height)}
background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')};
&:hover {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class ZeroExInstantContainer extends React.Component<ZeroExInstantContain
height={{ default: 'auto', sm: '100%' }}
position="relative"
>
<Container zIndex={zIndex.errorPopup} position="relative">
<Container position="relative">
<LatestError />
</Container>
<Container
Expand Down
38 changes: 23 additions & 15 deletions packages/instant/src/style/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,38 @@ const generateMediaWrapper = (screenWidth: ScreenWidths) => (...args: any[]) =>
}
`;

const media = {
export const media = {
small: generateMediaWrapper(ScreenWidths.Sm),
medium: generateMediaWrapper(ScreenWidths.Md),
large: generateMediaWrapper(ScreenWidths.Lg),
};

export interface ScreenSpecifications {
default: string;
sm?: string;
md?: string;
lg?: string;
export interface ScreenSpecification<T> {
default: T;
sm?: T;
md?: T;
lg?: T;
}
export type MediaChoice = string | ScreenSpecifications;
export const stylesForMedia = (cssPropertyName: string, choice: MediaChoice): InterpolationValue[] => {
if (typeof choice === 'string') {
export type OptionallyScreenSpecific<T> = T | ScreenSpecification<T>;
export type MediaChoice = OptionallyScreenSpecific<string>;
/**
* Given a css property name and a OptionallyScreenSpecific value,
* generates css properties with screen-specific viewport styling
*/
export function stylesForMedia<T extends string | number>(
cssPropertyName: string,
choice: OptionallyScreenSpecific<T>,
): InterpolationValue[] {
if (typeof choice === 'object') {
return css`
${cssPropertyName}: ${choice};
`;
}

return css`
${cssPropertyName}: ${choice.default};
${choice.lg && media.large`${cssPropertyName}: ${choice.lg}`}
${choice.md && media.medium`${cssPropertyName}: ${choice.md}`}
${choice.sm && media.small`${cssPropertyName}: ${choice.sm}`}
`;
};
} else {
return css`
${cssPropertyName}: ${choice};
`;
}
}
3 changes: 2 additions & 1 deletion packages/instant/src/style/z_index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const zIndex = {
errorPopup: 1,
errorPopBehind: 1,
mainContainer: 2,
panel: 3,
errorPopUp: 4,
};