@douyinfe/semi-ui
Version:
A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.
141 lines (140 loc) • 6.52 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _omit2 = _interopRequireDefault(require("lodash/omit"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* The logic of JS for text truncation is referenced from antd typography
* https://github.com/ant-design/ant-design/blob/master/components/typography/util.tsx
*
* For more thinking and analysis about this function, please refer to Feishu document
* https://bytedance.feishu.cn/docs/doccnqovjjyoKm2U5O13bj30aTh
*/
let ellipsisContainer;
function pxToNumber(value) {
if (!value) {
return 0;
}
const match = value.match(/^\d*(\.\d*)?/);
return match ? Number(match[0]) : 0;
}
function styleToString(style) {
// There are some different behavior between Firefox & Chrome.
// We have to handle this ourself.
const styleNames = Array.prototype.slice.apply(style);
return styleNames.map(name => `${name}: ${style.getPropertyValue(name)};`).join('');
}
const getRenderText = function (originEle, rows) {
let content = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
let fixedContent = arguments.length > 3 ? arguments[3] : undefined;
let ellipsisStr = arguments.length > 4 ? arguments[4] : undefined;
let suffix = arguments.length > 5 ? arguments[5] : undefined;
let ellipsisPos = arguments.length > 6 ? arguments[6] : undefined;
let isStrong = arguments.length > 7 ? arguments[7] : undefined;
if (content.length === 0) {
return '';
}
if (!ellipsisContainer) {
ellipsisContainer = document.createElement('div');
ellipsisContainer.setAttribute('aria-hidden', 'true');
document.body.appendChild(ellipsisContainer);
}
// Get origin style
const originStyle = window.getComputedStyle(originEle);
const originCSS = styleToString(originStyle);
const lineHeight = pxToNumber(originStyle.lineHeight);
const maxHeight = Math.round(lineHeight * (rows + 1) + pxToNumber(originStyle.paddingTop) + pxToNumber(originStyle.paddingBottom));
// Set shadow
ellipsisContainer.setAttribute('style', originCSS);
ellipsisContainer.style.position = 'fixed';
ellipsisContainer.style.left = '0';
// 当 window.getComputedStyle 得到的 width 值为 auto 时,通过 getBoundingClientRect 得到准确宽度
// When the width value obtained by window.getComputedStyle is auto, get the exact width through getBoundingClientRect
if (originStyle.getPropertyValue('width') === 'auto' && originEle.offsetWidth) {
ellipsisContainer.style.width = `${originEle.offsetWidth}px`;
}
ellipsisContainer.style.height = 'auto';
ellipsisContainer.style.top = '-999999px';
ellipsisContainer.style.zIndex = '-1000';
isStrong && (ellipsisContainer.style.fontWeight = '600');
// clean up css overflow
ellipsisContainer.style.textOverflow = 'clip';
ellipsisContainer.style.webkitLineClamp = 'none';
// Clear container content
ellipsisContainer.innerHTML = '';
// Check if ellipsis in measure div is enough for content
function inRange() {
// If content does not wrap due to line break strategy, width should be judged to determine whether it's in range
const widthInRange = ellipsisContainer.scrollWidth <= ellipsisContainer.offsetWidth;
const heightInRange = ellipsisContainer.scrollHeight < maxHeight;
return rows === 1 ? widthInRange && heightInRange : heightInRange;
}
// ========================= Find match ellipsis content =========================
// Create origin content holder
const ellipsisContentHolder = document.createElement('span');
const textNode = document.createTextNode(content);
ellipsisContentHolder.appendChild(textNode);
if (suffix.length > 0) {
const ellipsisTextNode = document.createTextNode(suffix);
ellipsisContentHolder.appendChild(ellipsisTextNode);
}
ellipsisContainer.appendChild(ellipsisContentHolder);
// Expand node needs to be added only when text needTruncated
Object.values((0, _omit2.default)(fixedContent, 'expand')).map(node => node && ellipsisContainer.appendChild(node.cloneNode(true)));
function appendExpandNode() {
ellipsisContainer.innerHTML = '';
ellipsisContainer.appendChild(ellipsisContentHolder);
Object.values(fixedContent).map(node => node && ellipsisContainer.appendChild(node.cloneNode(true)));
}
function getCurrentText(text, pos) {
const end = text.length;
if (!pos) {
return ellipsisStr;
}
if (ellipsisPos === 'end') {
return text.slice(0, pos) + ellipsisStr;
}
return text.slice(0, pos) + ellipsisStr + text.slice(end - pos, end);
}
// Get maximum text
function measureText(textNode, fullText) {
let startLoc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
let endLoc = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : fullText.length;
let lastSuccessLoc = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
const midLoc = Math.floor((startLoc + endLoc) / 2);
const currentText = getCurrentText(fullText, midLoc);
textNode.textContent = currentText;
// console.log('calculating....', currentText);
if (startLoc >= endLoc - 1 && endLoc > 0) {
// Loop when step is small
for (let step = endLoc; step >= startLoc; step -= 1) {
const currentStepText = getCurrentText(fullText, step);
textNode.textContent = currentStepText;
if (inRange()) {
return currentStepText;
}
}
} else if (endLoc === 0) {
return ellipsisStr;
}
if (inRange()) {
return measureText(textNode, fullText, midLoc, endLoc, midLoc);
}
return measureText(textNode, fullText, startLoc, midLoc, lastSuccessLoc);
}
let resText = content;
// First judge whether the total length of fullText, plus suffix (possible)
// and copied icon (possible) meets expectations?
// If it does not meet expectations, add an expand button to find the largest content that meets size limit
// 首先判断总文本长度,加上可能有的 suffix,复制按钮长度,看结果是否符合预期
// 如果不符合预期,则再加上展开按钮,找最大符合尺寸的内容
if (!inRange()) {
appendExpandNode();
resText = measureText(textNode, content, 0, ellipsisPos === 'middle' ? Math.floor(content.length / 2) : content.length);
}
ellipsisContainer.innerHTML = '';
return resText;
};
var _default = exports.default = getRenderText;
;