element-plus
Version:
A Component Library for Vue 3
1 lines • 7.78 kB
Source Map (JSON)
{"version":3,"file":"notify.mjs","sources":["../../../../../../packages/components/notification/src/notify.ts"],"sourcesContent":["import { createVNode, render } from 'vue'\nimport isServer from '@element-plus/utils/isServer'\nimport PopupManager from '@element-plus/utils/popup-manager'\nimport { isVNode } from '@element-plus/utils/util'\nimport { debugWarn } from '@element-plus/utils/error'\nimport NotificationConstructor from './notification.vue'\nimport { notificationTypes } from './notification'\n\nimport type { ComponentPublicInstance, VNode } from 'vue'\nimport type {\n NotificationOptions,\n Notify,\n NotifyFn,\n NotificationQueue,\n NotificationProps,\n} from './notification'\n\n// This should be a queue but considering there were `non-autoclosable` notifications.\nconst notifications: Record<\n NotificationOptions['position'],\n NotificationQueue\n> = {\n 'top-left': [],\n 'top-right': [],\n 'bottom-left': [],\n 'bottom-right': [],\n}\n\n// the gap size between each notification\nconst GAP_SIZE = 16\nlet seed = 1\n\nconst notify: NotifyFn & Partial<Notify> = function (options = {}) {\n if (isServer) return { close: () => undefined }\n\n if (typeof options === 'string' || isVNode(options)) {\n options = { message: options }\n }\n\n const position = options.position || 'top-right'\n\n let verticalOffset = options.offset || 0\n notifications[position].forEach(({ vm }) => {\n verticalOffset += (vm.el?.offsetHeight || 0) + GAP_SIZE\n })\n verticalOffset += GAP_SIZE\n\n const id = `notification_${seed++}`\n const userOnClose = options.onClose\n const props: Partial<NotificationProps> = {\n // default options end\n zIndex: PopupManager.nextZIndex(),\n offset: verticalOffset,\n ...options,\n id,\n onClose: () => {\n close(id, position, userOnClose)\n },\n }\n\n let appendTo: HTMLElement | null = document.body\n if (options.appendTo instanceof HTMLElement) {\n appendTo = options.appendTo\n } else if (typeof options.appendTo === 'string') {\n appendTo = document.querySelector(options.appendTo)\n }\n\n // should fallback to default value with a warning\n if (!(appendTo instanceof HTMLElement)) {\n debugWarn(\n 'ElNotification',\n 'the appendTo option is not an HTMLElement. Falling back to document.body.'\n )\n appendTo = document.body\n }\n\n const container = document.createElement('div')\n\n const vm = createVNode(\n NotificationConstructor,\n props,\n isVNode(props.message)\n ? {\n default: () => props.message,\n }\n : null\n )\n\n // clean notification element preventing mem leak\n vm.props!.onDestroy = () => {\n render(null, container)\n }\n\n // instances will remove this item when close function gets called. So we do not need to worry about it.\n render(vm, container)\n notifications[position].push({ vm })\n appendTo.appendChild(container.firstElementChild!)\n\n return {\n // instead of calling the onClose function directly, setting this value so that we can have the full lifecycle\n // for out component, so that all closing steps will not be skipped.\n close: () => {\n ;(\n vm.component!.proxy as ComponentPublicInstance<{ visible: boolean }>\n ).visible = false\n },\n }\n}\nnotificationTypes.forEach((type) => {\n notify[type] = (options = {}) => {\n if (typeof options === 'string' || isVNode(options)) {\n options = {\n message: options,\n }\n }\n return notify({\n ...options,\n type,\n })\n }\n})\n\n/**\n * This function gets called when user click `x` button or press `esc` or the time reached its limitation.\n * Emitted by transition@before-leave event so that we can fetch the current notification.offsetHeight, if this was called\n * by @after-leave the DOM element will be removed from the page thus we can no longer fetch the offsetHeight.\n * @param {String} id notification id to be closed\n * @param {Position} position the positioning strategy\n * @param {Function} userOnClose the callback called when close passed by user\n */\nexport function close(\n id: string,\n position: NotificationOptions['position'],\n userOnClose?: (vm: VNode) => void\n): void {\n // maybe we can store the index when inserting the vm to notification list.\n const orientedNotifications = notifications[position]\n const idx = orientedNotifications.findIndex(\n ({ vm }) => vm.component?.props.id === id\n )\n if (idx === -1) return\n const { vm } = orientedNotifications[idx]\n if (!vm) return\n // calling user's on close function before notification gets removed from DOM.\n userOnClose?.(vm)\n\n // note that this is called @before-leave, that's why we were able to fetch this property.\n const removedHeight = vm.el!.offsetHeight\n const verticalPos = position.split('-')[0]\n orientedNotifications.splice(idx, 1)\n const len = orientedNotifications.length\n if (len < 1) return\n // starting from the removing item.\n for (let i = idx; i < len; i++) {\n // new position equals the current offsetTop minus removed height plus 16px(the gap size between each item)\n const { el, component } = orientedNotifications[i].vm\n const pos = parseInt(el!.style[verticalPos], 10) - removedHeight - GAP_SIZE\n component!.props.offset = pos\n }\n}\n\nexport function closeAll(): void {\n // loop through all directions, close them at once.\n for (const orientedNotifications of Object.values(notifications)) {\n orientedNotifications.forEach(({ vm }) => {\n // same as the previous close method, we'd like to make sure lifecycle gets handle properly.\n ;(\n vm.component!.proxy as ComponentPublicInstance<{ visible: boolean }>\n ).visible = false\n })\n }\n}\n\nnotify.closeAll = closeAll\n\nexport default notify as Notify\n"],"names":["NotificationConstructor"],"mappings":";;;;;;;;;AAkBA,MAAM,gBAGF;AAAA,EACF,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,eAAe;AAAA,EACf,gBAAgB;AAAA;AAIlB,MAAM,WAAW;AACjB,IAAI,OAAO;MAEL,SAAqC,SAAU,UAAU,IAAI;AACjE,MAAI;AAAU,WAAO,EAAE,OAAO,MAAM;AAEpC,MAAI,OAAO,YAAY,YAAY,QAAQ,UAAU;AACnD,cAAU,EAAE,SAAS;AAAA;AAGvB,QAAM,WAAW,QAAQ,YAAY;AAErC,MAAI,iBAAiB,QAAQ,UAAU;AACvC,gBAAc,UAAU,QAAQ,CAAC,EAAE,cAAS;AA1C9C;AA2CI,sBAAmB,YAAG,OAAH,mBAAO,iBAAgB,KAAK;AAAA;AAEjD,oBAAkB;AAElB,QAAM,KAAK,gBAAgB;AAC3B,QAAM,cAAc,QAAQ;AAC5B,QAAM,QAAoC;AAAA,IAExC,QAAQ,aAAa;AAAA,IACrB,QAAQ;AAAA,OACL;AAAA,IACH;AAAA,IACA,SAAS,MAAM;AACb,YAAM,IAAI,UAAU;AAAA;AAAA;AAIxB,MAAI,WAA+B,SAAS;AAC5C,MAAI,QAAQ,oBAAoB,aAAa;AAC3C,eAAW,QAAQ;AAAA,aACV,OAAO,QAAQ,aAAa,UAAU;AAC/C,eAAW,SAAS,cAAc,QAAQ;AAAA;AAI5C,MAAI,sBAAsB,cAAc;AACtC,cACE,kBACA;AAEF,eAAW,SAAS;AAAA;AAGtB,QAAM,YAAY,SAAS,cAAc;AAEzC,QAAM,KAAK,YACTA,QACA,OACA,QAAQ,MAAM,WACV;AAAA,IACE,SAAS,MAAM,MAAM;AAAA,MAEvB;AAIN,KAAG,MAAO,YAAY,MAAM;AAC1B,WAAO,MAAM;AAAA;AAIf,SAAO,IAAI;AACX,gBAAc,UAAU,KAAK,EAAE;AAC/B,WAAS,YAAY,UAAU;AAE/B,SAAO;AAAA,IAGL,OAAO,MAAM;AACX;AAAC,MACC,GAAG,UAAW,MACd,UAAU;AAAA;AAAA;AAAA;AAIlB,kBAAkB,QAAQ,CAAC,SAAS;AAClC,SAAO,QAAQ,CAAC,UAAU,OAAO;AAC/B,QAAI,OAAO,YAAY,YAAY,QAAQ,UAAU;AACnD,gBAAU;AAAA,QACR,SAAS;AAAA;AAAA;AAGb,WAAO,OAAO;AAAA,SACT;AAAA,MACH;AAAA;AAAA;AAAA;eAcJ,IACA,UACA,aACM;AAEN,QAAM,wBAAwB,cAAc;AAC5C,QAAM,MAAM,sBAAsB,UAChC,CAAC,EAAE,cAAM;AA1Ib;AA0IgB,sBAAG,cAAH,mBAAc,MAAM,QAAO;AAAA;AAEzC,MAAI,QAAQ;AAAI;AAChB,QAAM,EAAE,OAAO,sBAAsB;AACrC,MAAI,CAAC;AAAI;AAET,6CAAc;AAGd,QAAM,gBAAgB,GAAG,GAAI;AAC7B,QAAM,cAAc,SAAS,MAAM,KAAK;AACxC,wBAAsB,OAAO,KAAK;AAClC,QAAM,MAAM,sBAAsB;AAClC,MAAI,MAAM;AAAG;AAEb,WAAS,IAAI,KAAK,IAAI,KAAK,KAAK;AAE9B,UAAM,EAAE,IAAI,cAAc,sBAAsB,GAAG;AACnD,UAAM,MAAM,SAAS,GAAI,MAAM,cAAc,MAAM,gBAAgB;AACnE,cAAW,MAAM,SAAS;AAAA;AAAA;oBAIG;AAE/B,aAAW,yBAAyB,OAAO,OAAO,gBAAgB;AAChE,0BAAsB,QAAQ,CAAC,EAAE,SAAS;AAExC;AAAC,MACC,GAAG,UAAW,MACd,UAAU;AAAA;AAAA;AAAA;AAKlB,OAAO,WAAW;;;;"}