import React, { Component } from 'react'; import { Tooltip } from 'antd'; import classNames from 'classnames'; import styles from './index.less'; /* eslint react/no-did-mount-set-state: 0 */ /* eslint no-param-reassign: 0 */ const isSupportLineClamp = !(document.body.style.webkitLineClamp !== undefined); const EllipsisText = ({ text, length, tooltip, ...other }) => { if (typeof text !== 'string') { throw new Error('Ellipsis children must be string.'); } if (text.length <= length || length < 0) { return {text}; } const tail = '...'; let displayText; if (length - tail.length <= 0) { displayText = ''; } else { displayText = text.slice(0, (length - tail.length)); } if (tooltip) { return {displayText}{tail}; } return ( {displayText}{tail} ); }; export default class Ellipsis extends Component { state = { text: '', targetCount: 0, } componentDidMount() { if (this.node) { this.computeLine(); } } componentWillReceiveProps(nextProps) { if (this.props.lines !== nextProps.lines) { this.computeLine(); } } computeLine = () => { const { lines } = this.props; if (lines && !isSupportLineClamp) { const text = this.shadowChildren.innerText; const lineHeight = parseInt(getComputedStyle(this.root).lineHeight, 10); const targetHeight = lines * lineHeight; this.content.style.height = `${targetHeight}px`; const totalHeight = this.shadowChildren.offsetHeight; const shadowNode = this.shadow.firstChild; if (totalHeight <= targetHeight) { this.setState({ text, targetCount: text.length, }); return; } // bisection const len = text.length; const mid = Math.floor(len / 2); const count = this.bisection(targetHeight, mid, 0, len, text, shadowNode); this.setState({ text, targetCount: count, }); } } bisection = (th, m, b, e, text, shadowNode) => { const suffix = '...'; let mid = m; let end = e; let begin = b; shadowNode.innerHTML = text.substring(0, mid) + suffix; let sh = shadowNode.offsetHeight; if (sh <= th) { shadowNode.innerHTML = text.substring(0, mid + 1) + suffix; sh = shadowNode.offsetHeight; if (sh > th) { return mid; } else { begin = mid; mid = Math.floor((end - begin) / 2) + begin; return this.bisection(th, mid, begin, end, text, shadowNode); } } else { if (mid - 1 < 0) { return mid; } shadowNode.innerHTML = text.substring(0, mid - 1) + suffix; sh = shadowNode.offsetHeight; if (sh <= th) { return mid - 1; } else { end = mid; mid = Math.floor((end - begin) / 2) + begin; return this.bisection(th, mid, begin, end, text, shadowNode); } } } handleRoot = (n) => { this.root = n; } handleContent = (n) => { this.content = n; } handleNode = (n) => { this.node = n; } handleShadow = (n) => { this.shadow = n; } handleShadowChildren = (n) => { this.shadowChildren = n; } render() { const { text, targetCount } = this.state; const { children, lines, length, className, tooltip, ...restProps } = this.props; const cls = classNames(styles.ellipsis, className, { [styles.lines]: (lines && !isSupportLineClamp), [styles.lineClamp]: (lines && isSupportLineClamp), }); if (!lines && !length) { return ({children}); } // length if (!lines) { return (); } const id = `antd-pro-ellipsis-${`${new Date().getTime()}${Math.floor(Math.random() * 100)}`}`; // support document.body.style.webkitLineClamp if (isSupportLineClamp) { const style = `#${id}{-webkit-line-clamp:${lines};}`; return (
{ tooltip ? ({children}) : children }
); } const childNode = ( { (targetCount > 0) && text.substring(0, targetCount) } { (targetCount > 0) && (targetCount < text.length) && '...' } ); return (
{ tooltip ? ( {childNode} ) : childNode }
{children}
{text}
); } }