mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 14:01:56 +08:00
Migration: Message & Modal to hooks component
This commit is contained in:
parent
659f41ec5a
commit
724cd44d69
@ -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,73 +30,62 @@ 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 })
|
||||
|
||||
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
|
||||
const id = window.setTimeout(() => {
|
||||
setVisible(false)
|
||||
onClose()
|
||||
}, duration)
|
||||
return () => window.clearTimeout(id)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('message', `message-${type}`, { 'message-show': this.state.visible })}
|
||||
onTransitionEnd={() => !this.state.visible && removeComponent()}
|
||||
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
|
||||
|
@ -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,57 +32,44 @@ 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 = (
|
||||
<div
|
||||
className={classnames('modal-mask', { 'modal-show': show })}
|
||||
ref={this.$mask}
|
||||
onClick={this.handleMaskClick}
|
||||
ref={maskRef}
|
||||
onClick={handleMaskClick}
|
||||
>
|
||||
<div
|
||||
className={classnames('modal', `modal-${size}`, className)}
|
||||
style={style}
|
||||
ref={this.$modal}
|
||||
>
|
||||
<div className="modal-title">{title}</div>
|
||||
<div
|
||||
@ -102,6 +88,5 @@ export class Modal extends React.Component<ModalProps, {}> {
|
||||
</div>
|
||||
)
|
||||
|
||||
return createPortal(modal, this.$container)
|
||||
}
|
||||
return createPortal(modal, portalRef.current)
|
||||
}
|
||||
|
@ -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)
|
||||
}, [])
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user