133 lines
3.1 KiB
TypeScript
133 lines
3.1 KiB
TypeScript
import { createVNode, render } from 'vue'
|
|
import { defu } from 'defu'
|
|
import NotificationConstructor from './notification.vue'
|
|
import type {
|
|
NotificationOptions,
|
|
NotificationPlacement,
|
|
NotificationQueue,
|
|
} from './types'
|
|
|
|
const notifications: Record<NotificationPlacement, NotificationQueue> = {
|
|
'top-left': [],
|
|
'top-right': [],
|
|
'bottom-left': [],
|
|
'bottom-right': [],
|
|
}
|
|
|
|
const GAP_SIZE = 16
|
|
let SEED = 1
|
|
|
|
const DEFAULT_OPTIONS: NotificationOptions = {
|
|
text: '',
|
|
placement: 'top-right',
|
|
duration: 5000,
|
|
onClose: () => {},
|
|
}
|
|
|
|
const notify = function (options: NotificationOptions, context = null) {
|
|
// if (process.server) return { close: () => undefined };
|
|
|
|
options = defu(options, DEFAULT_OPTIONS)
|
|
|
|
const orientedNotifications = notifications[options.placement!]
|
|
const id = options.id
|
|
? `${options.placement!}_${options.id}`
|
|
: `notification_${SEED++}`
|
|
|
|
const idx = orientedNotifications.findIndex(
|
|
({ vm }) => vm.component?.props.id === id,
|
|
)
|
|
if (idx > -1)
|
|
return
|
|
|
|
let verticalOffset = options.offset || 0
|
|
notifications[options.placement!].forEach(({ vm }) => {
|
|
verticalOffset += (vm.el?.offsetHeight || 0) + GAP_SIZE
|
|
})
|
|
verticalOffset += GAP_SIZE
|
|
|
|
const userOnClose = options.onClose
|
|
const props = {
|
|
...options,
|
|
offset: verticalOffset,
|
|
id,
|
|
onClose: () => {
|
|
close(id, options.placement!, userOnClose)
|
|
},
|
|
}
|
|
|
|
const container = document.createElement('div')
|
|
|
|
const vm = createVNode(
|
|
NotificationConstructor,
|
|
props,
|
|
options.text
|
|
? {
|
|
default: () => options.text,
|
|
}
|
|
: null,
|
|
)
|
|
vm.appContext = context ?? notify._context
|
|
vm.props!.onDestroy = () => {
|
|
render(null, container)
|
|
}
|
|
|
|
render(vm, container)
|
|
notifications[options.placement!].push({ vm })
|
|
document.body.appendChild(container.firstElementChild!)
|
|
|
|
return {
|
|
close: () => {
|
|
vm.component!.exposed!.close()
|
|
},
|
|
}
|
|
}
|
|
|
|
export function close(
|
|
id: NotificationOptions['id'],
|
|
placement: NotificationOptions['placement'],
|
|
userOnClose: NotificationOptions['onClose'],
|
|
) {
|
|
const orientedNotifications = notifications[placement!]
|
|
const idx = orientedNotifications.findIndex(
|
|
({ vm }) => vm.component?.props.id === id,
|
|
)
|
|
if (idx === -1)
|
|
return
|
|
|
|
const { vm } = orientedNotifications[idx]
|
|
|
|
if (!vm)
|
|
return
|
|
|
|
userOnClose?.(vm)
|
|
|
|
const removedHeight = vm.el!.offsetHeight
|
|
const verticalPos = placement!.split('-')[0]
|
|
orientedNotifications.splice(idx, 1)
|
|
|
|
if (orientedNotifications.length < 1)
|
|
return
|
|
|
|
for (let i = idx; i < orientedNotifications.length; i++) {
|
|
const { el, component } = orientedNotifications[i].vm
|
|
const styles = getComputedStyle(el as Element)
|
|
const pos = Number.parseInt(styles.getPropertyValue(verticalPos), 10)
|
|
|
|
component!.props.offset = pos - removedHeight - GAP_SIZE
|
|
}
|
|
}
|
|
|
|
export function closeAll() {
|
|
for (const orientedNotifications of Object.values(notifications)) {
|
|
orientedNotifications.forEach(({ vm }) => {
|
|
vm.component!.exposed!.close()
|
|
})
|
|
}
|
|
}
|
|
|
|
notify.closeAll = closeAll
|
|
notify._context = null
|
|
|
|
export default notify
|