double-ui-vue
Version:
405 lines (404 loc) • 11.9 kB
JavaScript
import { defineComponent, computed, isRef, createVNode, Transition, withDirectives, Fragment, vShow, render, reactive, watch, onBeforeUnmount } from "vue";
var style = "";
const GetScrollbarWidth = () => {
const oDiv = document.createElement("div");
oDiv.style.cssText = "position:absolute;top:-1000px;width:100px;height:100px;overflow:hidden;opacity:0;";
const noScroll = document.body.appendChild(oDiv).clientWidth;
oDiv.style.overflowY = "scroll";
const scroll = oDiv.clientWidth;
document.body.removeChild(oDiv);
const bw = noScroll - scroll;
return bw < 6 ? 6 : bw;
};
const TipModal = defineComponent({
name: "TipModal",
props: {
tag: {
type: Object,
default: () => ({})
},
countTag: {
type: Object,
default: () => ({})
},
position: {
type: Boolean,
default: false
},
tipShow: {
type: Boolean,
default: false
},
dropShow: {
type: Boolean,
default: false
},
content: {
type: String,
default: ""
},
split: {
type: Boolean,
default: true
},
changeTipShow: {
type: Function,
default: () => {
}
},
tipHandle: {
type: Function,
default: () => {
}
}
},
setup(props) {
const getScrollWidth = (text) => {
const maxWidth = 452;
const div = document.createElement("div");
div.innerText = text;
div.style.position = "absolute";
div.style.left = "0";
div.style.bottom = "0";
div.style.zIndex = "-100";
div.style.display = "inline-block";
div.style.height = "0";
div.style.opacity = "0";
div.style.overflow = "hidden";
div.style.whiteSpace = "nowrap";
div.style.fontSize = "14px";
document.body.appendChild(div);
const {
scrollWidth
} = div;
document.body.removeChild(div);
return scrollWidth > maxWidth ? scrollWidth - maxWidth : 0;
};
const contentArr = computed(() => {
const content = isRef(props.content) ? props.content.value : props.content;
if (!content)
return [];
return content.split("\u3001").map((d, i) => ({
id: `content-${i}`,
text: d,
scroll: d.length > 32 ? getScrollWidth(d) : 0
}));
});
const popoverTipHandle = (str) => {
if (!props.split)
return;
props.tipHandle(str);
};
const contentEnter = () => {
if (props.split)
return;
props.changeTipShow(false);
};
return () => {
const {
split,
content,
tipShow,
position
} = props;
return createVNode(Transition, {
"name": position ? "selectDownUpExtend" : "selectDownUpExtendTop"
}, {
default: () => [withDirectives(createVNode("div", {
"class": ["d-popover-tip", split ? "d-popover-tip-split" : "d-popover-tip-words", position ? "d-popover-tip-bottom" : "d-popover-tip-top"],
"onWheel": (e) => e.stopPropagation(),
"onMouseenter": () => popoverTipHandle("enter"),
"onMouseleave": () => popoverTipHandle("leave")
}, [createVNode("section", {
"class": "d-popover-tip-content",
"onMouseenter": contentEnter
}, [split ? createVNode(Fragment, null, [contentArr.value.map((item) => createVNode("article", {
"class": ["d-popover-tip-content-item", item.scroll && "d-popover-tip-content-item-before"]
}, [createVNode("span", {
"key": item.id,
"class": "d-popover-tip-item-text",
"style": {
transform: `translateX(${-item.scroll}px)`
}
}, [item.text])]))]) : createVNode("article", {
"class": "d-popover-tip-content-words"
}, [isRef(content) ? content.value : content])]), createVNode("section", {
"class": "d-popover-tip-triangle"
}, null)]), [[vShow, tipShow]])]
});
};
}
});
const CountWh = (vm) => {
const { content, split } = vm.component.props;
const contentArr = (isRef(content) ? content.value : content).split("\u3001");
const { body } = document;
const element = document.createElement("div");
element.className = "d-popover-tip";
element.style.left = "0";
element.style.bottom = "0";
element.style.opacity = "0";
element.style.zIndex = "-100";
let html = '<section class="d-popover-tip-content">';
if (split) {
contentArr.forEach((d) => {
html += `<article class="d-popover-tip-content-item"><span class="d-popover-tip-item-text">${d}</span></article>`;
});
} else {
html += `<article class="d-popover-tip-content-words">${content}</article>`;
}
html += "</section>";
element.innerHTML = html;
body.appendChild(element);
const { width, height } = element.getBoundingClientRect();
body.removeChild(element);
const scrollWidth = GetScrollbarWidth();
return { width, height, scrollWidth };
};
const createArticle = (str) => {
const article = document.createElement("article");
article.className = "d-popover-tip-content-item";
article.style.display = "inline-block";
article.style.height = "0";
article.style.opacity = "0";
article.innerHTML = `<span class="d-popover-tip-item-text">${str}</span>`;
document.body.appendChild(article);
const w = article.clientWidth + 4;
document.body.removeChild(article);
return w;
};
const countContentWidth = (vm) => {
const {
component: {
props: { content }
}
} = vm;
const contentArr = (isRef(content) ? content.value : content).split("\u3001");
const numArr = [];
const len = contentArr.length;
let num = 0;
for (let i = 0; i < len; i++) {
const iw = createArticle(contentArr[i]);
num += iw;
if (num > 468) {
numArr.push(num - iw);
if (i === len - 1)
numArr.push(num);
else
num = iw;
} else if (num === 468) {
return 468;
}
}
return numArr && numArr.length ? Math.max(...numArr) : num;
};
const CalcPopoverTipPosition = (triggerDom, targetHeight) => {
const { clientHeight } = document.body;
const { pageYOffset } = window;
const { top, height } = triggerDom.getBoundingClientRect();
let targetOffsetY;
let P = true;
const triggerPositionTop = top - 8;
const triggerPositionBtm = clientHeight - (top + height + 8);
if (triggerPositionTop >= targetHeight) {
targetOffsetY = triggerPositionTop - targetHeight + pageYOffset;
P = false;
} else if (triggerPositionBtm >= targetHeight) {
targetOffsetY = top + height + 8 + pageYOffset;
} else {
targetOffsetY = clientHeight - targetHeight + pageYOffset;
}
return { Y: targetOffsetY, P };
};
const resetPosition = (instance) => {
const { vm, tag, countTag } = instance;
const isr = isRef(vm.component.props.content);
if (!(isr ? vm.component.props.content.value : vm.component.props.content)) {
return;
}
const { innerWidth, pageXOffset } = window;
const tagEle = isRef(tag) ? tag.value : tag;
const countTagEle = isRef(countTag) ? countTag.value : countTag;
const { left: tagLeft, width: tagWidth } = tagEle.getBoundingClientRect();
const { left: cLeft, width: cWidth } = countTagEle.getBoundingClientRect();
if (innerWidth <= cLeft + cWidth / 2 || innerWidth <= cWidth || cLeft < 0)
return;
const { width, height, scrollWidth } = CountWh(vm);
const { Y, P } = CalcPopoverTipPosition(tagEle, height);
if (vm.component.props.split) {
const maxWidth = countContentWidth(vm) + 13;
vm.el.style.maxWidth = `${maxWidth}px`;
}
vm.component.props.position = P;
vm.el.style.top = `${Y}px`;
const triangle = vm.el.lastChild;
let popoverLeft = 0;
let triangleLeft;
if (width === innerWidth) {
if (pageXOffset > 0)
popoverLeft = 0;
triangleLeft = cLeft + cWidth / 2 - 6;
} else {
if (tagLeft === 0) {
popoverLeft = 0;
} else {
const discoverWidth = innerWidth - tagLeft;
const centerPlace = cLeft + cWidth / 2;
const popoverLeftWidth = width / 2;
if (centerPlace < popoverLeftWidth) {
popoverLeft = 0;
} else if (width <= tagWidth) {
if (width <= discoverWidth)
popoverLeft = centerPlace - popoverLeftWidth;
else
popoverLeft = innerWidth - width;
} else {
if (centerPlace >= popoverLeftWidth) {
if (innerWidth - centerPlace <= popoverLeftWidth) {
popoverLeft = innerWidth - width - scrollWidth;
} else
popoverLeft = centerPlace - popoverLeftWidth;
} else {
popoverLeft = 0;
}
}
}
triangleLeft = cLeft - popoverLeft + cWidth / 2 - 6;
}
vm.el.style.left = `${popoverLeft + pageXOffset}px`;
triangle.style.left = `${triangleLeft}px`;
instance.setShow(true);
};
let dom;
const TipModalHandle = (options) => {
const { tipShow, props, changeTipShow, tipHandle } = options;
const { tag, countTag } = props;
if (!dom) {
dom = document.createElement("div");
document.body.appendChild(dom);
}
const vm = createVNode(TipModal, { tipShow, ...props, changeTipShow, tipHandle });
const instance = {
vm,
tag,
countTag,
setContent(content) {
vm.component.props.content = content;
},
setShow: changeTipShow,
resetPosition() {
resetPosition(instance);
},
remove() {
if (vm.component.isUnmounted)
return;
render(null, dom);
}
};
render(vm, dom);
return instance;
};
const PopoverTip = defineComponent({
name: "PopoverTip",
props: {
tag: {
type: Object,
default: () => ({})
},
countTag: {
type: Object,
default: () => ({})
},
content: {
type: String,
default: ""
},
split: {
type: Boolean,
default: true
},
dropShow: {
type: Boolean,
default: false
}
},
setup(props, {
slots
}) {
let tipModal;
let enterTimer;
let leaveTimer;
const state = reactive({
tipShow: false
});
const changeTipShow = (status) => {
state.tipShow = status;
if (tipModal)
tipModal.vm.component.props.tipShow = status;
};
PopoverTip.changeTipShow = changeTipShow;
const tipHandle = (str) => {
if (str === "enter") {
clearTimeout(leaveTimer);
leaveTimer = null;
} else {
leaveTimer = window.setTimeout(() => {
changeTipShow(false);
}, 500);
}
};
const mouseEnter = () => {
if (leaveTimer) {
clearTimeout(leaveTimer);
leaveTimer = null;
}
if (!props.content || props.dropShow || state.tipShow)
return;
enterTimer = window.setTimeout(() => {
if (tipModal) {
tipModal.resetPosition();
} else {
tipModal = TipModalHandle({
tipShow: state.tipShow,
props,
changeTipShow,
tipHandle
});
if (tipModal)
tipModal.resetPosition();
PopoverTip.tipModal = tipModal;
}
}, 360);
};
const mouseLeave = () => {
if (!props.content)
return;
if (enterTimer) {
clearTimeout(enterTimer);
enterTimer = null;
}
leaveTimer = window.setTimeout(() => {
if (tipModal)
tipModal.setShow(false);
}, 500);
};
watch(() => props.content, (n, o) => {
if (n === o || !tipModal)
return;
tipModal.vm.component.props.content = n;
});
onBeforeUnmount(() => {
if (enterTimer)
clearTimeout(enterTimer);
if (leaveTimer)
clearTimeout(leaveTimer);
if (tipModal)
tipModal.remove();
});
return () => createVNode("span", {
"onMouseenter": mouseEnter,
"onMouseleave": mouseLeave
}, [slots.default()]);
}
});
export { PopoverTip as default };