@instructure/ui-truncate-text 11.7.3-snapshot-7 → 11.7.3-snapshot-26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -2
- package/es/TruncateText/v1/index.js +63 -55
- package/es/TruncateText/v1/theme.js +5 -3
- package/es/TruncateText/v1/utils/cleanData.js +5 -3
- package/es/TruncateText/v1/utils/measureText.js +6 -6
- package/es/TruncateText/v1/utils/truncate.js +32 -27
- package/es/TruncateText/v2/index.js +63 -55
- package/es/TruncateText/v2/utils/cleanData.js +5 -3
- package/es/TruncateText/v2/utils/measureText.js +6 -6
- package/es/TruncateText/v2/utils/truncate.js +32 -27
- package/lib/TruncateText/v1/index.js +63 -55
- package/lib/TruncateText/v1/theme.js +5 -3
- package/lib/TruncateText/v1/utils/cleanData.js +5 -3
- package/lib/TruncateText/v1/utils/measureText.js +6 -6
- package/lib/TruncateText/v1/utils/truncate.js +32 -27
- package/lib/TruncateText/v2/index.js +63 -55
- package/lib/TruncateText/v2/utils/cleanData.js +5 -3
- package/lib/TruncateText/v2/utils/measureText.js +6 -6
- package/lib/TruncateText/v2/utils/truncate.js +32 -27
- package/package.json +14 -14
- package/tsconfig.build.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [11.7.3-snapshot-
|
|
6
|
+
## [11.7.3-snapshot-26](https://github.com/instructure/instructure-ui/compare/v11.7.2...v11.7.3-snapshot-26) (2026-05-05)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **many:** update dependencies, remove lots of Babel plugins, remove Webpack 4 support ([f916fca](https://github.com/instructure/instructure-ui/commit/f916fcafdddcb2d7de401f93e8ff92cfdfa47bba))
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var _dec, _class
|
|
1
|
+
var _dec, _class;
|
|
2
2
|
/*
|
|
3
3
|
* The MIT License (MIT)
|
|
4
4
|
*
|
|
@@ -39,21 +39,27 @@ import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
|
39
39
|
category: components
|
|
40
40
|
---
|
|
41
41
|
**/
|
|
42
|
-
let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _dec(_class =
|
|
42
|
+
let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _dec(_class = class TruncateText extends Component {
|
|
43
|
+
static displayName = "TruncateText";
|
|
44
|
+
static componentId = 'TruncateText';
|
|
45
|
+
static allowedProps = allowedProps;
|
|
46
|
+
static defaultProps = {
|
|
47
|
+
maxLines: 1,
|
|
48
|
+
ellipsis: '\u2026',
|
|
49
|
+
truncate: 'character',
|
|
50
|
+
position: 'end',
|
|
51
|
+
ignore: [' ', '.', ','],
|
|
52
|
+
debounce: 0
|
|
53
|
+
};
|
|
54
|
+
ref = null;
|
|
55
|
+
_text;
|
|
56
|
+
_debounced;
|
|
57
|
+
_stage = null;
|
|
58
|
+
_wasTruncated;
|
|
59
|
+
_resizeListener;
|
|
60
|
+
_prevWidth = null;
|
|
43
61
|
constructor(props) {
|
|
44
62
|
super(props);
|
|
45
|
-
this.ref = null;
|
|
46
|
-
this._text = void 0;
|
|
47
|
-
this._debounced = void 0;
|
|
48
|
-
this._stage = null;
|
|
49
|
-
this._wasTruncated = void 0;
|
|
50
|
-
this._resizeListener = void 0;
|
|
51
|
-
this._prevWidth = null;
|
|
52
|
-
this.update = () => {
|
|
53
|
-
if (this.ref) {
|
|
54
|
-
this.setState(this.initialState);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
63
|
this.state = this.initialState;
|
|
58
64
|
}
|
|
59
65
|
get _ref() {
|
|
@@ -63,33 +69,35 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
63
69
|
return {
|
|
64
70
|
isTruncated: false,
|
|
65
71
|
needsSecondRender: true,
|
|
66
|
-
truncatedElement:
|
|
67
|
-
truncatedText:
|
|
72
|
+
truncatedElement: undefined,
|
|
73
|
+
truncatedText: undefined
|
|
68
74
|
};
|
|
69
75
|
}
|
|
70
76
|
componentDidMount() {
|
|
71
|
-
const
|
|
72
|
-
children
|
|
73
|
-
makeStyles
|
|
74
|
-
|
|
77
|
+
const {
|
|
78
|
+
children,
|
|
79
|
+
makeStyles
|
|
80
|
+
} = this.props;
|
|
81
|
+
makeStyles?.();
|
|
75
82
|
if (children) {
|
|
76
|
-
var _getBoundingClientRec;
|
|
77
83
|
this.checkChildren();
|
|
78
84
|
const txt = ensureSingleChild(children);
|
|
79
|
-
this._text = txt ? txt :
|
|
85
|
+
this._text = txt ? txt : undefined;
|
|
80
86
|
this.truncate();
|
|
81
87
|
this._debounced = debounce(this.update, this.props.debounce, {
|
|
82
88
|
leading: true,
|
|
83
89
|
trailing: true
|
|
84
90
|
});
|
|
85
|
-
this._prevWidth =
|
|
91
|
+
this._prevWidth = getBoundingClientRect(this.ref)?.width;
|
|
86
92
|
this._resizeListener = new ResizeObserver(entries => {
|
|
87
93
|
// requestAnimationFrame call is needed becuase some truncatetext test cases
|
|
88
94
|
// failed due to ResizeObserver was not able to deliver all observations within a single animation frame
|
|
89
95
|
// see: https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
|
|
90
96
|
requestAnimationFrame(() => {
|
|
91
97
|
for (const entry of entries) {
|
|
92
|
-
const
|
|
98
|
+
const {
|
|
99
|
+
width
|
|
100
|
+
} = entry.contentRect;
|
|
93
101
|
if (this._prevWidth !== width) {
|
|
94
102
|
this._prevWidth = width;
|
|
95
103
|
this.props.debounce === 0 ? this.update() : this._debounced();
|
|
@@ -122,15 +130,17 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
122
130
|
return true;
|
|
123
131
|
}
|
|
124
132
|
componentDidUpdate(prevProps) {
|
|
125
|
-
const
|
|
126
|
-
children
|
|
127
|
-
onUpdate
|
|
128
|
-
makeStyles
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
const {
|
|
134
|
+
children,
|
|
135
|
+
onUpdate,
|
|
136
|
+
makeStyles
|
|
137
|
+
} = this.props;
|
|
138
|
+
makeStyles?.();
|
|
139
|
+
const {
|
|
140
|
+
isTruncated,
|
|
141
|
+
needsSecondRender,
|
|
142
|
+
truncatedText
|
|
143
|
+
} = this.state;
|
|
134
144
|
if (children) {
|
|
135
145
|
// for some reason in React 19 prevPros and this.props are a different
|
|
136
146
|
// object even if their contents are the same, so we cannot use !==
|
|
@@ -139,14 +149,14 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
139
149
|
// reset internal text variable if children change
|
|
140
150
|
this.checkChildren();
|
|
141
151
|
const txt = ensureSingleChild(children);
|
|
142
|
-
this._text = txt ? txt :
|
|
152
|
+
this._text = txt ? txt : undefined;
|
|
143
153
|
}
|
|
144
154
|
// require the double render whenever props change
|
|
145
155
|
this.setState(this.initialState);
|
|
146
156
|
return;
|
|
147
157
|
}
|
|
148
158
|
if (!needsSecondRender && (isTruncated || this._wasTruncated)) {
|
|
149
|
-
onUpdate
|
|
159
|
+
onUpdate?.(isTruncated, truncatedText);
|
|
150
160
|
this._wasTruncated = isTruncated;
|
|
151
161
|
} else {
|
|
152
162
|
this.truncate();
|
|
@@ -171,16 +181,20 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
171
181
|
return isTooDeep;
|
|
172
182
|
})(), `[TruncateText] Some children are too deep in the node tree and will not render.`);
|
|
173
183
|
}
|
|
184
|
+
update = () => {
|
|
185
|
+
if (this.ref) {
|
|
186
|
+
this.setState(this.initialState);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
174
189
|
truncate() {
|
|
175
190
|
if (!this.state.needsSecondRender) {
|
|
176
191
|
return;
|
|
177
192
|
}
|
|
178
193
|
if (canUseDOM) {
|
|
179
|
-
var _this$props$styles;
|
|
180
194
|
const result = truncate(this._stage, {
|
|
181
195
|
...this.props,
|
|
182
|
-
parent: this.ref ? this.ref :
|
|
183
|
-
lineHeight:
|
|
196
|
+
parent: this.ref ? this.ref : undefined,
|
|
197
|
+
lineHeight: this.props.styles?.lineHeight
|
|
184
198
|
});
|
|
185
199
|
if (result) {
|
|
186
200
|
const element = this.renderChildren(result.isTruncated, result.data, result.constraints.width);
|
|
@@ -192,8 +206,7 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
192
206
|
});
|
|
193
207
|
}
|
|
194
208
|
} else {
|
|
195
|
-
|
|
196
|
-
const textContent = (_this$ref = this.ref) !== null && _this$ref !== void 0 && _this$ref.textContent ? (_this$ref2 = this.ref) === null || _this$ref2 === void 0 ? void 0 : _this$ref2.textContent : void 0;
|
|
209
|
+
const textContent = this.ref?.textContent ? this.ref?.textContent : undefined;
|
|
197
210
|
// if dom isn't available, use original children
|
|
198
211
|
this.setState({
|
|
199
212
|
needsSecondRender: false,
|
|
@@ -204,7 +217,6 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
204
217
|
}
|
|
205
218
|
}
|
|
206
219
|
renderChildren(truncated, data, width) {
|
|
207
|
-
var _this$props$styles2;
|
|
208
220
|
if (!truncated) {
|
|
209
221
|
return this._text;
|
|
210
222
|
}
|
|
@@ -225,21 +237,24 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
225
237
|
// potentially be without this, text in `width: auto` elements won't expand
|
|
226
238
|
// to accommodate more text, once truncated
|
|
227
239
|
childElements.push(_jsx("span", {
|
|
228
|
-
css:
|
|
240
|
+
css: this.props.styles?.spacer,
|
|
229
241
|
style: {
|
|
230
|
-
width: width ||
|
|
242
|
+
width: width || undefined
|
|
231
243
|
}
|
|
232
244
|
}, "spacer"));
|
|
233
245
|
const children = Children.map(childElements, child => child);
|
|
234
246
|
return this._text.props ? safeCloneElement(this._text, this._text.props, children) : children;
|
|
235
247
|
}
|
|
236
248
|
render() {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
249
|
+
const {
|
|
250
|
+
truncatedElement
|
|
251
|
+
} = this.state;
|
|
252
|
+
const {
|
|
253
|
+
children
|
|
254
|
+
} = this.props;
|
|
240
255
|
return _jsxs("span", {
|
|
241
256
|
"data-cid": "TruncateText",
|
|
242
|
-
css:
|
|
257
|
+
css: this.props.styles?.truncateText,
|
|
243
258
|
ref: el => {
|
|
244
259
|
this.ref = el;
|
|
245
260
|
},
|
|
@@ -251,13 +266,6 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
251
266
|
})), truncatedElement]
|
|
252
267
|
});
|
|
253
268
|
}
|
|
254
|
-
}
|
|
255
|
-
maxLines: 1,
|
|
256
|
-
ellipsis: '\u2026',
|
|
257
|
-
truncate: 'character',
|
|
258
|
-
position: 'end',
|
|
259
|
-
ignore: [' ', '.', ','],
|
|
260
|
-
debounce: 0
|
|
261
|
-
}, _TruncateText)) || _class);
|
|
269
|
+
}) || _class);
|
|
262
270
|
export default TruncateText;
|
|
263
271
|
export { TruncateText };
|
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
* @return {Object} The final theme object with the overrides and component variables
|
|
29
29
|
*/
|
|
30
30
|
const generateComponentTheme = theme => {
|
|
31
|
-
const
|
|
31
|
+
const {
|
|
32
|
+
typography
|
|
33
|
+
} = theme;
|
|
32
34
|
const componentVariables = {
|
|
33
|
-
fontFamily: typography
|
|
34
|
-
lineHeight: typography
|
|
35
|
+
fontFamily: typography?.fontFamily,
|
|
36
|
+
lineHeight: typography?.lineHeight
|
|
35
37
|
};
|
|
36
38
|
return {
|
|
37
39
|
...componentVariables
|
|
@@ -39,9 +39,11 @@ import { cloneArray } from '@instructure/ui-utils';
|
|
|
39
39
|
* @param {boolean} repeat=false Do a thorough clean.
|
|
40
40
|
*/
|
|
41
41
|
function cleanData(stringData, options, repeat = false) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const {
|
|
43
|
+
truncate,
|
|
44
|
+
ignore,
|
|
45
|
+
ellipsis
|
|
46
|
+
} = options;
|
|
45
47
|
let newData = cloneArray(stringData);
|
|
46
48
|
let ellipsisNode = -1;
|
|
47
49
|
let ellipsisIndex = -1;
|
|
@@ -56,16 +56,16 @@ function measure(string, domNode) {
|
|
|
56
56
|
let width = 0;
|
|
57
57
|
// grab individual font styles
|
|
58
58
|
// some browsers don't report a value for style['font']
|
|
59
|
-
context.font = [style
|
|
60
|
-
if (
|
|
59
|
+
context.font = [style?.fontWeight, style?.fontStyle, style?.fontSize, style?.fontFamily].join(' ');
|
|
60
|
+
if (style?.textTransform === 'uppercase') {
|
|
61
61
|
text = string.toUpperCase();
|
|
62
|
-
} else if (
|
|
62
|
+
} else if (style?.textTransform === 'lowercase') {
|
|
63
63
|
text = string.toLowerCase();
|
|
64
|
-
} else if (
|
|
64
|
+
} else if (style?.textTransform === 'capitalize') {
|
|
65
65
|
text = string.replace(/\b\w/g, str => str.toUpperCase());
|
|
66
66
|
}
|
|
67
|
-
if (
|
|
68
|
-
letterOffset = text.length * parseInt(style
|
|
67
|
+
if (style?.letterSpacing !== 'normal') {
|
|
68
|
+
letterOffset = text.length * parseInt(style?.letterSpacing);
|
|
69
69
|
}
|
|
70
70
|
width = context.measureText(text).width + letterOffset;
|
|
71
71
|
// returns the max potential width of the text, assuming the text was on a single line
|
|
@@ -55,17 +55,17 @@ function truncate(element, options) {
|
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
class Truncator {
|
|
58
|
+
_options;
|
|
59
|
+
_stage;
|
|
60
|
+
_parent;
|
|
61
|
+
_nodeMap;
|
|
62
|
+
_defaultStringData;
|
|
63
|
+
_nodeDataIndexes;
|
|
64
|
+
_maxHeight;
|
|
65
|
+
_maxWidth;
|
|
66
|
+
_maxLines;
|
|
58
67
|
constructor(element, options = {}) {
|
|
59
|
-
|
|
60
|
-
this._stage = void 0;
|
|
61
|
-
this._parent = void 0;
|
|
62
|
-
this._nodeMap = void 0;
|
|
63
|
-
this._defaultStringData = void 0;
|
|
64
|
-
this._nodeDataIndexes = void 0;
|
|
65
|
-
this._maxHeight = void 0;
|
|
66
|
-
this._maxWidth = void 0;
|
|
67
|
-
this._maxLines = void 0;
|
|
68
|
-
const parentElement = element !== null && element !== void 0 && element.parentElement ? element === null || element === void 0 ? void 0 : element.parentElement : void 0;
|
|
68
|
+
const parentElement = element?.parentElement ? element?.parentElement : undefined;
|
|
69
69
|
this._options = {
|
|
70
70
|
parent: options.parent || parentElement,
|
|
71
71
|
maxLines: options.maxLines || 1,
|
|
@@ -84,7 +84,7 @@ class Truncator {
|
|
|
84
84
|
if (options.parent) {
|
|
85
85
|
this._parent = this._options.parent;
|
|
86
86
|
} else {
|
|
87
|
-
const parentEl = this._stage.parentElement ? this._stage.parentElement :
|
|
87
|
+
const parentEl = this._stage.parentElement ? this._stage.parentElement : undefined;
|
|
88
88
|
this._parent = this._options.maxLines === 'auto' ? parentEl : this._stage;
|
|
89
89
|
}
|
|
90
90
|
this.setup();
|
|
@@ -97,10 +97,11 @@ class Truncator {
|
|
|
97
97
|
if (!style) {
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
|
-
const
|
|
101
|
-
maxLines
|
|
102
|
-
truncate
|
|
103
|
-
lineHeight
|
|
100
|
+
const {
|
|
101
|
+
maxLines,
|
|
102
|
+
truncate,
|
|
103
|
+
lineHeight
|
|
104
|
+
} = this._options;
|
|
104
105
|
// if no explicit lineHeight is inherited, use lineHeight multiplier for calculations
|
|
105
106
|
const actualLineHeight = style.lineHeight === 'normal' ? lineHeight * parseFloat(style.fontSize) : parseFloat(style.lineHeight);
|
|
106
107
|
const node = this._stage.firstChild.children ? this._stage.firstChild : this._stage;
|
|
@@ -126,9 +127,10 @@ class Truncator {
|
|
|
126
127
|
this._maxLines = maxLines === 'auto' ? Math.round(this._maxHeight / actualLineHeight) : maxLines;
|
|
127
128
|
}
|
|
128
129
|
getNodeMap(rootNode) {
|
|
129
|
-
const
|
|
130
|
-
shouldTruncateWhenInvisible
|
|
131
|
-
truncate
|
|
130
|
+
const {
|
|
131
|
+
shouldTruncateWhenInvisible,
|
|
132
|
+
truncate
|
|
133
|
+
} = this._options;
|
|
132
134
|
const nodes = Array.from(rootNode.childNodes);
|
|
133
135
|
const map = [];
|
|
134
136
|
// parse child nodes and build a data map to associate each node with its data
|
|
@@ -188,10 +190,11 @@ class Truncator {
|
|
|
188
190
|
return fits;
|
|
189
191
|
}
|
|
190
192
|
truncate() {
|
|
191
|
-
const
|
|
192
|
-
ellipsis
|
|
193
|
-
ignore
|
|
194
|
-
position
|
|
193
|
+
const {
|
|
194
|
+
ellipsis,
|
|
195
|
+
ignore,
|
|
196
|
+
position
|
|
197
|
+
} = this._options;
|
|
195
198
|
const middle = position === 'middle';
|
|
196
199
|
let truncated = false;
|
|
197
200
|
let truncatedText = '';
|
|
@@ -304,16 +307,18 @@ class Truncator {
|
|
|
304
307
|
};
|
|
305
308
|
};
|
|
306
309
|
const truncateEvaluate = (truncatedLength, middle) => {
|
|
307
|
-
const
|
|
308
|
-
truncatedArray
|
|
310
|
+
const {
|
|
311
|
+
truncatedArray
|
|
312
|
+
} = truncateArray(truncatedLength, this._defaultStringData, this._nodeDataIndexes, middle);
|
|
309
313
|
return this.checkFit(truncatedArray);
|
|
310
314
|
};
|
|
311
315
|
|
|
312
316
|
// find the best number of element to truncate
|
|
313
317
|
const finestMatch = binarySearch(0, this._nodeDataIndexes.length, truncateEvaluate, middle, this._nodeDataIndexes.length);
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
318
|
+
({
|
|
319
|
+
truncated,
|
|
320
|
+
truncatedArray: stringData
|
|
321
|
+
} = truncateArray(finestMatch, this._defaultStringData, this._nodeDataIndexes, middle));
|
|
317
322
|
stringData = cleanData(stringData, this._options, true);
|
|
318
323
|
for (let i = 0; i < stringData.length; i++) {
|
|
319
324
|
const data = stringData[i];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var _dec, _class
|
|
1
|
+
var _dec, _class;
|
|
2
2
|
/*
|
|
3
3
|
* The MIT License (MIT)
|
|
4
4
|
*
|
|
@@ -38,21 +38,27 @@ import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
|
38
38
|
category: components
|
|
39
39
|
---
|
|
40
40
|
**/
|
|
41
|
-
let TruncateText = (_dec = withStyle(generateStyle), _dec(_class =
|
|
41
|
+
let TruncateText = (_dec = withStyle(generateStyle), _dec(_class = class TruncateText extends Component {
|
|
42
|
+
static displayName = "TruncateText";
|
|
43
|
+
static componentId = 'TruncateText';
|
|
44
|
+
static allowedProps = allowedProps;
|
|
45
|
+
static defaultProps = {
|
|
46
|
+
maxLines: 1,
|
|
47
|
+
ellipsis: '\u2026',
|
|
48
|
+
truncate: 'character',
|
|
49
|
+
position: 'end',
|
|
50
|
+
ignore: [' ', '.', ','],
|
|
51
|
+
debounce: 0
|
|
52
|
+
};
|
|
53
|
+
ref = null;
|
|
54
|
+
_text;
|
|
55
|
+
_debounced;
|
|
56
|
+
_stage = null;
|
|
57
|
+
_wasTruncated;
|
|
58
|
+
_resizeListener;
|
|
59
|
+
_prevWidth = null;
|
|
42
60
|
constructor(props) {
|
|
43
61
|
super(props);
|
|
44
|
-
this.ref = null;
|
|
45
|
-
this._text = void 0;
|
|
46
|
-
this._debounced = void 0;
|
|
47
|
-
this._stage = null;
|
|
48
|
-
this._wasTruncated = void 0;
|
|
49
|
-
this._resizeListener = void 0;
|
|
50
|
-
this._prevWidth = null;
|
|
51
|
-
this.update = () => {
|
|
52
|
-
if (this.ref) {
|
|
53
|
-
this.setState(this.initialState);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
62
|
this.state = this.initialState;
|
|
57
63
|
}
|
|
58
64
|
get _ref() {
|
|
@@ -62,33 +68,35 @@ let TruncateText = (_dec = withStyle(generateStyle), _dec(_class = (_TruncateTex
|
|
|
62
68
|
return {
|
|
63
69
|
isTruncated: false,
|
|
64
70
|
needsSecondRender: true,
|
|
65
|
-
truncatedElement:
|
|
66
|
-
truncatedText:
|
|
71
|
+
truncatedElement: undefined,
|
|
72
|
+
truncatedText: undefined
|
|
67
73
|
};
|
|
68
74
|
}
|
|
69
75
|
componentDidMount() {
|
|
70
|
-
const
|
|
71
|
-
children
|
|
72
|
-
makeStyles
|
|
73
|
-
|
|
76
|
+
const {
|
|
77
|
+
children,
|
|
78
|
+
makeStyles
|
|
79
|
+
} = this.props;
|
|
80
|
+
makeStyles?.();
|
|
74
81
|
if (children) {
|
|
75
|
-
var _getBoundingClientRec;
|
|
76
82
|
this.checkChildren();
|
|
77
83
|
const txt = ensureSingleChild(children);
|
|
78
|
-
this._text = txt ? txt :
|
|
84
|
+
this._text = txt ? txt : undefined;
|
|
79
85
|
this.truncate();
|
|
80
86
|
this._debounced = debounce(this.update, this.props.debounce, {
|
|
81
87
|
leading: true,
|
|
82
88
|
trailing: true
|
|
83
89
|
});
|
|
84
|
-
this._prevWidth =
|
|
90
|
+
this._prevWidth = getBoundingClientRect(this.ref)?.width;
|
|
85
91
|
this._resizeListener = new ResizeObserver(entries => {
|
|
86
92
|
// requestAnimationFrame call is needed becuase some truncatetext test cases
|
|
87
93
|
// failed due to ResizeObserver was not able to deliver all observations within a single animation frame
|
|
88
94
|
// see: https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
|
|
89
95
|
requestAnimationFrame(() => {
|
|
90
96
|
for (const entry of entries) {
|
|
91
|
-
const
|
|
97
|
+
const {
|
|
98
|
+
width
|
|
99
|
+
} = entry.contentRect;
|
|
92
100
|
if (this._prevWidth !== width) {
|
|
93
101
|
this._prevWidth = width;
|
|
94
102
|
this.props.debounce === 0 ? this.update() : this._debounced();
|
|
@@ -121,15 +129,17 @@ let TruncateText = (_dec = withStyle(generateStyle), _dec(_class = (_TruncateTex
|
|
|
121
129
|
return true;
|
|
122
130
|
}
|
|
123
131
|
componentDidUpdate(prevProps) {
|
|
124
|
-
const
|
|
125
|
-
children
|
|
126
|
-
onUpdate
|
|
127
|
-
makeStyles
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
const {
|
|
133
|
+
children,
|
|
134
|
+
onUpdate,
|
|
135
|
+
makeStyles
|
|
136
|
+
} = this.props;
|
|
137
|
+
makeStyles?.();
|
|
138
|
+
const {
|
|
139
|
+
isTruncated,
|
|
140
|
+
needsSecondRender,
|
|
141
|
+
truncatedText
|
|
142
|
+
} = this.state;
|
|
133
143
|
if (children) {
|
|
134
144
|
// for some reason in React 19 prevPros and this.props are a different
|
|
135
145
|
// object even if their contents are the same, so we cannot use !==
|
|
@@ -138,14 +148,14 @@ let TruncateText = (_dec = withStyle(generateStyle), _dec(_class = (_TruncateTex
|
|
|
138
148
|
// reset internal text variable if children change
|
|
139
149
|
this.checkChildren();
|
|
140
150
|
const txt = ensureSingleChild(children);
|
|
141
|
-
this._text = txt ? txt :
|
|
151
|
+
this._text = txt ? txt : undefined;
|
|
142
152
|
}
|
|
143
153
|
// require the double render whenever props change
|
|
144
154
|
this.setState(this.initialState);
|
|
145
155
|
return;
|
|
146
156
|
}
|
|
147
157
|
if (!needsSecondRender && (isTruncated || this._wasTruncated)) {
|
|
148
|
-
onUpdate
|
|
158
|
+
onUpdate?.(isTruncated, truncatedText);
|
|
149
159
|
this._wasTruncated = isTruncated;
|
|
150
160
|
} else {
|
|
151
161
|
this.truncate();
|
|
@@ -170,16 +180,20 @@ let TruncateText = (_dec = withStyle(generateStyle), _dec(_class = (_TruncateTex
|
|
|
170
180
|
return isTooDeep;
|
|
171
181
|
})(), `[TruncateText] Some children are too deep in the node tree and will not render.`);
|
|
172
182
|
}
|
|
183
|
+
update = () => {
|
|
184
|
+
if (this.ref) {
|
|
185
|
+
this.setState(this.initialState);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
173
188
|
truncate() {
|
|
174
189
|
if (!this.state.needsSecondRender) {
|
|
175
190
|
return;
|
|
176
191
|
}
|
|
177
192
|
if (canUseDOM) {
|
|
178
|
-
var _this$props$styles;
|
|
179
193
|
const result = truncate(this._stage, {
|
|
180
194
|
...this.props,
|
|
181
|
-
parent: this.ref ? this.ref :
|
|
182
|
-
lineHeight:
|
|
195
|
+
parent: this.ref ? this.ref : undefined,
|
|
196
|
+
lineHeight: this.props.styles?.lineHeight
|
|
183
197
|
});
|
|
184
198
|
if (result) {
|
|
185
199
|
const element = this.renderChildren(result.isTruncated, result.data, result.constraints.width);
|
|
@@ -191,8 +205,7 @@ let TruncateText = (_dec = withStyle(generateStyle), _dec(_class = (_TruncateTex
|
|
|
191
205
|
});
|
|
192
206
|
}
|
|
193
207
|
} else {
|
|
194
|
-
|
|
195
|
-
const textContent = (_this$ref = this.ref) !== null && _this$ref !== void 0 && _this$ref.textContent ? (_this$ref2 = this.ref) === null || _this$ref2 === void 0 ? void 0 : _this$ref2.textContent : void 0;
|
|
208
|
+
const textContent = this.ref?.textContent ? this.ref?.textContent : undefined;
|
|
196
209
|
// if dom isn't available, use original children
|
|
197
210
|
this.setState({
|
|
198
211
|
needsSecondRender: false,
|
|
@@ -203,7 +216,6 @@ let TruncateText = (_dec = withStyle(generateStyle), _dec(_class = (_TruncateTex
|
|
|
203
216
|
}
|
|
204
217
|
}
|
|
205
218
|
renderChildren(truncated, data, width) {
|
|
206
|
-
var _this$props$styles2;
|
|
207
219
|
if (!truncated) {
|
|
208
220
|
return this._text;
|
|
209
221
|
}
|
|
@@ -226,21 +238,24 @@ let TruncateText = (_dec = withStyle(generateStyle), _dec(_class = (_TruncateTex
|
|
|
226
238
|
// Breadcrumb is modifying this element's display to inline to prevent layout issues
|
|
227
239
|
// TODO: find a better way to handle this
|
|
228
240
|
childElements.push(_jsx("span", {
|
|
229
|
-
css:
|
|
241
|
+
css: this.props.styles?.spacer,
|
|
230
242
|
style: {
|
|
231
|
-
width: width ||
|
|
243
|
+
width: width || undefined
|
|
232
244
|
}
|
|
233
245
|
}, "spacer"));
|
|
234
246
|
const children = Children.map(childElements, child => child);
|
|
235
247
|
return this._text.props ? safeCloneElement(this._text, this._text.props, children) : children;
|
|
236
248
|
}
|
|
237
249
|
render() {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
250
|
+
const {
|
|
251
|
+
truncatedElement
|
|
252
|
+
} = this.state;
|
|
253
|
+
const {
|
|
254
|
+
children
|
|
255
|
+
} = this.props;
|
|
241
256
|
return _jsxs("span", {
|
|
242
257
|
"data-cid": "TruncateText",
|
|
243
|
-
css:
|
|
258
|
+
css: this.props.styles?.truncateText,
|
|
244
259
|
ref: el => {
|
|
245
260
|
this.ref = el;
|
|
246
261
|
},
|
|
@@ -252,13 +267,6 @@ let TruncateText = (_dec = withStyle(generateStyle), _dec(_class = (_TruncateTex
|
|
|
252
267
|
})), truncatedElement]
|
|
253
268
|
});
|
|
254
269
|
}
|
|
255
|
-
}
|
|
256
|
-
maxLines: 1,
|
|
257
|
-
ellipsis: '\u2026',
|
|
258
|
-
truncate: 'character',
|
|
259
|
-
position: 'end',
|
|
260
|
-
ignore: [' ', '.', ','],
|
|
261
|
-
debounce: 0
|
|
262
|
-
}, _TruncateText)) || _class);
|
|
270
|
+
}) || _class);
|
|
263
271
|
export default TruncateText;
|
|
264
272
|
export { TruncateText };
|
|
@@ -39,9 +39,11 @@ import { cloneArray } from '@instructure/ui-utils';
|
|
|
39
39
|
* @param {boolean} repeat=false Do a thorough clean.
|
|
40
40
|
*/
|
|
41
41
|
function cleanData(stringData, options, repeat = false) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const {
|
|
43
|
+
truncate,
|
|
44
|
+
ignore,
|
|
45
|
+
ellipsis
|
|
46
|
+
} = options;
|
|
45
47
|
let newData = cloneArray(stringData);
|
|
46
48
|
let ellipsisNode = -1;
|
|
47
49
|
let ellipsisIndex = -1;
|
|
@@ -56,16 +56,16 @@ function measure(string, domNode) {
|
|
|
56
56
|
let width = 0;
|
|
57
57
|
// grab individual font styles
|
|
58
58
|
// some browsers don't report a value for style['font']
|
|
59
|
-
context.font = [style
|
|
60
|
-
if (
|
|
59
|
+
context.font = [style?.fontWeight, style?.fontStyle, style?.fontSize, style?.fontFamily].join(' ');
|
|
60
|
+
if (style?.textTransform === 'uppercase') {
|
|
61
61
|
text = string.toUpperCase();
|
|
62
|
-
} else if (
|
|
62
|
+
} else if (style?.textTransform === 'lowercase') {
|
|
63
63
|
text = string.toLowerCase();
|
|
64
|
-
} else if (
|
|
64
|
+
} else if (style?.textTransform === 'capitalize') {
|
|
65
65
|
text = string.replace(/\b\w/g, str => str.toUpperCase());
|
|
66
66
|
}
|
|
67
|
-
if (
|
|
68
|
-
letterOffset = text.length * parseInt(style
|
|
67
|
+
if (style?.letterSpacing !== 'normal') {
|
|
68
|
+
letterOffset = text.length * parseInt(style?.letterSpacing);
|
|
69
69
|
}
|
|
70
70
|
width = context.measureText(text).width + letterOffset;
|
|
71
71
|
// returns the max potential width of the text, assuming the text was on a single line
|