element3
Version:
A Component Library for Vue3
226 lines (218 loc) • 5.28 kB
JavaScript
import {
nextTick,
ref,
onBeforeMount,
getCurrentInstance,
onBeforeUnmount,
watch,
toRefs
} from 'vue'
import PopupManager from '../../src/utils/popup/popup-manager'
import getScrollBarWidth from '../../src/utils/scrollbar-width'
import merge from '../../src/utils/merge'
import { getStyle, addClass, removeClass, hasClass } from '../../src/utils/dom'
let idSeed = 1
let scrollBarWidth
const popupProps = {
visible: {
type: Boolean,
default: false
},
openDelay: {},
closeDelay: {},
zIndex: {},
modal: {
type: Boolean,
default: false
},
modalFade: {
type: Boolean,
default: true
},
modalClass: {},
modalAppendToBody: {
type: Boolean,
default: false
},
lockScroll: {
type: Boolean,
default: true
},
closeOnPressEscape: {
type: Boolean,
default: false
},
closeOnClickModal: {
type: Boolean,
default: false
}
}
function usePopup(props) {
const { visible, modal, modalAppendToBody, lockScroll, closeDelay } = toRefs(
props
)
const opened = ref(false)
const bodyPaddingRight = ref(null)
const computedBodyPaddingRight = ref(0)
const withoutHiddenClass = ref(true)
const rendered = ref(false)
const instance = getCurrentInstance()
let _popupId = 0
let _closeTimer = 0
let _openTimer = 0
let _opening = false
let _closing = false
const open = (options) => {
if (!rendered.value) {
rendered.value = true
}
const props = merge(instance.proxy, options)
/**
* fix bug (#577)
*/
if (_closeTimer) {
clearTimeout(_closeTimer)
_closeTimer = 0
}
clearTimeout(_openTimer)
const delay = Number(props.openDelay) || 0
if (delay > 0) {
_openTimer = setTimeout(() => {
_openTimer = 0
doOpen(props)
}, delay)
} else {
doOpen(props)
}
}
const doOpen = (props) => {
if (instance.proxy.$isServer) return
if (_opening) return
if (opened.value) return
_opening = true
const dom = instance.proxy.$el
const modal = props.modal
const zIndex = props.zIndex
if (zIndex) {
PopupManager.zIndex = zIndex.value
}
if (modal) {
if (_closing) {
PopupManager.closeModal(_popupId)
_closing = false
}
PopupManager.openModal(
_popupId,
PopupManager.nextZIndex(),
modalAppendToBody.value ? undefined : dom,
props.modalClass,
props.modalFade
)
if (props.lockScroll) {
withoutHiddenClass.value = !hasClass(
document.body,
'el-popup-parent--hidden'
)
if (withoutHiddenClass.value) {
bodyPaddingRight.value = document.body.style.paddingRight
computedBodyPaddingRight.value = parseInt(
getStyle(document.body, 'paddingRight'),
10
)
}
scrollBarWidth = getScrollBarWidth()
const bodyHasOverflow =
document.documentElement.clientHeight < document.body.scrollHeight
const bodyOverflowY = getStyle(document.body, 'overflowY')
if (
scrollBarWidth > 0 &&
(bodyHasOverflow || bodyOverflowY === 'scroll') &&
withoutHiddenClass.value
) {
document.body.style.paddingRight =
computedBodyPaddingRight.value + scrollBarWidth + 'px'
}
addClass(document.body, 'el-popup-parent--hidden')
}
}
if (getComputedStyle(dom).position === 'static') {
dom.style.position = 'absolute'
}
dom.style.zIndex = PopupManager.nextZIndex()
opened.value = true
instance.onOpen && instance.onOpen()
doAfterOpen()
}
const doAfterOpen = () => {
_opening = false
}
const close = () => {
if (_closing) return
if (_openTimer) {
clearTimeout(_openTimer)
_openTimer = 0
}
clearTimeout(_closeTimer)
const delay = Number(closeDelay && closeDelay.value)
if (delay > 0) {
_closeTimer = setTimeout(() => {
_closeTimer = 0
doClose()
}, delay)
} else {
doClose()
}
}
const doClose = () => {
_closing = true
instance.onClose && instance.onClose()
if (lockScroll.value) {
setTimeout(restoreBodyStyle, 200)
}
opened.value = false
doAfterClose()
}
const doAfterClose = () => {
PopupManager.closeModal(_popupId)
_closing = false
}
const restoreBodyStyle = () => {
if (modal.value && withoutHiddenClass.value) {
document.body.style.paddingRight = bodyPaddingRight.value
removeClass(document.body, 'el-popup-parent--hidden')
}
withoutHiddenClass.value = true
}
watch(visible, (val) => {
if (val) {
if (_opening) return
if (!rendered.value) {
rendered.value = true
nextTick(() => {
open()
})
} else {
open()
}
} else {
close()
}
})
onBeforeMount(() => {
_popupId = 'popup-' + idSeed++
PopupManager.register(_popupId, instance)
})
onBeforeUnmount(() => {
PopupManager.deregister(_popupId)
PopupManager.closeModal(_popupId)
restoreBodyStyle()
})
return {
opened,
visible,
open,
rendered,
close
}
}
export { popupProps, usePopup }