packages/data-render/src/widgets/components/TextEllipsis/index.tsx (128 lines of code) (raw):

import React, { FC, useMemo, useRef, useEffect } from 'react'; import classnames from 'classnames'; import { useSet } from '../../utils/hooks'; import './index.less'; interface IProps { data: string; height: number; leftSlot?: JSX.Element | string; rightSlot?: JSX.Element | string; contentStyle?: any; } const initState = { hidden: true, // 先隐藏截取执行完成之后展示出来 diff: 0, isEllipsis: true, isFirst: true, // 第一次 }; const TextEllipsis: FC<IProps> = (props) => { const { height, leftSlot, rightSlot, data, contentStyle } = props; const [state, setState] = useSet({ cutText: data, // 截取后剩下的文字 lastLength: data.length, // 上一次的长度 ...initState, }); const conRef = useRef(null); const { cutText, hidden, isEllipsis, lastLength, diff, isFirst } = state; useEffect(() => { const onsize = function () { setState({ cutText: props.data, lastLength: props.data.length, ...initState, }); }; window.addEventListener('resize', onsize); return () => window.removeEventListener('resize', onsize); }, []); useEffect(() => { if (!data) { return; } setState({ cutText: props.data, lastLength: props.data.length, ...initState, }); }, [data]); useEffect(() => { // 无显示内容不处理 if (!data) { return; } // 第一次已经处理过直接返回 if (isFirst) { firstTimeRender(); return; } // 进行截取处理 const { clientHeight }: any = conRef.current || {}; const cutTextLength = cutText.length; const length = Math.floor((cutTextLength - diff) / 2 + diff); // 真实高度超出固定高度,需要继续截取字符串 if (clientHeight - height > 5) { setState({ cutText: cutText.slice(0, length), lastLength: cutTextLength, }); return; } // 找到合适值,终止遍历 if (cutTextLength === diff) { setState({ hidden: false }); return; } // 回退截取,找到一个合适值 setState({ diff: cutTextLength, cutText: data.slice(0, lastLength), }); }, [cutText]); const firstTimeRender = () => { const { clientHeight }: any = conRef.current || {}; const cutTextLength = cutText.length; // 真实高度超出固定高度,需要截取字符串 if (clientHeight - height > 5) { setState({ cutText: cutText.slice(0, cutTextLength / 2), lastLength: cutTextLength, isFirst: false, }); return; } // 真实高度没有超出固定高度, 直接显示,不需要... setState({ isEllipsis: false, hidden: false }); }; return useMemo(() => { if (!data) { return null; } return ( <div className="text-ellipsis-box" style={{ height: isEllipsis ? height : 'auto' }}> <div ref={conRef} className={classnames('text-ellipsis-view', { 'text-ellipsis-hidden': hidden })} > {isEllipsis ? ( <> {leftSlot && <span>{leftSlot}</span>} <span style={contentStyle}>{`${cutText}...`}</span> <span className="text-ellipsis-all" onClick={() => setState({ isEllipsis: false })}> 展开 </span> {rightSlot && <span>{rightSlot}</span>} </> ) : ( <> {leftSlot && <span>{leftSlot}</span>} <span style={contentStyle}>{data}</span> {data !== cutText && ( <span className="text-ellipsis-all" onClick={() => setState({ isEllipsis: true })}> 收起 </span> )} {rightSlot && <span>{rightSlot}</span>} </> )} </div> </div> ); }, [cutText, hidden, isEllipsis]); }; export default TextEllipsis;