diff --git a/src/components/Message/index.tsx b/src/components/Message/index.tsx index f98f355..5833888 100644 --- a/src/components/Message/index.tsx +++ b/src/components/Message/index.tsx @@ -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 { +export class Message extends React.Component { + + /** + * 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: , + type: 'info', + icon: , 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 ( -
!this.state.visible && removeComponent()}> - {icon} - {content} -
+
!this.state.visible && removeComponent()} + > + {icon} + {content} +
) } } -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 = + + const 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(, 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 diff --git a/src/components/Message/style.scss b/src/components/Message/style.scss index e5a542f..f0d3900 100644 --- a/src/components/Message/style.scss +++ b/src/components/Message/style.scss @@ -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); }