antd-mobile
Version:
<div align="center">
105 lines • 4.63 kB
JavaScript
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];
}