UNPKG

magiccube-vue3

Version:

vue3-js版组件库

160 lines (150 loc) 6.23 kB
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() }) }, }, }