前言
在项目中使用了antd
的ProTable
,其tooltip
、ellipsis
、copyable
在某些情况下具有局限性,比如:1、渲染html
内容;2、长文本使用tooltip
的情况下,宽度没有自动适应导致tooltip
纵向太长,如下图:

所以,我需要重新封装一个适用的组件:ColumnOverflow
组件介绍
ColumnOverflow
是一个用于处理文本溢出的 React 组件,它提供了以下功能:
- 文本溢出时自动显示省略号
- 鼠标悬停时显示完整内容的 Tooltip
- 支持 HTML 内容渲染
- 可选的复制功能
- 支持水平和垂直方向的溢出检测
- ...可扩展
组件API定义
ts
type Props = {
title: string; // 显示的文本内容(支持HTML)
copyText?: string; // 复制按钮复制的文本,默认为title
direction?: 'horizontal' | 'vertical'; // 溢出检测方向
isShowCopyable?: boolean; // 是否显示复制按钮
styles?: object; // Tooltip 的自定义样式
placement?: TooltipPlacement; // 气泡框位置
};
布局实现
组件采用 styled-components
进行样式管理,使用 Flex
布局实现灵活的空间分配:
ts
import styled from 'styled-components';
export const ColumnOverflowStyle = styled.div`
.column-overflow {
position: relative;
width: 100%;
display: flex;
align-items: center;
&__content {
flex: 1;
min-width: 0;
}
&__hidden {
width: 100%;
> div {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
}
}
`;
溢出检测
组件通过比较元素的 scrollWidth/scrollHeight
和 clientWidth/clientHeight
来检测内容是否溢出:
ts
const visibilityChange = useCallback(
(event: any) => {
const ev = event.target;
let ev_size = ev.scrollWidth;
let content_size = ev.clientWidth;
if (direction === 'vertical') {
ev_size = ev.scrollHeight;
content_size = ev.clientHeight;
}
if (ev_size > content_size) {
setShowTooltip(true);
} else {
setShowTooltip(false);
}
},
[direction]
);
HTML 内容渲染
组件直接支持在 title
属性中传入 HTML
内容,并在 Tooltip
和主内容区域进行渲染:
tsx
<Tooltip
overlayInnerStyle={{ whiteSpace: 'pre-wrap', ...styles }}
title={<div dangerouslySetInnerHTML={{ __html: title }} />}
open={showTooltip}
>
<div className="column-overflow__hidden">
<div dangerouslySetInnerHTML={{ __html: title }} />
</div>
</Tooltip>
复制功能集成
通过 Ant Design
的 Typography.Text
组件实现复制功能,没传copytext
的情况下默认为title
。为什么要设置title
和copyText
两个API
是因为,title
可能是需要渲染html
的,而我们复制的内容并不需要html
tsx
{isShowCopyable && (
<Typography.Text
className="ml10"
copyable={{
text: title
}}
/>
)}
使用示例
html
// 基础使用
<ColumnOverflow title="这是一段很长的文本..." />
// 带HTML内容
<ColumnOverflow title="<span style='color: red'>带HTML样式的文本</span>" />
// 启用复制功能,自定义复制内容
<ColumnOverflow
title="<span style='color: red'>带样式的文本</span>"
copyText="纯文本内容"
isShowCopyable={true}
/>
// 垂直方向溢出检测
<ColumnOverflow
title="多行文本..."
direction="vertical"
/>
组件完整代码
ColumnOverflow.tsx
tsx
import React, { useState, useCallback } from 'react';
import { Tooltip, Typography } from 'antd';
import { ColumnOverflowStyle } from './styles';
import type { TooltipPlacement } from 'antd/lib/tooltip';
type Props = {
title: string;
copyText?: string;
direction?: 'horizontal' | 'vertical';
isShowCopyable?: boolean;
styles?: object;
placement?: TooltipPlacement;
};
const ColumnOverflow: React.FC<Props> = React.memo(
({
title,
copyText = title,
direction = 'horizontal',
isShowCopyable = false,
styles = {},
placement = 'top'
}) => {
const [showTooltip, setShowTooltip] = useState(false);
const visibilityChange = useCallback(
(event: any) => {
const ev = event.target;
let ev_size = ev.scrollWidth;
let content_size = ev.clientWidth;
if (direction === 'vertical') {
ev_size = ev.scrollHeight;
content_size = ev.clientHeight;
}
if (ev_size > content_size) {
setShowTooltip(true);
} else {
setShowTooltip(false);
}
},
[direction]
);
let copyableElement = null;
if (isShowCopyable) {
copyableElement = (
<Typography.Text
className="ml10"
copyable={{
text: copyText
}}
/>
);
}
const htmlDom = <div dangerouslySetInnerHTML={{ __html: title }} />;
return (
<ColumnOverflowStyle>
<div
className="column-overflow"
onMouseEnter={visibilityChange}
onMouseLeave={() => setShowTooltip(false)}
>
<div className="column-overflow__content">
<Tooltip
overlayStyle={{ ...styles }}
overlayInnerStyle={{
whiteSpace: 'pre-wrap'
}}
title={htmlDom}
open={showTooltip}
placement={placement}
>
<div className="column-overflow__hidden">{htmlDom}</div>
</Tooltip>
</div>
{copyableElement}
</div>
</ColumnOverflowStyle>
);
}
);
export default ColumnOverflow;
- 以上代码是
antd 4.x
的用法,antd 5.x
废弃了overlayStyle
和overlayInnerStyle
的写法,将以下组件替换即可:
tsx
<Tooltip
styles={{
root: { ...styles },
body: {
whiteSpace: 'pre-wrap'
}
}}
title={htmlDom}
open={showTooltip}
placement={placement}
>
<div className="column-overflow__hidden">{htmlDom}</div>
</Tooltip>
styles.ts
见上文的布局实现
使用效果:
- 使用前:

-
使用后:
antd 4.x
-
使用后:
antd 5.x
-
渲染
html
内容
