amis-rpc-design/node_modules/antd/es/typography/Base/Ellipsis.js
2023-10-07 19:42:30 +08:00

160 lines
5.7 KiB
JavaScript

"use client";
import * as React from 'react';
import toArray from "rc-util/es/Children/toArray";
import useIsomorphicLayoutEffect from "rc-util/es/hooks/useLayoutEffect";
function cuttable(node) {
const type = typeof node;
return type === 'string' || type === 'number';
}
function getNodesLen(nodeList) {
let totalLen = 0;
nodeList.forEach(node => {
if (cuttable(node)) {
totalLen += String(node).length;
} else {
totalLen += 1;
}
});
return totalLen;
}
function sliceNodes(nodeList, len) {
let currLen = 0;
const currentNodeList = [];
for (let i = 0; i < nodeList.length; i += 1) {
// Match to return
if (currLen === len) {
return currentNodeList;
}
const node = nodeList[i];
const canCut = cuttable(node);
const nodeLen = canCut ? String(node).length : 1;
const nextLen = currLen + nodeLen;
// Exceed but current not which means we need cut this
// This will not happen on validate ReactElement
if (nextLen > len) {
const restLen = len - currLen;
currentNodeList.push(String(node).slice(0, restLen));
return currentNodeList;
}
currentNodeList.push(node);
currLen = nextLen;
}
return nodeList;
}
const NONE = 0;
const PREPARE = 1;
const WALKING = 2;
const DONE_WITH_ELLIPSIS = 3;
const DONE_WITHOUT_ELLIPSIS = 4;
const Ellipsis = _ref => {
let {
enabledMeasure,
children,
text,
width,
fontSize,
rows,
onEllipsis
} = _ref;
const [[startLen, midLen, endLen], setCutLength] = React.useState([0, 0, 0]);
// record last done with ellipsis width
const [lastLen, setLastLen] = React.useState(0);
const [walkingState, setWalkingState] = React.useState(NONE);
const [singleRowHeight, setSingleRowHeight] = React.useState(0);
const singleRowRef = React.useRef(null);
const midRowRef = React.useRef(null);
const nodeList = React.useMemo(() => toArray(text), [text]);
const totalLen = React.useMemo(() => getNodesLen(nodeList), [nodeList]);
const mergedChildren = React.useMemo(() => {
if (!enabledMeasure || walkingState !== DONE_WITH_ELLIPSIS) {
// if has lastLen, use it as temporary width to avoid lots of text to squeeze space.
if (lastLen && walkingState !== DONE_WITHOUT_ELLIPSIS && enabledMeasure) return children(sliceNodes(nodeList, lastLen), lastLen < totalLen);
return children(nodeList, false);
}
return children(sliceNodes(nodeList, midLen), midLen < totalLen);
}, [enabledMeasure, walkingState, children, nodeList, midLen, totalLen]);
// ======================== Walk ========================
useIsomorphicLayoutEffect(() => {
if (enabledMeasure && width && fontSize && totalLen) {
setWalkingState(PREPARE);
setCutLength([0, Math.ceil(totalLen / 2), totalLen]);
}
}, [enabledMeasure, width, fontSize, text, totalLen, rows]);
useIsomorphicLayoutEffect(() => {
var _a;
if (walkingState === PREPARE) {
setSingleRowHeight(((_a = singleRowRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) || 0);
}
}, [walkingState]);
useIsomorphicLayoutEffect(() => {
var _a, _b;
if (singleRowHeight) {
if (walkingState === PREPARE) {
// Ignore if position is enough
const midHeight = ((_a = midRowRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) || 0;
const maxHeight = rows * singleRowHeight;
if (midHeight <= maxHeight) {
setWalkingState(DONE_WITHOUT_ELLIPSIS);
onEllipsis(false);
} else {
setWalkingState(WALKING);
}
} else if (walkingState === WALKING) {
if (startLen !== endLen) {
const midHeight = ((_b = midRowRef.current) === null || _b === void 0 ? void 0 : _b.offsetHeight) || 0;
const maxHeight = rows * singleRowHeight;
let nextStartLen = startLen;
let nextEndLen = endLen;
// We reach the last round
if (startLen === endLen - 1) {
nextEndLen = startLen;
} else if (midHeight <= maxHeight) {
nextStartLen = midLen;
} else {
nextEndLen = midLen;
}
const nextMidLen = Math.ceil((nextStartLen + nextEndLen) / 2);
setCutLength([nextStartLen, nextMidLen, nextEndLen]);
} else {
setWalkingState(DONE_WITH_ELLIPSIS);
setLastLen(midLen);
onEllipsis(true);
}
}
}
}, [walkingState, startLen, endLen, rows, singleRowHeight]);
// ======================= Render =======================
const measureStyle = {
width,
whiteSpace: 'normal',
margin: 0,
padding: 0
};
const renderMeasure = (content, ref, style) => /*#__PURE__*/React.createElement("span", {
"aria-hidden": true,
ref: ref,
style: Object.assign({
position: 'fixed',
display: 'block',
left: 0,
top: 0,
zIndex: -9999,
visibility: 'hidden',
pointerEvents: 'none',
fontSize: Math.ceil(fontSize / 2) * 2
}, style)
}, content);
const renderMeasureSlice = (len, ref) => {
const sliceNodeList = sliceNodes(nodeList, len);
return renderMeasure(children(sliceNodeList, true), ref, measureStyle);
};
return /*#__PURE__*/React.createElement(React.Fragment, null, mergedChildren, enabledMeasure && walkingState !== DONE_WITH_ELLIPSIS && walkingState !== DONE_WITHOUT_ELLIPSIS && /*#__PURE__*/React.createElement(React.Fragment, null, renderMeasure('lg', singleRowRef, {
wordBreak: 'keep-all',
whiteSpace: 'nowrap'
}), walkingState === PREPARE ? renderMeasure(children(nodeList, false), midRowRef, measureStyle) : renderMeasureSlice(midLen, midRowRef)));
};
if (process.env.NODE_ENV !== 'production') {
Ellipsis.displayName = 'Ellipsis';
}
export default Ellipsis;