Improve: optimize message component

This commit is contained in:
jas0ncn 2018-10-25 00:23:27 +08:00
parent e6b0e9a4ed
commit d18ca26bad
2 changed files with 124 additions and 41 deletions

View File

@ -4,89 +4,123 @@ import classnames from 'classnames'
import { unmountComponentAtNode, render } from 'react-dom'
import './style.scss'
const noop = () => {}
const TYPE_ICON_MAP = {
info: 'info',
success: 'check',
warning: 'info-o',
error: 'close'
}
type NoticeType = 'success' | 'info' | 'warning' | 'error'
type ConfigOnClose = () => void
interface ArgsProps {
content: string
type: NoticeType
duration?: number
onClose?: () => void
onClose?: typeof noop
}
interface MessageProps {
content?: string
type?: NoticeType
icon?: React.ReactNode
onClose?: () => void
duration?: number
removeComponent: () => void
onClose?: typeof noop
removeComponent: typeof noop
}
class Message extends React.Component <MessageProps, {}> {
export class Message extends React.Component <MessageProps, {}> {
/**
* static function to call Message directly
*/
static info = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'info', content, duration, onClose })
static success = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'success', content, duration, onClose })
static warning = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'warning', content, duration, onClose })
static error = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'error', content, duration, onClose })
static defaultProps: MessageProps = {
content: '',
icon: <Icon type={'info'} size={26}></Icon>,
type: 'info',
icon: <Icon type="info" size={16} />,
duration: 1500,
onClose: () => {},
removeComponent: () => {}
onClose: noop,
removeComponent: noop
}
state = {
visible: false
}
componentDidMount () {
this.setState({
visible: true
})
// TODO: optimize animation
// fix do not show animation when element mounted
setTimeout(() => this.setState({ visible: true }), 0)
setTimeout(() => {
this.setState({
visible: false
})
this.setState({ visible: false })
this.props.onClose()
}, this.props.duration)
}
render () {
const { removeComponent, icon, content } = this.props
const { removeComponent, icon, content, type } = this.props
return (
<div className={classnames('message', { 'message-show': this.state.visible })} onTransitionEnd={() => !this.state.visible && removeComponent()}>
{icon}
{content}
<div
className={classnames('message', `message-${type}`, { 'message-show': this.state.visible })}
onTransitionEnd={() => !this.state.visible && removeComponent()}
>
<span className="message-icon">{icon}</span>
<span className="message-content">{content}</span>
</div>
)
}
}
function notice (args: ArgsProps) {
export function showMessage (args: ArgsProps) {
// create container element
const container = document.createElement('div')
document.body.appendChild(container)
// remove container when component unmount
const removeComponent = () => {
const isUnMount = unmountComponentAtNode(container)
if (isUnMount) {
document.body.removeChild(container)
}
}
const icon = <Icon type={args.type} size={26}></Icon>
const icon = <Icon type={TYPE_ICON_MAP[args.type]} size={16}></Icon>
const { type, content, duration, onClose } = args
const props: MessageProps = {
icon,
content: args.content,
type,
content,
removeComponent,
duration: args.duration,
onClose: args.onClose
duration,
onClose
}
render(<Message {...props} />, container)
}
export interface MessageApi {
info (content: string, type: NoticeType, duration?: number, onClose?: ConfigOnClose)
}
const api: any = {
}
const types = ['success', 'info', 'warning', 'error']
types.forEach(type => {
api[type] = (content: string, type: NoticeType, duration?: number, onClose?: ConfigOnClose) => notice({
content, duration, type, onClose
})
})
export default api as MessageApi

View File

@ -3,12 +3,61 @@
.message {
position: fixed;
top: 20px;
left: 50%;
right: 20px;
border-radius: 4px;
opacity: 0;
transition: all 2000ms ease-out;
background: $color-white;
display: flex;
box-shadow: 0 0 20px rgba($color-primary-dark, 0.2);
transition: all 200ms ease;
transform: translateX(100%);
.message-icon {
width: 36px;
flex: 1;
border-radius: 4px 0 0 4px;
display: flex;
justify-content: center;
align-items: center;
> i {
color: $color-white;
}
}
.message-content {
padding: 10px 15px;
font-size: 13px;
color: $color-primary-darken;
}
}
.message-info {
.message-icon {
background: linear-gradient(135deg, $color-primary, $color-primary-dark);
}
}
.message-success {
.message-icon {
background: linear-gradient(135deg, $color-green, darken($color-green, 5%));
}
}
.message-warning {
.message-icon {
background: linear-gradient(135deg, $color-orange, darken($color-orange, 5%));
}
}
.message-error {
.message-icon {
background: linear-gradient(135deg, $color-red, darken($color-red, 10%));
}
}
.message-show {
opacity: 1;
transition: all 2000ms ease-out;
transition: all 200ms ease;
transform: translateX(0);
}