@instructure/ui-truncate-text 10.26.1 → 11.0.0
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 +23 -2
- package/es/TruncateText/index.js +24 -9
- package/es/TruncateText/props.js +1 -13
- package/es/TruncateText/utils/measureText.js +8 -8
- package/es/TruncateText/utils/truncate.js +5 -2
- package/lib/TruncateText/index.js +22 -8
- package/lib/TruncateText/props.js +1 -14
- package/lib/TruncateText/utils/measureText.js +8 -8
- package/lib/TruncateText/utils/truncate.js +5 -2
- package/package.json +16 -18
- package/src/TruncateText/index.tsx +22 -11
- package/src/TruncateText/props.ts +2 -19
- package/src/TruncateText/utils/measureText.ts +11 -11
- package/src/TruncateText/utils/truncate.ts +7 -2
- package/tsconfig.build.json +0 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/TruncateText/index.d.ts +1 -1
- package/types/TruncateText/index.d.ts.map +1 -1
- package/types/TruncateText/props.d.ts +2 -3
- package/types/TruncateText/props.d.ts.map +1 -1
- package/types/TruncateText/utils/truncate.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,30 @@
|
|
|
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
|
-
|
|
6
|
+
# [11.0.0](https://github.com/instructure/instructure-ui/compare/v10.26.0...v11.0.0) (2025-10-06)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **many:** instUI v11 release ([36f5438](https://github.com/instructure/instructure-ui/commit/36f54382669186227ba24798bbf7201ef2f5cd4c))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### BREAKING CHANGES
|
|
15
|
+
|
|
16
|
+
* **many:** InstUI v11 contains the following breaking changes:
|
|
17
|
+
- React 16 and 17 are no longer supported
|
|
18
|
+
- remove `PropTypes` from all packages
|
|
19
|
+
- remove `CodeEditor` component
|
|
20
|
+
- remove `@instui/theme-registry` package
|
|
21
|
+
- remove `@testable`, `@experimental`, `@hack` decorators
|
|
22
|
+
- InstUISettingsProvider's `as` prop is removed
|
|
23
|
+
- `canvas.use()`, `canvasHighContrast.use()` functions are removed
|
|
24
|
+
- `canvasThemeLocal`, `canvasHighContrastThemeLocal` are removed
|
|
25
|
+
- `variables` field on theme objects are removed
|
|
26
|
+
- remove deprecated props from Table: Row's `isStacked`, Body's
|
|
27
|
+
`isStacked`, `hover`, and `headers`
|
|
28
|
+
- `Table`'s `caption` prop is now required
|
|
29
|
+
- `ui-dom-utils`'s `getComputedStyle` can now return `undefined`
|
|
9
30
|
|
|
10
31
|
|
|
11
32
|
|
package/es/TruncateText/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var _dec,
|
|
1
|
+
var _dec, _class, _TruncateText;
|
|
2
2
|
/*
|
|
3
3
|
* The MIT License (MIT)
|
|
4
4
|
*
|
|
@@ -26,21 +26,20 @@ var _dec, _dec2, _dec3, _class, _TruncateText;
|
|
|
26
26
|
import { Children, Component } from 'react';
|
|
27
27
|
import { debounce } from '@instructure/debounce';
|
|
28
28
|
import { canUseDOM, getBoundingClientRect } from '@instructure/ui-dom-utils';
|
|
29
|
-
import { safeCloneElement, ensureSingleChild
|
|
29
|
+
import { safeCloneElement, ensureSingleChild } from '@instructure/ui-react-utils';
|
|
30
30
|
import { logError as error } from '@instructure/console';
|
|
31
|
-
import { testable } from '@instructure/ui-testable';
|
|
32
31
|
import { withStyle } from '@instructure/emotion';
|
|
33
32
|
import generateStyle from './styles';
|
|
34
33
|
import generateComponentTheme from './theme';
|
|
35
34
|
import truncate from './utils/truncate';
|
|
36
|
-
import {
|
|
35
|
+
import { allowedProps } from './props';
|
|
37
36
|
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
38
37
|
/**
|
|
39
38
|
---
|
|
40
39
|
category: components
|
|
41
40
|
---
|
|
42
41
|
**/
|
|
43
|
-
let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme),
|
|
42
|
+
let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _dec(_class = (_TruncateText = class TruncateText extends Component {
|
|
44
43
|
constructor(props) {
|
|
45
44
|
super(props);
|
|
46
45
|
this.ref = null;
|
|
@@ -109,6 +108,19 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
109
108
|
this._debounced.cancel();
|
|
110
109
|
}
|
|
111
110
|
}
|
|
111
|
+
shallowCompare(obj1, obj2) {
|
|
112
|
+
const keys1 = Object.keys(obj1);
|
|
113
|
+
const keys2 = Object.keys(obj2);
|
|
114
|
+
if (keys1.length !== keys2.length) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
for (const key of keys1) {
|
|
118
|
+
if (obj1[key] !== obj2[key]) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
112
124
|
componentDidUpdate(prevProps) {
|
|
113
125
|
const _this$props2 = this.props,
|
|
114
126
|
children = _this$props2.children,
|
|
@@ -120,7 +132,9 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
120
132
|
needsSecondRender = _this$state.needsSecondRender,
|
|
121
133
|
truncatedText = _this$state.truncatedText;
|
|
122
134
|
if (children) {
|
|
123
|
-
|
|
135
|
+
// for some reason in React 19 prevPros and this.props are a different
|
|
136
|
+
// object even if their contents are the same, so we cannot use !==
|
|
137
|
+
if (!this.shallowCompare(prevProps, this.props)) {
|
|
124
138
|
if (prevProps.children !== this.props.children) {
|
|
125
139
|
// reset internal text variable if children change
|
|
126
140
|
this.checkChildren();
|
|
@@ -215,7 +229,7 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
215
229
|
style: {
|
|
216
230
|
width: width || void 0
|
|
217
231
|
}
|
|
218
|
-
}));
|
|
232
|
+
}, "spacer"));
|
|
219
233
|
const children = Children.map(childElements, child => child);
|
|
220
234
|
return this._text.props ? safeCloneElement(this._text, this._text.props, children) : children;
|
|
221
235
|
}
|
|
@@ -224,6 +238,7 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
224
238
|
const truncatedElement = this.state.truncatedElement;
|
|
225
239
|
const children = this.props.children;
|
|
226
240
|
return _jsxs("span", {
|
|
241
|
+
"data-cid": "TruncateText",
|
|
227
242
|
css: (_this$props$styles3 = this.props.styles) === null || _this$props$styles3 === void 0 ? void 0 : _this$props$styles3.truncateText,
|
|
228
243
|
ref: el => {
|
|
229
244
|
this.ref = el;
|
|
@@ -236,13 +251,13 @@ let TruncateText = (_dec = withStyle(generateStyle, generateComponentTheme), _de
|
|
|
236
251
|
})), truncatedElement]
|
|
237
252
|
});
|
|
238
253
|
}
|
|
239
|
-
}, _TruncateText.displayName = "TruncateText", _TruncateText.componentId = 'TruncateText', _TruncateText.allowedProps = allowedProps, _TruncateText.
|
|
254
|
+
}, _TruncateText.displayName = "TruncateText", _TruncateText.componentId = 'TruncateText', _TruncateText.allowedProps = allowedProps, _TruncateText.defaultProps = {
|
|
240
255
|
maxLines: 1,
|
|
241
256
|
ellipsis: '\u2026',
|
|
242
257
|
truncate: 'character',
|
|
243
258
|
position: 'end',
|
|
244
259
|
ignore: [' ', '.', ','],
|
|
245
260
|
debounce: 0
|
|
246
|
-
}, _TruncateText)) || _class)
|
|
261
|
+
}, _TruncateText)) || _class);
|
|
247
262
|
export default TruncateText;
|
|
248
263
|
export { TruncateText };
|
package/es/TruncateText/props.js
CHANGED
|
@@ -22,17 +22,5 @@
|
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import PropTypes from 'prop-types';
|
|
26
|
-
const propTypes = {
|
|
27
|
-
children: PropTypes.node.isRequired,
|
|
28
|
-
maxLines: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
29
|
-
position: PropTypes.oneOf(['end', 'middle']),
|
|
30
|
-
truncate: PropTypes.oneOf(['character', 'word']),
|
|
31
|
-
ellipsis: PropTypes.string,
|
|
32
|
-
ignore: PropTypes.arrayOf(PropTypes.string),
|
|
33
|
-
debounce: PropTypes.number,
|
|
34
|
-
onUpdate: PropTypes.func,
|
|
35
|
-
shouldTruncateWhenInvisible: PropTypes.bool
|
|
36
|
-
};
|
|
37
25
|
const allowedProps = ['children', 'maxLines', 'position', 'truncate', 'ellipsis', 'ignore', 'debounce', 'onUpdate', 'shouldTruncateWhenInvisible'];
|
|
38
|
-
export {
|
|
26
|
+
export { allowedProps };
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import {
|
|
25
|
+
import { getCSSStyleDeclaration } from '@instructure/ui-dom-utils';
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* ---
|
|
@@ -43,7 +43,7 @@ function measureText(nodes, parentNode) {
|
|
|
43
43
|
return width;
|
|
44
44
|
}
|
|
45
45
|
function measure(string, domNode) {
|
|
46
|
-
const style =
|
|
46
|
+
const style = getCSSStyleDeclaration(domNode);
|
|
47
47
|
// we use a canvas in a doc fragment to prevent having to render the string full width in the DOM
|
|
48
48
|
const canvas = document.createElement('canvas');
|
|
49
49
|
document.createDocumentFragment().appendChild(canvas);
|
|
@@ -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.fontWeight, style.fontStyle, style.fontSize, style.fontFamily].join(' ');
|
|
60
|
-
if (style.textTransform === 'uppercase') {
|
|
59
|
+
context.font = [style === null || style === void 0 ? void 0 : style.fontWeight, style === null || style === void 0 ? void 0 : style.fontStyle, style === null || style === void 0 ? void 0 : style.fontSize, style === null || style === void 0 ? void 0 : style.fontFamily].join(' ');
|
|
60
|
+
if ((style === null || style === void 0 ? void 0 : style.textTransform) === 'uppercase') {
|
|
61
61
|
text = string.toUpperCase();
|
|
62
|
-
} else if (style.textTransform === 'lowercase') {
|
|
62
|
+
} else if ((style === null || style === void 0 ? void 0 : style.textTransform) === 'lowercase') {
|
|
63
63
|
text = string.toLowerCase();
|
|
64
|
-
} else if (style.textTransform === 'capitalize') {
|
|
64
|
+
} else if ((style === null || style === void 0 ? void 0 : style.textTransform) === 'capitalize') {
|
|
65
65
|
text = string.replace(/\b\w/g, str => str.toUpperCase());
|
|
66
66
|
}
|
|
67
|
-
if (style.letterSpacing !== 'normal') {
|
|
68
|
-
letterOffset = text.length * parseInt(style.letterSpacing);
|
|
67
|
+
if ((style === null || style === void 0 ? void 0 : style.letterSpacing) !== 'normal') {
|
|
68
|
+
letterOffset = text.length * parseInt(style === null || style === void 0 ? void 0 : 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
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import escapeHtml from 'escape-html';
|
|
26
26
|
import { cloneArray } from '@instructure/ui-utils';
|
|
27
27
|
import { logError as error } from '@instructure/console';
|
|
28
|
-
import {
|
|
28
|
+
import { getCSSStyleDeclaration, getBoundingClientRect, isVisible } from '@instructure/ui-dom-utils';
|
|
29
29
|
import measureText from './measureText';
|
|
30
30
|
import cleanString from './cleanString';
|
|
31
31
|
import cleanData from './cleanData';
|
|
@@ -93,11 +93,14 @@ class Truncator {
|
|
|
93
93
|
if (!this._stage) {
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
|
+
const style = getCSSStyleDeclaration(this._parent);
|
|
97
|
+
if (!style) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
96
100
|
const _this$_options = this._options,
|
|
97
101
|
maxLines = _this$_options.maxLines,
|
|
98
102
|
truncate = _this$_options.truncate,
|
|
99
103
|
lineHeight = _this$_options.lineHeight;
|
|
100
|
-
const style = getComputedStyle(this._parent);
|
|
101
104
|
// if no explicit lineHeight is inherited, use lineHeight multiplier for calculations
|
|
102
105
|
const actualLineHeight = style.lineHeight === 'normal' ? lineHeight * parseFloat(style.fontSize) : parseFloat(style.lineHeight);
|
|
103
106
|
const node = this._stage.firstChild.children ? this._stage.firstChild : this._stage;
|
|
@@ -11,16 +11,14 @@ var _canUseDOM = require("@instructure/ui-dom-utils/lib/canUseDOM.js");
|
|
|
11
11
|
var _getBoundingClientRect = require("@instructure/ui-dom-utils/lib/getBoundingClientRect.js");
|
|
12
12
|
var _safeCloneElement = require("@instructure/ui-react-utils/lib/safeCloneElement.js");
|
|
13
13
|
var _ensureSingleChild = require("@instructure/ui-react-utils/lib/ensureSingleChild.js");
|
|
14
|
-
var _hack = require("@instructure/ui-react-utils/lib/hack.js");
|
|
15
14
|
var _console = require("@instructure/console");
|
|
16
|
-
var _testable = require("@instructure/ui-testable/lib/testable.js");
|
|
17
15
|
var _emotion = require("@instructure/emotion");
|
|
18
16
|
var _styles = _interopRequireDefault(require("./styles"));
|
|
19
17
|
var _theme = _interopRequireDefault(require("./theme"));
|
|
20
18
|
var _truncate = _interopRequireDefault(require("./utils/truncate"));
|
|
21
19
|
var _props = require("./props");
|
|
22
20
|
var _jsxRuntime = require("@emotion/react/jsx-runtime");
|
|
23
|
-
var _dec,
|
|
21
|
+
var _dec, _class, _TruncateText;
|
|
24
22
|
/*
|
|
25
23
|
* The MIT License (MIT)
|
|
26
24
|
*
|
|
@@ -49,7 +47,7 @@ var _dec, _dec2, _dec3, _class, _TruncateText;
|
|
|
49
47
|
category: components
|
|
50
48
|
---
|
|
51
49
|
**/
|
|
52
|
-
let TruncateText = exports.TruncateText = (_dec = (0, _emotion.withStyle)(_styles.default, _theme.default),
|
|
50
|
+
let TruncateText = exports.TruncateText = (_dec = (0, _emotion.withStyle)(_styles.default, _theme.default), _dec(_class = (_TruncateText = class TruncateText extends _react.Component {
|
|
53
51
|
constructor(props) {
|
|
54
52
|
super(props);
|
|
55
53
|
this.ref = null;
|
|
@@ -118,6 +116,19 @@ let TruncateText = exports.TruncateText = (_dec = (0, _emotion.withStyle)(_style
|
|
|
118
116
|
this._debounced.cancel();
|
|
119
117
|
}
|
|
120
118
|
}
|
|
119
|
+
shallowCompare(obj1, obj2) {
|
|
120
|
+
const keys1 = Object.keys(obj1);
|
|
121
|
+
const keys2 = Object.keys(obj2);
|
|
122
|
+
if (keys1.length !== keys2.length) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
for (const key of keys1) {
|
|
126
|
+
if (obj1[key] !== obj2[key]) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
121
132
|
componentDidUpdate(prevProps) {
|
|
122
133
|
const _this$props2 = this.props,
|
|
123
134
|
children = _this$props2.children,
|
|
@@ -129,7 +140,9 @@ let TruncateText = exports.TruncateText = (_dec = (0, _emotion.withStyle)(_style
|
|
|
129
140
|
needsSecondRender = _this$state.needsSecondRender,
|
|
130
141
|
truncatedText = _this$state.truncatedText;
|
|
131
142
|
if (children) {
|
|
132
|
-
|
|
143
|
+
// for some reason in React 19 prevPros and this.props are a different
|
|
144
|
+
// object even if their contents are the same, so we cannot use !==
|
|
145
|
+
if (!this.shallowCompare(prevProps, this.props)) {
|
|
133
146
|
if (prevProps.children !== this.props.children) {
|
|
134
147
|
// reset internal text variable if children change
|
|
135
148
|
this.checkChildren();
|
|
@@ -224,7 +237,7 @@ let TruncateText = exports.TruncateText = (_dec = (0, _emotion.withStyle)(_style
|
|
|
224
237
|
style: {
|
|
225
238
|
width: width || void 0
|
|
226
239
|
}
|
|
227
|
-
}));
|
|
240
|
+
}, "spacer"));
|
|
228
241
|
const children = _react.Children.map(childElements, child => child);
|
|
229
242
|
return this._text.props ? (0, _safeCloneElement.safeCloneElement)(this._text, this._text.props, children) : children;
|
|
230
243
|
}
|
|
@@ -233,6 +246,7 @@ let TruncateText = exports.TruncateText = (_dec = (0, _emotion.withStyle)(_style
|
|
|
233
246
|
const truncatedElement = this.state.truncatedElement;
|
|
234
247
|
const children = this.props.children;
|
|
235
248
|
return (0, _jsxRuntime.jsxs)("span", {
|
|
249
|
+
"data-cid": "TruncateText",
|
|
236
250
|
css: (_this$props$styles3 = this.props.styles) === null || _this$props$styles3 === void 0 ? void 0 : _this$props$styles3.truncateText,
|
|
237
251
|
ref: el => {
|
|
238
252
|
this.ref = el;
|
|
@@ -245,12 +259,12 @@ let TruncateText = exports.TruncateText = (_dec = (0, _emotion.withStyle)(_style
|
|
|
245
259
|
})), truncatedElement]
|
|
246
260
|
});
|
|
247
261
|
}
|
|
248
|
-
}, _TruncateText.displayName = "TruncateText", _TruncateText.componentId = 'TruncateText', _TruncateText.allowedProps = _props.allowedProps, _TruncateText.
|
|
262
|
+
}, _TruncateText.displayName = "TruncateText", _TruncateText.componentId = 'TruncateText', _TruncateText.allowedProps = _props.allowedProps, _TruncateText.defaultProps = {
|
|
249
263
|
maxLines: 1,
|
|
250
264
|
ellipsis: '\u2026',
|
|
251
265
|
truncate: 'character',
|
|
252
266
|
position: 'end',
|
|
253
267
|
ignore: [' ', '.', ','],
|
|
254
268
|
debounce: 0
|
|
255
|
-
}, _TruncateText)) || _class)
|
|
269
|
+
}, _TruncateText)) || _class);
|
|
256
270
|
var _default = exports.default = TruncateText;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
3
|
Object.defineProperty(exports, "__esModule", {
|
|
5
4
|
value: true
|
|
6
5
|
});
|
|
7
|
-
exports.
|
|
8
|
-
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
6
|
+
exports.allowedProps = void 0;
|
|
9
7
|
/*
|
|
10
8
|
* The MIT License (MIT)
|
|
11
9
|
*
|
|
@@ -30,15 +28,4 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
|
30
28
|
* SOFTWARE.
|
|
31
29
|
*/
|
|
32
30
|
|
|
33
|
-
const propTypes = exports.propTypes = {
|
|
34
|
-
children: _propTypes.default.node.isRequired,
|
|
35
|
-
maxLines: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
|
|
36
|
-
position: _propTypes.default.oneOf(['end', 'middle']),
|
|
37
|
-
truncate: _propTypes.default.oneOf(['character', 'word']),
|
|
38
|
-
ellipsis: _propTypes.default.string,
|
|
39
|
-
ignore: _propTypes.default.arrayOf(_propTypes.default.string),
|
|
40
|
-
debounce: _propTypes.default.number,
|
|
41
|
-
onUpdate: _propTypes.default.func,
|
|
42
|
-
shouldTruncateWhenInvisible: _propTypes.default.bool
|
|
43
|
-
};
|
|
44
31
|
const allowedProps = exports.allowedProps = ['children', 'maxLines', 'position', 'truncate', 'ellipsis', 'ignore', 'debounce', 'onUpdate', 'shouldTruncateWhenInvisible'];
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var
|
|
7
|
+
var _getCSSStyleDeclaration = require("@instructure/ui-dom-utils/lib/getCSSStyleDeclaration.js");
|
|
8
8
|
/*
|
|
9
9
|
* The MIT License (MIT)
|
|
10
10
|
*
|
|
@@ -48,7 +48,7 @@ function measureText(nodes, parentNode) {
|
|
|
48
48
|
return width;
|
|
49
49
|
}
|
|
50
50
|
function measure(string, domNode) {
|
|
51
|
-
const style = (0,
|
|
51
|
+
const style = (0, _getCSSStyleDeclaration.getCSSStyleDeclaration)(domNode);
|
|
52
52
|
// we use a canvas in a doc fragment to prevent having to render the string full width in the DOM
|
|
53
53
|
const canvas = document.createElement('canvas');
|
|
54
54
|
document.createDocumentFragment().appendChild(canvas);
|
|
@@ -61,16 +61,16 @@ function measure(string, domNode) {
|
|
|
61
61
|
let width = 0;
|
|
62
62
|
// grab individual font styles
|
|
63
63
|
// some browsers don't report a value for style['font']
|
|
64
|
-
context.font = [style.fontWeight, style.fontStyle, style.fontSize, style.fontFamily].join(' ');
|
|
65
|
-
if (style.textTransform === 'uppercase') {
|
|
64
|
+
context.font = [style === null || style === void 0 ? void 0 : style.fontWeight, style === null || style === void 0 ? void 0 : style.fontStyle, style === null || style === void 0 ? void 0 : style.fontSize, style === null || style === void 0 ? void 0 : style.fontFamily].join(' ');
|
|
65
|
+
if ((style === null || style === void 0 ? void 0 : style.textTransform) === 'uppercase') {
|
|
66
66
|
text = string.toUpperCase();
|
|
67
|
-
} else if (style.textTransform === 'lowercase') {
|
|
67
|
+
} else if ((style === null || style === void 0 ? void 0 : style.textTransform) === 'lowercase') {
|
|
68
68
|
text = string.toLowerCase();
|
|
69
|
-
} else if (style.textTransform === 'capitalize') {
|
|
69
|
+
} else if ((style === null || style === void 0 ? void 0 : style.textTransform) === 'capitalize') {
|
|
70
70
|
text = string.replace(/\b\w/g, str => str.toUpperCase());
|
|
71
71
|
}
|
|
72
|
-
if (style.letterSpacing !== 'normal') {
|
|
73
|
-
letterOffset = text.length * parseInt(style.letterSpacing);
|
|
72
|
+
if ((style === null || style === void 0 ? void 0 : style.letterSpacing) !== 'normal') {
|
|
73
|
+
letterOffset = text.length * parseInt(style === null || style === void 0 ? void 0 : style.letterSpacing);
|
|
74
74
|
}
|
|
75
75
|
width = context.measureText(text).width + letterOffset;
|
|
76
76
|
// returns the max potential width of the text, assuming the text was on a single line
|
|
@@ -8,7 +8,7 @@ exports.default = void 0;
|
|
|
8
8
|
var _escapeHtml = _interopRequireDefault(require("escape-html"));
|
|
9
9
|
var _cloneArray = require("@instructure/ui-utils/lib/cloneArray.js");
|
|
10
10
|
var _console = require("@instructure/console");
|
|
11
|
-
var
|
|
11
|
+
var _getCSSStyleDeclaration = require("@instructure/ui-dom-utils/lib/getCSSStyleDeclaration.js");
|
|
12
12
|
var _getBoundingClientRect = require("@instructure/ui-dom-utils/lib/getBoundingClientRect.js");
|
|
13
13
|
var _isVisible = require("@instructure/ui-dom-utils/lib/isVisible.js");
|
|
14
14
|
var _measureText = _interopRequireDefault(require("./measureText"));
|
|
@@ -102,11 +102,14 @@ class Truncator {
|
|
|
102
102
|
if (!this._stage) {
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
|
+
const style = (0, _getCSSStyleDeclaration.getCSSStyleDeclaration)(this._parent);
|
|
106
|
+
if (!style) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
105
109
|
const _this$_options = this._options,
|
|
106
110
|
maxLines = _this$_options.maxLines,
|
|
107
111
|
truncate = _this$_options.truncate,
|
|
108
112
|
lineHeight = _this$_options.lineHeight;
|
|
109
|
-
const style = (0, _getComputedStyle.getComputedStyle)(this._parent);
|
|
110
113
|
// if no explicit lineHeight is inherited, use lineHeight multiplier for calculations
|
|
111
114
|
const actualLineHeight = style.lineHeight === 'normal' ? lineHeight * parseFloat(style.fontSize) : parseFloat(style.lineHeight);
|
|
112
115
|
const node = this._stage.firstChild.children ? this._stage.firstChild : this._stage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-truncate-text",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.0",
|
|
4
4
|
"description": "A TruncateText component made by Instructure Inc.",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -24,30 +24,28 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@babel/runtime": "^7.27.6",
|
|
27
|
-
"@instructure/console": "
|
|
28
|
-
"@instructure/debounce": "
|
|
29
|
-
"@instructure/emotion": "
|
|
30
|
-
"@instructure/shared-types": "
|
|
31
|
-
"@instructure/ui-dom-utils": "
|
|
32
|
-
"@instructure/ui-react-utils": "
|
|
33
|
-
"@instructure/ui-
|
|
34
|
-
"
|
|
35
|
-
"escape-html": "^1.0.3",
|
|
36
|
-
"prop-types": "^15.8.1"
|
|
27
|
+
"@instructure/console": "11.0.0",
|
|
28
|
+
"@instructure/debounce": "11.0.0",
|
|
29
|
+
"@instructure/emotion": "11.0.0",
|
|
30
|
+
"@instructure/shared-types": "11.0.0",
|
|
31
|
+
"@instructure/ui-dom-utils": "11.0.0",
|
|
32
|
+
"@instructure/ui-react-utils": "11.0.0",
|
|
33
|
+
"@instructure/ui-utils": "11.0.0",
|
|
34
|
+
"escape-html": "^1.0.3"
|
|
37
35
|
},
|
|
38
36
|
"devDependencies": {
|
|
39
|
-
"@instructure/ui-axe-check": "
|
|
40
|
-
"@instructure/ui-babel-preset": "
|
|
41
|
-
"@instructure/ui-color-utils": "
|
|
42
|
-
"@instructure/ui-text": "
|
|
43
|
-
"@instructure/ui-themes": "
|
|
37
|
+
"@instructure/ui-axe-check": "11.0.0",
|
|
38
|
+
"@instructure/ui-babel-preset": "11.0.0",
|
|
39
|
+
"@instructure/ui-color-utils": "11.0.0",
|
|
40
|
+
"@instructure/ui-text": "11.0.0",
|
|
41
|
+
"@instructure/ui-themes": "11.0.0",
|
|
44
42
|
"@testing-library/jest-dom": "^6.6.3",
|
|
45
|
-
"@testing-library/react": "
|
|
43
|
+
"@testing-library/react": "15.0.7",
|
|
46
44
|
"@types/escape-html": "^1.0.4",
|
|
47
45
|
"vitest": "^3.2.2"
|
|
48
46
|
},
|
|
49
47
|
"peerDependencies": {
|
|
50
|
-
"react": ">=
|
|
48
|
+
"react": ">=18 <=19"
|
|
51
49
|
},
|
|
52
50
|
"publishConfig": {
|
|
53
51
|
"access": "public"
|
|
@@ -22,25 +22,23 @@
|
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import { Children, Component } from 'react'
|
|
25
|
+
import { Children, Component, type JSX } from 'react'
|
|
26
26
|
|
|
27
27
|
import { debounce } from '@instructure/debounce'
|
|
28
28
|
import type { Debounced } from '@instructure/debounce'
|
|
29
29
|
import { canUseDOM, getBoundingClientRect } from '@instructure/ui-dom-utils'
|
|
30
30
|
import {
|
|
31
31
|
safeCloneElement,
|
|
32
|
-
ensureSingleChild
|
|
33
|
-
hack
|
|
32
|
+
ensureSingleChild
|
|
34
33
|
} from '@instructure/ui-react-utils'
|
|
35
34
|
import { logError as error } from '@instructure/console'
|
|
36
|
-
import { testable } from '@instructure/ui-testable'
|
|
37
35
|
import { withStyle } from '@instructure/emotion'
|
|
38
36
|
|
|
39
37
|
import generateStyle from './styles'
|
|
40
38
|
import generateComponentTheme from './theme'
|
|
41
39
|
|
|
42
40
|
import truncate from './utils/truncate'
|
|
43
|
-
import {
|
|
41
|
+
import { allowedProps, TruncateTextState } from './props'
|
|
44
42
|
import type { TruncateTextProps } from './props'
|
|
45
43
|
|
|
46
44
|
/**
|
|
@@ -49,13 +47,10 @@ category: components
|
|
|
49
47
|
---
|
|
50
48
|
**/
|
|
51
49
|
@withStyle(generateStyle, generateComponentTheme)
|
|
52
|
-
@testable()
|
|
53
|
-
@hack(['shouldTruncateWhenInvisible'])
|
|
54
50
|
class TruncateText extends Component<TruncateTextProps, TruncateTextState> {
|
|
55
51
|
static readonly componentId = 'TruncateText'
|
|
56
52
|
|
|
57
53
|
static allowedProps = allowedProps
|
|
58
|
-
static propTypes = propTypes
|
|
59
54
|
|
|
60
55
|
static defaultProps = {
|
|
61
56
|
maxLines: 1,
|
|
@@ -142,15 +137,29 @@ class TruncateText extends Component<TruncateTextProps, TruncateTextState> {
|
|
|
142
137
|
}
|
|
143
138
|
}
|
|
144
139
|
|
|
140
|
+
shallowCompare(obj1: any, obj2: any) {
|
|
141
|
+
const keys1 = Object.keys(obj1)
|
|
142
|
+
const keys2 = Object.keys(obj2)
|
|
143
|
+
if (keys1.length !== keys2.length) {
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
for (const key of keys1) {
|
|
147
|
+
if (obj1[key] !== obj2[key]) {
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return true
|
|
152
|
+
}
|
|
153
|
+
|
|
145
154
|
componentDidUpdate(prevProps: TruncateTextProps) {
|
|
146
155
|
const { children, onUpdate, makeStyles } = this.props
|
|
147
|
-
|
|
148
156
|
makeStyles?.()
|
|
149
|
-
|
|
150
157
|
const { isTruncated, needsSecondRender, truncatedText } = this.state
|
|
151
158
|
|
|
152
159
|
if (children) {
|
|
153
|
-
|
|
160
|
+
// for some reason in React 19 prevPros and this.props are a different
|
|
161
|
+
// object even if their contents are the same, so we cannot use !==
|
|
162
|
+
if (!this.shallowCompare(prevProps, this.props)) {
|
|
154
163
|
if (prevProps.children !== this.props.children) {
|
|
155
164
|
// reset internal text variable if children change
|
|
156
165
|
this.checkChildren()
|
|
@@ -263,6 +272,7 @@ class TruncateText extends Component<TruncateTextProps, TruncateTextState> {
|
|
|
263
272
|
<span
|
|
264
273
|
css={this.props.styles?.spacer}
|
|
265
274
|
style={{ width: width || undefined }}
|
|
275
|
+
key="spacer" // TODO this is a temp solution so tests on the CI pass... for v11_rc
|
|
266
276
|
/>
|
|
267
277
|
)
|
|
268
278
|
|
|
@@ -277,6 +287,7 @@ class TruncateText extends Component<TruncateTextProps, TruncateTextState> {
|
|
|
277
287
|
const { children } = this.props
|
|
278
288
|
return (
|
|
279
289
|
<span
|
|
290
|
+
data-cid="TruncateText"
|
|
280
291
|
css={this.props.styles?.truncateText}
|
|
281
292
|
ref={(el) => {
|
|
282
293
|
this.ref = el
|
|
@@ -23,12 +23,8 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { ReactNode } from 'react'
|
|
26
|
-
import PropTypes from 'prop-types'
|
|
27
26
|
|
|
28
|
-
import type {
|
|
29
|
-
PropValidators,
|
|
30
|
-
TruncateTextTheme
|
|
31
|
-
} from '@instructure/shared-types'
|
|
27
|
+
import type { TruncateTextTheme } from '@instructure/shared-types'
|
|
32
28
|
import type { WithStyleProps, ComponentStyle } from '@instructure/emotion'
|
|
33
29
|
|
|
34
30
|
type CleanDataOptions = {
|
|
@@ -95,19 +91,6 @@ type TruncateTextState = {
|
|
|
95
91
|
truncatedElement?: ReactNode
|
|
96
92
|
truncatedText?: string
|
|
97
93
|
}
|
|
98
|
-
|
|
99
|
-
const propTypes: PropValidators<PropKeys> = {
|
|
100
|
-
children: PropTypes.node.isRequired,
|
|
101
|
-
maxLines: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
102
|
-
position: PropTypes.oneOf(['end', 'middle']),
|
|
103
|
-
truncate: PropTypes.oneOf(['character', 'word']),
|
|
104
|
-
ellipsis: PropTypes.string,
|
|
105
|
-
ignore: PropTypes.arrayOf(PropTypes.string),
|
|
106
|
-
debounce: PropTypes.number,
|
|
107
|
-
onUpdate: PropTypes.func,
|
|
108
|
-
shouldTruncateWhenInvisible: PropTypes.bool
|
|
109
|
-
}
|
|
110
|
-
|
|
111
94
|
const allowedProps: AllowedPropKeys = [
|
|
112
95
|
'children',
|
|
113
96
|
'maxLines',
|
|
@@ -127,4 +110,4 @@ export type {
|
|
|
127
110
|
TruncateTextState,
|
|
128
111
|
TruncateTextStyle
|
|
129
112
|
}
|
|
130
|
-
export {
|
|
113
|
+
export { allowedProps }
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import {
|
|
25
|
+
import { getCSSStyleDeclaration } from '@instructure/ui-dom-utils'
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* ---
|
|
@@ -44,7 +44,7 @@ function measureText(nodes: Node[], parentNode?: Node) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function measure(string: string | null, domNode?: Node) {
|
|
47
|
-
const style =
|
|
47
|
+
const style = getCSSStyleDeclaration(domNode)
|
|
48
48
|
// we use a canvas in a doc fragment to prevent having to render the string full width in the DOM
|
|
49
49
|
const canvas = document.createElement('canvas')
|
|
50
50
|
document.createDocumentFragment().appendChild(canvas)
|
|
@@ -60,22 +60,22 @@ function measure(string: string | null, domNode?: Node) {
|
|
|
60
60
|
// grab individual font styles
|
|
61
61
|
// some browsers don't report a value for style['font']
|
|
62
62
|
context.font = [
|
|
63
|
-
style
|
|
64
|
-
style
|
|
65
|
-
style
|
|
66
|
-
style
|
|
63
|
+
style?.fontWeight,
|
|
64
|
+
style?.fontStyle,
|
|
65
|
+
style?.fontSize,
|
|
66
|
+
style?.fontFamily
|
|
67
67
|
].join(' ')
|
|
68
68
|
|
|
69
|
-
if (style
|
|
69
|
+
if (style?.textTransform === 'uppercase') {
|
|
70
70
|
text = string.toUpperCase()
|
|
71
|
-
} else if (style
|
|
71
|
+
} else if (style?.textTransform === 'lowercase') {
|
|
72
72
|
text = string.toLowerCase()
|
|
73
|
-
} else if (style
|
|
73
|
+
} else if (style?.textTransform === 'capitalize') {
|
|
74
74
|
text = string.replace(/\b\w/g, (str: string) => str.toUpperCase())
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
if (style
|
|
78
|
-
letterOffset = text.length * parseInt(style
|
|
77
|
+
if (style?.letterSpacing !== 'normal') {
|
|
78
|
+
letterOffset = text.length * parseInt(style?.letterSpacing as string)
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
width = context.measureText(text).width + letterOffset
|
|
@@ -27,7 +27,7 @@ import escapeHtml from 'escape-html'
|
|
|
27
27
|
import { cloneArray } from '@instructure/ui-utils'
|
|
28
28
|
import { logError as error } from '@instructure/console'
|
|
29
29
|
import {
|
|
30
|
-
|
|
30
|
+
getCSSStyleDeclaration,
|
|
31
31
|
getBoundingClientRect,
|
|
32
32
|
isVisible
|
|
33
33
|
} from '@instructure/ui-dom-utils'
|
|
@@ -122,8 +122,13 @@ class Truncator {
|
|
|
122
122
|
if (!this._stage) {
|
|
123
123
|
return
|
|
124
124
|
}
|
|
125
|
+
|
|
126
|
+
const style = getCSSStyleDeclaration(this._parent)
|
|
127
|
+
if (!style) {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
125
131
|
const { maxLines, truncate, lineHeight } = this._options
|
|
126
|
-
const style = getComputedStyle(this._parent)
|
|
127
132
|
// if no explicit lineHeight is inherited, use lineHeight multiplier for calculations
|
|
128
133
|
const actualLineHeight =
|
|
129
134
|
style.lineHeight === 'normal'
|