1
- import React , {
2
- CSSProperties ,
3
- ReactNode ,
4
- useCallback ,
5
- useLayoutEffect ,
6
- useRef ,
7
- useState ,
8
- } from 'react' ;
1
+ import React , { ReactNode , useCallback } from 'react' ;
9
2
import { Tooltip } from 'antd' ;
10
3
import { AbstractTooltipProps , RenderFunction } from 'antd/lib/tooltip' ;
11
4
import classNames from 'classnames' ;
12
5
13
6
import Resize from '../resize' ;
7
+ import useTextStyle from './useTextStyle' ;
14
8
import './style.scss' ;
15
9
16
10
export interface IEllipsisTextProps extends AbstractTooltipProps {
17
11
/**
18
12
* 文本内容
19
13
*/
20
- value : string | number | ReactNode | RenderFunction ;
14
+ value : ReactNode | RenderFunction ;
21
15
/**
22
16
* 提示内容
23
17
* @default value
@@ -41,12 +35,6 @@ export interface IEllipsisTextProps extends AbstractTooltipProps {
41
35
[ propName : string ] : any ;
42
36
}
43
37
44
- export interface NewHTMLElement extends HTMLElement {
45
- currentStyle ?: CSSStyleDeclaration ;
46
- }
47
-
48
- const DEFAULT_MAX_WIDTH = 120 ;
49
-
50
38
const EllipsisText = ( props : IEllipsisTextProps ) => {
51
39
const {
52
40
value,
@@ -56,190 +44,22 @@ const EllipsisText = (props: IEllipsisTextProps) => {
56
44
watchParentSizeChange = false ,
57
45
...otherProps
58
46
} = props ;
47
+ const [ ref , isOverflow , style , onResize ] = useTextStyle ( value , maxWidth ) ;
59
48
60
- const ellipsisRef = useRef < HTMLSpanElement > ( null ) ;
61
49
const observerEle =
62
- watchParentSizeChange && ellipsisRef . current ?. parentElement
63
- ? ellipsisRef . current ?. parentElement
64
- : null ;
65
-
66
- const [ visible , setVisible ] = useState ( false ) ;
67
- const [ width , setWidth ] = useState < number | string > ( DEFAULT_MAX_WIDTH ) ;
68
- const [ cursor , setCursor ] = useState ( 'default' ) ;
69
-
70
- useLayoutEffect ( ( ) => {
71
- onResize ( ) ;
72
- } , [ value , maxWidth ] ) ;
73
-
74
- /**
75
- * @description : 根据属性名,获取dom的属性值
76
- * @param {NewHTMLElement } dom
77
- * @param {string } attr
78
- * @return {* }
79
- */
80
- const getStyle = ( dom : NewHTMLElement , attr : string ) => {
81
- // Compatible width IE8
82
- // @ts -ignore
83
- return window . getComputedStyle ( dom ) [ attr ] || dom . currentStyle [ attr ] ;
84
- } ;
85
-
86
- /**
87
- * @description : 根据属性名,获取dom的属性值为number的属性。如: height、width。。。
88
- * @param {NewHTMLElement } dom
89
- * @param {string } attr
90
- * @return {* }
91
- */
92
- const getNumTypeStyleValue = ( dom : NewHTMLElement , attr : string ) => {
93
- return parseInt ( getStyle ( dom , attr ) ) ;
94
- } ;
95
-
96
- /**
97
- * @description : 10 -> 10,
98
- * @description : 10px -> 10,
99
- * @description : 90% -> ele.width * 0.9
100
- * @description : calc(100% - 32px) -> ele.width - 32
101
- * @param {* } ele
102
- * @param {string & number } maxWidth
103
- * @return {* }
104
- */
105
- const transitionWidth = ( ele : HTMLElement , maxWidth : string | number ) => {
106
- const eleWidth = getActualWidth ( ele ) ;
107
-
108
- if ( typeof maxWidth === 'number' ) {
109
- return maxWidth > eleWidth ? eleWidth : maxWidth ; // 如果父元素的宽度小于传入的最大宽度,返回父元素的宽度
110
- }
111
-
112
- const numMatch = maxWidth . match ( / ^ ( \d + ) ( p x ) ? $ / ) ;
113
- if ( numMatch ) {
114
- return + numMatch [ 1 ] > eleWidth ? eleWidth : + numMatch [ 1 ] ; // 如果父元素的宽度小于传入的最大宽度,返回父元素的宽度
115
- }
116
-
117
- const percentMatch = maxWidth . match ( / ^ ( \d + ) % $ / ) ;
118
- if ( percentMatch ) {
119
- return eleWidth * ( parseInt ( percentMatch [ 1 ] ) / 100 ) ;
120
- }
121
-
122
- const relativeMatch = maxWidth . match ( / ^ c a l c \( 1 0 0 % - ( \d + ) p x \) $ / ) ;
123
- if ( relativeMatch ) {
124
- return eleWidth - parseInt ( relativeMatch [ 1 ] ) ;
125
- }
126
-
127
- return eleWidth ;
128
- } ;
129
-
130
- const hideEleContent = ( node : HTMLElement ) => {
131
- node . style . display = 'none' ;
132
- } ;
133
-
134
- const showEleContent = ( node : HTMLElement ) => {
135
- node . style . display = 'inline-block' ;
136
- } ;
137
-
138
- /**
139
- * @description : 获取能够得到宽度的最近父元素宽度。行内元素无法获得宽度,需向上查找父元素
140
- * @param {HTMLElement } ele
141
- * @return {* }
142
- */
143
- const getContainerWidth = ( ele : HTMLElement ) : number | string => {
144
- if ( ! ele ) return DEFAULT_MAX_WIDTH ;
145
-
146
- const { scrollWidth, parentElement } = ele ;
147
-
148
- // 如果是行内元素,获取不到宽度,则向上寻找父元素
149
- if ( scrollWidth === 0 ) {
150
- return getContainerWidth ( parentElement ! ) ;
151
- }
152
- // 如果设置了最大宽度,则直接返回宽度
153
- if ( maxWidth ) {
154
- return transitionWidth ( ele , maxWidth ) ;
155
- }
156
-
157
- hideEleContent ( ellipsisRef . current ! ) ;
158
-
159
- const availableWidth = getAvailableWidth ( ele ) ;
160
-
161
- return availableWidth < 0 ? 0 : availableWidth ;
162
- } ;
163
-
164
- /**
165
- * @description : 获取dom元素的内容宽度
166
- * @param {HTMLElement } ele
167
- * @return {* }
168
- */
169
- const getRangeWidth = ( ele : HTMLElement ) : any => {
170
- const range = document . createRange ( ) ;
171
- range . selectNodeContents ( ele ) ;
172
- const rangeWidth = range . getBoundingClientRect ( ) . width ;
173
-
174
- return rangeWidth ;
175
- } ;
176
-
177
- /**
178
- * @description : 获取元素不包括 padding 的宽度
179
- * @param {HTMLElement } ele
180
- * @return {* }
181
- */
182
- const getActualWidth = ( ele : HTMLElement ) => {
183
- const width = ele . getBoundingClientRect ( ) . width ;
184
- const paddingLeft = getNumTypeStyleValue ( ele , 'paddingLeft' ) ;
185
- const paddingRight = getNumTypeStyleValue ( ele , 'paddingRight' ) ;
186
- return width - paddingLeft - paddingRight ;
187
- } ;
188
-
189
- /**
190
- * @description : 获取dom的可用宽度
191
- * @param {HTMLElement } ele
192
- * @return {* }
193
- */
194
- const getAvailableWidth = ( ele : HTMLElement ) => {
195
- const width = getActualWidth ( ele ) ;
196
- const contentWidth = getRangeWidth ( ele ) ;
197
- const ellipsisWidth = width - contentWidth ;
198
- return ellipsisWidth ;
199
- } ;
200
-
201
- /**
202
- * @description : 计算父元素的宽度是否满足内容的大小
203
- * @return {* }
204
- */
205
- const onResize = ( ) => {
206
- const ellipsisNode = ellipsisRef . current ! ;
207
- const parentElement = ellipsisNode . parentElement ! ;
208
- const rangeWidth = getRangeWidth ( ellipsisNode ) ;
209
- const containerWidth = getContainerWidth ( parentElement ) ;
210
- const visible = rangeWidth > containerWidth ;
211
- setVisible ( visible ) ;
212
- setWidth ( containerWidth ) ;
213
- const parentCursor = getStyle ( parentElement , 'cursor' ) ;
214
- if ( parentCursor !== 'default' ) {
215
- // 继承父元素的 hover 手势
216
- setCursor ( parentCursor ) ;
217
- } else {
218
- // 截取文本时,则改变 hover 手势为 pointer
219
- visible && setCursor ( 'pointer' ) ;
220
- }
221
- showEleContent ( ellipsisNode ) ;
222
- } ;
50
+ watchParentSizeChange && ref . current ?. parentElement ? ref . current ?. parentElement : null ;
223
51
224
52
const renderText = useCallback ( ( ) => {
225
- const style : CSSProperties = {
226
- maxWidth : width ,
227
- cursor,
228
- } ;
229
53
return (
230
- < span
231
- ref = { ellipsisRef }
232
- className = { classNames ( 'dtc-ellipsis-text' , className ) }
233
- style = { style }
234
- >
54
+ < span ref = { ref } className = { classNames ( 'dtc-ellipsis-text' , className ) } style = { style } >
235
55
{ typeof value === 'function' ? value ( ) : value }
236
56
</ span >
237
57
) ;
238
- } , [ width , cursor , value ] ) ;
58
+ } , [ style , value ] ) ;
239
59
240
60
return (
241
61
< Resize onResize = { onResize } observerEle = { observerEle } >
242
- { visible ? (
62
+ { isOverflow ? (
243
63
< Tooltip title = { title } mouseEnterDelay = { 0 } mouseLeaveDelay = { 0 } { ...otherProps } >
244
64
{ renderText ( ) }
245
65
</ Tooltip >
0 commit comments