Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(components): [message]: offset #8379

Merged
merged 3 commits into from Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -3,7 +3,7 @@ import { describe, expect, it, test, vi } from 'vitest'
import { getStyle } from '@element-plus/utils'
import { rAF } from '@element-plus/test-utils/tick'
import { ElMessage } from '..'
import Message from '../src/message-method'
import Message from '../src/method'

const selector = '.el-message'
// TODO: testing the original transition with `nextTick`'
Expand Down
2 changes: 1 addition & 1 deletion packages/components/message/__tests__/message.test.ts
Expand Up @@ -36,7 +36,7 @@ describe('Message.vue', () => {
expect(wrapper.text()).toEqual(AXIOM)
expect(vm.visible).toBe(true)
expect(vm.iconComponent).toBe(TypeComponentsMap['info'])
expect(vm.customStyle).toEqual({ top: '20px', zIndex: 0 })
expect(vm.customStyle).toEqual({ top: '16px', zIndex: 0 })
})

test('should be able to render VNode', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/components/message/index.ts
@@ -1,6 +1,6 @@
import { withInstallFunction } from '@element-plus/utils'

import Message from './src/message-method'
import Message from './src/method'

export const ElMessage = withInstallFunction(Message, '$message')
export default ElMessage
Expand Down
30 changes: 30 additions & 0 deletions packages/components/message/src/instance.ts
@@ -0,0 +1,30 @@
import { shallowReactive } from 'vue'
import type { VNode } from 'vue'
import type { Mutable } from '@element-plus/utils'
import type { MessageHandler, MessageInstance, MessageProps } from './message'

export type MessageContext = {
id: string
vnode: VNode
handler: MessageHandler
vm: MessageInstance
props: Mutable<MessageProps>
}

export const instances: MessageContext[] = shallowReactive([])

export const getInstance = (id: string) => {
const idx = instances.findIndex((instance) => instance.id === id)
const current = instances[idx]
let prev: MessageContext | undefined
if (idx > 0) {
prev = instances[idx - 1]
}
return { current, prev }
}

export const getLastOffset = (id: string): number => {
const { prev } = getInstance(id)
if (!prev) return 0
return prev.vm.bottom
}
10 changes: 1 addition & 9 deletions packages/components/message/src/message.ts
Expand Up @@ -26,7 +26,7 @@ export const messageDefaults = mutable({
onClose: undefined,
showClose: false,
type: 'info',
offset: 20,
offset: 16,
zIndex: 0,
grouping: false,
repeatNum: 1,
Expand Down Expand Up @@ -140,11 +140,3 @@ export interface Message extends MessageFn {
info: MessageTypedFn
error: MessageTypedFn
}

export type MessageQueueItem = {
vnode: VNode
handler: MessageHandler
vm: MessageInstance
}

export type MessageQueue = MessageQueueItem[]
25 changes: 19 additions & 6 deletions packages/components/message/src/message.vue
Expand Up @@ -7,6 +7,7 @@
<div
v-show="visible"
:id="id"
ref="messageRef"
:class="[
ns.b(),
{ [ns.m(type)]: type && !icon },
Expand Down Expand Up @@ -44,13 +45,14 @@

<script lang="ts" setup>
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 { getLastOffset } from './instance'
import type { BadgeProps } from '@element-plus/components/badge'
import type { CSSProperties } from 'vue'
Expand All @@ -64,23 +66,29 @@ const props = defineProps(messageProps)
defineEmits(messageEmits)
const ns = useNamespace('message')
const messageRef = ref<HTMLDivElement>()
const visible = ref(false)
const badgeType = ref<BadgeProps['type']>(
props.type ? (props.type === 'error' ? 'danger' : props.type) : 'info'
)
const height = ref(0)
let stopTimer: (() => void) | undefined = undefined
const badgeType = computed<BadgeProps['type']>(() =>
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 lastOffset = computed(() => getLastOffset(props.id))
const offset = computed(() => props.offset + lastOffset.value)
const bottom = computed((): number => height.value + offset.value)
const customStyle = computed<CSSProperties>(() => ({
top: `${props.offset}px`,
top: `${offset.value}px`,
zIndex: props.zIndex,
}))
Expand Down Expand Up @@ -121,8 +129,13 @@ watch(
useEventListener(document, 'keydown', keydown)
useResizeObserver(messageRef, () => {
height.value = messageRef.value!.getBoundingClientRect().height
})
defineExpose({
visible,
bottom,
close,
})
</script>
Expand Up @@ -12,7 +12,9 @@ import { useZIndex } from '@element-plus/hooks'
import { messageConfig } from '@element-plus/components/config-provider/src/config-provider'
import MessageConstructor from './message.vue'
import { messageDefaults, messageTypes } from './message'
import { instances } from './instance'

import type { MessageContext } from './instance'
import type { AppContext } from 'vue'
import type {
Message,
Expand All @@ -22,11 +24,8 @@ import type {
MessageOptions,
MessageParams,
MessageParamsNormalized,
MessageQueue,
MessageQueueItem,
} from './message'

const instances: MessageQueue = []
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.
Expand Down Expand Up @@ -60,49 +59,29 @@ const normalizeOptions = (params?: MessageParams) => {
return normalized as MessageParamsNormalized
}

const closeMessage = (instance: MessageQueueItem) => {
const closeMessage = (instance: MessageContext) => {
const idx = instances.indexOf(instance)
if (idx === -1) return

instances.splice(idx, 1)
const { vnode, handler } = instance
const { handler } = instance
handler.close()

const removedHeight = vnode.el!.offsetHeight
// adjust other instances vertical offset
const len = instances.length
if (len < 1) return
for (let i = idx; i < len; i++) {
const pos =
Number.parseInt(instances[i].vnode.el!.style['top'], 10) -
removedHeight -
16

instances[i].vnode.component!.props.offset = pos
}
}

const createMessage = (
{ appendTo, ...options }: MessageParamsNormalized,
context?: AppContext | null
): MessageQueueItem => {
): MessageContext => {
const { nextZIndex } = useZIndex()

const id = `message_${seed++}`
const userOnClose = options.onClose

let verticalOffset = options.offset
instances.forEach(({ vnode: vm }) => {
verticalOffset += (vm.el?.offsetHeight || 0) + 16
})
verticalOffset += 16

const container = document.createElement('div')

const props = {
...options,
zIndex: options.zIndex ?? nextZIndex(),
offset: verticalOffset,
id,
onClose: () => {
userOnClose?.()
Expand Down Expand Up @@ -141,10 +120,12 @@ const createMessage = (
},
}

const instance = {
const instance: MessageContext = {
id,
vnode,
vm,
handler,
props: (vnode.component as any).props,
}

return instance
Expand All @@ -168,8 +149,8 @@ const message: MessageFn &
({ vnode: vm }) => vm.props?.message === normalized.message
)
if (instance) {
;(instance.vnode.component as any).props.repeatNum += 1
;(instance.vnode.component as any).props.type = normalized.type
instance.props.repeatNum += 1
instance.props.type = normalized.type
return instance.handler
}
}
Expand Down