util.js
6.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
import { render, unmountComponentAtNode } from 'react-dom';
import * as React from 'react';
import toArray from "rc-util/es/Children/toArray"; // We only handle element & text node.
var ELEMENT_NODE = 1;
var TEXT_NODE = 3;
var COMMENT_NODE = 8;
var ellipsisContainer;
var wrapperStyle = {
padding: 0,
margin: 0,
display: 'inline',
lineHeight: 'inherit'
};
function pxToNumber(value) {
if (!value) return 0;
var 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.
var styleNames = Array.prototype.slice.apply(style);
return styleNames.map(function (name) {
return "".concat(name, ": ").concat(style.getPropertyValue(name), ";");
}).join('');
}
function mergeChildren(children) {
var childList = [];
children.forEach(function (child) {
var prevChild = childList[childList.length - 1];
if (typeof child === 'string' && typeof prevChild === 'string') {
childList[childList.length - 1] += child;
} else {
childList.push(child);
}
});
return childList;
}
export default (function (originEle, option, content, fixedContent, ellipsisStr) {
if (!ellipsisContainer) {
ellipsisContainer = document.createElement('div');
ellipsisContainer.setAttribute('aria-hidden', 'true');
document.body.appendChild(ellipsisContainer);
}
var rows = option.rows,
_option$suffix = option.suffix,
suffix = _option$suffix === void 0 ? '' : _option$suffix; // Get origin style
var originStyle = window.getComputedStyle(originEle);
var originCSS = styleToString(originStyle);
var lineHeight = pxToNumber(originStyle.lineHeight);
var 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';
ellipsisContainer.style.height = 'auto';
ellipsisContainer.style.minHeight = 'auto';
ellipsisContainer.style.maxHeight = 'auto';
ellipsisContainer.style.top = '-999999px';
ellipsisContainer.style.zIndex = '-1000'; // clean up css overflow
ellipsisContainer.style.textOverflow = 'clip';
ellipsisContainer.style.whiteSpace = 'normal';
ellipsisContainer.style.webkitLineClamp = 'none'; // Render in the fake container
var contentList = mergeChildren(toArray(content));
render( /*#__PURE__*/React.createElement("div", {
style: wrapperStyle
}, /*#__PURE__*/React.createElement("span", {
style: wrapperStyle
}, contentList, suffix), /*#__PURE__*/React.createElement("span", {
style: wrapperStyle
}, fixedContent)), ellipsisContainer); // wrap in an div for old version react
// Check if ellipsis in measure div is height enough for content
function inRange() {
return ellipsisContainer.offsetHeight < maxHeight;
} // Skip ellipsis if already match
if (inRange()) {
unmountComponentAtNode(ellipsisContainer);
return {
content: content,
text: ellipsisContainer.innerHTML,
ellipsis: false
};
} // We should clone the childNode since they're controlled by React and we can't reuse it without warning
var childNodes = Array.prototype.slice.apply(ellipsisContainer.childNodes[0].childNodes[0].cloneNode(true).childNodes).filter(function (_ref) {
var nodeType = _ref.nodeType;
return nodeType !== COMMENT_NODE;
});
var fixedNodes = Array.prototype.slice.apply(ellipsisContainer.childNodes[0].childNodes[1].cloneNode(true).childNodes);
unmountComponentAtNode(ellipsisContainer); // ========================= Find match ellipsis content =========================
var ellipsisChildren = [];
ellipsisContainer.innerHTML = ''; // Create origin content holder
var ellipsisContentHolder = document.createElement('span');
ellipsisContainer.appendChild(ellipsisContentHolder);
var ellipsisTextNode = document.createTextNode(ellipsisStr + suffix);
ellipsisContentHolder.appendChild(ellipsisTextNode);
fixedNodes.forEach(function (childNode) {
ellipsisContainer.appendChild(childNode);
}); // Append before fixed nodes
function appendChildNode(node) {
ellipsisContentHolder.insertBefore(node, ellipsisTextNode);
} // Get maximum text
function measureText(textNode, fullText) {
var startLoc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
var endLoc = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : fullText.length;
var lastSuccessLoc = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
var midLoc = Math.floor((startLoc + endLoc) / 2);
var currentText = fullText.slice(0, midLoc);
textNode.textContent = currentText;
if (startLoc >= endLoc - 1) {
// Loop when step is small
for (var step = endLoc; step >= startLoc; step -= 1) {
var currentStepText = fullText.slice(0, step);
textNode.textContent = currentStepText;
if (inRange() || !currentStepText) {
return step === fullText.length ? {
finished: false,
reactNode: fullText
} : {
finished: true,
reactNode: currentStepText
};
}
}
}
if (inRange()) {
return measureText(textNode, fullText, midLoc, endLoc, midLoc);
}
return measureText(textNode, fullText, startLoc, midLoc, lastSuccessLoc);
}
function measureNode(childNode, index) {
var type = childNode.nodeType;
if (type === ELEMENT_NODE) {
// We don't split element, it will keep if whole element can be displayed.
appendChildNode(childNode);
if (inRange()) {
return {
finished: false,
reactNode: contentList[index]
};
} // Clean up if can not pull in
ellipsisContentHolder.removeChild(childNode);
return {
finished: true,
reactNode: null
};
}
if (type === TEXT_NODE) {
var fullText = childNode.textContent || '';
var textNode = document.createTextNode(fullText);
appendChildNode(textNode);
return measureText(textNode, fullText);
} // Not handle other type of content
// PS: This code should not be attached after react 16
/* istanbul ignore next */
return {
finished: false,
reactNode: null
};
}
childNodes.some(function (childNode, index) {
var _measureNode = measureNode(childNode, index),
finished = _measureNode.finished,
reactNode = _measureNode.reactNode;
if (reactNode) {
ellipsisChildren.push(reactNode);
}
return finished;
});
return {
content: ellipsisChildren,
text: ellipsisContainer.innerHTML,
ellipsis: true
};
});