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' ;
92import { Tooltip } from 'antd' ;
103import { AbstractTooltipProps , RenderFunction } from 'antd/lib/tooltip' ;
114import classNames from 'classnames' ;
125
136import Resize from '../resize' ;
7+ import useTextResize from './useTextStyle' ;
148import './style.scss' ;
159
1610export interface IEllipsisTextProps extends AbstractTooltipProps {
1711 /**
1812 * 文本内容
1913 */
20- value : string | number | ReactNode | RenderFunction ;
14+ value : ReactNode | RenderFunction ;
2115 /**
2216 * 提示内容
2317 * @default value
@@ -41,12 +35,6 @@ export interface IEllipsisTextProps extends AbstractTooltipProps {
4135 [ propName : string ] : any ;
4236}
4337
44- export interface NewHTMLElement extends HTMLElement {
45- currentStyle ?: CSSStyleDeclaration ;
46- }
47-
48- const DEFAULT_MAX_WIDTH = 120 ;
49-
5038const EllipsisText = ( props : IEllipsisTextProps ) => {
5139 const {
5240 value,
@@ -56,190 +44,22 @@ const EllipsisText = (props: IEllipsisTextProps) => {
5644 watchParentSizeChange = false ,
5745 ...otherProps
5846 } = props ;
47+ const [ ref , isOverflow , style , onResize ] = useTextResize ( value , maxWidth ) ;
5948
60- const ellipsisRef = useRef < HTMLSpanElement > ( null ) ;
6149 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 ;
22351
22452 const renderText = useCallback ( ( ) => {
225- const style : CSSProperties = {
226- maxWidth : width ,
227- cursor,
228- } ;
22953 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 } >
23555 { typeof value === 'function' ? value ( ) : value }
23656 </ span >
23757 ) ;
238- } , [ width , cursor , value ] ) ;
58+ } , [ style , value ] ) ;
23959
24060 return (
24161 < Resize onResize = { onResize } observerEle = { observerEle } >
242- { visible ? (
62+ { isOverflow ? (
24363 < Tooltip title = { title } mouseEnterDelay = { 0 } mouseLeaveDelay = { 0 } { ...otherProps } >
24464 { renderText ( ) }
24565 </ Tooltip >
0 commit comments