mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-06-17 10:35:11 +08:00
193 lines
4.2 KiB
Vue
193 lines
4.2 KiB
Vue
<template>
|
|
<slot name="reference"></slot>
|
|
<Teleport to="body">
|
|
<div
|
|
v-if="popoverVisible || !destroyOnClose"
|
|
v-show="popoverVisible"
|
|
class="tmagic-design-popper"
|
|
ref="popperElementRef"
|
|
:tabindex="tabindex"
|
|
:class="popperClass"
|
|
:style="style"
|
|
@mouseenter.once="popperMouseenterHandler"
|
|
>
|
|
<slot></slot>
|
|
<span class="tmagic-design-popper-arrow" data-popper-arrow></span>
|
|
</div>
|
|
</Teleport>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
|
import type { Instance } from '@popperjs/core';
|
|
import { createPopper } from '@popperjs/core';
|
|
|
|
import { useZIndex } from './index';
|
|
import type { PopoverProps } from './types';
|
|
|
|
defineSlots<{
|
|
/** 触发 Popover 显示的 HTML 元素 */
|
|
reference(props: {}): any;
|
|
/** Popover 内嵌 HTML 文本 */
|
|
default(props: {}): any;
|
|
}>();
|
|
|
|
defineOptions({
|
|
name: 'TMPopper',
|
|
inheritAttrs: false,
|
|
});
|
|
|
|
const props = withDefaults(defineProps<PopoverProps>(), {
|
|
trigger: 'hover',
|
|
disabled: false,
|
|
visible: undefined,
|
|
tabindex: 0,
|
|
destroyOnClose: false,
|
|
});
|
|
|
|
const popoverVisible = ref(false);
|
|
|
|
const visibleWatch = watch(
|
|
() => props.visible,
|
|
(visible) => {
|
|
if (typeof visible === 'undefined') {
|
|
nextTick(() => {
|
|
visibleWatch();
|
|
});
|
|
return;
|
|
}
|
|
|
|
popoverVisible.value = visible;
|
|
},
|
|
{
|
|
immediate: true,
|
|
},
|
|
);
|
|
|
|
const style = computed(() => {
|
|
if (!props.width) {
|
|
return {};
|
|
}
|
|
|
|
let { width } = props;
|
|
|
|
if (typeof width === 'number') {
|
|
width = `${width}px`;
|
|
}
|
|
|
|
return {
|
|
width,
|
|
};
|
|
});
|
|
|
|
const referenceElementRef = ref<HTMLElement>();
|
|
const popperElementRef = ref<HTMLElement>();
|
|
|
|
const instanceRef = shallowRef<Instance | undefined>();
|
|
|
|
onMounted(() => {
|
|
referenceElementRef.value = getCurrentInstance()?.proxy?.$el.nextElementSibling;
|
|
});
|
|
|
|
const zIndex = useZIndex();
|
|
watch([referenceElementRef, popperElementRef, popoverVisible], ([referenceElement, popperElement, popoverVisible]) => {
|
|
destroy();
|
|
if (!referenceElement || !popperElement || !popoverVisible) return;
|
|
|
|
popperElement.style.zIndex = `${zIndex.nextZIndex()}`;
|
|
|
|
instanceRef.value = createPopper(referenceElement, popperElement, {
|
|
placement: props.placement || 'bottom',
|
|
strategy: 'absolute',
|
|
modifiers: [
|
|
{
|
|
name: 'offset',
|
|
options: {
|
|
offset: [0, 10],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
const popperMouseenterHandler = () => {
|
|
popperElementRef.value?.focus();
|
|
};
|
|
|
|
const clickHandler = () => {
|
|
if (props.disabled) return;
|
|
|
|
popoverVisible.value = !popoverVisible.value;
|
|
};
|
|
|
|
const mouseenterHandler = () => {
|
|
if (props.disabled) return;
|
|
|
|
if (timer) {
|
|
clearTimeout(timer);
|
|
}
|
|
|
|
popoverVisible.value = true;
|
|
};
|
|
|
|
let timer: NodeJS.Timeout | null = null;
|
|
const mouseleaveHandler = () => {
|
|
if (props.disabled) return;
|
|
|
|
if (timer) {
|
|
clearTimeout(timer);
|
|
}
|
|
|
|
timer = globalThis.setTimeout(() => {
|
|
popoverVisible.value = false;
|
|
timer = null;
|
|
}, 350);
|
|
};
|
|
|
|
if (props.trigger === 'click' && typeof props.visible === 'undefined') {
|
|
watch(
|
|
referenceElementRef,
|
|
(el, prevEl) => {
|
|
el?.addEventListener('click', clickHandler);
|
|
prevEl?.removeEventListener('click', clickHandler);
|
|
},
|
|
{
|
|
immediate: true,
|
|
},
|
|
);
|
|
}
|
|
|
|
if (props.trigger === 'hover' && typeof props.visible === 'undefined') {
|
|
watch(
|
|
referenceElementRef,
|
|
(el, prevEl) => {
|
|
el?.addEventListener('mouseenter', mouseenterHandler);
|
|
prevEl?.removeEventListener('mouseenter', mouseenterHandler);
|
|
el?.addEventListener('mouseleave', mouseleaveHandler);
|
|
prevEl?.removeEventListener('mouseleave', mouseleaveHandler);
|
|
},
|
|
{
|
|
immediate: true,
|
|
},
|
|
);
|
|
|
|
watch(popperElementRef, (el, prevEl) => {
|
|
el?.addEventListener('mouseenter', mouseenterHandler);
|
|
prevEl?.removeEventListener('mouseenter', mouseenterHandler);
|
|
el?.addEventListener('mouseleave', mouseleaveHandler);
|
|
prevEl?.removeEventListener('mouseleave', mouseleaveHandler);
|
|
});
|
|
}
|
|
|
|
const destroy = () => {
|
|
if (!instanceRef.value) return;
|
|
|
|
instanceRef.value.destroy();
|
|
instanceRef.value = undefined;
|
|
};
|
|
|
|
onBeforeUnmount(() => {
|
|
destroy();
|
|
});
|
|
</script>
|