@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
161 lines (160 loc) • 6.25 kB
JavaScript
import { _ as __rest } from "./tslib.es6.js";
import React__default, { useLayoutEffect, useEffect, useState, useRef } from "react";
import classNames from "classnames";
import { C as ComponentDefaults } from "./typings.js";
import { c as canUseDom } from "./can-use-dom.js";
import { a as useRtl } from "./configprovider2.js";
const useIsomorphicLayoutEffect = canUseDom ? useLayoutEffect : useEffect;
const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { content: "", direction: "end", rows: 1, expandText: "", collapseText: "", symbol: "...", lineHeight: "20" });
const classPrefix = `nut-ellipsis`;
const Ellipsis = (props) => {
var _a, _b;
const _c = Object.assign(Object.assign({}, defaultProps), props), { children, content, className, direction, rows, expandText, collapseText, symbol, lineHeight, onClick, onChange } = _c, rest = __rest(_c, ["children", "content", "className", "direction", "rows", "expandText", "collapseText", "symbol", "lineHeight", "onClick", "onChange"]);
const rtl = useRtl();
let container = null;
let maxHeight = 0;
const [exceeded, setExceeded] = useState(false);
const [expanded, setExpanded] = useState(false);
const ellipsis = useRef();
const root = useRef(null);
const rtlClasses = classNames({
[`${classPrefix}-rtl`]: rtl
});
const classes = classNames(classPrefix, rtlClasses, className);
useIsomorphicLayoutEffect(() => {
if (content) {
createContainer();
}
}, [content]);
const createContainer = () => {
if (!root.current)
return;
const originStyle = window.getComputedStyle(root.current);
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.display = "block";
const lineH = pxToNumber(originStyle.lineHeight === "normal" ? lineHeight : originStyle.lineHeight);
maxHeight = Math.floor(lineH * (Number(rows) + 0.5) + pxToNumber(originStyle.paddingTop) + pxToNumber(originStyle.paddingBottom));
container.innerText = content;
document.body.appendChild(container);
calcEllipse();
document.body.removeChild(container);
};
const calcEllipse = () => {
if (container.offsetHeight <= maxHeight) {
setExceeded(false);
} else {
setExceeded(true);
const end = content.length;
const middle = Math.floor((0 + end) / 2);
const ellipsised = direction === "middle" ? tailorMiddle([0, middle], [middle, end]) : tailor(0, end);
ellipsis.current = ellipsised;
}
};
const tailor = (left, right) => {
const actionText = expanded ? collapseText : expandText;
const end = content.length;
if (right - left <= 1) {
if (direction === "end") {
return {
leading: content.slice(0, left) + symbol
};
}
return {
tailing: symbol + content.slice(right, end)
};
}
const middle = Math.round((left + right) / 2);
if (direction === "end") {
container.innerText = content.slice(0, middle) + symbol + actionText;
} else {
container.innerText = actionText + symbol + content.slice(middle, end);
}
if (container.offsetHeight <= maxHeight) {
if (direction === "end") {
return tailor(middle, right);
}
return tailor(left, middle);
}
if (direction === "end") {
return tailor(left, middle);
}
return tailor(middle, right);
};
const tailorMiddle = (leftPart, rightPart) => {
const actionText = expanded ? collapseText : expandText;
const end = content.length;
if (leftPart[1] - leftPart[0] <= 1 && rightPart[1] - rightPart[0] <= 1) {
return {
leading: content.slice(0, leftPart[0]) + symbol,
tailing: symbol + content.slice(rightPart[1], end)
};
}
const leftPartMiddle = Math.floor((leftPart[0] + leftPart[1]) / 2);
const rightPartMiddle = Math.ceil((rightPart[0] + rightPart[1]) / 2);
container.innerText = content.slice(0, leftPartMiddle) + symbol + actionText + symbol + content.slice(rightPartMiddle, end);
if (container.offsetHeight <= maxHeight) {
return tailorMiddle([leftPartMiddle, leftPart[1]], [rightPart[0], rightPartMiddle]);
}
return tailorMiddle([leftPart[0], leftPartMiddle], [rightPartMiddle, rightPart[1]]);
};
const pxToNumber = (value) => {
if (!value)
return 0;
const match = value.match(/^\d*(\.\d*)?/);
return match ? Number(match[0]) : 0;
};
const clickHandle = (type) => {
if (type === 1) {
setExpanded(true);
onChange && onChange("expand");
} else {
setExpanded(false);
onChange && onChange("collapse");
}
};
const handleClick = () => {
onClick && onClick();
};
return React__default.createElement(
"div",
Object.assign({ className: classes, onClick: handleClick, ref: root }, rest),
!exceeded ? content : null,
exceeded && !expanded ? React__default.createElement(
"span",
null,
(_a = ellipsis.current) === null || _a === void 0 ? void 0 : _a.leading,
expandText ? React__default.createElement("span", { className: "nut-ellipsis-text", onClick: (e) => {
e.stopPropagation();
clickHandle(1);
} }, expandText) : null,
(_b = ellipsis.current) === null || _b === void 0 ? void 0 : _b.tailing
) : null,
exceeded && expanded ? React__default.createElement(
"span",
null,
content,
expandText ? React__default.createElement("span", { className: "nut-ellipsis-text", onClick: (e) => {
e.stopPropagation();
clickHandle(2);
} }, collapseText) : null
) : null
);
};
Ellipsis.displayName = "NutEllipsis";
export {
Ellipsis as default
};