equal-vue
Version:
Equal is a Vue 3 hackable UI library empowered by tailwindcss
138 lines (120 loc) • 3.7 kB
text/typescript
import NotificationVue from './ItNotification.vue'
import {
createApp,
ComponentPublicInstance,
VNode,
h,
reactive,
inject,
} from 'vue'
import { Positions } from '@/models/enums'
import { EqualUIConfiguration } from '@/types/variant'
interface INotificationOptions {
id: number | null
title: string | number
text: string | number
show: boolean
duration: number
placement: string
onClose: () => void
[key: string]: any
}
const notifications: { id: number; component: ComponentPublicInstance }[] = []
let idStart = 0
// Has to be used only inside the setup()
export const useNotification = () => {
const config = inject<EqualUIConfiguration>(
'config',
{} as EqualUIConfiguration,
)
return (options: Partial<INotificationOptions> = {}, children?: VNode) => {
options.placement = options.placement || Positions.TR
const onClose = options.onClose
const id = idStart++
options.onClose = () => {
useNotification.close(id, {}, onClose)
}
const newProps = reactive(
Object.assign(options, {
id,
ref: 'elRef',
show: false,
onShowChange: (val: boolean) => {},
}),
)
newProps.onShowChange = (val) => {
newProps.show = val
}
const tempDiv = document.createElement('div')
const instance = createApp({
render: () => h(NotificationVue, newProps, () => children),
})
instance.provide('config', config)
const mountedInstance = instance.mount(tempDiv)
document.body.appendChild(mountedInstance.$el)
newProps.show = true
const offsets: { [key: string]: number } = {
[Positions.TR]: 0,
[Positions.T]: 0,
[Positions.B]: 0,
[Positions.TL]: 0,
[Positions.BR]: 0,
[Positions.BL]: 0,
}
notifications
.filter(
(el) =>
(el.component.$refs.elRef as INotificationOptions).placement ===
options.placement,
)
.forEach((el) => {
offsets[options.placement!] +=
(el.component.$el as HTMLElement).offsetHeight + 6
})
;(mountedInstance.$refs.elRef as INotificationOptions).position[
options.placement.split('-')[0]
] = offsets[options.placement] + 6
if (options.placement.split('-')[1]) {
;(mountedInstance.$refs.elRef as INotificationOptions).position[
options.placement.split('-')[1]
] = 10
} else {
;(mountedInstance.$refs.elRef as INotificationOptions).position['right'] =
'50%'
}
notifications.push({ id, component: mountedInstance })
return useNotification.close.bind(this, id, newProps)
}
}
useNotification.close = (
id: number,
props?: Record<string, any>,
onClose?: () => void,
) => {
const index = notifications.findIndex((el) => el.id === (id || props?.id))
if (!notifications[index]) {
return
}
props!.show = false
const height = (notifications[index].component.$el as HTMLElement)
.offsetHeight
if (onClose) {
onClose()
}
notifications
.filter(
(el) =>
(el.component.$refs.elRef as INotificationOptions).placement ===
(notifications[index].component.$refs.elRef as INotificationOptions)
.placement,
)
.forEach((el) => {
const data = el.component.$refs.elRef as INotificationOptions
if (data.id! >= (id || props?.id)) {
const elPlacement = data.placement.split('-')
data.position[elPlacement[0]] =
data.position[elPlacement[0]] - (height + 6)
}
})
notifications.splice(index, 1)
}