2021-07-03 19:04:03 +08:00
|
|
|
import { h, ref, Teleport, onUnmounted } from 'vue'
|
|
|
|
import { NOOP } from '@vue/shared'
|
2021-11-29 15:58:44 +08:00
|
|
|
import { isClient } from '@vueuse/core'
|
2021-09-04 19:29:28 +08:00
|
|
|
import {
|
|
|
|
createGlobalNode,
|
|
|
|
removeGlobalNode,
|
|
|
|
} from '@element-plus/utils/global-nodes'
|
2021-07-03 19:04:03 +08:00
|
|
|
|
|
|
|
import type { VNode, Ref } from 'vue'
|
|
|
|
|
2021-11-29 15:58:44 +08:00
|
|
|
export const useTeleport = (
|
|
|
|
contentRenderer: () => VNode,
|
|
|
|
appendToBody: Ref<boolean>
|
|
|
|
) => {
|
2021-07-03 19:04:03 +08:00
|
|
|
const isTeleportVisible = ref(false)
|
|
|
|
|
2021-11-29 15:58:44 +08:00
|
|
|
if (!isClient) {
|
2021-07-03 19:04:03 +08:00
|
|
|
return {
|
|
|
|
isTeleportVisible,
|
|
|
|
showTeleport: NOOP,
|
|
|
|
hideTeleport: NOOP,
|
|
|
|
renderTeleport: NOOP,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-28 04:44:27 +08:00
|
|
|
let $el: HTMLElement | null = null
|
2021-07-03 19:04:03 +08:00
|
|
|
|
|
|
|
const showTeleport = () => {
|
|
|
|
isTeleportVisible.value = true
|
|
|
|
// this allows the delayed showing strategy since the the content itself could be enterable
|
|
|
|
// e.g. el-popper
|
|
|
|
if ($el !== null) return
|
|
|
|
|
|
|
|
$el = createGlobalNode()
|
|
|
|
}
|
|
|
|
|
|
|
|
const hideTeleport = () => {
|
|
|
|
isTeleportVisible.value = false
|
|
|
|
if ($el !== null) {
|
|
|
|
removeGlobalNode($el)
|
|
|
|
$el = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const renderTeleport = () => {
|
|
|
|
return appendToBody.value !== true
|
|
|
|
? contentRenderer()
|
|
|
|
: isTeleportVisible.value
|
2021-09-04 19:29:28 +08:00
|
|
|
? [h(Teleport, { to: $el }, contentRenderer())]
|
|
|
|
: undefined
|
2021-07-03 19:04:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
onUnmounted(hideTeleport)
|
|
|
|
|
|
|
|
return {
|
|
|
|
isTeleportVisible,
|
|
|
|
showTeleport,
|
|
|
|
hideTeleport,
|
|
|
|
renderTeleport,
|
|
|
|
}
|
|
|
|
}
|