(component): [tooltip]新增 visible 参数;修复定位; content 变化时更新位置

This commit is contained in:
sight 2022-09-06 12:14:52 +08:00
parent 4b30714dab
commit 20a49818a3
4 changed files with 115 additions and 21 deletions

View File

@ -23,8 +23,18 @@ export default {
<script setup lang="ts"> <script setup lang="ts">
import "./index.less"; import "./index.less";
import postionFns from "./calcPosition"; import postionFns from "./calcPosition";
import { CSSProperties, ref, watch, onMounted } from "vue"; import {
CSSProperties,
ref,
watch,
onMounted,
watchEffect,
nextTick,
onBeforeUnmount,
useSlots,
} from "vue";
import { on } from "../../utils/domUtil"; import { on } from "../../utils/domUtil";
import { useThrottleFn } from "@vueuse/core";
export interface LayPopperProps { export interface LayPopperProps {
el: any; el: any;
@ -36,6 +46,7 @@ export interface LayPopperProps {
disabled?: boolean; disabled?: boolean;
isCanHide?: boolean; isCanHide?: boolean;
isAutoShow?: boolean; isAutoShow?: boolean;
visible?: boolean;
} }
const props = withDefaults(defineProps<LayPopperProps>(), { const props = withDefaults(defineProps<LayPopperProps>(), {
@ -46,8 +57,11 @@ const props = withDefaults(defineProps<LayPopperProps>(), {
isCanHide: true, isCanHide: true,
isAutoShow: false, isAutoShow: false,
trigger: "hover", trigger: "hover",
visible: false,
}); });
const slots = useSlots();
const EVENT_MAP: any = { const EVENT_MAP: any = {
hover: ["mouseenter", null, "mouseleave", false], hover: ["mouseenter", null, "mouseleave", false],
click: ["click", document, "click", true], click: ["click", document, "click", true],
@ -62,13 +76,17 @@ const style = ref<CSSProperties>({ top: -window.innerHeight + "px", left: 0 });
const checkTarget = ref(false); const checkTarget = ref(false);
const popper = ref<HTMLDivElement>({} as HTMLDivElement); const popper = ref<HTMLDivElement>({} as HTMLDivElement);
const innnerPosition = ref(props.position); const innnerPosition = ref(props.position);
const innerVisible = ref(!props.isCanHide); const innerVisible = ref(props.visible);
const isExist = ref(!props.isCanHide); const isExist = ref(props.visible);
watch( watch(
() => props.isCanHide, () => props.visible,
(val) => { (val) => {
innerVisible.value = !val; if (val) {
doShow();
} else {
doHidden();
}
} }
); );
@ -83,26 +101,25 @@ watch(popper, (val) => {
} }
}); });
watch( watch([() => props.content, () => slots.content && slots?.content()], (val) => {
() => props.content, innerVisible.value && invokeShowPosistion();
(val) => { });
innerVisible.value && invokeShowPosistion();
}
);
const doShow = function () { const doShow = function () {
if (!props.disabled) { if (!props.disabled) {
if (!isExist.value) { if (!isExist.value) {
isExist.value = true; isExist.value = true;
setTimeout(() => (innerVisible.value = true), 0); nextTick(() => {
innerVisible.value = true;
});
} else { } else {
innerVisible.value = true; innerVisible.value = true;
} }
} }
}; };
const doHidden = function (e: MouseEvent) { const doHidden = function (e?: MouseEvent) {
if (checkTarget.value && props.el.contains(e.target)) { if (checkTarget.value && props.el.contains(e?.target)) {
return; return;
} }
@ -127,13 +144,41 @@ const showPosistion = function () {
const invokeShowPosistion = function () { const invokeShowPosistion = function () {
if (innerVisible.value) { if (innerVisible.value) {
popper.value.offsetWidth === 0 popper.value.offsetWidth === 0
? setTimeout(showPosistion, 0) ? nextTick(() => showPosistion())
: showPosistion(); : showPosistion();
// nextTick(() => {
setTimeout(() => innerVisible.value && showPosistion(), 2); showPosistion();
});
} }
}; };
let scrollElements: HTMLElement[] | undefined;
const isScrollElement = (element: HTMLElement) => {
return (
element.scrollHeight > element.offsetHeight ||
element.scrollWidth > element.offsetWidth
);
};
const getScrollElements = (container: HTMLElement | undefined) => {
const scrollElements: HTMLElement[] = [];
let element: HTMLElement | undefined = container;
while (element && element !== document.documentElement) {
if (isScrollElement(element)) {
scrollElements.push(element);
}
element = element.parentElement ?? undefined;
}
return scrollElements;
};
const handleScroll = useThrottleFn(() => {
if (innerVisible.value) {
invokeShowPosistion();
}
}, 15);
// //
on(props.el, triggerArr[0], doShow); on(props.el, triggerArr[0], doShow);
on(triggerArr[1] ?? props.el, triggerArr[2], doHidden); on(triggerArr[1] ?? props.el, triggerArr[2], doHidden);
@ -141,5 +186,20 @@ checkTarget.value = triggerArr[3];
onMounted(() => { onMounted(() => {
invokeShowPosistion(); invokeShowPosistion();
scrollElements = getScrollElements(props.el);
for (const item of scrollElements) {
item.addEventListener("scroll", handleScroll);
}
window.addEventListener("resize", handleScroll);
});
onBeforeUnmount(() => {
if (scrollElements) {
for (const item of scrollElements) {
item.removeEventListener("scroll", handleScroll);
}
scrollElements = undefined;
}
window.removeEventListener("resize", handleScroll);
}); });
</script> </script>

View File

@ -11,7 +11,7 @@
<script lang="ts"> <script lang="ts">
import "./index.less"; import "./index.less";
import LayPopper from "../popper/index.vue"; import LayPopper from "../popper/index.vue";
import { defineComponent, ref } from "vue"; import { defineComponent, PropType, ref } from "vue";
import { useEventListener } from "@vueuse/core"; import { useEventListener } from "@vueuse/core";
export default defineComponent({ export default defineComponent({
name: "LayTooltip", name: "LayTooltip",
@ -43,6 +43,14 @@ export default defineComponent({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
visible: {
type: Boolean,
default: false,
},
trigger: {
type: String as PropType<"click" | "hover">,
default: "hover",
},
}, },
setup() { setup() {
const isMounted = ref(false); const isMounted = ref(false);

View File

@ -389,11 +389,11 @@ export default {
| 属性 | 描述 | 类型 | 默认值 | 可选值 | | 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ------------------- | -------------------------------------------------- | ----------------------------------------- | -------- | ----------------------------- | | ------------------- | -------------------------------------------------- | ----------------------------------------- | -------- | ----------------------------- |
| v-model | 当前激活 | `string` | - | - | | v-model | 当前激活 | `string` | - | - |
| type | 主题样式 | `string` | - | - | | type | 主题样式 | `string` | - | `brief` `card` |
| tabPosition | 位置 | `string` | `bottom` | `top` `bottom` `left` `right` | | tabPosition | 位置 | `string` | `bottom` | `top` `bottom` `left` `right` |
| allow-close | 允许关闭 | `boolean` | `false` | `true` `false` | | allow-close | 允许关闭 | `boolean` | `false` | `true` `false` |
| before-close | `Function`关闭之前的回调钩子函数 | 参数(`id`), `return false` 表示不进行关闭 | - | - | | before-close | 关闭之前的回调钩子函数,参数(`id`), `return false` 表示不进行关闭 | `Function` | - | - |
| before-leave | `Function`切换标签之前的回调钩子函数 | 参数(`id`), `return false` 表示不进行切换 | - | - | | before-leave | 切换标签之前的回调钩子函数, 参数(`id`), `return false` 表示不进行关闭 | `Function` | - | - |
| activeBarTransition | 是否开启 activeBar 动画,仅 brief 有效,默认 `false` | `boolean` | `false` | `true` `false` | | activeBarTransition | 是否开启 activeBar 动画,仅 brief 有效,默认 `false` | `boolean` | `false` | `true` `false` |
::: :::

View File

@ -48,9 +48,35 @@ setup() {
<lay-button>tooltip</lay-button> <lay-button>tooltip</lay-button>
</lay-tooltip> </lay-tooltip>
</template> </template>
:::
::: title 外部控制
:::
::: demo
<template>
<lay-button @click="visible = !visible">{{visible ? '显示' : '隐藏'}}</lay-button>
<div style="padding: 100px">
<lay-tooltip position="right" content="时光都淡了,我还伴着你。" :visible="visible">
<lay-button>tooltip</lay-button>
</lay-tooltip>
</div>
</template>
<script>
import { ref,watch } from 'vue';
export default {
setup() {
const visible = ref(false)
return {
visible,
}
}
}
</script>
<style> <style>
</style> </style>
::: :::
::: title 显示位置 ::: title 显示位置