UNPKG

rabbit-simple-ui

Version:

A simple UI component library based on JavaScript

250 lines (216 loc) 8.42 kB
/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { $el, bind } from '../../dom-utils'; import { CssTransition, Scrollable } from '../../mixins'; import { type, useHTMLString } from '../../utils'; import Button from '../button'; import PREFIX from '../prefix'; const MiniModalBtn = new Button(); const ICONTYPE = { info: 'ios-information-circle', success: 'ios-checkmark-circle', warning: 'ios-alert', error: 'ios-close-circle', confirm: 'ios-help-circle', loading: 'loading-solid' }; let DEFAULT_ZINDEX = 1010; interface MinModalOptions { width?: string | number; title?: string; content?: string; okText?: string; cancelText?: string; loading?: boolean; keyboard?: boolean; scrollable?: boolean; lockScroll?: boolean; dangerouslyUseHTMLString?: boolean; onOk?: () => void; onCancel?: () => void; } class MiniModal { readonly VERSION: string; constructor() { this.VERSION = 'v1.0'; } public info(config: MinModalOptions): void { this._create('info', config); } public success(config: MinModalOptions): void { this._create('success', config); } public warning(config: MinModalOptions): void { this._create('warning', config); } public error(config: MinModalOptions): void { this._create('error', config); } public confirm(config: MinModalOptions): void { this._create('confirm', config); } public remove(): void { const { ModalParent, ModalMask, ModalWrap } = this._getNode(); this._destroy(ModalParent, ModalMask, ModalWrap); } private _create(type: string, config: MinModalOptions): void { DEFAULT_ZINDEX++; const { width = 416, title = '', content = '', okText = '确定', cancelText = '取消', loading = false, keyboard = false, scrollable = false, lockScroll = true, onOk, onCancel, dangerouslyUseHTMLString = false } = config; // @ts-ignore const icon = ICONTYPE[type]; const isShowCancelBtn = type === 'confirm' ? `<button type="button" class="${PREFIX.button} ${PREFIX.button}-text">${cancelText}</button>` : ''; const template = ` <div class="${PREFIX.modal}-mini-modal"> <div class="${PREFIX.modal}-mask" style="z-index: ${DEFAULT_ZINDEX};"></div> <div class="${PREFIX.modal}-wrap" style="z-index: ${DEFAULT_ZINDEX};"> <div class="${PREFIX.modal}" style="width: ${width}px;"> <div class="${PREFIX.modal}-content"> <div class="${PREFIX.modal}-body"> <div class="${PREFIX.modal}-confirm"> <div class="${PREFIX.modal}-confirm-head"> <div class="${PREFIX.modal}-confirm-head-icon ${PREFIX.modal}-confirm-head-icon-${type}"> <i class="${PREFIX.icon} ${PREFIX.icon}-${icon}"></i> </div> <div class="${PREFIX.modal}-confirm-head-title"></div> </div> <div class="${PREFIX.modal}-confirm-body"></div> <div class="${PREFIX.modal}-confirm-footer"> ${isShowCancelBtn} <button type="button" class="${PREFIX.button} ${PREFIX.button}-primary"><span>${okText}</span></button> </div> </div> </div> </div> </div> </div> </div>`; document.body.insertAdjacentHTML('beforeend', template); const { ModalParent, ModalMask, ModalWrap, Modal } = this._getNode(); this._setMainContent(Modal, title, content, dangerouslyUseHTMLString); this._setAnimation('in', ModalMask, ModalWrap); this._setScrollable(scrollable, lockScroll); this._handleBtnClick(ModalParent, ModalMask, ModalWrap, Modal, loading, onOk, onCancel); this._keyboardClosed(type, keyboard, ModalParent, ModalMask, ModalWrap, onCancel); } private _setMainContent( modal: Element, title: string, content: string, isUseHTML: boolean ): void { const ModalHead = modal.querySelector(`.${PREFIX.modal}-confirm-head-title`)!; const ModalBody = modal.querySelector(`.${PREFIX.modal}-confirm-body`)!; useHTMLString(ModalHead, title, isUseHTML); useHTMLString(ModalBody, content, isUseHTML); } private _handleBtnClick( parent: HTMLElement, mask: Element, wrap: Element, modal: Element, loading: boolean, onOk: any, onCancel: any ): void { const ModalOkBtn = modal.querySelector(`.${PREFIX.button}-primary`)!; const ModalCacnelBtn = modal.querySelector(`.${PREFIX.button}-text`); const remove = () => this._destroy(parent, mask, wrap); const okEv = () => { onOk && type.isFn(onOk); if (loading) { MiniModalBtn.config(ModalOkBtn).loading = loading; return; } remove(); }; const cancelEv = () => { remove(); onCancel && type.isFn(onCancel); }; bind(ModalOkBtn, 'click', okEv); if (ModalCacnelBtn) bind(ModalCacnelBtn, 'click', cancelEv); } private _keyboardClosed( _type: string, keyboard: boolean, parent: HTMLElement, mask: Element, wrap: Element, onCancel: any ): void { if (!keyboard) return; const event = (e: KeyboardEvent) => { if (e.key === 'Escape') { e.stopPropagation(); // 判断页面是否有modal实例,如果有才执行事件,避免重复执行 if (document.body.contains(parent)) { // 取消的回调,只在MinModal.confirm()下有效 if (_type === 'confirm') onCancel && type.isFn(onCancel); setTimeout(() => this._destroy(parent, mask, wrap), 0); } } }; window.onkeydown = (e: KeyboardEvent) => event(e); } private _destroy(parent: HTMLElement, mask: Element, wrap: Element): void { this._setAnimation('out', mask, wrap); setTimeout(() => { parent.remove(); this._setScrollable(true, false); }, 150); } private _setAnimation(type: 'in' | 'out', elem1: Element, elem2: Element): void { const maskAniCls = type === 'in' ? { enterCls: 'rab-fade-in' } : { leaveCls: 'rab-fade-out' }; const modalAniCls = type === 'in' ? { enterCls: 'zoom-big-enter' } : { leaveCls: 'zoom-big-leave' }; CssTransition(elem1, { inOrOut: type, ...maskAniCls, timeout: 250, rmCls: true }); CssTransition(elem2, { inOrOut: type, ...modalAniCls, timeout: 200, rmCls: true }); } private _setScrollable(scrollable: boolean, lockScroll: boolean): void { Scrollable({ scroll: scrollable, lock: lockScroll }); } private _getNode(): { ModalParent: HTMLElement; ModalMask: Element; ModalWrap: Element; Modal: Element; } { const ModalParent = $el(`.${PREFIX.modal}-mini-modal`) as HTMLElement; const ModalMask = ModalParent.querySelector(`.${PREFIX.modal}-mask`)!; const ModalWrap = ModalParent.querySelector(`.${PREFIX.modal}-wrap`)!; const Modal = ModalWrap.querySelector(`.${PREFIX.modal}`)!; return { ModalParent, ModalMask, ModalWrap, Modal }; } } export default MiniModal;