120 lines
3.0 KiB
TypeScript
120 lines
3.0 KiB
TypeScript
import type { Directive, DirectiveBinding } from "vue";
|
||
import Toolkit from "../Toolkit";
|
||
|
||
export enum PopupType {
|
||
TEXT,
|
||
IMG,
|
||
HTML,
|
||
EL
|
||
}
|
||
|
||
/** */
|
||
export type PopupConfig = {
|
||
|
||
type: PopupType,
|
||
value?: string | HTMLElement;
|
||
canShow?: () => boolean;
|
||
beforeShow?: (type: PopupType, value: string | HTMLElement) => Promise<void>;
|
||
afterHidden?: (type: PopupType, value: string | HTMLElement) => Promise<void>;
|
||
}
|
||
|
||
// Popup 弹出提示 DOM 节点,全局唯一
|
||
let popup: HTMLElement | null;
|
||
|
||
const VPopup: Directive = {
|
||
|
||
mounted(el: HTMLElement, binding: DirectiveBinding<PopupConfig>) {
|
||
// 转配置
|
||
let config: PopupConfig;
|
||
if (binding.arg && binding.arg === "config") {
|
||
config = binding.value as PopupConfig;
|
||
} else {
|
||
config = {
|
||
type: PopupType.TEXT,
|
||
value: binding.value as any as string,
|
||
canShow: () => true
|
||
};
|
||
}
|
||
// Popup 节点
|
||
if (!popup) {
|
||
popup = document.getElementById("tui-popup");
|
||
}
|
||
let isShowing = false;
|
||
// 显示
|
||
el.addEventListener("mouseenter", async e => {
|
||
if (!config.value) {
|
||
return;
|
||
}
|
||
if (config.beforeShow) {
|
||
await config.beforeShow(config.type, config.value);
|
||
}
|
||
if (config.canShow && config.canShow() && popup) {
|
||
let el: HTMLElement | null = null;
|
||
if (!config) {
|
||
el = document.createElement("div");
|
||
el.className = "text";
|
||
el.textContent = config as string;
|
||
popup.appendChild(el);
|
||
}
|
||
switch (config.type) {
|
||
case PopupType.TEXT:
|
||
// 文本
|
||
el = document.createElement("div");
|
||
el.className = "text";
|
||
el.textContent = config.value as string;
|
||
popup.appendChild(el);
|
||
break;
|
||
case PopupType.IMG:
|
||
// 图片
|
||
el = document.createElement("img");
|
||
(el as HTMLImageElement).src = config.value as string;
|
||
popup.appendChild(el);
|
||
break;
|
||
case PopupType.HTML:
|
||
// HTML 字符串
|
||
popup.appendChild(Toolkit.toDOM(config.value as string));
|
||
break;
|
||
case PopupType.EL:
|
||
// DOM 节点
|
||
if (config.value instanceof HTMLElement) {
|
||
const valueEl = config.value as HTMLElement;
|
||
valueEl.style.display = "block";
|
||
popup.appendChild(valueEl);
|
||
break;
|
||
} else {
|
||
console.error(config);
|
||
throw new Error("Vue 指令错误:v-popup:el 的值不是 HTML 元素");
|
||
}
|
||
}
|
||
popup.style.left = (e.x + 20) + "px";
|
||
popup.style.top = (e.y + 14) + "px";
|
||
popup.style.visibility = "visible";
|
||
isShowing = true;
|
||
}
|
||
}, false);
|
||
// 移动
|
||
el.addEventListener("mousemove", async (e) => {
|
||
if (config.canShow && config.canShow() && isShowing && popup) {
|
||
popup.style.left = (e.x + 20) + "px";
|
||
popup.style.top = (e.y + 14) + "px";
|
||
}
|
||
}, false);
|
||
// 隐藏
|
||
el.addEventListener("mouseleave", async () => {
|
||
if (popup) {
|
||
popup.style.visibility = "hidden";
|
||
popup.innerText = "";
|
||
popup.style.left = "0px";
|
||
popup.style.top = "0px";
|
||
|
||
// 隐藏后事件
|
||
if (config.afterHidden && config.value) {
|
||
await config.afterHidden(config.type, config.value);
|
||
}
|
||
}
|
||
}, false);
|
||
}
|
||
};
|
||
|
||
export default VPopup;
|