@atlaskit/renderer 144.0.2 → 144.1.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 +15 -0
- package/dist/cjs/react/index.js +8 -5
- package/dist/cjs/react/nodes/inline.js +46 -3
- package/dist/cjs/react/nodes/paragraph.js +5 -2
- package/dist/cjs/react/nodes/text-wrapper.js +2 -1
- package/dist/cjs/react/utils/render-text-segments.js +13 -3
- package/dist/cjs/react/utils/render-text.js +23 -0
- package/dist/cjs/ui/Renderer/index.js +1 -1
- package/dist/cjs/ui/annotations/draft/component.js +15 -14
- package/dist/es2019/react/index.js +8 -5
- package/dist/es2019/react/nodes/inline.js +33 -3
- package/dist/es2019/react/nodes/paragraph.js +5 -2
- package/dist/es2019/react/nodes/text-wrapper.js +2 -1
- package/dist/es2019/react/utils/render-text-segments.js +9 -2
- package/dist/es2019/react/utils/render-text.js +18 -0
- package/dist/es2019/ui/Renderer/index.js +1 -1
- package/dist/es2019/ui/annotations/draft/component.js +13 -14
- package/dist/esm/react/index.js +8 -5
- package/dist/esm/react/nodes/inline.js +45 -3
- package/dist/esm/react/nodes/paragraph.js +5 -2
- package/dist/esm/react/nodes/text-wrapper.js +2 -1
- package/dist/esm/react/utils/render-text-segments.js +13 -3
- package/dist/esm/react/utils/render-text.js +18 -0
- package/dist/esm/ui/Renderer/index.js +1 -1
- package/dist/esm/ui/annotations/draft/component.js +15 -14
- package/dist/types/react/index.d.ts +1 -0
- package/dist/types/react/nodes/inline.d.ts +16 -0
- package/dist/types/react/nodes/paragraph.d.ts +1 -1
- package/dist/types/react/nodes/text-wrapper.d.ts +1 -0
- package/dist/types/react/types.d.ts +5 -0
- package/dist/types/react/utils/render-text-segments.d.ts +1 -1
- package/dist/types/react/utils/render-text.d.ts +12 -0
- package/dist/types/ui/annotations/draft/component.d.ts +4 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @atlaskit/renderer
|
|
2
2
|
|
|
3
|
+
## 144.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`778fd05c90b9a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/778fd05c90b9a) -
|
|
8
|
+
Reduce per-text-node and per-paragraph allocations in the React serializer (plain-text fast path,
|
|
9
|
+
non-allocating empty-paragraph check) behind the new `platform_renderer_text_paragraph_fast_path`
|
|
10
|
+
experiment. No visual or DOM change.
|
|
11
|
+
|
|
12
|
+
## 144.0.3
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
|
|
3
18
|
## 144.0.2
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/cjs/react/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var _utils = require("@atlaskit/editor-common/utils");
|
|
|
16
16
|
var _validator = require("@atlaskit/editor-common/validator");
|
|
17
17
|
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
18
18
|
var _utils2 = require("@atlaskit/editor-prosemirror/utils");
|
|
19
|
+
var _expVal = require("@atlaskit/platform-feature-experiments/exp-val");
|
|
19
20
|
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
|
|
20
21
|
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
21
22
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
@@ -30,8 +31,7 @@ var _contentGetter = require("./utils/content-getter");
|
|
|
30
31
|
var _getStandaloneBackgroundColorMarks = require("./utils/getStandaloneBackgroundColorMarks");
|
|
31
32
|
var _links = require("./utils/links");
|
|
32
33
|
var _markBlockAsInline = require("./utils/markBlockAsInline");
|
|
33
|
-
var
|
|
34
|
-
var _segmentText = require("./utils/segment-text");
|
|
34
|
+
var _renderText = require("./utils/render-text");
|
|
35
35
|
var _excluded = ["key"];
|
|
36
36
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
37
37
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -66,6 +66,7 @@ var ReactSerializer = exports.default = /*#__PURE__*/function () {
|
|
|
66
66
|
(0, _defineProperty2.default)(this, "allowPlaceholderText", true);
|
|
67
67
|
(0, _defineProperty2.default)(this, "allowCustomPanels", false);
|
|
68
68
|
(0, _defineProperty2.default)(this, "surroundTextNodesWithTextWrapper", false);
|
|
69
|
+
(0, _defineProperty2.default)(this, "textFastPath", false);
|
|
69
70
|
(0, _defineProperty2.default)(this, "allowAnnotations", false);
|
|
70
71
|
(0, _defineProperty2.default)(this, "standaloneBackgroundColorMarks", []);
|
|
71
72
|
(0, _defineProperty2.default)(this, "inlinePositions", new Set());
|
|
@@ -209,6 +210,7 @@ var ReactSerializer = exports.default = /*#__PURE__*/function () {
|
|
|
209
210
|
this.allowMediaLinking = init.allowMediaLinking;
|
|
210
211
|
this.allowAnnotations = Boolean(init.allowAnnotations);
|
|
211
212
|
this.surroundTextNodesWithTextWrapper = Boolean(init.surroundTextNodesWithTextWrapper);
|
|
213
|
+
this.textFastPath = (0, _expVal.expVal)('platform_renderer_text_paragraph_fast_path', 'isEnabled', false);
|
|
212
214
|
this.media = init.media;
|
|
213
215
|
this.mentionNodeDataProvider = init.mentionNodeDataProvider;
|
|
214
216
|
this.emojiProviderLookupOrder = init.emojiProviderLookupOrder;
|
|
@@ -386,11 +388,11 @@ var ReactSerializer = exports.default = /*#__PURE__*/function () {
|
|
|
386
388
|
startPos: startPos + parentDepth,
|
|
387
389
|
endPos: endPos + parentDepth,
|
|
388
390
|
textHighlighter: this.textHighlighter,
|
|
389
|
-
marks: mark.marks
|
|
391
|
+
marks: mark.marks,
|
|
392
|
+
plainTextFastPath: this.textFastPath
|
|
390
393
|
}, mark.text);
|
|
391
394
|
}
|
|
392
|
-
|
|
393
|
-
return (0, _renderTextSegments.renderTextSegments)(segments, this.textHighlighter, mark.marks, startPos);
|
|
395
|
+
return (0, _renderText.renderText)(mark.text, this.textHighlighter, mark.marks, startPos, this.textFastPath);
|
|
394
396
|
}
|
|
395
397
|
}, {
|
|
396
398
|
key: "renderNode",
|
|
@@ -671,6 +673,7 @@ var ReactSerializer = exports.default = /*#__PURE__*/function () {
|
|
|
671
673
|
var startPos = this.startPos + path.length;
|
|
672
674
|
return _objectSpread({
|
|
673
675
|
asInline: this.inlinePositions.has(startPos) ? 'on' : undefined,
|
|
676
|
+
plainTextFastPath: this.textFastPath,
|
|
674
677
|
text: node.text,
|
|
675
678
|
providers: this.providers,
|
|
676
679
|
eventHandlers: this.eventHandlers,
|
|
@@ -5,13 +5,56 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.default = Inline;
|
|
8
|
+
exports.hasRenderableChild = hasRenderableChild;
|
|
8
9
|
var _react = _interopRequireDefault(require("react"));
|
|
10
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
11
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
12
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
13
|
+
/**
|
|
14
|
+
* Allocation-free equivalent of `React.Children.toArray(children).length > 0`.
|
|
15
|
+
*
|
|
16
|
+
* Mirrors `toArray` semantics: `null`, `undefined` and booleans are dropped; arrays are flattened;
|
|
17
|
+
* everything else (strings including `''`, numbers, elements, fragments, portals) counts as a child.
|
|
18
|
+
*/
|
|
19
|
+
function hasRenderableChild(children) {
|
|
20
|
+
if (children == null || typeof children === 'boolean') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(children)) {
|
|
24
|
+
var _iterator = _createForOfIteratorHelper(children),
|
|
25
|
+
_step;
|
|
26
|
+
try {
|
|
27
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
28
|
+
var child = _step.value;
|
|
29
|
+
if (hasRenderableChild(child)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
} catch (err) {
|
|
34
|
+
_iterator.e(err);
|
|
35
|
+
} finally {
|
|
36
|
+
_iterator.f();
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Renders inline content, substituting a non-breaking space when there is nothing to render so
|
|
45
|
+
* an empty paragraph keeps its height.
|
|
46
|
+
*
|
|
47
|
+
* `plainTextFastPath` is threaded down from `ReactSerializer`, which evaluates the experiment
|
|
48
|
+
* once per instance. This component renders once per paragraph, so it must not evaluate the
|
|
49
|
+
* experiment itself. When the prop is absent the original behaviour is used.
|
|
50
|
+
*/
|
|
9
51
|
// Ignored via go/ees005
|
|
10
52
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
53
|
function Inline(props) {
|
|
12
|
-
var children = props.children
|
|
13
|
-
|
|
14
|
-
|
|
54
|
+
var children = props.children,
|
|
55
|
+
plainTextFastPath = props.plainTextFastPath;
|
|
56
|
+
var isEmpty = plainTextFastPath ? !hasRenderableChild(children) : _react.default.Children.toArray(children).length === 0;
|
|
57
|
+
if (isEmpty) {
|
|
15
58
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, "\xA0");
|
|
16
59
|
}
|
|
17
60
|
return children;
|
|
@@ -12,7 +12,8 @@ function Paragraph(_ref) {
|
|
|
12
12
|
var children = _ref.children,
|
|
13
13
|
dataAttributes = _ref.dataAttributes,
|
|
14
14
|
localId = _ref.localId,
|
|
15
|
-
asInline = _ref.asInline
|
|
15
|
+
asInline = _ref.asInline,
|
|
16
|
+
plainTextFastPath = _ref.plainTextFastPath;
|
|
16
17
|
return (
|
|
17
18
|
/*#__PURE__*/
|
|
18
19
|
// ignore the eslint warning Text primitive does not support dataAttributes
|
|
@@ -22,6 +23,8 @@ function Paragraph(_ref) {
|
|
|
22
23
|
_react.default.createElement("p", (0, _extends2.default)({}, dataAttributes, {
|
|
23
24
|
"data-local-id": localId,
|
|
24
25
|
"data-as-inline": asInline
|
|
25
|
-
}), /*#__PURE__*/_react.default.createElement(_inline.default,
|
|
26
|
+
}), /*#__PURE__*/_react.default.createElement(_inline.default, {
|
|
27
|
+
plainTextFastPath: plainTextFastPath
|
|
28
|
+
}, children))
|
|
26
29
|
);
|
|
27
30
|
}
|
|
@@ -18,7 +18,8 @@ var TextWrapper = function TextWrapper(props) {
|
|
|
18
18
|
startPos: startPos,
|
|
19
19
|
endPos: endPos,
|
|
20
20
|
textHighlighter: props.textHighlighter,
|
|
21
|
-
marks: props.marks
|
|
21
|
+
marks: props.marks,
|
|
22
|
+
plainTextFastPath: props.plainTextFastPath
|
|
22
23
|
}, children);
|
|
23
24
|
};
|
|
24
25
|
var _default = exports.default = TextWrapper;
|
|
@@ -6,16 +6,26 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.renderTextSegments = renderTextSegments;
|
|
8
8
|
var _react = _interopRequireDefault(require("react"));
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/** Collects the mark type names a highlighter component receives for a text node. */
|
|
10
|
+
var toMarkNames = function toMarkNames(marksList) {
|
|
11
|
+
return new Set(marksList.map(function (m) {
|
|
12
12
|
return m.type.name;
|
|
13
13
|
}));
|
|
14
|
+
};
|
|
15
|
+
function renderTextSegments(segments, textHighlighter, marksList, startPos) {
|
|
16
|
+
var lazyMarks = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
|
|
17
|
+
var Component = textHighlighter === null || textHighlighter === void 0 ? void 0 : textHighlighter.component;
|
|
18
|
+
// With `lazyMarks`, the Set is only built once a highlighted segment is actually rendered, so
|
|
19
|
+
// a highlighter with no matches on this text allocates nothing extra.
|
|
20
|
+
var marks = lazyMarks ? undefined : toMarkNames(marksList);
|
|
14
21
|
function renderSegment(segment) {
|
|
15
22
|
var idx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
16
23
|
if (segment.type === 'plain' || !Component) {
|
|
17
24
|
return segment.text;
|
|
18
25
|
}
|
|
26
|
+
if (!marks) {
|
|
27
|
+
marks = toMarkNames(marksList);
|
|
28
|
+
}
|
|
19
29
|
return /*#__PURE__*/_react.default.createElement("span", {
|
|
20
30
|
"data-highlighted": true,
|
|
21
31
|
"data-vc": "highlighted-text",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.renderText = renderText;
|
|
7
|
+
var _renderTextSegments = require("./render-text-segments");
|
|
8
|
+
var _segmentText = require("./segment-text");
|
|
9
|
+
/**
|
|
10
|
+
* Renders a text node's string, applying the text highlighter when one is configured.
|
|
11
|
+
*
|
|
12
|
+
* When `fastPath` is enabled and there is no highlighter (or no text), the input string is
|
|
13
|
+
* returned directly. This is output-identical to the segmented path, which for a single plain
|
|
14
|
+
* segment also returns the bare string, but skips the per-text-node segment array/object and
|
|
15
|
+
* marks Set allocations.
|
|
16
|
+
*/
|
|
17
|
+
function renderText(text, textHighlighter, marks, startPos, fastPath) {
|
|
18
|
+
if (fastPath && (!textHighlighter || !text)) {
|
|
19
|
+
return text !== null && text !== void 0 ? text : '';
|
|
20
|
+
}
|
|
21
|
+
var segments = (0, _segmentText.segmentText)(text, textHighlighter);
|
|
22
|
+
return (0, _renderTextSegments.renderTextSegments)(segments, textHighlighter, marks, startPos, fastPath);
|
|
23
|
+
}
|
|
@@ -71,7 +71,7 @@ var DEGRADED_SEVERITY_THRESHOLD = exports.DEGRADED_SEVERITY_THRESHOLD = 3000;
|
|
|
71
71
|
var TABLE_INFO_TIMEOUT = 10000;
|
|
72
72
|
var RENDER_EVENT_SAMPLE_RATE = 0.1;
|
|
73
73
|
var packageName = "@atlaskit/renderer";
|
|
74
|
-
var packageVersion = "144.0.
|
|
74
|
+
var packageVersion = "144.0.3";
|
|
75
75
|
var setAsQueryContainerStyles = (0, _react2.css)({
|
|
76
76
|
containerName: 'ak-renderer-wrapper',
|
|
77
77
|
containerType: 'inline-size'
|
|
@@ -9,8 +9,7 @@ exports.getAnnotationIndex = exports.applyAnnotationOnText = exports.TextWithAnn
|
|
|
9
9
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
10
10
|
var _react = _interopRequireWildcard(require("react"));
|
|
11
11
|
var _react2 = require("@emotion/react");
|
|
12
|
-
var
|
|
13
|
-
var _segmentText = require("../../../react/utils/segment-text");
|
|
12
|
+
var _renderText = require("../../../react/utils/render-text");
|
|
14
13
|
var _AnnotationManagerContext = require("../contexts/AnnotationManagerContext");
|
|
15
14
|
var _AnnotationRangeContext = require("../contexts/AnnotationRangeContext");
|
|
16
15
|
var _types = require("../types");
|
|
@@ -85,10 +84,12 @@ var applyAnnotationOnText = exports.applyAnnotationOnText = function applyAnnota
|
|
|
85
84
|
shouldApplyAnnotationAt = _ref2.shouldApplyAnnotationAt,
|
|
86
85
|
draftPosition = _ref2.draftPosition,
|
|
87
86
|
textHighlighter = _ref2.textHighlighter,
|
|
88
|
-
marks = _ref2.marks
|
|
87
|
+
marks = _ref2.marks,
|
|
88
|
+
_ref2$plainTextFastPa = _ref2.plainTextFastPath,
|
|
89
|
+
plainTextFastPath = _ref2$plainTextFastPa === void 0 ? false : _ref2$plainTextFastPa;
|
|
89
90
|
var annotateIndex = getAnnotationIndex(shouldApplyAnnotationAt, texts.length);
|
|
90
91
|
return texts.map(function (value, index) {
|
|
91
|
-
var
|
|
92
|
+
var rendered = (0, _renderText.renderText)(value, textHighlighter, marks || [], draftPosition.from, plainTextFastPath);
|
|
92
93
|
if (annotateIndex === index) {
|
|
93
94
|
return (
|
|
94
95
|
// Ignored via go/ees005
|
|
@@ -96,7 +97,7 @@ var applyAnnotationOnText = exports.applyAnnotationOnText = function applyAnnota
|
|
|
96
97
|
(0, _react2.jsx)(AnnotationDraft, {
|
|
97
98
|
key: index,
|
|
98
99
|
draftPosition: draftPosition
|
|
99
|
-
},
|
|
100
|
+
}, rendered)
|
|
100
101
|
);
|
|
101
102
|
}
|
|
102
103
|
return (
|
|
@@ -104,7 +105,7 @@ var applyAnnotationOnText = exports.applyAnnotationOnText = function applyAnnota
|
|
|
104
105
|
// eslint-disable-next-line react/no-array-index-key
|
|
105
106
|
(0, _react2.jsx)(_react.default.Fragment, {
|
|
106
107
|
key: index
|
|
107
|
-
},
|
|
108
|
+
}, rendered)
|
|
108
109
|
);
|
|
109
110
|
});
|
|
110
111
|
};
|
|
@@ -113,7 +114,9 @@ var TextWithAnnotationDraft = exports.TextWithAnnotationDraft = function TextWit
|
|
|
113
114
|
endPos = _ref3.endPos,
|
|
114
115
|
children = _ref3.children,
|
|
115
116
|
textHighlighter = _ref3.textHighlighter,
|
|
116
|
-
marks = _ref3.marks
|
|
117
|
+
marks = _ref3.marks,
|
|
118
|
+
_ref3$plainTextFastPa = _ref3.plainTextFastPath,
|
|
119
|
+
plainTextFastPath = _ref3$plainTextFastPa === void 0 ? false : _ref3$plainTextFastPa;
|
|
117
120
|
var textPosition = _react.default.useMemo(function () {
|
|
118
121
|
return {
|
|
119
122
|
start: startPos,
|
|
@@ -133,28 +136,26 @@ var TextWithAnnotationDraft = exports.TextWithAnnotationDraft = function TextWit
|
|
|
133
136
|
return (0, _react2.jsx)(_react.Fragment, null, children);
|
|
134
137
|
}
|
|
135
138
|
if (shouldApplyAnnotationAt === false || !nextDraftPosition) {
|
|
136
|
-
|
|
137
|
-
return (0, _react2.jsx)(_react.Fragment, null, (0, _renderTextSegments.renderTextSegments)(segments, textHighlighter, marks || [], startPos));
|
|
139
|
+
return (0, _react2.jsx)(_react.Fragment, null, (0, _renderText.renderText)(textString, textHighlighter, marks || [], startPos, plainTextFastPath));
|
|
138
140
|
}
|
|
139
141
|
if (shouldApplyAnnotationAt === _types.InsertDraftPosition.AROUND_TEXT) {
|
|
140
|
-
var _segments = (0, _segmentText.segmentText)(textString, textHighlighter);
|
|
141
142
|
return (0, _react2.jsx)(AnnotationDraft, {
|
|
142
143
|
key: 0,
|
|
143
144
|
draftPosition: nextDraftPosition
|
|
144
|
-
}, (0,
|
|
145
|
+
}, (0, _renderText.renderText)(textString, textHighlighter, marks || [], startPos, plainTextFastPath));
|
|
145
146
|
}
|
|
146
147
|
var offsets = (0, _text.calcTextSplitOffset)(nextDraftPosition, textPosition, textString);
|
|
147
148
|
var texts = (0, _text.splitText)(textString, offsets);
|
|
148
149
|
if (!texts) {
|
|
149
|
-
|
|
150
|
-
return (0, _react2.jsx)(_react.Fragment, null, (0, _renderTextSegments.renderTextSegments)(_segments2, textHighlighter, marks || [], startPos));
|
|
150
|
+
return (0, _react2.jsx)(_react.Fragment, null, (0, _renderText.renderText)(textString, textHighlighter, marks || [], startPos, plainTextFastPath));
|
|
151
151
|
}
|
|
152
152
|
var components = applyAnnotationOnText({
|
|
153
153
|
texts: texts,
|
|
154
154
|
shouldApplyAnnotationAt: shouldApplyAnnotationAt,
|
|
155
155
|
draftPosition: nextDraftPosition,
|
|
156
156
|
textHighlighter: textHighlighter,
|
|
157
|
-
marks: marks
|
|
157
|
+
marks: marks,
|
|
158
|
+
plainTextFastPath: plainTextFastPath
|
|
158
159
|
});
|
|
159
160
|
return (0, _react2.jsx)(_react.Fragment, null, components);
|
|
160
161
|
};
|
|
@@ -5,6 +5,7 @@ import { getColumnWidths } from '@atlaskit/editor-common/utils';
|
|
|
5
5
|
import { getMarksByOrder, isSameMark } from '@atlaskit/editor-common/validator';
|
|
6
6
|
import { MarkType } from '@atlaskit/editor-prosemirror/model';
|
|
7
7
|
import { findChildrenByType } from '@atlaskit/editor-prosemirror/utils';
|
|
8
|
+
import { expVal } from '@atlaskit/platform-feature-experiments/exp-val';
|
|
8
9
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
9
10
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
10
11
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
@@ -19,8 +20,7 @@ import { createContentGetter } from './utils/content-getter';
|
|
|
19
20
|
import { getStandaloneBackgroundColorMarks } from './utils/getStandaloneBackgroundColorMarks';
|
|
20
21
|
import { isNestedHeaderLinksEnabled } from './utils/links';
|
|
21
22
|
import { markBlockAsInline } from './utils/markBlockAsInline';
|
|
22
|
-
import {
|
|
23
|
-
import { segmentText } from './utils/segment-text';
|
|
23
|
+
import { renderText } from './utils/render-text';
|
|
24
24
|
function mergeMarks(marksAndNodes) {
|
|
25
25
|
return marksAndNodes.reduce((acc, markOrNode) => {
|
|
26
26
|
const prev = acc.length && acc[acc.length - 1] || null;
|
|
@@ -50,6 +50,7 @@ export default class ReactSerializer {
|
|
|
50
50
|
_defineProperty(this, "allowPlaceholderText", true);
|
|
51
51
|
_defineProperty(this, "allowCustomPanels", false);
|
|
52
52
|
_defineProperty(this, "surroundTextNodesWithTextWrapper", false);
|
|
53
|
+
_defineProperty(this, "textFastPath", false);
|
|
53
54
|
_defineProperty(this, "allowAnnotations", false);
|
|
54
55
|
_defineProperty(this, "standaloneBackgroundColorMarks", []);
|
|
55
56
|
_defineProperty(this, "inlinePositions", new Set());
|
|
@@ -196,6 +197,7 @@ export default class ReactSerializer {
|
|
|
196
197
|
this.allowMediaLinking = init.allowMediaLinking;
|
|
197
198
|
this.allowAnnotations = Boolean(init.allowAnnotations);
|
|
198
199
|
this.surroundTextNodesWithTextWrapper = Boolean(init.surroundTextNodesWithTextWrapper);
|
|
200
|
+
this.textFastPath = expVal('platform_renderer_text_paragraph_fast_path', 'isEnabled', false);
|
|
199
201
|
this.media = init.media;
|
|
200
202
|
this.mentionNodeDataProvider = init.mentionNodeDataProvider;
|
|
201
203
|
this.emojiProviderLookupOrder = init.emojiProviderLookupOrder;
|
|
@@ -360,11 +362,11 @@ export default class ReactSerializer {
|
|
|
360
362
|
startPos: startPos + parentDepth,
|
|
361
363
|
endPos: endPos + parentDepth,
|
|
362
364
|
textHighlighter: this.textHighlighter,
|
|
363
|
-
marks: mark.marks
|
|
365
|
+
marks: mark.marks,
|
|
366
|
+
plainTextFastPath: this.textFastPath
|
|
364
367
|
}, mark.text);
|
|
365
368
|
}
|
|
366
|
-
|
|
367
|
-
return renderTextSegments(segments, this.textHighlighter, mark.marks, startPos);
|
|
369
|
+
return renderText(mark.text, this.textHighlighter, mark.marks, startPos, this.textFastPath);
|
|
368
370
|
}
|
|
369
371
|
renderNode(
|
|
370
372
|
// Ignored via go/ees005
|
|
@@ -597,6 +599,7 @@ export default class ReactSerializer {
|
|
|
597
599
|
const startPos = this.startPos + path.length;
|
|
598
600
|
return {
|
|
599
601
|
asInline: this.inlinePositions.has(startPos) ? 'on' : undefined,
|
|
602
|
+
plainTextFastPath: this.textFastPath,
|
|
600
603
|
text: node.text,
|
|
601
604
|
providers: this.providers,
|
|
602
605
|
eventHandlers: this.eventHandlers,
|
|
@@ -1,13 +1,43 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Allocation-free equivalent of `React.Children.toArray(children).length > 0`.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors `toArray` semantics: `null`, `undefined` and booleans are dropped; arrays are flattened;
|
|
7
|
+
* everything else (strings including `''`, numbers, elements, fragments, portals) counts as a child.
|
|
8
|
+
*/
|
|
9
|
+
export function hasRenderableChild(children) {
|
|
10
|
+
if (children == null || typeof children === 'boolean') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(children)) {
|
|
14
|
+
for (const child of children) {
|
|
15
|
+
if (hasRenderableChild(child)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Renders inline content, substituting a non-breaking space when there is nothing to render so
|
|
26
|
+
* an empty paragraph keeps its height.
|
|
27
|
+
*
|
|
28
|
+
* `plainTextFastPath` is threaded down from `ReactSerializer`, which evaluates the experiment
|
|
29
|
+
* once per instance. This component renders once per paragraph, so it must not evaluate the
|
|
30
|
+
* experiment itself. When the prop is absent the original behaviour is used.
|
|
31
|
+
*/
|
|
3
32
|
// Ignored via go/ees005
|
|
4
33
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
34
|
export default function Inline(props) {
|
|
6
35
|
const {
|
|
7
|
-
children
|
|
36
|
+
children,
|
|
37
|
+
plainTextFastPath
|
|
8
38
|
} = props;
|
|
9
|
-
const
|
|
10
|
-
if (
|
|
39
|
+
const isEmpty = plainTextFastPath ? !hasRenderableChild(children) : React.Children.toArray(children).length === 0;
|
|
40
|
+
if (isEmpty) {
|
|
11
41
|
return /*#__PURE__*/React.createElement(React.Fragment, null, "\xA0");
|
|
12
42
|
}
|
|
13
43
|
return children;
|
|
@@ -5,7 +5,8 @@ export default function Paragraph({
|
|
|
5
5
|
children,
|
|
6
6
|
dataAttributes,
|
|
7
7
|
localId,
|
|
8
|
-
asInline
|
|
8
|
+
asInline,
|
|
9
|
+
plainTextFastPath
|
|
9
10
|
}) {
|
|
10
11
|
return (
|
|
11
12
|
/*#__PURE__*/
|
|
@@ -16,6 +17,8 @@ export default function Paragraph({
|
|
|
16
17
|
React.createElement("p", _extends({}, dataAttributes, {
|
|
17
18
|
"data-local-id": localId,
|
|
18
19
|
"data-as-inline": asInline
|
|
19
|
-
}), /*#__PURE__*/React.createElement(Inline,
|
|
20
|
+
}), /*#__PURE__*/React.createElement(Inline, {
|
|
21
|
+
plainTextFastPath: plainTextFastPath
|
|
22
|
+
}, children))
|
|
20
23
|
);
|
|
21
24
|
}
|
|
@@ -15,7 +15,8 @@ const TextWrapper = props => {
|
|
|
15
15
|
startPos: startPos,
|
|
16
16
|
endPos: endPos,
|
|
17
17
|
textHighlighter: props.textHighlighter,
|
|
18
|
-
marks: props.marks
|
|
18
|
+
marks: props.marks,
|
|
19
|
+
plainTextFastPath: props.plainTextFastPath
|
|
19
20
|
}, children);
|
|
20
21
|
};
|
|
21
22
|
export default TextWrapper;
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
/** Collects the mark type names a highlighter component receives for a text node. */
|
|
3
|
+
const toMarkNames = marksList => new Set(marksList.map(m => m.type.name));
|
|
4
|
+
export function renderTextSegments(segments, textHighlighter, marksList, startPos, lazyMarks = false) {
|
|
3
5
|
const Component = textHighlighter === null || textHighlighter === void 0 ? void 0 : textHighlighter.component;
|
|
4
|
-
|
|
6
|
+
// With `lazyMarks`, the Set is only built once a highlighted segment is actually rendered, so
|
|
7
|
+
// a highlighter with no matches on this text allocates nothing extra.
|
|
8
|
+
let marks = lazyMarks ? undefined : toMarkNames(marksList);
|
|
5
9
|
function renderSegment(segment, idx = 0) {
|
|
6
10
|
if (segment.type === 'plain' || !Component) {
|
|
7
11
|
return segment.text;
|
|
8
12
|
}
|
|
13
|
+
if (!marks) {
|
|
14
|
+
marks = toMarkNames(marksList);
|
|
15
|
+
}
|
|
9
16
|
return /*#__PURE__*/React.createElement("span", {
|
|
10
17
|
"data-highlighted": true,
|
|
11
18
|
"data-vc": "highlighted-text",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { renderTextSegments } from './render-text-segments';
|
|
2
|
+
import { segmentText } from './segment-text';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renders a text node's string, applying the text highlighter when one is configured.
|
|
6
|
+
*
|
|
7
|
+
* When `fastPath` is enabled and there is no highlighter (or no text), the input string is
|
|
8
|
+
* returned directly. This is output-identical to the segmented path, which for a single plain
|
|
9
|
+
* segment also returns the bare string, but skips the per-text-node segment array/object and
|
|
10
|
+
* marks Set allocations.
|
|
11
|
+
*/
|
|
12
|
+
export function renderText(text, textHighlighter, marks, startPos, fastPath) {
|
|
13
|
+
if (fastPath && (!textHighlighter || !text)) {
|
|
14
|
+
return text !== null && text !== void 0 ? text : '';
|
|
15
|
+
}
|
|
16
|
+
const segments = segmentText(text, textHighlighter);
|
|
17
|
+
return renderTextSegments(segments, textHighlighter, marks, startPos, fastPath);
|
|
18
|
+
}
|
|
@@ -56,7 +56,7 @@ export const DEGRADED_SEVERITY_THRESHOLD = 3000;
|
|
|
56
56
|
const TABLE_INFO_TIMEOUT = 10000;
|
|
57
57
|
const RENDER_EVENT_SAMPLE_RATE = 0.1;
|
|
58
58
|
const packageName = "@atlaskit/renderer";
|
|
59
|
-
const packageVersion = "144.0.
|
|
59
|
+
const packageVersion = "144.0.3";
|
|
60
60
|
const setAsQueryContainerStyles = css({
|
|
61
61
|
containerName: 'ak-renderer-wrapper',
|
|
62
62
|
containerType: 'inline-size'
|
|
@@ -8,8 +8,7 @@ import React, { Fragment, useCallback } from 'react';
|
|
|
8
8
|
// eslint-disable-next-line @typescript-eslint/consistent-type-imports, @atlaskit/ui-styling-standard/use-compiled -- emotion jsx pragma; go/DSP-18766
|
|
9
9
|
import { css, jsx } from '@emotion/react'; // oxlint-ignore @typescript-eslint/consistent-type-imports -- classic @jsx jsx factory + jsx.JSX.Element types
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
import { segmentText } from '../../../react/utils/segment-text';
|
|
11
|
+
import { renderText } from '../../../react/utils/render-text';
|
|
13
12
|
import { useAnnotationManagerDispatch } from '../contexts/AnnotationManagerContext';
|
|
14
13
|
import { useAnnotationRangeState } from '../contexts/AnnotationRangeContext';
|
|
15
14
|
import { InsertDraftPosition } from '../types';
|
|
@@ -78,11 +77,12 @@ export const applyAnnotationOnText = ({
|
|
|
78
77
|
shouldApplyAnnotationAt,
|
|
79
78
|
draftPosition,
|
|
80
79
|
textHighlighter,
|
|
81
|
-
marks
|
|
80
|
+
marks,
|
|
81
|
+
plainTextFastPath = false
|
|
82
82
|
}) => {
|
|
83
83
|
const annotateIndex = getAnnotationIndex(shouldApplyAnnotationAt, texts.length);
|
|
84
84
|
return texts.map((value, index) => {
|
|
85
|
-
const
|
|
85
|
+
const rendered = renderText(value, textHighlighter, marks || [], draftPosition.from, plainTextFastPath);
|
|
86
86
|
if (annotateIndex === index) {
|
|
87
87
|
return (
|
|
88
88
|
// Ignored via go/ees005
|
|
@@ -90,7 +90,7 @@ export const applyAnnotationOnText = ({
|
|
|
90
90
|
jsx(AnnotationDraft, {
|
|
91
91
|
key: index,
|
|
92
92
|
draftPosition: draftPosition
|
|
93
|
-
},
|
|
93
|
+
}, rendered)
|
|
94
94
|
);
|
|
95
95
|
}
|
|
96
96
|
return (
|
|
@@ -98,7 +98,7 @@ export const applyAnnotationOnText = ({
|
|
|
98
98
|
// eslint-disable-next-line react/no-array-index-key
|
|
99
99
|
jsx(React.Fragment, {
|
|
100
100
|
key: index
|
|
101
|
-
},
|
|
101
|
+
}, rendered)
|
|
102
102
|
);
|
|
103
103
|
});
|
|
104
104
|
};
|
|
@@ -107,7 +107,8 @@ export const TextWithAnnotationDraft = ({
|
|
|
107
107
|
endPos,
|
|
108
108
|
children,
|
|
109
109
|
textHighlighter,
|
|
110
|
-
marks
|
|
110
|
+
marks,
|
|
111
|
+
plainTextFastPath = false
|
|
111
112
|
}) => {
|
|
112
113
|
const textPosition = React.useMemo(() => ({
|
|
113
114
|
start: startPos,
|
|
@@ -127,28 +128,26 @@ export const TextWithAnnotationDraft = ({
|
|
|
127
128
|
return jsx(Fragment, null, children);
|
|
128
129
|
}
|
|
129
130
|
if (shouldApplyAnnotationAt === false || !nextDraftPosition) {
|
|
130
|
-
|
|
131
|
-
return jsx(Fragment, null, renderTextSegments(segments, textHighlighter, marks || [], startPos));
|
|
131
|
+
return jsx(Fragment, null, renderText(textString, textHighlighter, marks || [], startPos, plainTextFastPath));
|
|
132
132
|
}
|
|
133
133
|
if (shouldApplyAnnotationAt === InsertDraftPosition.AROUND_TEXT) {
|
|
134
|
-
const segments = segmentText(textString, textHighlighter);
|
|
135
134
|
return jsx(AnnotationDraft, {
|
|
136
135
|
key: 0,
|
|
137
136
|
draftPosition: nextDraftPosition
|
|
138
|
-
},
|
|
137
|
+
}, renderText(textString, textHighlighter, marks || [], startPos, plainTextFastPath));
|
|
139
138
|
}
|
|
140
139
|
const offsets = calcTextSplitOffset(nextDraftPosition, textPosition, textString);
|
|
141
140
|
const texts = splitText(textString, offsets);
|
|
142
141
|
if (!texts) {
|
|
143
|
-
|
|
144
|
-
return jsx(Fragment, null, renderTextSegments(segments, textHighlighter, marks || [], startPos));
|
|
142
|
+
return jsx(Fragment, null, renderText(textString, textHighlighter, marks || [], startPos, plainTextFastPath));
|
|
145
143
|
}
|
|
146
144
|
const components = applyAnnotationOnText({
|
|
147
145
|
texts,
|
|
148
146
|
shouldApplyAnnotationAt,
|
|
149
147
|
draftPosition: nextDraftPosition,
|
|
150
148
|
textHighlighter,
|
|
151
|
-
marks
|
|
149
|
+
marks,
|
|
150
|
+
plainTextFastPath
|
|
152
151
|
});
|
|
153
152
|
return jsx(Fragment, null, components);
|
|
154
153
|
};
|
package/dist/esm/react/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { getColumnWidths } from '@atlaskit/editor-common/utils';
|
|
|
12
12
|
import { getMarksByOrder, isSameMark } from '@atlaskit/editor-common/validator';
|
|
13
13
|
import { MarkType } from '@atlaskit/editor-prosemirror/model';
|
|
14
14
|
import { findChildrenByType } from '@atlaskit/editor-prosemirror/utils';
|
|
15
|
+
import { expVal } from '@atlaskit/platform-feature-experiments/exp-val';
|
|
15
16
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
16
17
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
17
18
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
@@ -26,8 +27,7 @@ import { createContentGetter } from './utils/content-getter';
|
|
|
26
27
|
import { getStandaloneBackgroundColorMarks } from './utils/getStandaloneBackgroundColorMarks';
|
|
27
28
|
import { isNestedHeaderLinksEnabled } from './utils/links';
|
|
28
29
|
import { markBlockAsInline } from './utils/markBlockAsInline';
|
|
29
|
-
import {
|
|
30
|
-
import { segmentText } from './utils/segment-text';
|
|
30
|
+
import { renderText } from './utils/render-text';
|
|
31
31
|
function mergeMarks(marksAndNodes) {
|
|
32
32
|
return marksAndNodes.reduce(function (acc, markOrNode) {
|
|
33
33
|
var prev = acc.length && acc[acc.length - 1] || null;
|
|
@@ -59,6 +59,7 @@ var ReactSerializer = /*#__PURE__*/function () {
|
|
|
59
59
|
_defineProperty(this, "allowPlaceholderText", true);
|
|
60
60
|
_defineProperty(this, "allowCustomPanels", false);
|
|
61
61
|
_defineProperty(this, "surroundTextNodesWithTextWrapper", false);
|
|
62
|
+
_defineProperty(this, "textFastPath", false);
|
|
62
63
|
_defineProperty(this, "allowAnnotations", false);
|
|
63
64
|
_defineProperty(this, "standaloneBackgroundColorMarks", []);
|
|
64
65
|
_defineProperty(this, "inlinePositions", new Set());
|
|
@@ -202,6 +203,7 @@ var ReactSerializer = /*#__PURE__*/function () {
|
|
|
202
203
|
this.allowMediaLinking = init.allowMediaLinking;
|
|
203
204
|
this.allowAnnotations = Boolean(init.allowAnnotations);
|
|
204
205
|
this.surroundTextNodesWithTextWrapper = Boolean(init.surroundTextNodesWithTextWrapper);
|
|
206
|
+
this.textFastPath = expVal('platform_renderer_text_paragraph_fast_path', 'isEnabled', false);
|
|
205
207
|
this.media = init.media;
|
|
206
208
|
this.mentionNodeDataProvider = init.mentionNodeDataProvider;
|
|
207
209
|
this.emojiProviderLookupOrder = init.emojiProviderLookupOrder;
|
|
@@ -379,11 +381,11 @@ var ReactSerializer = /*#__PURE__*/function () {
|
|
|
379
381
|
startPos: startPos + parentDepth,
|
|
380
382
|
endPos: endPos + parentDepth,
|
|
381
383
|
textHighlighter: this.textHighlighter,
|
|
382
|
-
marks: mark.marks
|
|
384
|
+
marks: mark.marks,
|
|
385
|
+
plainTextFastPath: this.textFastPath
|
|
383
386
|
}, mark.text);
|
|
384
387
|
}
|
|
385
|
-
|
|
386
|
-
return renderTextSegments(segments, this.textHighlighter, mark.marks, startPos);
|
|
388
|
+
return renderText(mark.text, this.textHighlighter, mark.marks, startPos, this.textFastPath);
|
|
387
389
|
}
|
|
388
390
|
}, {
|
|
389
391
|
key: "renderNode",
|
|
@@ -664,6 +666,7 @@ var ReactSerializer = /*#__PURE__*/function () {
|
|
|
664
666
|
var startPos = this.startPos + path.length;
|
|
665
667
|
return _objectSpread({
|
|
666
668
|
asInline: this.inlinePositions.has(startPos) ? 'on' : undefined,
|
|
669
|
+
plainTextFastPath: this.textFastPath,
|
|
667
670
|
text: node.text,
|
|
668
671
|
providers: this.providers,
|
|
669
672
|
eventHandlers: this.eventHandlers,
|
|
@@ -1,11 +1,53 @@
|
|
|
1
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
2
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
3
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
1
4
|
import React from 'react';
|
|
2
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Allocation-free equivalent of `React.Children.toArray(children).length > 0`.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors `toArray` semantics: `null`, `undefined` and booleans are dropped; arrays are flattened;
|
|
10
|
+
* everything else (strings including `''`, numbers, elements, fragments, portals) counts as a child.
|
|
11
|
+
*/
|
|
12
|
+
export function hasRenderableChild(children) {
|
|
13
|
+
if (children == null || typeof children === 'boolean') {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(children)) {
|
|
17
|
+
var _iterator = _createForOfIteratorHelper(children),
|
|
18
|
+
_step;
|
|
19
|
+
try {
|
|
20
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
21
|
+
var child = _step.value;
|
|
22
|
+
if (hasRenderableChild(child)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} catch (err) {
|
|
27
|
+
_iterator.e(err);
|
|
28
|
+
} finally {
|
|
29
|
+
_iterator.f();
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Renders inline content, substituting a non-breaking space when there is nothing to render so
|
|
38
|
+
* an empty paragraph keeps its height.
|
|
39
|
+
*
|
|
40
|
+
* `plainTextFastPath` is threaded down from `ReactSerializer`, which evaluates the experiment
|
|
41
|
+
* once per instance. This component renders once per paragraph, so it must not evaluate the
|
|
42
|
+
* experiment itself. When the prop is absent the original behaviour is used.
|
|
43
|
+
*/
|
|
3
44
|
// Ignored via go/ees005
|
|
4
45
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
46
|
export default function Inline(props) {
|
|
6
|
-
var children = props.children
|
|
7
|
-
|
|
8
|
-
|
|
47
|
+
var children = props.children,
|
|
48
|
+
plainTextFastPath = props.plainTextFastPath;
|
|
49
|
+
var isEmpty = plainTextFastPath ? !hasRenderableChild(children) : React.Children.toArray(children).length === 0;
|
|
50
|
+
if (isEmpty) {
|
|
9
51
|
return /*#__PURE__*/React.createElement(React.Fragment, null, "\xA0");
|
|
10
52
|
}
|
|
11
53
|
return children;
|
|
@@ -5,7 +5,8 @@ export default function Paragraph(_ref) {
|
|
|
5
5
|
var children = _ref.children,
|
|
6
6
|
dataAttributes = _ref.dataAttributes,
|
|
7
7
|
localId = _ref.localId,
|
|
8
|
-
asInline = _ref.asInline
|
|
8
|
+
asInline = _ref.asInline,
|
|
9
|
+
plainTextFastPath = _ref.plainTextFastPath;
|
|
9
10
|
return (
|
|
10
11
|
/*#__PURE__*/
|
|
11
12
|
// ignore the eslint warning Text primitive does not support dataAttributes
|
|
@@ -15,6 +16,8 @@ export default function Paragraph(_ref) {
|
|
|
15
16
|
React.createElement("p", _extends({}, dataAttributes, {
|
|
16
17
|
"data-local-id": localId,
|
|
17
18
|
"data-as-inline": asInline
|
|
18
|
-
}), /*#__PURE__*/React.createElement(Inline,
|
|
19
|
+
}), /*#__PURE__*/React.createElement(Inline, {
|
|
20
|
+
plainTextFastPath: plainTextFastPath
|
|
21
|
+
}, children))
|
|
19
22
|
);
|
|
20
23
|
}
|
|
@@ -11,7 +11,8 @@ var TextWrapper = function TextWrapper(props) {
|
|
|
11
11
|
startPos: startPos,
|
|
12
12
|
endPos: endPos,
|
|
13
13
|
textHighlighter: props.textHighlighter,
|
|
14
|
-
marks: props.marks
|
|
14
|
+
marks: props.marks,
|
|
15
|
+
plainTextFastPath: props.plainTextFastPath
|
|
15
16
|
}, children);
|
|
16
17
|
};
|
|
17
18
|
export default TextWrapper;
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/** Collects the mark type names a highlighter component receives for a text node. */
|
|
3
|
+
var toMarkNames = function toMarkNames(marksList) {
|
|
4
|
+
return new Set(marksList.map(function (m) {
|
|
5
5
|
return m.type.name;
|
|
6
6
|
}));
|
|
7
|
+
};
|
|
8
|
+
export function renderTextSegments(segments, textHighlighter, marksList, startPos) {
|
|
9
|
+
var lazyMarks = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
|
|
10
|
+
var Component = textHighlighter === null || textHighlighter === void 0 ? void 0 : textHighlighter.component;
|
|
11
|
+
// With `lazyMarks`, the Set is only built once a highlighted segment is actually rendered, so
|
|
12
|
+
// a highlighter with no matches on this text allocates nothing extra.
|
|
13
|
+
var marks = lazyMarks ? undefined : toMarkNames(marksList);
|
|
7
14
|
function renderSegment(segment) {
|
|
8
15
|
var idx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
9
16
|
if (segment.type === 'plain' || !Component) {
|
|
10
17
|
return segment.text;
|
|
11
18
|
}
|
|
19
|
+
if (!marks) {
|
|
20
|
+
marks = toMarkNames(marksList);
|
|
21
|
+
}
|
|
12
22
|
return /*#__PURE__*/React.createElement("span", {
|
|
13
23
|
"data-highlighted": true,
|
|
14
24
|
"data-vc": "highlighted-text",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { renderTextSegments } from './render-text-segments';
|
|
2
|
+
import { segmentText } from './segment-text';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renders a text node's string, applying the text highlighter when one is configured.
|
|
6
|
+
*
|
|
7
|
+
* When `fastPath` is enabled and there is no highlighter (or no text), the input string is
|
|
8
|
+
* returned directly. This is output-identical to the segmented path, which for a single plain
|
|
9
|
+
* segment also returns the bare string, but skips the per-text-node segment array/object and
|
|
10
|
+
* marks Set allocations.
|
|
11
|
+
*/
|
|
12
|
+
export function renderText(text, textHighlighter, marks, startPos, fastPath) {
|
|
13
|
+
if (fastPath && (!textHighlighter || !text)) {
|
|
14
|
+
return text !== null && text !== void 0 ? text : '';
|
|
15
|
+
}
|
|
16
|
+
var segments = segmentText(text, textHighlighter);
|
|
17
|
+
return renderTextSegments(segments, textHighlighter, marks, startPos, fastPath);
|
|
18
|
+
}
|
|
@@ -61,7 +61,7 @@ export var DEGRADED_SEVERITY_THRESHOLD = 3000;
|
|
|
61
61
|
var TABLE_INFO_TIMEOUT = 10000;
|
|
62
62
|
var RENDER_EVENT_SAMPLE_RATE = 0.1;
|
|
63
63
|
var packageName = "@atlaskit/renderer";
|
|
64
|
-
var packageVersion = "144.0.
|
|
64
|
+
var packageVersion = "144.0.3";
|
|
65
65
|
var setAsQueryContainerStyles = css({
|
|
66
66
|
containerName: 'ak-renderer-wrapper',
|
|
67
67
|
containerType: 'inline-size'
|
|
@@ -8,8 +8,7 @@ import React, { Fragment, useCallback } from 'react';
|
|
|
8
8
|
// eslint-disable-next-line @typescript-eslint/consistent-type-imports, @atlaskit/ui-styling-standard/use-compiled -- emotion jsx pragma; go/DSP-18766
|
|
9
9
|
import { css, jsx } from '@emotion/react'; // oxlint-ignore @typescript-eslint/consistent-type-imports -- classic @jsx jsx factory + jsx.JSX.Element types
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
import { segmentText } from '../../../react/utils/segment-text';
|
|
11
|
+
import { renderText } from '../../../react/utils/render-text';
|
|
13
12
|
import { useAnnotationManagerDispatch } from '../contexts/AnnotationManagerContext';
|
|
14
13
|
import { useAnnotationRangeState } from '../contexts/AnnotationRangeContext';
|
|
15
14
|
import { InsertDraftPosition } from '../types';
|
|
@@ -76,10 +75,12 @@ export var applyAnnotationOnText = function applyAnnotationOnText(_ref2) {
|
|
|
76
75
|
shouldApplyAnnotationAt = _ref2.shouldApplyAnnotationAt,
|
|
77
76
|
draftPosition = _ref2.draftPosition,
|
|
78
77
|
textHighlighter = _ref2.textHighlighter,
|
|
79
|
-
marks = _ref2.marks
|
|
78
|
+
marks = _ref2.marks,
|
|
79
|
+
_ref2$plainTextFastPa = _ref2.plainTextFastPath,
|
|
80
|
+
plainTextFastPath = _ref2$plainTextFastPa === void 0 ? false : _ref2$plainTextFastPa;
|
|
80
81
|
var annotateIndex = getAnnotationIndex(shouldApplyAnnotationAt, texts.length);
|
|
81
82
|
return texts.map(function (value, index) {
|
|
82
|
-
var
|
|
83
|
+
var rendered = renderText(value, textHighlighter, marks || [], draftPosition.from, plainTextFastPath);
|
|
83
84
|
if (annotateIndex === index) {
|
|
84
85
|
return (
|
|
85
86
|
// Ignored via go/ees005
|
|
@@ -87,7 +88,7 @@ export var applyAnnotationOnText = function applyAnnotationOnText(_ref2) {
|
|
|
87
88
|
jsx(AnnotationDraft, {
|
|
88
89
|
key: index,
|
|
89
90
|
draftPosition: draftPosition
|
|
90
|
-
},
|
|
91
|
+
}, rendered)
|
|
91
92
|
);
|
|
92
93
|
}
|
|
93
94
|
return (
|
|
@@ -95,7 +96,7 @@ export var applyAnnotationOnText = function applyAnnotationOnText(_ref2) {
|
|
|
95
96
|
// eslint-disable-next-line react/no-array-index-key
|
|
96
97
|
jsx(React.Fragment, {
|
|
97
98
|
key: index
|
|
98
|
-
},
|
|
99
|
+
}, rendered)
|
|
99
100
|
);
|
|
100
101
|
});
|
|
101
102
|
};
|
|
@@ -104,7 +105,9 @@ export var TextWithAnnotationDraft = function TextWithAnnotationDraft(_ref3) {
|
|
|
104
105
|
endPos = _ref3.endPos,
|
|
105
106
|
children = _ref3.children,
|
|
106
107
|
textHighlighter = _ref3.textHighlighter,
|
|
107
|
-
marks = _ref3.marks
|
|
108
|
+
marks = _ref3.marks,
|
|
109
|
+
_ref3$plainTextFastPa = _ref3.plainTextFastPath,
|
|
110
|
+
plainTextFastPath = _ref3$plainTextFastPa === void 0 ? false : _ref3$plainTextFastPa;
|
|
108
111
|
var textPosition = React.useMemo(function () {
|
|
109
112
|
return {
|
|
110
113
|
start: startPos,
|
|
@@ -124,28 +127,26 @@ export var TextWithAnnotationDraft = function TextWithAnnotationDraft(_ref3) {
|
|
|
124
127
|
return jsx(Fragment, null, children);
|
|
125
128
|
}
|
|
126
129
|
if (shouldApplyAnnotationAt === false || !nextDraftPosition) {
|
|
127
|
-
|
|
128
|
-
return jsx(Fragment, null, renderTextSegments(segments, textHighlighter, marks || [], startPos));
|
|
130
|
+
return jsx(Fragment, null, renderText(textString, textHighlighter, marks || [], startPos, plainTextFastPath));
|
|
129
131
|
}
|
|
130
132
|
if (shouldApplyAnnotationAt === InsertDraftPosition.AROUND_TEXT) {
|
|
131
|
-
var _segments = segmentText(textString, textHighlighter);
|
|
132
133
|
return jsx(AnnotationDraft, {
|
|
133
134
|
key: 0,
|
|
134
135
|
draftPosition: nextDraftPosition
|
|
135
|
-
},
|
|
136
|
+
}, renderText(textString, textHighlighter, marks || [], startPos, plainTextFastPath));
|
|
136
137
|
}
|
|
137
138
|
var offsets = calcTextSplitOffset(nextDraftPosition, textPosition, textString);
|
|
138
139
|
var texts = splitText(textString, offsets);
|
|
139
140
|
if (!texts) {
|
|
140
|
-
|
|
141
|
-
return jsx(Fragment, null, renderTextSegments(_segments2, textHighlighter, marks || [], startPos));
|
|
141
|
+
return jsx(Fragment, null, renderText(textString, textHighlighter, marks || [], startPos, plainTextFastPath));
|
|
142
142
|
}
|
|
143
143
|
var components = applyAnnotationOnText({
|
|
144
144
|
texts: texts,
|
|
145
145
|
shouldApplyAnnotationAt: shouldApplyAnnotationAt,
|
|
146
146
|
draftPosition: nextDraftPosition,
|
|
147
147
|
textHighlighter: textHighlighter,
|
|
148
|
-
marks: marks
|
|
148
|
+
marks: marks,
|
|
149
|
+
plainTextFastPath: plainTextFastPath
|
|
149
150
|
});
|
|
150
151
|
return jsx(Fragment, null, components);
|
|
151
152
|
};
|
|
@@ -104,6 +104,7 @@ export default class ReactSerializer implements Serializer<JSX.Element> {
|
|
|
104
104
|
private initStartPos;
|
|
105
105
|
private startPos;
|
|
106
106
|
private surroundTextNodesWithTextWrapper;
|
|
107
|
+
private textFastPath;
|
|
107
108
|
private media?;
|
|
108
109
|
private mentionNodeDataProvider?;
|
|
109
110
|
private emojiProviderLookupOrder?;
|
|
@@ -1 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Allocation-free equivalent of `React.Children.toArray(children).length > 0`.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `toArray` semantics: `null`, `undefined` and booleans are dropped; arrays are flattened;
|
|
6
|
+
* everything else (strings including `''`, numbers, elements, fragments, portals) counts as a child.
|
|
7
|
+
*/
|
|
8
|
+
export declare function hasRenderableChild(children: React.ReactNode): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Renders inline content, substituting a non-breaking space when there is nothing to render so
|
|
11
|
+
* an empty paragraph keeps its height.
|
|
12
|
+
*
|
|
13
|
+
* `plainTextFastPath` is threaded down from `ReactSerializer`, which evaluates the experiment
|
|
14
|
+
* once per instance. This component renders once per paragraph, so it must not evaluate the
|
|
15
|
+
* experiment itself. When the prop is absent the original behaviour is used.
|
|
16
|
+
*/
|
|
1
17
|
export default function Inline(props: any): any;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { NodeProps } from '../types';
|
|
3
|
-
export default function Paragraph({ children, dataAttributes, localId, asInline, }: NodeProps): React.JSX.Element;
|
|
3
|
+
export default function Paragraph({ children, dataAttributes, localId, asInline, plainTextFastPath, }: NodeProps): React.JSX.Element;
|
|
@@ -45,6 +45,11 @@ export interface NodeMeta {
|
|
|
45
45
|
hideExtensionKeysWhilePending?: string[];
|
|
46
46
|
marks: PMNode['marks'];
|
|
47
47
|
nodeType: NodeType['name'];
|
|
48
|
+
/**
|
|
49
|
+
* Whether the text and paragraph fast path experiment is enabled for this render.
|
|
50
|
+
* Evaluated once per `ReactSerializer` instance rather than per node.
|
|
51
|
+
*/
|
|
52
|
+
plainTextFastPath?: boolean;
|
|
48
53
|
portal?: HTMLElement | undefined;
|
|
49
54
|
providers?: ProviderFactory | undefined;
|
|
50
55
|
rendererAppearance?: RendererAppearance;
|
|
@@ -2,4 +2,4 @@ import React from 'react';
|
|
|
2
2
|
import type { Mark } from '@atlaskit/editor-prosemirror/model';
|
|
3
3
|
import type { TextHighlighter } from '../types';
|
|
4
4
|
import type { TextSegment } from './segment-text';
|
|
5
|
-
export declare function renderTextSegments(segments: Array<TextSegment>, textHighlighter: TextHighlighter | undefined, marksList: readonly Mark[], startPos: number): string | React.JSX.Element;
|
|
5
|
+
export declare function renderTextSegments(segments: Array<TextSegment>, textHighlighter: TextHighlighter | undefined, marksList: readonly Mark[], startPos: number, lazyMarks?: boolean): string | React.JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { Mark } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import type { TextHighlighter } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Renders a text node's string, applying the text highlighter when one is configured.
|
|
6
|
+
*
|
|
7
|
+
* When `fastPath` is enabled and there is no highlighter (or no text), the input string is
|
|
8
|
+
* returned directly. This is output-identical to the segmented path, which for a single plain
|
|
9
|
+
* segment also returns the bare string, but skips the per-text-node segment array/object and
|
|
10
|
+
* marks Set allocations.
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderText(text: string | undefined, textHighlighter: TextHighlighter | undefined, marks: readonly Mark[], startPos: number, fastPath: boolean): string | React.JSX.Element;
|
|
@@ -14,17 +14,19 @@ export declare const AnnotationDraft: ({ draftPosition, children, }: React.Props
|
|
|
14
14
|
type ApplyAnnotationsProps = {
|
|
15
15
|
draftPosition: Position;
|
|
16
16
|
marks?: readonly Mark[];
|
|
17
|
+
plainTextFastPath?: boolean;
|
|
17
18
|
shouldApplyAnnotationAt: InsertDraftPosition.INSIDE | InsertDraftPosition.START | InsertDraftPosition.END;
|
|
18
19
|
textHighlighter?: TextHighlighter;
|
|
19
20
|
texts: string[];
|
|
20
21
|
};
|
|
21
22
|
export declare const getAnnotationIndex: (annotationPosition: InsertDraftPosition, fragmentCount: number) => number;
|
|
22
|
-
export declare const applyAnnotationOnText: ({ texts, shouldApplyAnnotationAt, draftPosition, textHighlighter, marks, }: ApplyAnnotationsProps) => JSX.Element[];
|
|
23
|
+
export declare const applyAnnotationOnText: ({ texts, shouldApplyAnnotationAt, draftPosition, textHighlighter, marks, plainTextFastPath, }: ApplyAnnotationsProps) => JSX.Element[];
|
|
23
24
|
type Props = React.PropsWithChildren<{
|
|
24
25
|
endPos: number;
|
|
25
26
|
marks?: readonly Mark[];
|
|
27
|
+
plainTextFastPath?: boolean;
|
|
26
28
|
startPos: number;
|
|
27
29
|
textHighlighter?: TextHighlighter;
|
|
28
30
|
}>;
|
|
29
|
-
export declare const TextWithAnnotationDraft: ({ startPos, endPos, children, textHighlighter, marks, }: Props) => jsx.JSX.Element;
|
|
31
|
+
export declare const TextWithAnnotationDraft: ({ startPos, endPos, children, textHighlighter, marks, plainTextFastPath, }: Props) => jsx.JSX.Element;
|
|
30
32
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/renderer",
|
|
3
|
-
"version": "144.0
|
|
3
|
+
"version": "144.1.0",
|
|
4
4
|
"description": "Renderer component",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@atlaskit/status": "^5.12.0",
|
|
74
74
|
"@atlaskit/task-decision": "^21.10.0",
|
|
75
75
|
"@atlaskit/theme": "^28.2.0",
|
|
76
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
76
|
+
"@atlaskit/tmp-editor-statsig": "^208.0.0",
|
|
77
77
|
"@atlaskit/tokens": "^20.0.0",
|
|
78
78
|
"@atlaskit/tooltip": "^24.3.0",
|
|
79
79
|
"@atlaskit/visually-hidden": "^4.4.0",
|