UNPKG

rabbit-simple-ui

Version:

A simple UI component library based on JavaScript

335 lines (266 loc) 10.8 kB
import { CssTransition, warn } from '../../mixins'; import { type, destroyElem, destroyElemByKey, useHTMLString } from '../../utils'; import { usePromiseCallback } from '../../mixins'; import PREFIX from '../prefix'; import { $el, createElem, setCss, setHtml } from '../../dom-utils'; interface NoticeGlobalAPI { top?: number; // 通知组件距离顶端的距离,单位像素 duration?: number; // 默认自动关闭的延时,单位秒 } interface NoticeOptions { key?: string | number; // 当前通知的唯一标识 icon?: string; // 自定义图标 title?: string; // 通知提醒的标题 desc?: string; // 通知提醒的内容,为空或不填时,自动应用仅标题模式下的样式 style?: string; // 自定义内联样式 onClose?: () => void; // 点击通知关闭按钮时的回调 onClick?: () => void; // 点击通知时触发的回调函数 duration?: number; // 自动关闭的延时,单位秒,不关闭可以写 0 closable?: boolean; // 是否显示右上角关闭按钮,默认为 true className?: string; // 自定义 CSS class dangerouslyUseHTMLString?: boolean; } interface Events { open(config: NoticeOptions): Promise<void>; info(config: NoticeOptions): Promise<void>; success(config: NoticeOptions): Promise<void>; warning(config: NoticeOptions): Promise<void>; error(config: NoticeOptions): Promise<void>; close(key: string): void; destroy(): void; } const NotPrefixKey = 'rab-notice'; const NotMoveEnter = `${PREFIX.notice}-move-enter`; const NotMoveLeave = `${PREFIX.notice}-move-leave`; const iconTypes = { info: 'ios-information-circle', success: 'ios-checkmark-circle', warning: 'ios-alert', error: 'ios-close-circle', loading: 'loading-solid' }; const DEFAULT_NOTICE: { top: number; duration: number; } = { top: 24, duration: 4.5 }; let zIndex = 1180; // 创建实例的最外层父容器 function createNoticeInsanceWrapper(): HTMLElement { const NoticeWrapper = createElem('div'); NoticeWrapper.className = `${PREFIX.notice}`; setCss(NoticeWrapper, 'zIndex', `${zIndex}`); setCss(NoticeWrapper, 'right', '0'); setTimeout(() => setCss(NoticeWrapper, 'top', `${DEFAULT_NOTICE.top}px`), 0); document.body.appendChild(NoticeWrapper); return NoticeWrapper; } class $Notice implements Events { readonly VERSION: string; readonly instances: Array<HTMLElement>; constructor() { this.VERSION = 'v1.0'; // 存储已经创建的实例,在 destroy方法里需要用到 this.instances = []; createNoticeInsanceWrapper(); } public open(config: NoticeOptions): Promise<void> { this._createInstance('normal', config); return usePromiseCallback(DEFAULT_NOTICE.duration, config.duration); } public info(config: NoticeOptions): Promise<void> { this._createInstance('info', config); return usePromiseCallback(DEFAULT_NOTICE.duration, config.duration); } public success(config: NoticeOptions): Promise<void> { this._createInstance('success', config); return usePromiseCallback(DEFAULT_NOTICE.duration, config.duration); } public warning(config: NoticeOptions): Promise<void> { this._createInstance('warning', config); return usePromiseCallback(DEFAULT_NOTICE.duration, config.duration); } public error(config: NoticeOptions): Promise<void> { this._createInstance('error', config); return usePromiseCallback(DEFAULT_NOTICE.duration, config.duration); } public config(options: NoticeGlobalAPI): void { if (options.top) { DEFAULT_NOTICE.top = options.top; } if (options.duration || options.duration === 0) { DEFAULT_NOTICE.duration = options.duration; } } public close(key: string): void { destroyElemByKey({ key, duration: 0.1, clsLeave: NotMoveLeave, prefixKey: NotPrefixKey }); } public destroy(): void { this.instances.forEach((instance) => { destroyElem(instance, { clsLeave: NotMoveLeave, duration: 0.1 }); }); // 清空存放的所有实例 this.instances.length = 0; } private _autoSetZindex(): void { zIndex++; setCss($el(`.${PREFIX.notice}`), 'zIndex', `${zIndex}`); } private _createInstance(type: string, config: NoticeOptions): HTMLElement { this._autoSetZindex(); const Notice = createElem('div'); const NoticeContent = createElem('div'); const NoticeCustomContent = createElem('div'); const NoticeTitle = createElem('div'); const NoticeDesc = createElem('div'); this._setCls( type, [Notice, NoticeContent, NoticeCustomContent, NoticeTitle, NoticeDesc], config.className ); this._setKey(Notice, config.key); this._setTitle(NoticeTitle, config.title, config.dangerouslyUseHTMLString); this._setDesc( Notice, NoticeCustomContent, NoticeDesc, config.desc, config.dangerouslyUseHTMLString ); this._setIcon(type, NoticeCustomContent, NoticeDesc, config.icon); this._setClosable(Notice, config.closable, config.onClose); this._customStyle(Notice, config.style); NoticeCustomContent.append(NoticeTitle, NoticeDesc); NoticeContent.appendChild(NoticeCustomContent); Notice.appendChild(NoticeContent); document.querySelector(`.${PREFIX.notice}`)?.appendChild(Notice); CssTransition(Notice, { inOrOut: 'in', enterCls: NotMoveEnter }); this.instances.push(Notice); this._handleNoticeClick(Notice, config.onClick); this._autoClose(Notice, config.duration); return Notice; } private _setCls(type: string, nodes: Array<HTMLElement>, customCls?: string): void { const nodesCls = [ `${PREFIX.noticeChild} ${customCls ? customCls : ''}`, `${PREFIX.noticeChild}-content`, `${PREFIX.noticeChild}-custom-content ${PREFIX.notice}-with-${type}`, `${PREFIX.notice}-title`, `${PREFIX.notice}-desc` ]; let i = 0; const { length } = nodes; for (; i < length; i++) { const node = nodes[i]; node.className = nodesCls[i]; } } private _setKey(node: HTMLElement, key: any): void { if (!key) return; node.setAttribute(`${NotPrefixKey}-key`, key); } private _setTitle(node: HTMLElement, title?: string, dangerouslyUseHTMLString?: boolean): void { // 必须设置一个通知提醒标题 if (!title) { warn('You must set a notification to remind the title'); return; } // 是否支持传入 HTML 片段 useHTMLString(node, title, dangerouslyUseHTMLString); } private _setDesc( parent: HTMLElement, children_custm: HTMLElement, child_desc: HTMLElement, desc?: string, dangerouslyUseHTMLString?: boolean ): void { if (!desc) return; parent.classList.add(`${PREFIX.noticeChild}-with-desc`); children_custm.classList.add(`${PREFIX.notice}-with-desc`); // 是否支持传入 HTML 片段 useHTMLString(child_desc, desc, dangerouslyUseHTMLString); } private _setIcon( type: string, child_custom: HTMLElement, child_desc: HTMLElement, customIcon?: string ): void { // 不带状态图标的类型 if (type === 'noraml') return; if (type !== 'normal' || customIcon) { child_custom.classList.add(`${PREFIX.notice}-with-icon`); } let isOutline = ''; // 带有状态图标并且是否带有提示内容,如果有则将图标设为 outline 外观 if (child_desc.innerHTML) isOutline = '-outline'; const NoticeIcon = createElem('span'); NoticeIcon.className = `${PREFIX.notice}-icon ${PREFIX.notice}-icon-${type}`; // 是否自定义状态图标 if (customIcon) { setHtml(NoticeIcon, customIcon); } else { // @ts-ignore const defaultIcon = `<i class="${PREFIX.icon} ${PREFIX.icon}-${iconTypes[type]}${isOutline}"></i>`; setHtml(NoticeIcon, defaultIcon); } child_custom.prepend(NoticeIcon); } private _setClosable(parent: HTMLElement, closable?: boolean, onClose?: () => void): void { // 默认显示右上角关闭按钮 type.isUndef(closable) ? (closable = true) : closable; if (!closable) return; parent.classList.add(`${PREFIX.noticeChild}-closable`); const NoticeClose = createElem('a'); const closeIcon = `<i class="${PREFIX.icon} ${PREFIX.icon}-ios-close"></i>`; NoticeClose.className = `${PREFIX.noticeChild}-close`; setHtml(NoticeClose, closeIcon); this._handleClose(parent, NoticeClose, onClose); parent.appendChild(NoticeClose); } // 自定义内联样式 private _customStyle(node: HTMLElement, style?: string): void { if (!style) return; setCss(node, 'cssText', style); } // 点击通知时触发的回调函数 private _handleNoticeClick(parent: HTMLElement, onClick?: () => void): void { parent.onclick = (e) => { e.stopPropagation(); if (onClick) type.isFn(onClick); }; } private _handleClose(parent: HTMLElement, closeBtn: HTMLElement, onClose?: () => void): void { closeBtn.onclick = (e) => { e.stopPropagation(); if (onClose) type.isFn(onClose); destroyElem(parent, { clsLeave: NotMoveLeave, duration: 0.1 }); }; } private _autoClose(instance: HTMLElement, duration?: number): void { // 为每个实例自己的 duration参数设置默认值,如果有传入值则使用自定义的值 type.isUndef(duration) ? (duration = DEFAULT_NOTICE.duration) : duration; destroyElem(instance, { duration, clsLeave: NotMoveLeave }); } } export default $Notice;