magiccube-vue3
Version:
vue3-js版组件库
160 lines (150 loc) • 6.23 kB
JavaScript
import store from '../../utils/store'
import * as utils from '../../utils/common'
const TOOLTIP_CLASS_NAME = 'mc-ellipsis-tooltip'
const state = store.getState()
// 支持自定义插入位置
function getTargetEl(){
if(state.teleportName) {
return document.querySelector(`#${state.teleportName}`) || document.querySelector(`.${state.teleportName}`) || document.body
} else {
return document.body
}
}
function onMouseIn(el, value, event, targetEl, popupInheritWidth){
if(!value) return false
event.stopPropagation()
event.preventDefault()
const winWidth = document.body.clientWidth
const winHeight = document.body.clientHeight
const mouseX = Math.max(0, event.clientX - event.offsetX)
const mouseY = event.clientY - event.offsetY
const tipWidth = popupInheritWidth || (event.target.scrollWidth < 300? 'auto' : 300)
const marginMouseTop = el.clientHeight ?? 32
const paddingWidth = 24
const x = mouseX + tipWidth + paddingWidth > winWidth? winWidth - (tipWidth + paddingWidth) : mouseX
const div = document.createElement('div')
div.className = TOOLTIP_CLASS_NAME
div.innerText = value
div.id = el.popupId
div.style.left = x + 'px'
div.style.width = tipWidth + 'px'
div.style.paddingLeft = paddingWidth / 2 + 'px'
div.style.paddingRight = paddingWidth / 2 + 'px'
div.style.paddingTop = 4 + 'px'
div.style.paddingBottom = 4 + 'px'
div.style.zIndex = 10000
targetEl.append(div)
const tipHeight = Math.max(div.offsetHeight, 24)
const y = mouseY + tipHeight + marginMouseTop > winHeight? mouseY - tipHeight - 8 : mouseY + marginMouseTop
div.style.minHeight = tipHeight + 'px'
div.style.top = y + 'px'
// 监听提示框父级包裹的内容是否发生变化 如果有变化则清除提示框
// TODO 会影响用户交互
// const parentEl = div.parentNode
// const abserverConfig = {
// attributes: false,
// childList: true,
// subtree: true
// }
// const abserverCallback = (mutationsList, observer) => {
// onMouseOut({e: event, targetEl})
// observer?.disconnect?.()
// }
// const observer = new MutationObserver(abserverCallback)
// observer.observe(parentEl, abserverConfig)
// 处理特殊情况下插入dom在页面跳转后不被自动注销
div.onmouseleave = (e) => removeExistPopup(targetEl, el.popupId)
}
function onMouseOut({el, e: event, targetEl}){
try{
event && event.stopPropagation()
event && event.preventDefault()
const list = targetEl?.children || []
for(let i=0;i<list.length;i++){
const node = list[i]
if(node.className.toString().indexOf(TOOLTIP_CLASS_NAME) > -1) targetEl.removeChild(node)
}
} catch(e) {
// error
console.error(e)
}
}
// 获取所有子元素的宽度(包含margin宽度)
const getAllChildrenWidth = (chlldList) => {
let width = 0
chlldList.forEach(child => {
const childStyle = getComputedStyle?.(child) || {}
const childAttr = child.getBoundingClientRect? child.getBoundingClientRect() : {}
if(childAttr.width) width += childAttr.width + mergeMarginAndPaddingWidth(childStyle)
})
return width
}
const mergeMarginAndPaddingWidth = (style) => {
const marginRight = style.marginRight
const marginLeft = style.marginLeft
const paddingRight = style.paddingRight
const paddingLeft = style.paddingLeft
return styleToNumber(marginRight) + styleToNumber(marginLeft) + styleToNumber(paddingRight) + styleToNumber(paddingLeft)
}
const styleToNumber = (val) => {
if(typeof val === 'number') return val
if(val.indexOf('%') > -1) return 0
if(val.indexOf('px') > -1) return Number(val.replace('px', ''))
}
const removeExistPopup = (targetEl, id) => {
const exsitPopup = document.getElementById(id)
if(exsitPopup){
targetEl.removeChild(exsitPopup)
}
}
export default {
name: 'Ellipsis',
directive: {
mounted(el, { value, modifiers }) {
const uuid = utils._uuid()
const targetEl = getTargetEl()
const popupInheritWidth = modifiers['inheritWidth']
const popupId = 'mopai_ellipsis_pid_' + uuid
el.popupId = popupId
el.addEventListener('mouseenter', (e) => {
removeExistPopup(targetEl, popupId)
if(el.children?.length){
const target = el.getBoundingClientRect? el.getBoundingClientRect() : {}
/**
* * v-ellipsis.multiChildren='...',多个子元素时,需要全部叠加计算宽度
*/
if(modifiers['multiChildren']){
const fullChildrenWidth = getAllChildrenWidth(el.children)
if(target.width < fullChildrenWidth) onMouseIn(el, value, e, targetEl, (popupInheritWidth && target.width))
} else {
const child = el.children[0] || {}
const childAttr = child.getBoundingClientRect? child.getBoundingClientRect() : {}
if(target.width < childAttr.width) onMouseIn(el, value, e, targetEl, (popupInheritWidth && target.width))
}
} else {
const wrapWidth = el.offsetWidth
const innerWidth = el.scrollWidth
if(wrapWidth < innerWidth) onMouseIn(el, value, e, targetEl, (popupInheritWidth && wrapWidth * 0.9))
}
})
el.addEventListener('mouseleave', (e) => {
try{
setTimeout(() => {
if(e.relatedTarget?.id !== popupId){
removeExistPopup(targetEl, popupId)
}
}, 100)
} catch(err) {
onMouseOut({el, e, targetEl})
}
})
},
updated(el, { value }) { },
unmounted(el) {
onMouseOut({
el,
targetEl: getTargetEl()
})
},
},
}