UNPKG

ivue-material-plus

Version:

A high quality UI components Library with Vue.js

233 lines (186 loc) • 5.77 kB
import { createVNode, VNode, render } from 'vue'; import { transferIndex, transferIncrease } from '../../utils/transfer-queue'; import Notice from './index.vue'; // type import type { Type, Position, Options } from './types/notice'; const prefixKey = 'ivue_notice_key_'; let name = 1; // 队列 const notifications = { 'top-left': [], 'top-right': [], 'bottom-left': [], 'bottom-right': [], }; // 每个通知之间的间隙大小 const GAP_SIZE = 16; let offset: number; let defaultDuration: number; // 存储自定义的通知id列表 const notificationsId: Record<string, boolean> = {}; function notice(type: Type, options: Options = {}) { // 自定义id const customizeId = options.id; // 每一个的id const id = customizeId || `${prefixKey}${name}`; // 通知列表开启了自定义id,不重复创建 if (notificationsId[customizeId]) { return; } // 标题 options.title = options.title || ''; // 描述 options.desc = options.desc || ''; // onclose options.onClose = options.onClose || function () {}; // duration options.duration = defaultDuration || options.duration; // 没有id if (!customizeId) { name++; } // 偏移距离 let verticalOffset = offset || options.offset || 0; // 偏移方向 const position = options.position || 'top-right'; notifications[position].forEach(({ vm }) => { verticalOffset += (vm.el.offsetHeight || 0) + GAP_SIZE; }); verticalOffset += GAP_SIZE; // 获取Index const handleGetIndex = () => { transferIncrease(); return transferIndex; }; // 当前index const tIndex = handleGetIndex(); // 用户设置的关闭回调 const userOnClose = options.onClose; options = { // default options end ...options, type, onClose: () => { close(id, position, userOnClose); }, offset: verticalOffset, id: id, zIndex: 1010 + tIndex, }; const vm: VNode = createVNode(Notice, options); const container = document.createElement('div'); // 有自定义id if (customizeId) { notificationsId[customizeId] = true; } // 清除通知元素防止内存泄漏 vm.props.onDestroy = () => { render(null, container); }; notifications[position].push({ vm }); // 当 close 函数被调用时,实例将删除此项。 // 所以我们不需要担心。 render(vm, container); document.body.appendChild(container.firstElementChild); return { close, }; } /** * 当用户点击`x` 按钮或按下`esc` 或时间达到其限制时,将调用此函数。 * 由transition@before-leave 事件发出,以便我们可以获取当前的notification.offsetHeight,如果它被调用 * 通过@after-leave DOM 元素将从页面中删除,因此我们无法再获取 offsetHeight。 * @param {String} id 要关闭的通知 id * @param {Position} 定位策略 * @param {Function} userOnClose 用户关闭时调用的回调 */ const close = ( id: string, position: Position = 'top-right', userOnClose?: (vm: VNode) => void ): void => { // 将 vm 插入通知列表时存储索引 const orientedNotifications = notifications[position]; // 当前的下标 const idx = orientedNotifications.findIndex(({ vm }) => { if (!vm) { return; } return vm.component.props.id === id; }); if (idx === -1) { return; } // vm const { vm } = orientedNotifications[idx]; if (!vm) { return; } // 在通知从 DOM 中移除之前调用用户的关闭函数。 userOnClose?.(vm); // 请注意,这称为@before-leave,这就是我们能够获取此属性的原因。 const removedHeight = vm.el.offsetHeight; const verticalPos = position.split('-')[0]; // 有自定义id删除 if (notificationsId[id]) { // 隐藏自定义的通知 orientedNotifications[idx].vm.component.proxy.data.visible = false; delete notificationsId[id]; } // 移除当前通知 notifications[position].splice(idx, 1); const len = notifications[position].length; if (len < 1) { return; } // 开始移除项 for (let i = idx; i < len; i++) { // 新位置等于当前的 offsetTop 减去移除的高度加上 16px(每个项目之间的间隙大小) const { el, component } = orientedNotifications[i].vm; const pos = parseInt(el.style[verticalPos], 10) - removedHeight - GAP_SIZE; // 修改偏移位置触发向上移动动画 component.props.offset = pos; } }; // 关闭所有 const closeAll = (): void => { // 遍历所有方向,立即关闭它们。 for (const key in notifications) { const orientedNotifications = notifications[key as Position]; orientedNotifications.forEach(({ vm }) => { // same as the previous close method, we'd like to make sure lifecycle gets handle properly. vm.component.proxy.data.visible = false; }); } }; const notification = { open(options: Options) { return notice('normal', options); }, info(options: Options) { return notice('info', options); }, warning(options: Options) { return notice('warning', options); }, success(options: Options) { return notice('success', options); }, error(options: Options) { return notice('error', options); }, // 全局配置 config(options: Options) { // 偏移位置 if (options.offset) { offset = options.offset; } // 延迟时间 if (options.duration > 0) { defaultDuration = options.duration; } }, close, closeAll, }; export default notification;