ftt-ui-components
Version:
some components for vuetify3/element-plus/... with vue3.
149 lines (121 loc) • 3.49 kB
text/typescript
import { render, createVNode } from 'vue'
import { messageDefaults, messageTypes } from './message'
import MessageConstructor from './message.vue'
import { isString, isVNode, isFunction, isElement } from '../../utils'
import { isClient } from '@vueuse/core'
import { instances } from './instance'
import { useZIndex } from '../../hooks'
import type { AppContext } from 'vue'
import type { MessageContext } from './instance'
import type {
Message,
MessageFn,
MessageHandler,
MessageOptions,
MessageParams,
MessageParamsNormalized,
messageType,
} from './message'
let seed = 1
const normalizeOptions = (params?: MessageParams) => {
const options: MessageOptions =
!params || isString(params) || isVNode(params) || isFunction(params)
? { message: params }
: params
const normalized = {
...messageDefaults,
...options,
}
if (!normalized.appendTo) {
normalized.appendTo = document.body
} else if (isString(normalized.appendTo)) {
let appendTo = document.querySelector<HTMLElement>(normalized.appendTo)
// should fallback to default value with a warning
if (!isElement(appendTo)) {
appendTo = document.body
}
normalized.appendTo = appendTo
}
return normalized as MessageParamsNormalized
}
const closeMessage = (instance: MessageContext) => {
const idx = instances.indexOf(instance)
if (idx === -1) return
instances.splice(idx, 1)
const { handler } = instance
handler.close()
}
const createMessage = (
{ appendTo, ...options }: MessageParamsNormalized,
context?: AppContext | null
): MessageContext => {
const { nextZIndex } = useZIndex()
const id = `message_${seed++}`
const userOnClose = options.onClose
const container = document.createElement('div')
const props = {
...options,
zIndex: nextZIndex() + options.zIndex,
id,
onClose() {
userOnClose?.()
closeMessage(instance)
},
onDestroy() {
render(null, container)
},
}
const vnode = createVNode(
MessageConstructor,
props,
isFunction(props.message) || isVNode(props.message)
? { default: props.message }
: null
)
vnode.appContext = context || message._context
render(vnode, container)
// appendTo.appendChild(container.firstElementChild)
appendTo.appendChild(container)
const vm = vnode.component
const handler: MessageHandler = {
close: () => (vm.exposeProxy.visible = false),
}
const instance: MessageContext = {
id,
vnode,
vm,
handler,
props: (vnode.component as any).props,
}
return instance
}
const message: MessageFn &
Partial<Message> & {
_context: AppContext | null
} = (options = {}, context) => {
if (!isClient) return { close: () => undefined }
const normalized = normalizeOptions(options)
if (normalized.grouping && instances.length) {
const instance = instances.find(
({ vnode: vm }) => vm.props?.message === normalized.message
)
if (instance) {
instance.props.repeatNum += 1
instance.props.type = normalized.type
return instance.handler
}
}
const instance = createMessage(normalized, context)
instances.push(instance)
return instance.handler
}
export function closeAll(type?: messageType): void {
for (const instance of instances) {
if (!type || type === instance.props.type) {
instance.handler.close()
}
}
}
message.closeAll = closeAll
message._context = null
export default message as Message