UNPKG

antd-mobile

Version:

<img src="https://gw.alipayobjects.com/mdn/rms_ee68a8/afts/img/A*hjjDS5Yy-ooAAAAAAAAAAAAAARQnAQ" alt="logo" width="100%" />

155 lines (139 loc) 5.63 kB
import React, { useRef, useState } from 'react'; import { mergeProps } from '../../utils/with-default-props'; import { withNativeProps } from '../../utils/native-props'; import { useResizeEffect } from '../../utils/use-resize-effect'; import { useIsomorphicLayoutEffect } from 'ahooks'; import { withStopPropagation } from '../../utils/with-stop-propagation'; const classPrefix = `adm-ellipsis`; const defaultProps = { direction: 'end', rows: 1, expandText: '', collapseText: '', stopPropagationForActionButtons: [] }; export const Ellipsis = p => { const props = mergeProps(defaultProps, p); const rootRef = useRef(null); const [ellipsised, setEllipsised] = useState({}); const [expanded, setExpanded] = useState(false); const [exceeded, setExceeded] = useState(false); function calcEllipsised() { const root = rootRef.current; if (!root) return; const originStyle = window.getComputedStyle(root); const container = document.createElement('div'); const styleNames = Array.prototype.slice.apply(originStyle); styleNames.forEach(name => { container.style.setProperty(name, originStyle.getPropertyValue(name)); }); container.style.position = 'fixed'; container.style.left = '999999px'; container.style.top = '999999px'; container.style.zIndex = '-1000'; container.style.height = 'auto'; container.style.minHeight = 'auto'; container.style.maxHeight = 'auto'; container.style.textOverflow = 'clip'; container.style.whiteSpace = 'normal'; container.style.webkitLineClamp = 'unset'; container.style.webkitBoxOrient = 'unset'; container.style.display = 'block'; const lineHeight = pxToNumber(originStyle.lineHeight); const maxHeight = Math.floor(lineHeight * (props.rows + 0.5) + pxToNumber(originStyle.paddingTop) + pxToNumber(originStyle.paddingBottom)); container.innerText = props.content; document.body.appendChild(container); if (container.offsetHeight <= maxHeight) { setExceeded(false); } else { setExceeded(true); const end = props.content.length; const actionText = expanded ? props.collapseText : props.expandText; function check(left, right) { if (right - left <= 1) { if (props.direction === 'end') { return { leading: props.content.slice(0, left) + '...' }; } else { return { tailing: '...' + props.content.slice(right, end) }; } } const middle = Math.round((left + right) / 2); if (props.direction === 'end') { container.innerText = props.content.slice(0, middle) + '...' + actionText; } else { container.innerText = actionText + '...' + props.content.slice(middle, end); } if (container.offsetHeight <= maxHeight) { if (props.direction === 'end') { return check(middle, right); } else { return check(left, middle); } } else { if (props.direction === 'end') { return check(left, middle); } else { return check(middle, right); } } } function checkMiddle(leftPart, rightPart) { if (leftPart[1] - leftPart[0] <= 1 && rightPart[1] - rightPart[0] <= 1) { return { leading: props.content.slice(0, leftPart[0]) + '...', tailing: '...' + props.content.slice(rightPart[1], end) }; } const leftPartMiddle = Math.floor((leftPart[0] + leftPart[1]) / 2); const rightPartMiddle = Math.floor((rightPart[0] + rightPart[1]) / 2); container.innerText = props.content.slice(0, leftPartMiddle) + '...' + actionText + '...' + props.content.slice(rightPartMiddle, end); if (container.offsetHeight <= maxHeight) { return checkMiddle([leftPartMiddle, leftPart[1]], [rightPart[0], rightPartMiddle]); } else { return checkMiddle([leftPart[0], leftPartMiddle], [rightPartMiddle, rightPart[1]]); } } const middle = Math.floor((0 + end) / 2); const ellipsised = props.direction === 'middle' ? checkMiddle([0, middle], [middle, end]) : check(0, end); setEllipsised(ellipsised); } document.body.removeChild(container); } useResizeEffect(calcEllipsised, rootRef); useIsomorphicLayoutEffect(() => { calcEllipsised(); }, [props.content, props.direction, props.rows, props.expandText, props.collapseText]); const expandActionElement = exceeded && props.expandText ? withStopPropagation(props.stopPropagationForActionButtons, React.createElement("a", { onClick: () => { setExpanded(true); } }, props.expandText)) : null; const collapseActionElement = exceeded && props.expandText ? withStopPropagation(props.stopPropagationForActionButtons, React.createElement("a", { onClick: () => { setExpanded(false); } }, props.collapseText)) : null; const renderContent = () => { if (!exceeded) { return props.content; } if (expanded) { return React.createElement(React.Fragment, null, props.content, collapseActionElement); } else { return React.createElement(React.Fragment, null, ellipsised.leading, expandActionElement, ellipsised.tailing); } }; return withNativeProps(props, React.createElement("div", { ref: rootRef, className: classPrefix }, renderContent())); }; function pxToNumber(value) { if (!value) return 0; const match = value.match(/^\d*(\.\d*)?/); return match ? Number(match[0]) : 0; }