mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-11-26 16:52:06 +05:30
Cover PanelTransition with types
This commit is contained in:
parent
99fc667a2b
commit
afde390401
@ -10,8 +10,8 @@ import { omit, debounce } from 'functions';
|
||||
* On each component update the `shouldMeasure` prop is being called and depending of
|
||||
* the value returned will be decided whether to call `onMeasure`.
|
||||
* By default `shouldMeasure` will compare the old and new values of the `state` prop.
|
||||
* Both `shouldMeasure` and `state` can be used to reduce the amount of meausres, which
|
||||
* will recude the count of forced reflows in browser.
|
||||
* Both `shouldMeasure` and `state` can be used to reduce the amount of measures, which
|
||||
* will reduce the count of forced reflows in browser.
|
||||
*
|
||||
* Usage:
|
||||
* <MeasureHeight
|
||||
@ -23,10 +23,10 @@ import { omit, debounce } from 'functions';
|
||||
* </MeasureHeight>
|
||||
*/
|
||||
|
||||
type ChildState = { [key: string]: any };
|
||||
type ChildState = mixed;
|
||||
|
||||
export default class MeasureHeight extends PureComponent<{
|
||||
shouldMeasure: (prevState: any, newState: any) => bool,
|
||||
shouldMeasure: (prevState: ChildState, newState: ChildState) => bool,
|
||||
onMeasure: (height: number) => void,
|
||||
state: ChildState
|
||||
}> {
|
||||
|
@ -1,3 +1,3 @@
|
||||
// @flow
|
||||
export type { State as AccountsState, Account } from './reducer';
|
||||
export { default as AccountSwitcher } from './AccountSwitcher';
|
||||
export type { Account } from './reducer';
|
||||
|
@ -1,3 +1,7 @@
|
||||
// @flow
|
||||
import type { User } from 'components/user';
|
||||
import type { AccountsState } from 'components/accounts';
|
||||
import type { Node, Element } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@ -56,7 +60,64 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
}, {});
|
||||
}
|
||||
|
||||
class PanelTransition extends Component {
|
||||
type ValidationError = string | {
|
||||
type: string,
|
||||
payload: { [key: string]: any },
|
||||
};
|
||||
|
||||
type AnimationProps = {|
|
||||
opacitySpring: number,
|
||||
transformSpring: number,
|
||||
|};
|
||||
|
||||
type AnimationContext = {
|
||||
key: string,
|
||||
style: AnimationProps,
|
||||
data: {
|
||||
Title: Node,
|
||||
Body: Element<any>,
|
||||
Footer: Node,
|
||||
Links: Node,
|
||||
hasBackButton: bool,
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
// context props
|
||||
auth: {
|
||||
error: string | {
|
||||
type: string,
|
||||
payload: {[key: string]: any},
|
||||
},
|
||||
isLoading: bool,
|
||||
login: string,
|
||||
},
|
||||
user: User,
|
||||
accounts: AccountsState,
|
||||
setErrors: ({ [key: string]: ValidationError }) => void,
|
||||
clearErrors: () => void,
|
||||
resolve: () => void,
|
||||
reject: () => void,
|
||||
|
||||
|
||||
// local props
|
||||
Title: Node,
|
||||
Body: typeof Component,
|
||||
Footer: Node,
|
||||
Links: Node,
|
||||
children: Node
|
||||
};
|
||||
|
||||
type State = {
|
||||
contextHeight: number,
|
||||
panelId: string | void,
|
||||
prevPanelId: string | void,
|
||||
isHeightDirty: bool,
|
||||
forceHeight: 1 | 0,
|
||||
direction: 'X' | 'Y',
|
||||
};
|
||||
|
||||
class PanelTransition extends Component<Props, State> {
|
||||
static displayName = 'PanelTransition';
|
||||
|
||||
static propTypes = {
|
||||
@ -106,9 +167,20 @@ class PanelTransition extends Component {
|
||||
|
||||
state = {
|
||||
contextHeight: 0,
|
||||
panelId: this.props.Body && this.props.Body.type.panelId
|
||||
panelId: this.props.Body && (this.props.Body: any).type.panelId,
|
||||
isHeightDirty: false,
|
||||
forceHeight: 0,
|
||||
direction: 'X',
|
||||
prevPanelId: undefined,
|
||||
};
|
||||
|
||||
isHeightMeasured: bool = false;
|
||||
wasAutoFocused: bool = false;
|
||||
body: null | {
|
||||
autoFocus: () => void,
|
||||
onFormSubmit: () => void,
|
||||
} = null;
|
||||
|
||||
timerIds = []; // this is a list of a probably running timeouts to clean on unmount
|
||||
|
||||
getChildContext() {
|
||||
@ -136,8 +208,8 @@ class PanelTransition extends Component {
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const nextPanel = nextProps.Body && nextProps.Body.type.panelId;
|
||||
const prevPanel = this.props.Body && this.props.Body.type.panelId;
|
||||
const nextPanel = nextProps.Body && (nextProps.Body: any).type.panelId;
|
||||
const prevPanel = this.props.Body && (this.props.Body: any).type.panelId;
|
||||
|
||||
if (nextPanel !== prevPanel) {
|
||||
const direction = this.getDirection(nextPanel, prevPanel);
|
||||
@ -177,7 +249,10 @@ class PanelTransition extends Component {
|
||||
throw new Error('Title, Body, Footer and Links are required');
|
||||
}
|
||||
|
||||
const {panelId, hasGoBack} = Body.type;
|
||||
const {panelId, hasGoBack}: {
|
||||
panelId: string,
|
||||
hasGoBack: bool,
|
||||
} = (Body: any).type;
|
||||
|
||||
const formHeight = this.state[`formHeight${panelId}`] || 0;
|
||||
|
||||
@ -255,7 +330,10 @@ class PanelTransition extends Component {
|
||||
|
||||
onFormSubmit = () => {
|
||||
this.props.clearErrors();
|
||||
this.body.onFormSubmit();
|
||||
|
||||
if (this.body) {
|
||||
this.body.onFormSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
onFormInvalid = (errors) => this.props.setErrors(errors);
|
||||
@ -271,7 +349,10 @@ class PanelTransition extends Component {
|
||||
*
|
||||
* @return {object}
|
||||
*/
|
||||
getTransitionStyles({key}, options = {}) {
|
||||
getTransitionStyles({key}, options = {}): {|
|
||||
transformSpring: number,
|
||||
opacitySpring: number,
|
||||
|} {
|
||||
const {isLeave = false} = options;
|
||||
const {panelId, prevPanelId} = this.state;
|
||||
|
||||
@ -279,6 +360,11 @@ class PanelTransition extends Component {
|
||||
const fromRight = 1;
|
||||
|
||||
const currentContext = contexts.find((context) => context.includes(key));
|
||||
|
||||
if (!currentContext) {
|
||||
throw new Error(`Can not find settings for ${key} panel`);
|
||||
}
|
||||
|
||||
let sign = currentContext.indexOf(panelId) > currentContext.indexOf(prevPanelId)
|
||||
? fromRight
|
||||
: fromLeft;
|
||||
@ -294,8 +380,14 @@ class PanelTransition extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
getDirection(next, prev) {
|
||||
return contexts.find((context) => context.includes(prev)).includes(next) ? 'X' : 'Y';
|
||||
getDirection(next, prev): 'X' | 'Y' {
|
||||
const context = contexts.find((context) => context.includes(prev));
|
||||
|
||||
if (!context) {
|
||||
throw new Error(`Can not find context for transition ${prev} -> ${next}`);
|
||||
}
|
||||
|
||||
return context.includes(next) ? 'X' : 'Y';
|
||||
}
|
||||
|
||||
onUpdateHeight = (height, key) => {
|
||||
@ -340,13 +432,17 @@ class PanelTransition extends Component {
|
||||
|
||||
shouldMeasureHeight() {
|
||||
const errorString = Object.values(this.props.auth.error || {})
|
||||
.reduce((acc, item) => {
|
||||
if (typeof item === 'string') {
|
||||
return acc + item;
|
||||
}
|
||||
.reduce(
|
||||
// $FlowFixMe
|
||||
(acc, item: ValidationError) => {
|
||||
if (typeof item === 'string') {
|
||||
return acc + item;
|
||||
}
|
||||
|
||||
return acc + item.type;
|
||||
}, '');
|
||||
return acc + item.type;
|
||||
},
|
||||
''
|
||||
);
|
||||
|
||||
return [
|
||||
errorString,
|
||||
@ -356,7 +452,7 @@ class PanelTransition extends Component {
|
||||
].join('');
|
||||
}
|
||||
|
||||
getHeader({key, style, data}) {
|
||||
getHeader({key, style, data}: AnimationContext) {
|
||||
const {Title} = data;
|
||||
const {transformSpring} = style;
|
||||
|
||||
@ -366,7 +462,7 @@ class PanelTransition extends Component {
|
||||
hasBackButton = hasBackButton(this.props);
|
||||
}
|
||||
|
||||
style = {
|
||||
const transitionStyle = {
|
||||
...this.getDefaultTransitionStyles(key, style),
|
||||
opacity: 1 // reset default
|
||||
};
|
||||
@ -391,7 +487,7 @@ class PanelTransition extends Component {
|
||||
);
|
||||
|
||||
return (
|
||||
<div key={`header/${key}`} style={style}>
|
||||
<div key={`header/${key}`} style={transitionStyle}>
|
||||
{hasBackButton ? backButton : null}
|
||||
<div style={scrollStyle}>
|
||||
{Title}
|
||||
@ -400,19 +496,20 @@ class PanelTransition extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
getBody({key, style, data}) {
|
||||
getBody({key, style, data}: AnimationContext) {
|
||||
const {Body} = data;
|
||||
const {transformSpring} = style;
|
||||
const {direction} = this.state;
|
||||
|
||||
let transform = this.translate(transformSpring, direction);
|
||||
let verticalOrigin = 'top';
|
||||
|
||||
if (direction === 'Y') {
|
||||
verticalOrigin = 'bottom';
|
||||
transform = {};
|
||||
}
|
||||
|
||||
style = {
|
||||
const transitionStyle = {
|
||||
...this.getDefaultTransitionStyles(key, style),
|
||||
top: 'auto', // reset default
|
||||
[verticalOrigin]: 0,
|
||||
@ -422,7 +519,7 @@ class PanelTransition extends Component {
|
||||
return (
|
||||
<MeasureHeight
|
||||
key={`body/${key}`}
|
||||
style={style}
|
||||
style={transitionStyle}
|
||||
state={this.shouldMeasureHeight()}
|
||||
onMeasure={(height) => this.onUpdateHeight(height, key)}
|
||||
>
|
||||
@ -435,25 +532,25 @@ class PanelTransition extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
getFooter({key, style, data}) {
|
||||
getFooter({key, style, data}: AnimationContext) {
|
||||
const {Footer} = data;
|
||||
|
||||
style = this.getDefaultTransitionStyles(key, style);
|
||||
const transitionStyle = this.getDefaultTransitionStyles(key, style);
|
||||
|
||||
return (
|
||||
<div key={`footer/${key}`} style={style}>
|
||||
<div key={`footer/${key}`} style={transitionStyle}>
|
||||
{Footer}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getLinks({key, style, data}) {
|
||||
getLinks({key, style, data}: AnimationContext) {
|
||||
const {Links} = data;
|
||||
|
||||
style = this.getDefaultTransitionStyles(key, style);
|
||||
const transitionStyle = this.getDefaultTransitionStyles(key, style);
|
||||
|
||||
return (
|
||||
<div key={`links/${key}`} style={style}>
|
||||
<div key={`links/${key}`} style={transitionStyle}>
|
||||
{Links}
|
||||
</div>
|
||||
);
|
||||
@ -466,7 +563,14 @@ class PanelTransition extends Component {
|
||||
*
|
||||
* @return {object}
|
||||
*/
|
||||
getDefaultTransitionStyles(key, {opacitySpring}) {
|
||||
getDefaultTransitionStyles(key: string, {opacitySpring}: $ReadOnly<AnimationProps>): {|
|
||||
position: string,
|
||||
top: number,
|
||||
left: number,
|
||||
width: string,
|
||||
opacity: number,
|
||||
pointerEvents: string,
|
||||
|} {
|
||||
return {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
|
@ -1,4 +1,5 @@
|
||||
// @flow
|
||||
import type { Node } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
@ -9,9 +10,9 @@ import styles from './panel.scss';
|
||||
import icons from './icons.scss';
|
||||
|
||||
export function Panel(props: {
|
||||
title: string,
|
||||
icon: string,
|
||||
children: *
|
||||
title?: string,
|
||||
icon?: string,
|
||||
children: Node,
|
||||
}) {
|
||||
let { title, icon } = props;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user