feat: ellipsis text automatically displays tooltip based on ellipsis (#6244)
* feat: ellipsis text automatically displays tooltip based on ellipsis * feat: ellipsis text automatically displays tooltip based on ellipsis --------- Co-authored-by: sqchen <9110848@qq.com> Co-authored-by: sqchen <chenshiqi@sshlx.com>
This commit is contained in:
parent
11b2b5bcc2
commit
a2bdcd6e49
@ -26,6 +26,12 @@ outline: deep
|
|||||||
|
|
||||||
<DemoPreview dir="demos/vben-ellipsis-text/tooltip" />
|
<DemoPreview dir="demos/vben-ellipsis-text/tooltip" />
|
||||||
|
|
||||||
|
## 自动显示 tooltip
|
||||||
|
|
||||||
|
通过`tooltip-when-ellipsis`设置,仅在文本长度超出导致省略号出现时才触发 tooltip。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-ellipsis-text/auto-display" />
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
@ -37,6 +43,8 @@ outline: deep
|
|||||||
| maxWidth | 文本区域最大宽度 | `number \| string` | `'100%'` |
|
| maxWidth | 文本区域最大宽度 | `number \| string` | `'100%'` |
|
||||||
| placement | 提示浮层的位置 | `'bottom'\|'left'\|'right'\|'top'` | `'top'` |
|
| placement | 提示浮层的位置 | `'bottom'\|'left'\|'right'\|'top'` | `'top'` |
|
||||||
| tooltip | 启用文本提示 | `boolean` | `true` |
|
| tooltip | 启用文本提示 | `boolean` | `true` |
|
||||||
|
| tooltipWhenEllipsis | 内容超出,自动启用文本提示 | `boolean` | `false` |
|
||||||
|
| ellipsisThreshold | 设置 tooltipWhenEllipsis 后才生效,文本截断检测的像素差异阈值,越大则判断越严格,如果碰见异常情况可以自己设置阈值 | `number` | `3` |
|
||||||
| tooltipBackgroundColor | 提示文本的背景颜色 | `string` | - |
|
| tooltipBackgroundColor | 提示文本的背景颜色 | `string` | - |
|
||||||
| tooltipColor | 提示文本的颜色 | `string` | - |
|
| tooltipColor | 提示文本的颜色 | `string` | - |
|
||||||
| tooltipFontSize | 提示文本的大小 | `string` | - |
|
| tooltipFontSize | 提示文本的大小 | `string` | - |
|
||||||
|
16
docs/src/demos/vben-ellipsis-text/auto-display/index.vue
Normal file
16
docs/src/demos/vben-ellipsis-text/auto-display/index.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { EllipsisText } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const text = `
|
||||||
|
Vben Admin 是一个基于 Vue3.0、Vite、 TypeScript 的后台解决方案,目标是为开发中大型项目提供开箱即用的解决方案。
|
||||||
|
`;
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<EllipsisText :line="2" :tooltip-when-ellipsis="true">
|
||||||
|
{{ text }}
|
||||||
|
</EllipsisText>
|
||||||
|
|
||||||
|
<EllipsisText :line="3" :tooltip-when-ellipsis="true">
|
||||||
|
{{ text }}
|
||||||
|
</EllipsisText>
|
||||||
|
</template>
|
@ -1,7 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
|
|
||||||
import { computed, ref, watchEffect } from 'vue';
|
import {
|
||||||
|
computed,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onMounted,
|
||||||
|
onUpdated,
|
||||||
|
ref,
|
||||||
|
watchEffect,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
import { VbenTooltip } from '@vben-core/shadcn-ui';
|
import { VbenTooltip } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
@ -33,6 +40,16 @@ interface Props {
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
tooltip?: boolean;
|
tooltip?: boolean;
|
||||||
|
/**
|
||||||
|
* 是否只在文本被截断时显示提示框
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
tooltipWhenEllipsis?: boolean;
|
||||||
|
/**
|
||||||
|
* 文本截断检测的像素差异阈值,越大则判断越严格
|
||||||
|
* @default 3
|
||||||
|
*/
|
||||||
|
ellipsisThreshold?: number;
|
||||||
/**
|
/**
|
||||||
* 提示框背景颜色,优先级高于 overlayStyle
|
* 提示框背景颜色,优先级高于 overlayStyle
|
||||||
*/
|
*/
|
||||||
@ -62,12 +79,15 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
placement: 'top',
|
placement: 'top',
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
|
tooltipWhenEllipsis: false,
|
||||||
|
ellipsisThreshold: 3,
|
||||||
tooltipBackgroundColor: '',
|
tooltipBackgroundColor: '',
|
||||||
tooltipColor: '',
|
tooltipColor: '',
|
||||||
tooltipFontSize: 14,
|
tooltipFontSize: 14,
|
||||||
tooltipMaxWidth: undefined,
|
tooltipMaxWidth: undefined,
|
||||||
tooltipOverlayStyle: () => ({ textAlign: 'justify' }),
|
tooltipOverlayStyle: () => ({ textAlign: 'justify' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{ expandChange: [boolean] }>();
|
const emit = defineEmits<{ expandChange: [boolean] }>();
|
||||||
|
|
||||||
const textMaxWidth = computed(() => {
|
const textMaxWidth = computed(() => {
|
||||||
@ -79,9 +99,67 @@ const textMaxWidth = computed(() => {
|
|||||||
const ellipsis = ref();
|
const ellipsis = ref();
|
||||||
const isExpand = ref(false);
|
const isExpand = ref(false);
|
||||||
const defaultTooltipMaxWidth = ref();
|
const defaultTooltipMaxWidth = ref();
|
||||||
|
const isEllipsis = ref(false);
|
||||||
|
|
||||||
const { width: eleWidth } = useElementSize(ellipsis);
|
const { width: eleWidth } = useElementSize(ellipsis);
|
||||||
|
|
||||||
|
// 检测文本是否被截断
|
||||||
|
const checkEllipsis = () => {
|
||||||
|
if (!ellipsis.value || !props.tooltipWhenEllipsis) return;
|
||||||
|
|
||||||
|
const element = ellipsis.value;
|
||||||
|
|
||||||
|
const originalText = element.textContent || '';
|
||||||
|
const originalTrimmed = originalText.trim();
|
||||||
|
|
||||||
|
// 对于空文本直接返回 false
|
||||||
|
if (!originalTrimmed) {
|
||||||
|
isEllipsis.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const widthDiff = element.scrollWidth - element.clientWidth;
|
||||||
|
const heightDiff = element.scrollHeight - element.clientHeight;
|
||||||
|
|
||||||
|
// 使用足够大的差异阈值确保只有真正被截断的文本才会显示 tooltip
|
||||||
|
isEllipsis.value =
|
||||||
|
props.line === 1
|
||||||
|
? widthDiff > props.ellipsisThreshold
|
||||||
|
: heightDiff > props.ellipsisThreshold;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用 ResizeObserver 监听尺寸变化
|
||||||
|
let resizeObserver: null | ResizeObserver = null;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (typeof ResizeObserver !== 'undefined' && props.tooltipWhenEllipsis) {
|
||||||
|
resizeObserver = new ResizeObserver(() => {
|
||||||
|
checkEllipsis();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ellipsis.value) {
|
||||||
|
resizeObserver.observe(ellipsis.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始检测
|
||||||
|
checkEllipsis();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用onUpdated钩子检测内容变化
|
||||||
|
onUpdated(() => {
|
||||||
|
if (props.tooltipWhenEllipsis) {
|
||||||
|
checkEllipsis();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (resizeObserver) {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
resizeObserver = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
watchEffect(
|
watchEffect(
|
||||||
() => {
|
() => {
|
||||||
if (props.tooltip && eleWidth.value) {
|
if (props.tooltip && eleWidth.value) {
|
||||||
@ -91,9 +169,13 @@ watchEffect(
|
|||||||
},
|
},
|
||||||
{ flush: 'post' },
|
{ flush: 'post' },
|
||||||
);
|
);
|
||||||
|
|
||||||
function onExpand() {
|
function onExpand() {
|
||||||
isExpand.value = !isExpand.value;
|
isExpand.value = !isExpand.value;
|
||||||
emit('expandChange', isExpand.value);
|
emit('expandChange', isExpand.value);
|
||||||
|
if (props.tooltipWhenEllipsis) {
|
||||||
|
checkEllipsis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleExpand() {
|
function handleExpand() {
|
||||||
@ -110,7 +192,9 @@ function handleExpand() {
|
|||||||
color: tooltipColor,
|
color: tooltipColor,
|
||||||
backgroundColor: tooltipBackgroundColor,
|
backgroundColor: tooltipBackgroundColor,
|
||||||
}"
|
}"
|
||||||
:disabled="!props.tooltip || isExpand"
|
:disabled="
|
||||||
|
!props.tooltip || isExpand || (props.tooltipWhenEllipsis && !isEllipsis)
|
||||||
|
"
|
||||||
:side="placement"
|
:side="placement"
|
||||||
>
|
>
|
||||||
<slot name="tooltip">
|
<slot name="tooltip">
|
||||||
|
Loading…
Reference in New Issue
Block a user