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