Migration: Message & Modal to hooks component

This commit is contained in:
Dreamacro 2019-07-02 23:22:19 +08:00
parent 659f41ec5a
commit 724cd44d69
3 changed files with 103 additions and 129 deletions

View File

@ -1,10 +1,10 @@
import * as React from 'react'
import { Icon } from '@components'
import React, { useState, useLayoutEffect } from 'react'
import classnames from 'classnames'
import { unmountComponentAtNode, render } from 'react-dom'
import { Icon } from '@components'
import { noop } from '@lib/helper'
import './style.scss'
const noop = () => {}
const TYPE_ICON_MAP = {
info: 'info',
success: 'check',
@ -30,74 +30,63 @@ interface MessageProps {
removeComponent: typeof noop
}
export class Message extends React.Component <MessageProps, {}> {
export function Message (props: MessageProps) {
const {
removeComponent = noop,
onClose = noop,
icon = <Icon type="info" size={16} />,
content = '',
type = 'info',
duration = 1500
} = props
/**
* static function to call Message directly
*/
static info = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'info', content, duration, onClose })
const [visible, setVisible] = useState(false)
static success = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'success', content, duration, onClose })
useLayoutEffect(() => {
window.setTimeout(() => setVisible(true), 0)
static warning = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'warning', content, duration, onClose })
const id = window.setTimeout(() => {
setVisible(false)
onClose()
}, duration)
return () => window.clearTimeout(id)
}, [])
static error = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'error', content, duration, onClose })
static defaultProps: MessageProps = {
content: '',
type: 'info',
icon: <Icon type="info" size={16} />,
duration: 1500,
onClose: noop,
removeComponent: noop
}
state = {
visible: false
}
componentDidMount () {
// TODO: optimize animation
// fix do not show animation when element mounted
setTimeout(() => this.setState({ visible: true }), 0)
setTimeout(() => {
this.setState({ visible: false })
this.props.onClose()
}, this.props.duration)
}
render () {
const { removeComponent, icon, content, type } = this.props
return (
<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>
)
}
return (
<div
className={classnames('message', `message-${type}`, { 'message-show': visible })}
onTransitionEnd={() => !visible && removeComponent()}
>
<span className="message-icon">{icon}</span>
<span className="message-content">{content}</span>
</div>
)
}
export const info = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'info', content, duration, onClose })
export const success = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'success', content, duration, onClose })
export const warning = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'warning', content, duration, onClose })
export const error = (
content: string,
duration?: number,
onClose?: typeof noop
) => showMessage({ type: 'error', content, duration, onClose })
export function showMessage (args: ArgsProps) {
// create container element
const container = document.createElement('div')

View File

@ -1,12 +1,11 @@
import * as React from 'react'
import React, { useRef, useLayoutEffect } from 'react'
import classnames from 'classnames'
import { createPortal } from 'react-dom'
import { BaseComponentProps } from '@models'
import { Button } from '@components'
import { noop } from '@lib/helper'
import './style.scss'
const noop = () => {}
interface ModalProps extends BaseComponentProps {
// show modal
show?: boolean
@ -33,75 +32,61 @@ interface ModalProps extends BaseComponentProps {
onClose?: typeof noop
}
export class Modal extends React.Component<ModalProps, {}> {
export function Modal (props: ModalProps) {
const {
show = true,
title = 'Modal',
size = 'small',
footer= true,
onOk = noop,
onClose = noop,
bodyClassName,
bodyStyle,
className,
style,
children
} = props
static defaultProps: ModalProps = {
show: true,
title: 'Modal',
size: 'small',
footer: true,
onOk: noop,
onClose: noop
}
const portalRef = useRef<HTMLDivElement>(document.createElement('div'))
const maskRef = useRef<HTMLDivElement>()
// portal container
$container: Element
useLayoutEffect(() => {
document.body.appendChild(portalRef.current)
return () => document.body.removeChild(portalRef.current)
}, [])
$modal = React.createRef<HTMLDivElement>()
$mask = React.createRef<HTMLDivElement>()
constructor (props) {
super(props)
// create container element
const container = document.createElement('div')
document.body.appendChild(container)
this.$container = container
}
componentWillUnmount () {
document.body.removeChild(this.$container)
}
private handleMaskClick = (e) => {
const { onClose } = this.props
if (e.target === this.$mask) {
function handleMaskClick (e) {
if (e.target === maskRef.current) {
onClose()
}
}
render () {
const { show, size, title, footer, children, className, bodyClassName, style, bodyStyle, onOk, onClose } = this.props
const modal = (
const modal = (
<div
className={classnames('modal-mask', { 'modal-show': show })}
ref={maskRef}
onClick={handleMaskClick}
>
<div
className={classnames('modal-mask', { 'modal-show': show })}
ref={this.$mask}
onClick={this.handleMaskClick}
className={classnames('modal', `modal-${size}`, className)}
style={style}
>
<div className="modal-title">{title}</div>
<div
className={classnames('modal', `modal-${size}`, className)}
style={style}
ref={this.$modal}
>
<div className="modal-title">{title}</div>
<div
className={classnames('modal-body', bodyClassName)}
style={bodyStyle}
>{children}</div>
{
footer && (
<div className="footer">
<Button onClick={() => onClose()}> </Button>
<Button type="primary" onClick={() => onOk()}> </Button>
</div>
)
}
</div>
className={classnames('modal-body', bodyClassName)}
style={bodyStyle}
>{children}</div>
{
footer && (
<div className="footer">
<Button onClick={() => onClose()}> </Button>
<Button type="primary" onClick={() => onOk()}> </Button>
</div>
)
}
</div>
)
</div>
)
return createPortal(modal, this.$container)
}
return createPortal(modal, portalRef.current)
}

View File

@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from 'react'
import React, { useState, useRef, useLayoutEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { BaseComponentProps, I18nProps } from '@models'
import { noop } from '@lib/helper'
@ -21,7 +21,7 @@ export function Tags (props: TagsProps) {
const [showExtend, setShowExtend] = useState(false)
const ulRef = useRef<HTMLUListElement>()
useEffect(() => {
useLayoutEffect(() => {
setShowExtend(ulRef.current.offsetHeight > 30)
}, [])