UNPKG

element3

Version:

A Component Library for Vue3

240 lines (216 loc) 5.42 kB
import { nextTick, ref, getCurrentInstance, onBeforeUnmount, watch, toRefs } from 'vue' import { PopupManager } from '../../src/utils/popup' // const PopperJS = require('element-ui/src/utils/popper') import { Popper } from '../../src/utils/new-popper' const stop = (e) => e.stopPropagation() const popperProps = { transformOrigin: { type: [Boolean, String], default: true }, placement: { type: String, default: 'bottom' }, boundariesPadding: { type: Number, default: 5 }, reference: {}, popper: {}, offset: { default: 0 }, modelValue: Boolean, visibleArrow: Boolean, arrowOffset: { type: Number, default: 35 }, appendToBody: { type: Boolean, default: true }, popperOptions: { type: Object, default() { return { gpuAcceleration: false } } }, disabled: { type: Boolean, default: false } } function usePopper(props, { emit, slots }, { referenceElm, popperElm }) { const { transformOrigin, placement, reference, popper, offset, modelValue, visibleArrow, arrowOffset, appendToBody, popperOptions, disabled } = toRefs(props) const showPopper = ref(false) const currentPlacement = ref('') const popperJS = ref(null) const instance = getCurrentInstance() function createPopper() { if (instance.proxy.$isServer) return currentPlacement.value = currentPlacement.value || placement.value if ( !/^(top|bottom|left|right)(-start|-end)?$/g.test(currentPlacement.value) ) { return } const options = popperOptions.value const popperRef = (popperElm.value = popperElm.value || (popper && popper.value) || instance.proxy.$refs.popper) let referenceRef = (referenceElm.value = referenceElm.value || (reference && reference.value) || instance.proxy.$refs.reference) if ( !referenceRef && slots.reference && slots.reference() && slots.reference()[0] ) { referenceRef = referenceElm.value = slots.reference()[0].el } if (!popperRef || !referenceRef) return if (visibleArrow.value) appendArrow(popperRef) if (appendToBody.value) document.body.appendChild(popperElm.value) if (popperJS.value && popperJS.value.destroy) { popperJS.value.destroy() } options.placement = currentPlacement.value options.offset = offset.value options.arrowOffset = arrowOffset.value popperJS.value = new Popper(referenceRef, popperRef, options) popperJS.value.onCreate(() => { emit('created', instance.proxy) resetTransformOrigin() nextTick(() => updatePopper()) }) if (typeof options.onUpdate === 'function') { popperJS.value.onUpdate(options.onUpdate) } popperJS.value._popper.style.zIndex = PopupManager.nextZIndex() popperElm.value.addEventListener('click', stop) } function updatePopper() { const popperJSRef = popperJS.value if (popperJSRef) { popperJSRef.update() if (popperJSRef._popper) { popperJSRef._popper.style.zIndex = PopupManager.nextZIndex() } } else { createPopper() } } function doDestroy(forceDestroy) { /* istanbul ignore if */ if (!popperJS.value || (showPopper.value && !forceDestroy)) return popperJS.value.destroy() popperJS.value = null } function destroyPopper() { if (popperJS.value) { resetTransformOrigin() } } function resetTransformOrigin() { if (!transformOrigin.value) return const placementMap = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' } const placement = popperJS.value._popper .getAttribute('x-placement') .split('-')[0] const origin = placementMap[placement] popperJS.value._popper.style.transformOrigin = typeof transformOrigin.value === 'string' ? transformOrigin.value : ['top', 'bottom'].indexOf(placement) > -1 ? `center ${origin}` : `${origin} center` } const appended = ref(false) function appendArrow(element) { let hash if (appended.value) { return } appended.value = true for (const item in element.attributes) { if (/^_v-/.test(element.attributes[item].name)) { hash = element.attributes[item].name break } } const arrow = document.createElement('div') if (hash) { arrow.setAttribute(hash, '') } arrow.setAttribute('x-arrow', '') arrow.className = 'popper__arrow' element.appendChild(arrow) } watch( modelValue, (val) => { showPopper.value = val emit('update:modelValue', val) }, { immediate: true } ) watch(showPopper, (val) => { if (disabled.value) return val ? updatePopper() : destroyPopper() emit('update:modelValue', val) }) onBeforeUnmount(() => { doDestroy(true) if (popperElm.value && popperElm.value.parentNode === document.body) { popperElm.value.removeEventListener('click', stop) document.body.removeChild(popperElm.value) } }) return { showPopper, currentPlacement, referenceElm, popperElm, popperJS, createPopper, updatePopper, doDestroy, destroyPopper, resetTransformOrigin, appendArrow } } export { popperProps, usePopper }