UNPKG

@nutui/nutui-react

Version:

京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序

161 lines (160 loc) 6.25 kB
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 };