141 lines
3.2 KiB
Vue
141 lines
3.2 KiB
Vue
<template>
|
|
<teleport to="body" v-if="isExist">
|
|
<transition v-show="innerVisible">
|
|
<div
|
|
ref="popper"
|
|
:class="['layui-popper', { 'layui-dark': isDark }]"
|
|
:style="style"
|
|
:position="position"
|
|
>
|
|
<slot>{{ content }}</slot>
|
|
<div class="layui-popper-arrow"></div>
|
|
</div>
|
|
</transition>
|
|
</teleport>
|
|
</template>
|
|
<script lang="ts">
|
|
const NAME = "LayPopper";
|
|
export default {
|
|
name: NAME,
|
|
};
|
|
</script>
|
|
|
|
<script setup lang="ts">
|
|
import "./index.less";
|
|
import postionFns from "./calcPosition";
|
|
import {
|
|
CSSProperties,
|
|
ref,
|
|
watch,
|
|
defineEmits,
|
|
onMounted,
|
|
} from "vue";
|
|
import { on } from "../../tools/domUtil";
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
el: any;
|
|
content?: string | Number;
|
|
position?: string;
|
|
trigger?: string;
|
|
enterable?: boolean;
|
|
isDark?: boolean;
|
|
disabled?: boolean;
|
|
visible?: boolean;
|
|
isCanHide?: boolean;
|
|
}>(),
|
|
{
|
|
position: 'top',
|
|
isDark: true,
|
|
disabled: false,
|
|
enterable: true,
|
|
visible: true,
|
|
isCanHide: true,
|
|
trigger: "hover",
|
|
}
|
|
);
|
|
const emit = defineEmits(["update:visible"]);
|
|
|
|
const EVENT_MAP: any = {
|
|
hover: ["mouseenter", null, "mouseleave", false],
|
|
click: ["click", document, "click", true],
|
|
};
|
|
|
|
const triggerArr = EVENT_MAP[props.trigger];
|
|
if (!triggerArr) {
|
|
console.error(`${NAME} render error!cause: 'Trigger' must be 'hover/click' `);
|
|
}
|
|
|
|
const style = ref<CSSProperties>({ top: -window.innerHeight + "px", left: 0 });
|
|
const checkTarget = ref(false);
|
|
const popper = ref<HTMLDivElement>({} as HTMLDivElement);
|
|
const innnerPosition = ref(props.position);
|
|
const innerVisible = ref(props.visible ?? true);
|
|
const isExist = ref(false);
|
|
|
|
watch(innerVisible, (val) => {
|
|
invokeShowPosistion();
|
|
emit("update:visible", val);
|
|
});
|
|
watch(popper, (val) => {
|
|
if (props.trigger === 'hover' && props.enterable) {
|
|
on(popper.value, EVENT_MAP['hover'][0], doShow);
|
|
on(popper.value, EVENT_MAP['hover'][2], doHidden);
|
|
}
|
|
});
|
|
watch(
|
|
() => props.content,
|
|
(val) => {
|
|
innerVisible.value && invokeShowPosistion();
|
|
}
|
|
);
|
|
|
|
const doShow = function () {
|
|
if (!props.disabled) {
|
|
innerVisible.value = true;
|
|
isExist.value = true;
|
|
}
|
|
};
|
|
|
|
const doHidden = function (e: MouseEvent) {
|
|
// ||(props.enterable && popper.value.contains(e.target as Node))
|
|
if (
|
|
(checkTarget.value && props.el.contains(e.target))
|
|
)
|
|
return;
|
|
// style.value = {top: (-window.innerHeight) + 'px',left:0};
|
|
// popper.value.remove();
|
|
if (props.isCanHide !== false) {
|
|
innerVisible.value = false;
|
|
}
|
|
innnerPosition.value = props.position;
|
|
};
|
|
|
|
// 计算位置显示
|
|
const showPosistion = function () {
|
|
postionFns[props.position] &&
|
|
(style.value = postionFns[props.position](
|
|
props.el,
|
|
popper.value,
|
|
innnerPosition
|
|
));
|
|
};
|
|
const invokeShowPosistion = function () {
|
|
if (innerVisible.value) {
|
|
popper.value.offsetWidth === 0
|
|
? setTimeout(showPosistion, 0)
|
|
: showPosistion();
|
|
// 延时确保计算位置正确
|
|
setTimeout(() => innerVisible.value && showPosistion(), 2);
|
|
}
|
|
};
|
|
|
|
// 事件绑定
|
|
on(props.el, triggerArr[0], doShow);
|
|
on(triggerArr[1] ?? props.el, triggerArr[2], doHidden);
|
|
checkTarget.value = triggerArr[3];
|
|
|
|
onMounted(() => {
|
|
invokeShowPosistion();
|
|
});
|
|
</script>
|