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 React, { useState, useLayoutEffect } from 'react'
|
||||||
import { Icon } from '@components'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { unmountComponentAtNode, render } from 'react-dom'
|
import { unmountComponentAtNode, render } from 'react-dom'
|
||||||
|
import { Icon } from '@components'
|
||||||
|
import { noop } from '@lib/helper'
|
||||||
import './style.scss'
|
import './style.scss'
|
||||||
|
|
||||||
const noop = () => {}
|
|
||||||
const TYPE_ICON_MAP = {
|
const TYPE_ICON_MAP = {
|
||||||
info: 'info',
|
info: 'info',
|
||||||
success: 'check',
|
success: 'check',
|
||||||
@ -30,74 +30,63 @@ interface MessageProps {
|
|||||||
removeComponent: typeof noop
|
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
|
||||||
|
|
||||||
/**
|
const [visible, setVisible] = useState(false)
|
||||||
* static function to call Message directly
|
|
||||||
*/
|
|
||||||
static info = (
|
|
||||||
content: string,
|
|
||||||
duration?: number,
|
|
||||||
onClose?: typeof noop
|
|
||||||
) => showMessage({ type: 'info', content, duration, onClose })
|
|
||||||
|
|
||||||
static success = (
|
useLayoutEffect(() => {
|
||||||
content: string,
|
window.setTimeout(() => setVisible(true), 0)
|
||||||
duration?: number,
|
|
||||||
onClose?: typeof noop
|
|
||||||
) => showMessage({ type: 'success', content, duration, onClose })
|
|
||||||
|
|
||||||
static warning = (
|
const id = window.setTimeout(() => {
|
||||||
content: string,
|
setVisible(false)
|
||||||
duration?: number,
|
onClose()
|
||||||
onClose?: typeof noop
|
}, duration)
|
||||||
) => showMessage({ type: 'warning', content, duration, onClose })
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('message', `message-${type}`, { 'message-show': this.state.visible })}
|
className={classnames('message', `message-${type}`, { 'message-show': visible })}
|
||||||
onTransitionEnd={() => !this.state.visible && removeComponent()}
|
onTransitionEnd={() => !visible && removeComponent()}
|
||||||
>
|
>
|
||||||
<span className="message-icon">{icon}</span>
|
<span className="message-icon">{icon}</span>
|
||||||
<span className="message-content">{content}</span>
|
<span className="message-content">{content}</span>
|
||||||
</div>
|
</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) {
|
export function showMessage (args: ArgsProps) {
|
||||||
// create container element
|
// create container element
|
||||||
const container = document.createElement('div')
|
const container = document.createElement('div')
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import * as React from 'react'
|
import React, { useRef, useLayoutEffect } from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { BaseComponentProps } from '@models'
|
import { BaseComponentProps } from '@models'
|
||||||
import { Button } from '@components'
|
import { Button } from '@components'
|
||||||
|
import { noop } from '@lib/helper'
|
||||||
import './style.scss'
|
import './style.scss'
|
||||||
|
|
||||||
const noop = () => {}
|
|
||||||
|
|
||||||
interface ModalProps extends BaseComponentProps {
|
interface ModalProps extends BaseComponentProps {
|
||||||
// show modal
|
// show modal
|
||||||
show?: boolean
|
show?: boolean
|
||||||
@ -33,57 +32,44 @@ interface ModalProps extends BaseComponentProps {
|
|||||||
onClose?: typeof noop
|
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 = {
|
const portalRef = useRef<HTMLDivElement>(document.createElement('div'))
|
||||||
show: true,
|
const maskRef = useRef<HTMLDivElement>()
|
||||||
title: 'Modal',
|
|
||||||
size: 'small',
|
|
||||||
footer: true,
|
|
||||||
onOk: noop,
|
|
||||||
onClose: noop
|
|
||||||
}
|
|
||||||
|
|
||||||
// portal container
|
useLayoutEffect(() => {
|
||||||
$container: Element
|
document.body.appendChild(portalRef.current)
|
||||||
|
return () => document.body.removeChild(portalRef.current)
|
||||||
|
}, [])
|
||||||
|
|
||||||
$modal = React.createRef<HTMLDivElement>()
|
function handleMaskClick (e) {
|
||||||
|
if (e.target === maskRef.current) {
|
||||||
$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) {
|
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
|
||||||
const { show, size, title, footer, children, className, bodyClassName, style, bodyStyle, onOk, onClose } = this.props
|
|
||||||
const modal = (
|
const modal = (
|
||||||
<div
|
<div
|
||||||
className={classnames('modal-mask', { 'modal-show': show })}
|
className={classnames('modal-mask', { 'modal-show': show })}
|
||||||
ref={this.$mask}
|
ref={maskRef}
|
||||||
onClick={this.handleMaskClick}
|
onClick={handleMaskClick}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classnames('modal', `modal-${size}`, className)}
|
className={classnames('modal', `modal-${size}`, className)}
|
||||||
style={style}
|
style={style}
|
||||||
ref={this.$modal}
|
|
||||||
>
|
>
|
||||||
<div className="modal-title">{title}</div>
|
<div className="modal-title">{title}</div>
|
||||||
<div
|
<div
|
||||||
@ -102,6 +88,5 @@ export class Modal extends React.Component<ModalProps, {}> {
|
|||||||
</div>
|
</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 { useTranslation } from 'react-i18next'
|
||||||
import { BaseComponentProps, I18nProps } from '@models'
|
import { BaseComponentProps, I18nProps } from '@models'
|
||||||
import { noop } from '@lib/helper'
|
import { noop } from '@lib/helper'
|
||||||
@ -21,7 +21,7 @@ export function Tags (props: TagsProps) {
|
|||||||
const [showExtend, setShowExtend] = useState(false)
|
const [showExtend, setShowExtend] = useState(false)
|
||||||
|
|
||||||
const ulRef = useRef<HTMLUListElement>()
|
const ulRef = useRef<HTMLUListElement>()
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setShowExtend(ulRef.current.offsetHeight > 30)
|
setShowExtend(ulRef.current.offsetHeight > 30)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user