zent
Version:
一套前端设计语言和基于React的实现
237 lines (236 loc) • 9.16 kB
JavaScript
import { __assign, __extends } from "tslib";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { Component, createRef } from 'react';
import cx from 'classnames';
import identity from '../utils/identity';
import Pop from '../pop';
import { WindowResizeHandler } from '../utils/component/WindowResizeHandler';
import { getLineHeight } from '../utils/dom/getLineHeight';
import { containsEmoji } from '../utils/unicode/isEmoji';
import { containsCJK } from '../utils/unicode/isCJK';
var WORDBREAK_STYLES = {
wordBreak: 'break-all',
overflowWrap: 'break-word',
};
var ClampLines = (function (_super) {
__extends(ClampLines, _super);
function ClampLines(props) {
var _this = _super.call(this, props) || this;
_this.element = null;
_this.innerElement = createRef();
_this.resizeObserver = null;
_this.containerWidth = NaN;
_this.handleWindowResize = function () {
_this.setState({ holdsFullText: false }, _this.clampLines);
};
_this.handleContainerResize = function (entries) {
var _a = entries[0], contentBoxSize = _a.contentBoxSize, contentRect = _a.contentRect;
var width = NaN;
if (contentBoxSize) {
width = Array.isArray(contentBoxSize)
? contentBoxSize[0].inlineSize
: contentBoxSize.inlineSize;
}
else {
width = contentRect.width;
}
if (!Number.isNaN(_this.containerWidth) && width !== _this.containerWidth) {
_this.setState({ holdsFullText: false }, _this.clampLines);
}
_this.containerWidth = width;
};
_this.onContainerRefChange = function (node) {
_this.element = node;
_this.observe(node);
};
_this.onNoClampContainerRefChange = function (node) {
_this.observe(node);
};
_this.state = {
holdsFullText: false,
textSuited: '',
};
return _this;
}
ClampLines.prototype.componentDidUpdate = function (prevProps) {
var _this = this;
if (prevProps.text !== this.props.text ||
prevProps.mode !== this.props.mode ||
prevProps.lines !== this.props.lines) {
this.setState({ holdsFullText: false }, function () {
_this.clampLines();
});
}
};
ClampLines.prototype.componentDidMount = function () {
this.clampLines();
};
ClampLines.prototype.componentWillUnmount = function () {
var observer = this.getResizeObserver();
if (observer) {
observer.disconnect();
}
};
ClampLines.prototype.getResizeObserver = function () {
if (!this.resizeObserver && window.ResizeObserver) {
this.resizeObserver = new window.ResizeObserver(this.handleContainerResize);
}
return this.resizeObserver;
};
ClampLines.prototype.observe = function (node) {
var observer = this.getResizeObserver();
if (!observer || !this.props.resizable) {
return;
}
this.containerWidth = NaN;
observer.disconnect();
if (node) {
observer.observe(node);
}
};
ClampLines.prototype.clampLines = function () {
if (!this.innerElement.current || !this.element) {
return;
}
this.props.mode === 'performance'
? this._clampLinesFast()
: this._clampLinesAccurate();
};
ClampLines.prototype._clampLinesFast = function () {
var text = this.props.text;
var lineHeight = inferContentLineHeight(this.element, text);
var maxHeight = lineHeight * this.props.lines;
this.innerElement.current.textContent = text;
if (this.element.clientHeight <= maxHeight) {
this.setState({
textSuited: text,
holdsFullText: true,
});
return;
}
var chars = Array.from(text);
var start = 0;
var middle = 0;
var end = chars.length;
while (start < end) {
middle = Math.floor((start + end) / 2);
this.innerElement.current.textContent =
slice(chars, 0, middle) + this.getEllipsis();
var height = this.element.clientHeight;
if (height > maxHeight) {
end = middle;
}
else {
start = middle + 1;
}
}
var overflowIndex = end - 1;
var textSuited = slice(chars, 0, overflowIndex) + this.getEllipsis();
this.innerElement.current.textContent = textSuited;
this.setState({
textSuited: textSuited,
holdsFullText: false,
});
};
ClampLines.prototype._clampLinesAccurate = function () {
var miniLineHeight = getLineHeight(this.element);
var text = this.props.text;
var chars = Array.from(text);
var lines = this.props.lines;
var prevIndex = 0;
var prevStr = chars[prevIndex];
var textSuited = '';
this.innerElement.current.textContent = prevStr;
var prevHeight = this.element.clientHeight;
var i = 1;
for (; i < chars.length && lines > 0; i++) {
var str = prevStr + chars[i];
this.innerElement.current.textContent = str;
var height = this.element.clientHeight;
if (height - prevHeight >= miniLineHeight) {
lines--;
}
if (lines > 0) {
prevHeight = height;
prevIndex = i;
prevStr = str;
}
else {
while (prevIndex) {
prevStr = slice(chars, 0, prevIndex--);
var str_1 = prevStr + this.getEllipsis();
this.innerElement.current.textContent = str_1;
if (prevHeight === this.element.clientHeight) {
textSuited = str_1;
break;
}
}
}
}
if (lines > 0) {
this.setState({
textSuited: text,
holdsFullText: true,
});
}
else {
this.setState({
textSuited: textSuited,
holdsFullText: false,
});
}
};
ClampLines.prototype.getEllipsis = function () {
return !this.state.holdsFullText ? this.props.ellipsis : '';
};
ClampLines.prototype.renderResizable = function () {
if (this.props.resizable && !window.ResizeObserver) {
return _jsx(WindowResizeHandler, { onResize: this.handleWindowResize }, void 0);
}
return null;
};
ClampLines.prototype.renderClampedText = function () {
var className = this.props.className;
var classString = cx('zent-clamp-lines', className);
return (_jsxs("div", __assign({ className: classString, style: WORDBREAK_STYLES, "data-zv": '10.0.17' }, { children: [_jsxs("div", __assign({ ref: this.onContainerRefChange, "data-zv": '10.0.17' }, { children: [_jsx("span", __assign({ ref: this.innerElement, "data-zv": '10.0.17' }, { children: this.state.textSuited }), void 0), this.props.extra] }), void 0), this.renderResizable()] }), void 0));
};
ClampLines.prototype.render = function () {
var _a = this.props, text = _a.text, className = _a.className, showPop = _a.showPop, popWidth = _a.popWidth, trigger = _a.trigger, renderPop = _a.renderPop;
if (!text) {
return null;
}
if (this.state.holdsFullText) {
return (_jsxs("div", __assign({ ref: this.onNoClampContainerRefChange, className: className, style: WORDBREAK_STYLES, "data-zv": '10.0.17' }, { children: [text, this.renderResizable()] }), void 0));
}
if (showPop) {
return (_jsx(Pop, __assign({ trigger: trigger, content: _jsx("div", __assign({ style: __assign({ maxWidth: popWidth }, WORDBREAK_STYLES), "data-zv": '10.0.17' }, { children: renderPop(text) }), void 0) }, { children: this.renderClampedText() }), void 0));
}
return this.renderClampedText();
};
ClampLines.defaultProps = {
className: '',
lines: 2,
ellipsis: '...',
showPop: true,
popWidth: 250,
trigger: 'hover',
renderPop: identity,
resizable: false,
mode: 'performance',
extra: null,
};
return ClampLines;
}(Component));
export { ClampLines };
function slice(chars, start, end) {
return chars.slice(start, end).join('');
}
function inferContentLineHeight(node, str) {
var emojiHeight = containsEmoji(str)
? getLineHeight(node, '🇨🇳')
: -Infinity;
var cjkHeight = containsCJK(str) ? getLineHeight(node, '世界') : -Infinity;
var asciiHeight = getLineHeight(node);
return Math.max(emojiHeight, cjkHeight, asciiHeight);
}
export default ClampLines;