diff --git a/packages/components/message/src/message-method.ts b/packages/components/message/src/message-method.ts index 5cf68c48a6464a..1a33555b6e5f0c 100644 --- a/packages/components/message/src/message-method.ts +++ b/packages/components/message/src/message-method.ts @@ -1,4 +1,4 @@ -import { createVNode, render } from 'vue' +import { createVNode, render, shallowReactive } from 'vue' import { isClient } from '@vueuse/core' import { debugWarn, @@ -26,7 +26,7 @@ import type { MessageQueueItem, } from './message' -const instances: MessageQueue = [] +export const messageInstances: MessageQueue = shallowReactive([]) let seed = 1 // TODO: Since Notify.ts is basically the same like this file. So we could do some encapsulation against them to reduce code duplication. @@ -60,29 +60,13 @@ const normalizeOptions = (params?: MessageParams) => { return normalized as MessageParamsNormalized } -const MESSAGE_HEIGHT = 48 -const MESSAGE_GAP = 16 - -const getNextOffset = (offset: number) => offset + MESSAGE_HEIGHT + MESSAGE_GAP - -const computeOffset = () => { - if (instances.length === 0) return - - instances.reduce((prev, next) => { - next.props.offset = prev - return getNextOffset(prev) - }, instances[0].options.offset) -} - const closeMessage = (instance: MessageQueueItem) => { - const idx = instances.indexOf(instance) + const idx = messageInstances.indexOf(instance) if (idx === -1) return - instances.splice(idx, 1) + messageInstances.splice(idx, 1) const { handler } = instance handler.close() - - computeOffset() } const createMessage = ( @@ -94,20 +78,11 @@ const createMessage = ( const id = `message_${seed++}` const userOnClose = options.onClose - let offset: number - const lastInstance = instances[instances.length - 1] - if (lastInstance) { - offset = getNextOffset(lastInstance.vm.offset) - } else { - offset = options.offset - } - const container = document.createElement('div') const props = { ...options, zIndex: options.zIndex ?? nextZIndex(), - offset, id, onClose: () => { userOnClose?.() @@ -146,12 +121,11 @@ const createMessage = ( }, } - const instance = { + const instance: MessageQueueItem = { id, vnode, vm, handler, - options: { appendTo, ...options }, props: (vnode.component as any).props, } @@ -165,14 +139,17 @@ const message: MessageFn & ) => { if (!isClient) return { close: () => undefined } - if (isNumber(messageConfig.max) && instances.length >= messageConfig.max) { + if ( + isNumber(messageConfig.max) && + messageInstances.length >= messageConfig.max + ) { return { close: () => undefined } } const normalized = normalizeOptions(options) - if (normalized.grouping && instances.length) { - const instance = instances.find( + if (normalized.grouping && messageInstances.length) { + const instance = messageInstances.find( ({ vnode: vm }) => vm.props?.message === normalized.message ) if (instance) { @@ -184,7 +161,7 @@ const message: MessageFn & const instance = createMessage(normalized, context) - instances.push(instance) + messageInstances.push(instance) return instance.handler } @@ -196,7 +173,7 @@ messageTypes.forEach((type) => { }) export function closeAll(): void { - for (const instance of instances) { + for (const instance of messageInstances) { instance.handler.close() } } diff --git a/packages/components/message/src/message.ts b/packages/components/message/src/message.ts index 89490a2844dd5b..551e17f45b0ba7 100644 --- a/packages/components/message/src/message.ts +++ b/packages/components/message/src/message.ts @@ -26,7 +26,7 @@ export const messageDefaults = mutable({ onClose: undefined, showClose: false, type: 'info', - offset: 20, + offset: 16, zIndex: 0, grouping: false, repeatNum: 1, @@ -146,7 +146,6 @@ export type MessageQueueItem = { vnode: VNode handler: MessageHandler vm: MessageInstance - options: MessageParamsNormalized props: Mutable } diff --git a/packages/components/message/src/message.vue b/packages/components/message/src/message.vue index 4e025481c1c242..07f12ad0abb230 100644 --- a/packages/components/message/src/message.vue +++ b/packages/components/message/src/message.vue @@ -7,6 +7,7 @@
import { computed, onMounted, ref, watch } from 'vue' -import { useEventListener, useTimeoutFn } from '@vueuse/core' +import { useEventListener, useResizeObserver, useTimeoutFn } from '@vueuse/core' import { TypeComponents, TypeComponentsMap } from '@element-plus/utils' import { EVENT_CODE } from '@element-plus/constants' import ElBadge from '@element-plus/components/badge' import { ElIcon } from '@element-plus/components/icon' import { useNamespace } from '@element-plus/hooks' import { messageEmits, messageProps } from './message' +import { messageInstances } from './message-method' import type { BadgeProps } from '@element-plus/components/badge' import type { CSSProperties } from 'vue' @@ -64,23 +66,37 @@ const props = defineProps(messageProps) defineEmits(messageEmits) const ns = useNamespace('message') + +const messageRef = ref() const visible = ref(false) -const badgeType = ref( - props.type ? (props.type === 'error' ? 'danger' : props.type) : 'info' -) +const height = ref(0) + let stopTimer: (() => void) | undefined = undefined +const badgeType = computed(() => + props.type ? (props.type === 'error' ? 'danger' : props.type) : 'info' +) const typeClass = computed(() => { const type = props.type return { [ns.bm('icon', type)]: type && TypeComponentsMap[type] } }) - const iconComponent = computed( () => props.icon || TypeComponentsMap[props.type] || '' ) +const lastInstance = computed(() => { + const idx = messageInstances.findIndex((instance) => instance.id === props.id) + if (idx <= 0) return undefined + return messageInstances[idx - 1] +}) +const lastOffset = computed(() => { + if (!lastInstance.value) return 0 + return lastInstance.value.vm.bottom +}) +const offset = computed(() => props.offset + lastOffset.value) +const bottom = computed((): number => height.value + offset.value) const customStyle = computed(() => ({ - top: `${props.offset}px`, + top: `${offset.value}px`, zIndex: props.zIndex, })) @@ -121,8 +137,13 @@ watch( useEventListener(document, 'keydown', keydown) +useResizeObserver(messageRef, () => { + height.value = messageRef.value!.getBoundingClientRect().height +}) + defineExpose({ visible, + bottom, close, })