UNPKG

antd-mobile

Version:
105 lines 4.63 kB
import { useEvent } from 'rc-util'; import React from 'react'; import { unstable_batchedUpdates } from 'react-dom'; import runes from 'runes2'; const ELLIPSIS_TEXT = '...'; const measureStyle = { visibility: 'hidden', whiteSpace: 'inherit', lineHeight: 'inherit', fontSize: 'inherit' }; export default function useMeasure(containerRef, content, rows, direction, expanded, expandNode, collapseNode) { const contentChars = React.useMemo(() => runes(content), [content]); const [maxHeight, setMaxHeight] = React.useState(0); const [walkingIndexes, setWalkingIndexes] = React.useState([0, 0]); const midIndex = Math.ceil((walkingIndexes[0] + walkingIndexes[1]) / 2); const [status, setStatus] = React.useState(100 /* STABLE_NO_ELLIPSIS */); // ============================ Refs ============================ const singleRowMeasureRef = React.useRef(null); const fullMeasureRef = React.useRef(null); const midMeasureRef = React.useRef(null); const startMeasure = useEvent(() => { // use batch update to avoid async update trigger 2 render unstable_batchedUpdates(() => { setStatus(1 /* PREPARE */); setWalkingIndexes([0, direction === 'middle' ? Math.ceil(contentChars.length / 2) : contentChars.length]); }); }); // Initialize React.useLayoutEffect(() => { startMeasure(); }, [contentChars, rows]); // Measure element height React.useLayoutEffect(() => { var _a, _b; if (status === 1 /* PREPARE */) { const fullMeasureHeight = ((_a = fullMeasureRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) || 0; const singleRowMeasureHeight = ((_b = singleRowMeasureRef.current) === null || _b === void 0 ? void 0 : _b.offsetHeight) || 0; const rowMeasureHeight = singleRowMeasureHeight * (rows + 0.5); if (fullMeasureHeight <= rowMeasureHeight) { setStatus(100 /* STABLE_NO_ELLIPSIS */); } else { setMaxHeight(rowMeasureHeight); setStatus(2 /* MEASURE_WALKING */); } } }, [status]); // Walking measure React.useLayoutEffect(() => { var _a; if (status === 2 /* MEASURE_WALKING */) { const diff = walkingIndexes[1] - walkingIndexes[0]; const midHeight = ((_a = midMeasureRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) || 0; if (diff > 1) { if (midHeight > maxHeight) { setWalkingIndexes([walkingIndexes[0], midIndex]); } else { setWalkingIndexes([midIndex, walkingIndexes[1]]); } } else { if (midHeight > maxHeight) { setWalkingIndexes([walkingIndexes[0], walkingIndexes[0]]); } else { setWalkingIndexes([walkingIndexes[1], walkingIndexes[1]]); } setStatus(99 /* STABLE_ELLIPSIS */); } } }, [status, walkingIndexes]); // =========================== Render =========================== /** Render by cut index */ const renderContent = index => { const prefixContent = contentChars.slice(0, index); const suffixContent = contentChars.slice(contentChars.length - index); return React.createElement(React.Fragment, null, direction === 'start' && React.createElement(React.Fragment, null, expandNode, ELLIPSIS_TEXT), direction !== 'start' && prefixContent.join(''), direction === 'middle' && React.createElement(React.Fragment, null, ELLIPSIS_TEXT, expandNode, ELLIPSIS_TEXT), direction !== 'end' && suffixContent.join(''), direction === 'end' && React.createElement(React.Fragment, null, ELLIPSIS_TEXT, expandNode)); }; const finalContent = React.useMemo(() => { if (expanded || status === 100 /* STABLE_NO_ELLIPSIS */) { return React.createElement(React.Fragment, { key: 'display' }, content, status === 99 /* STABLE_ELLIPSIS */ && collapseNode); } if (status === 99 /* STABLE_ELLIPSIS */) { return renderContent(midIndex); } return null; }, [expanded, status, content, collapseNode, midIndex]); const allNodes = React.createElement(React.Fragment, null, status === 1 /* PREPARE */ && React.createElement("div", { key: 'full', "aria-hidden": true, ref: fullMeasureRef, style: measureStyle }, content, expandNode), status === 1 /* PREPARE */ && React.createElement("div", { key: 'stable', "aria-hidden": true, ref: singleRowMeasureRef, style: measureStyle }, '\u00A0'), status === 2 /* MEASURE_WALKING */ && React.createElement("div", { key: 'walking-mid', "aria-hidden": true, ref: midMeasureRef, style: measureStyle }, renderContent(midIndex)), finalContent); return [allNodes, startMeasure]; }