1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-07-14 15:35:35 +00:00
seahub/frontend/src/components/toast/toast.js

195 lines
4.1 KiB
JavaScript
Raw Normal View History

2018-10-09 02:23:32 +00:00
import React from 'react';
2018-12-07 04:59:25 +00:00
import { css } from 'glamor';
import PropTypes from 'prop-types';
import Transition from 'react-transition-group/Transition';
import Alert from './alert';
2018-10-09 02:23:32 +00:00
2018-12-07 04:59:25 +00:00
const animationEasing = {
deceleration: 'cubic-bezier(0.0, 0.0, 0.2, 1)',
acceleration: 'cubic-bezier(0.4, 0.0, 1, 1)',
spring: 'cubic-bezier(0.175, 0.885, 0.320, 1.175)'
2018-10-16 10:19:51 +00:00
};
2018-10-09 02:23:32 +00:00
2018-12-07 04:59:25 +00:00
const ANIMATION_DURATION = 240;
const openAnimation = css.keyframes('openAnimation', {
from: {
opacity: 0,
transform: 'translateY(-120%)'
2018-10-09 02:23:32 +00:00
},
2018-12-07 04:59:25 +00:00
to: {
transform: 'translateY(0)'
}
});
const closeAnimation = css.keyframes('closeAnimation', {
from: {
transform: 'scale(1)',
opacity: 1
2018-10-09 02:23:32 +00:00
},
2018-12-07 04:59:25 +00:00
to: {
transform: 'scale(0.9)',
opacity: 0
}
});
const animationStyles = css({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
height: 0,
transition: `all ${ANIMATION_DURATION}ms ${animationEasing.deceleration}`,
'&[data-state="entering"], &[data-state="entered"]': {
animation: `${openAnimation} ${ANIMATION_DURATION}ms ${animationEasing.spring} both`
2018-10-09 02:23:32 +00:00
},
2018-12-07 04:59:25 +00:00
'&[data-state="exiting"]': {
animation: `${closeAnimation} 120ms ${animationEasing.acceleration} both`
}
});
export default class Toast extends React.PureComponent {
static propTypes = {
/**
* The z-index of the toast.
*/
2018-12-07 04:59:25 +00:00
zIndex: PropTypes.number,
/**
* Duration of the toast.
*/
2018-12-07 04:59:25 +00:00
duration: PropTypes.number,
/**
* Function called when the toast is all the way closed.
*/
2018-12-07 04:59:25 +00:00
onRemove: PropTypes.func,
/**
* The type of the alert.
*/
intent: PropTypes.oneOf(['none', 'success', 'warning', 'danger']).isRequired,
2018-12-07 04:59:25 +00:00
/**
* The title of the alert.
*/
2018-12-07 04:59:25 +00:00
title: PropTypes.node,
/**
* Description of the alert.
*/
2018-12-07 04:59:25 +00:00
children: PropTypes.node,
/**
* When true, show a close icon button inside of the toast.
*/
2018-12-07 04:59:25 +00:00
hasCloseButton: PropTypes.bool,
/**
* When false, will close the Toast and call onRemove when finished.
*/
2018-12-07 04:59:25 +00:00
isShown: PropTypes.bool
};
2018-12-07 04:59:25 +00:00
static defaultProps = {
intent: 'none'
};
2018-12-07 04:59:25 +00:00
state = {
isShown: true,
height: 0
};
2018-12-07 04:59:25 +00:00
componentDidUpdate(prevProps) {
if (prevProps.isShown !== this.props.isShown) {
// eslint-disable-next-line react/no-did-update-set-state
2018-12-07 04:59:25 +00:00
this.setState({
isShown: this.props.isShown
});
}
}
componentDidMount() {
this.startCloseTimer();
}
componentWillUnmount() {
this.clearCloseTimer();
}
close = () => {
this.clearCloseTimer();
this.setState({
isShown: false
});
};
2018-12-07 04:59:25 +00:00
startCloseTimer = () => {
if (this.props.duration) {
this.closeTimer = setTimeout(() => {
this.close();
}, this.props.duration * 1000);
}
};
2018-12-07 04:59:25 +00:00
clearCloseTimer = () => {
if (this.closeTimer) {
clearTimeout(this.closeTimer);
this.closeTimer = null;
}
};
2018-12-07 04:59:25 +00:00
handleMouseEnter = () => {
this.clearCloseTimer();
};
2018-12-07 04:59:25 +00:00
handleMouseLeave = () => {
this.startCloseTimer();
};
2018-12-07 04:59:25 +00:00
onRef = ref => {
if (ref === null) return;
const { height } = ref.getBoundingClientRect();
this.setState({
height
});
};
2018-12-07 04:59:25 +00:00
render() {
return (
<Transition
appear
unmountOnExit
timeout={ANIMATION_DURATION}
in={this.state.isShown}
onExited={this.props.onRemove}
2018-12-07 04:59:25 +00:00
>
{state => (
<div
data-state={state}
className={animationStyles}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
style={{
height: this.state.height,
zIndex: this.props.zIndex,
marginBottom: this.state.isShown ? 0 : -this.state.height
}}
2018-12-07 04:59:25 +00:00
>
<div ref={this.onRef} style={{ padding: 8 }}>
<Alert
intent={this.props.intent}
title={this.props.title}
children={this.props.children}
isRemoveable={this.props.hasCloseButton}
onRemove={() => this.close()}
/>
</div>
</div>
)}
2018-12-07 04:59:25 +00:00
</Transition>
);
2018-10-09 02:23:32 +00:00
}
}