@atlaskit/renderer 137.1.6 → 137.1.7
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 +35 -0
- package/dist/cjs/react/index.js +8 -9
- package/dist/cjs/ui/Expand.js +60 -33
- package/dist/cjs/ui/Renderer/index.js +1 -1
- package/dist/cjs/ui/expand-body.js +122 -0
- package/dist/cjs/ui/utils/expand-search-text.js +72 -32
- package/dist/es2019/react/index.js +8 -9
- package/dist/es2019/ui/Expand.js +58 -31
- package/dist/es2019/ui/Renderer/index.js +1 -1
- package/dist/es2019/ui/expand-body.js +107 -0
- package/dist/es2019/ui/utils/expand-search-text.js +67 -31
- package/dist/esm/react/index.js +8 -9
- package/dist/esm/ui/Expand.js +60 -33
- package/dist/esm/ui/Renderer/index.js +1 -1
- package/dist/esm/ui/expand-body.js +114 -0
- package/dist/esm/ui/utils/expand-search-text.js +71 -31
- package/dist/types/ui/Expand.d.ts +5 -4
- package/dist/types/ui/expand-body.d.ts +64 -0
- package/dist/types/ui/utils/expand-search-text.d.ts +18 -7
- package/package.json +4 -7
package/dist/esm/ui/Expand.js
CHANGED
|
@@ -14,8 +14,7 @@ import { akEditorLineHeight, akEditorSwoopCubicBezier, akLayoutGutterOffset } fr
|
|
|
14
14
|
import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
|
|
15
15
|
import Tooltip from '@atlaskit/tooltip/Tooltip';
|
|
16
16
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
17
|
-
import {
|
|
18
|
-
import { getExpandSearchText } from './utils/expand-search-text';
|
|
17
|
+
import { ExpandBodyProvider, useExpandBody } from './expand-body';
|
|
19
18
|
import _uniqueId from 'lodash/uniqueId';
|
|
20
19
|
import { injectIntl } from 'react-intl';
|
|
21
20
|
import { MODE, PLATFORM } from '../analytics/events';
|
|
@@ -140,12 +139,18 @@ var contentContainerStylesNotExpanded = css({
|
|
|
140
139
|
// hidden="until-found" attribute on the outer container to hide content.
|
|
141
140
|
// We remove height:0/overflow:hidden/clip from the inner wrapper so the
|
|
142
141
|
// browser can actually search through the content.
|
|
142
|
+
//
|
|
143
|
+
// `user-select: none` must NOT be set here, unlike the variant above. WebKit scrolls to a
|
|
144
|
+
// find-in-page match by selecting it, so unselectable text is matched but never revealed: the
|
|
145
|
+
// reader is told there is a hit, the `hidden` attribute stays put, and the expand never opens.
|
|
146
|
+
// Chrome does not need a selection, so it hid the problem. Nothing is lost by leaving it out —
|
|
147
|
+
// content-visibility:hidden already makes a skipped subtree unselectable. It is only needed in the
|
|
148
|
+
// other variant, where the content is merely clipped and would otherwise be selectable.
|
|
143
149
|
var contentContainerStylesNotExpandedBrowserFind = css({
|
|
144
150
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors
|
|
145
151
|
'.expand-content-wrapper, .nestedExpand-content-wrapper': {
|
|
146
152
|
width: '100%',
|
|
147
|
-
display: 'block'
|
|
148
|
-
userSelect: 'none'
|
|
153
|
+
display: 'block'
|
|
149
154
|
}
|
|
150
155
|
});
|
|
151
156
|
var clearNextSiblingMarginTopStyle = css({
|
|
@@ -213,6 +218,7 @@ function Expand(_ref) {
|
|
|
213
218
|
nestedHeaderIds = _ref.nestedHeaderIds,
|
|
214
219
|
rendererContentMode = _ref.rendererContentMode,
|
|
215
220
|
node = _ref.node;
|
|
221
|
+
var ancestorBody = useExpandBody();
|
|
216
222
|
var _useState = useState(false),
|
|
217
223
|
_useState2 = _slicedToArray(_useState, 2),
|
|
218
224
|
expanded = _useState2[0],
|
|
@@ -222,12 +228,11 @@ function Expand(_ref) {
|
|
|
222
228
|
focused = _useState4[0],
|
|
223
229
|
setFocused = _useState4[1];
|
|
224
230
|
/**
|
|
225
|
-
* PGXT-9021:
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* client's first render agree and hydration cannot desync.
|
|
231
|
+
* PGXT-9021: once opened, the body stays rendered even if the reader closes the expand again.
|
|
232
|
+
* Throwing it away would re-run every macro and Forge fetch inside it on the next open, which is
|
|
233
|
+
* slower and visibly reloads content the reader has already seen. We only wanted to save work on
|
|
234
|
+
* the first page load, and by now that has happened. Starts false on both the server and the
|
|
235
|
+
* client, so hydration cannot disagree.
|
|
231
236
|
*/
|
|
232
237
|
var _useState5 = useState(false),
|
|
233
238
|
_useState6 = _slicedToArray(_useState5, 2),
|
|
@@ -251,15 +256,21 @@ function Expand(_ref) {
|
|
|
251
256
|
setExpanded(true);
|
|
252
257
|
setHasBeenExpanded(true);
|
|
253
258
|
}, []);
|
|
259
|
+
// The browser found the text inside this expand. Opening this one is not enough: if it sits
|
|
260
|
+
// inside other expands, those have to open too or the reader still cannot see it. Each expand
|
|
261
|
+
// asks the one above it, so a single match opens the whole chain and nothing else.
|
|
262
|
+
var openWithAncestors = useCallback(function () {
|
|
263
|
+
openBody();
|
|
264
|
+
ancestorBody === null || ancestorBody === void 0 || ancestorBody.openWithAncestors();
|
|
265
|
+
}, [ancestorBody, openBody]);
|
|
254
266
|
|
|
255
|
-
// Feature-detect hidden="until-found" support via the beforematch event.
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
var _useState7 = useState(false),
|
|
267
|
+
// Feature-detect hidden="until-found" support via the beforematch event. Chrome 102+,
|
|
268
|
+
// Firefox 139+ and Safari 26.2+ all have it; in a browser without it, hidden="until-found" is
|
|
269
|
+
// treated as plain boolean hidden (display:none), which would break the expand entirely.
|
|
270
|
+
// Starts as `undefined`, meaning "we do not know yet", and is only answered in an effect. We
|
|
271
|
+
// cannot check during render: the server has no browser to check, so it would say no there and
|
|
272
|
+
// yes on the client, and hydration would disagree.
|
|
273
|
+
var _useState7 = useState(undefined),
|
|
263
274
|
_useState8 = _slicedToArray(_useState7, 2),
|
|
264
275
|
supportsHiddenUntilFound = _useState8[0],
|
|
265
276
|
setSupportsHiddenUntilFound = _useState8[1];
|
|
@@ -276,9 +287,9 @@ function Expand(_ref) {
|
|
|
276
287
|
// because visibility:hidden blocks browser find. On expanded, we restore visibility to visible.
|
|
277
288
|
//
|
|
278
289
|
// Only applied when the browser supports hidden="until-found" (detected via onbeforematch).
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
//
|
|
290
|
+
// Without it we skip this entirely and fall back to the normal CSS hiding
|
|
291
|
+
// (visibility:hidden + height:0), which doesn't support find-in-page but still works
|
|
292
|
+
// correctly for expand/collapse.
|
|
282
293
|
useEffect(function () {
|
|
283
294
|
var contentContainer = contentContainerRef.current;
|
|
284
295
|
var contentWrapper = contentWrapperRef.current;
|
|
@@ -287,6 +298,13 @@ function Expand(_ref) {
|
|
|
287
298
|
}
|
|
288
299
|
if (supportsHiddenUntilFound && isExperimentEnabled('platform_editor_close_expand_find') && !expanded) {
|
|
289
300
|
contentWrapper.setAttribute('hidden', 'until-found');
|
|
301
|
+
// Products ship a CSS reset with `[hidden] { display: none }` — Confluence does. That is an
|
|
302
|
+
// author rule, so it beats the UA stylesheet's
|
|
303
|
+
// `[hidden="until-found"] { content-visibility: hidden }` and takes the content out of the
|
|
304
|
+
// page altogether, where find cannot reach it. Setting display next to the attribute keeps
|
|
305
|
+
// the two together, rather than relying on a class selector elsewhere out-weighing a reset
|
|
306
|
+
// we do not own.
|
|
307
|
+
contentWrapper.style.display = 'block';
|
|
290
308
|
// Override the CSS visibility:hidden from contentContainerStyles — hidden="until-found"
|
|
291
309
|
// now handles hiding via content-visibility:hidden, which allows browser find to index
|
|
292
310
|
// the content. We use 'visible' (not '') because '' only clears the inline style but
|
|
@@ -295,6 +313,7 @@ function Expand(_ref) {
|
|
|
295
313
|
contentWrapper.style.visibility = 'visible';
|
|
296
314
|
} else {
|
|
297
315
|
contentWrapper.removeAttribute('hidden');
|
|
316
|
+
contentWrapper.style.display = '';
|
|
298
317
|
contentContainer === null || contentContainer === void 0 || contentContainer.style.removeProperty('visibility');
|
|
299
318
|
contentWrapper.style.visibility = '';
|
|
300
319
|
}
|
|
@@ -306,22 +325,28 @@ function Expand(_ref) {
|
|
|
306
325
|
var contentWrapper = contentWrapperRef.current;
|
|
307
326
|
var unbindWrapperBeforeMatch = contentWrapper && supportsHiddenUntilFound ? bind(contentWrapper, {
|
|
308
327
|
type: 'beforematch',
|
|
309
|
-
listener:
|
|
328
|
+
listener: openWithAncestors
|
|
310
329
|
}) : undefined;
|
|
311
330
|
return function () {
|
|
312
331
|
unbindWrapperBeforeMatch === null || unbindWrapperBeforeMatch === void 0 || unbindWrapperBeforeMatch();
|
|
313
332
|
};
|
|
314
|
-
}, [expanded,
|
|
333
|
+
}, [expanded, openWithAncestors, supportsHiddenUntilFound]);
|
|
315
334
|
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
var
|
|
335
|
+
// While this is false, the blocks of the body show their text instead of rendering. It is true
|
|
336
|
+
// when:
|
|
337
|
+
// - the expand is open, or has been opened before, so the reader wants to see the content;
|
|
338
|
+
// - no node was given, so this expand never opted into lazy loading;
|
|
339
|
+
// - the experiment is off;
|
|
340
|
+
// - the browser has no hidden="until-found", so the text would not be findable and standing it in
|
|
341
|
+
// would gain nothing. While we still do not know, we assume the browser has it — assuming the
|
|
342
|
+
// opposite would render the whole body on the first paint and lose the saving entirely.
|
|
343
|
+
var revealed = expanded || hasBeenExpanded || !node || supportsHiddenUntilFound === false || !isExperimentEnabled('platform_editor_defer_collapsed_expand_body');
|
|
344
|
+
var expandBody = useMemo(function () {
|
|
345
|
+
return {
|
|
346
|
+
revealed: revealed,
|
|
347
|
+
openWithAncestors: openWithAncestors
|
|
348
|
+
};
|
|
349
|
+
}, [revealed, openWithAncestors]);
|
|
325
350
|
return jsx(Container, {
|
|
326
351
|
"data-testid": "expand-container-".concat(nodeType, "-").concat(id),
|
|
327
352
|
"data-node-type": nodeType,
|
|
@@ -386,7 +411,9 @@ function Expand(_ref) {
|
|
|
386
411
|
ref: contentWrapperRef
|
|
387
412
|
}, jsx(WidthProvider, null, jsx("div", {
|
|
388
413
|
css: clearNextSiblingMarginTopStyle
|
|
389
|
-
}),
|
|
414
|
+
}), jsx(ExpandBodyProvider, {
|
|
415
|
+
value: expandBody
|
|
416
|
+
}, children)))));
|
|
390
417
|
}
|
|
391
418
|
|
|
392
419
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
@@ -65,7 +65,7 @@ export var DEGRADED_SEVERITY_THRESHOLD = 3000;
|
|
|
65
65
|
var TABLE_INFO_TIMEOUT = 10000;
|
|
66
66
|
var RENDER_EVENT_SAMPLE_RATE = 0.1;
|
|
67
67
|
var packageName = "@atlaskit/renderer";
|
|
68
|
-
var packageVersion = "137.1.
|
|
68
|
+
var packageVersion = "137.1.6";
|
|
69
69
|
var setAsQueryContainerStyles = css({
|
|
70
70
|
containerName: 'ak-renderer-wrapper',
|
|
71
71
|
containerType: 'inline-size'
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React, { createContext, isValidElement, useContext } from 'react';
|
|
2
|
+
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
3
|
+
import { BLOCK_SEPARATOR, getBlockSearchText, holdsRevealableContent, isExpandNode } from './utils/expand-search-text';
|
|
4
|
+
var ExpandBodyContext = /*#__PURE__*/createContext(null);
|
|
5
|
+
export var ExpandBodyProvider = ExpandBodyContext.Provider;
|
|
6
|
+
export var useExpandBody = function useExpandBody() {
|
|
7
|
+
return useContext(ExpandBodyContext);
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* One or more neighbouring blocks of an expand's body. Shows their text until the expand is
|
|
11
|
+
* opened, then renders them. Falls back to rendering if there is no text, or if there is no
|
|
12
|
+
* expand above it.
|
|
13
|
+
*
|
|
14
|
+
* The text is rendered as-is, with no element around it. Nothing reads it but browser find,
|
|
15
|
+
* which only needs the characters to be in the DOM.
|
|
16
|
+
*/
|
|
17
|
+
export var ExpandBodyBlock = function ExpandBodyBlock(_ref) {
|
|
18
|
+
var children = _ref.children,
|
|
19
|
+
searchText = _ref.searchText;
|
|
20
|
+
var body = useExpandBody();
|
|
21
|
+
if (searchText === undefined || body === null || body.revealed) {
|
|
22
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, children);
|
|
23
|
+
}
|
|
24
|
+
return searchText;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Called for every node the serializer renders. If the node is a block of an expand's body,
|
|
29
|
+
* wraps it so it can show its text instead of rendering while that expand is collapsed.
|
|
30
|
+
* Everything else is returned untouched.
|
|
31
|
+
*
|
|
32
|
+
* A block holding an expand of its own is also returned untouched, so a table with a nested
|
|
33
|
+
* expand in it, or an extension holding stashed ADF, renders even while collapsed. That
|
|
34
|
+
* expand needs an element of its own, because the element browser find reveals is the only
|
|
35
|
+
* way we learn the match was inside it rather than higher up. We give up the saving on those
|
|
36
|
+
* blocks so the reader searches once instead of once per level.
|
|
37
|
+
*
|
|
38
|
+
* `ancestors` is the node's ancestor chain, nearest last.
|
|
39
|
+
*/
|
|
40
|
+
export var withExpandBodyBlock = function withExpandBodyBlock(node, ancestors, index, serialized) {
|
|
41
|
+
var parent = ancestors[ancestors.length - 1];
|
|
42
|
+
if (!parent || !isExpandNode(parent)) {
|
|
43
|
+
return serialized;
|
|
44
|
+
}
|
|
45
|
+
if (!isExperimentEnabled('platform_editor_defer_collapsed_expand_body') || holdsRevealableContent(node)) {
|
|
46
|
+
return serialized;
|
|
47
|
+
}
|
|
48
|
+
var searchText = getBlockSearchText(node);
|
|
49
|
+
if (searchText === undefined) {
|
|
50
|
+
return serialized;
|
|
51
|
+
}
|
|
52
|
+
return /*#__PURE__*/React.createElement(ExpandBodyBlock, {
|
|
53
|
+
key: "expand-body-block-".concat(index),
|
|
54
|
+
searchText: searchText
|
|
55
|
+
}, serialized);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** Whatever the serializer produced for one child: an element, a string, an array of either. */
|
|
59
|
+
|
|
60
|
+
var searchTextOf = function searchTextOf(child) {
|
|
61
|
+
return /*#__PURE__*/isValidElement(child) && child.type === ExpandBodyBlock ? child.props.searchText : undefined;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Joins neighbouring blocks that are showing text into one, so a run of them is a single string in
|
|
66
|
+
* the DOM rather than one per block. A body of twenty paragraphs becomes one text node instead of
|
|
67
|
+
* twenty.
|
|
68
|
+
*
|
|
69
|
+
* A block that is being rendered — a nested expand, say — ends the run, because the text either
|
|
70
|
+
* side of it has to stay either side of it.
|
|
71
|
+
*/
|
|
72
|
+
export var mergeExpandBodyText = function mergeExpandBodyText(children) {
|
|
73
|
+
// Called for every fragment in the document, and only an expand's body has anything to merge, so
|
|
74
|
+
// leave the array alone unless two neighbours are actually showing text.
|
|
75
|
+
var hasRun = children.some(function (child, index) {
|
|
76
|
+
return index > 0 && searchTextOf(child) !== undefined && searchTextOf(children[index - 1]) !== undefined;
|
|
77
|
+
});
|
|
78
|
+
if (!hasRun) {
|
|
79
|
+
return children;
|
|
80
|
+
}
|
|
81
|
+
var merged = [];
|
|
82
|
+
var run = [];
|
|
83
|
+
var endRun = function endRun() {
|
|
84
|
+
if (run.length === 0) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (run.length === 1) {
|
|
88
|
+
merged.push(run[0]);
|
|
89
|
+
} else {
|
|
90
|
+
var _key;
|
|
91
|
+
var text = run.map(searchTextOf).filter(Boolean).join(BLOCK_SEPARATOR);
|
|
92
|
+
// The blocks themselves, not the wrappers around them: nesting the wrappers would show
|
|
93
|
+
// each block's text again inside the joined run.
|
|
94
|
+
var blocks = run.map(function (child) {
|
|
95
|
+
return child.props.children;
|
|
96
|
+
});
|
|
97
|
+
merged.push( /*#__PURE__*/React.createElement(ExpandBodyBlock, {
|
|
98
|
+
key: (_key = run[0].key) !== null && _key !== void 0 ? _key : undefined,
|
|
99
|
+
searchText: text
|
|
100
|
+
}, blocks));
|
|
101
|
+
}
|
|
102
|
+
run = [];
|
|
103
|
+
};
|
|
104
|
+
children.forEach(function (child) {
|
|
105
|
+
if (searchTextOf(child) === undefined) {
|
|
106
|
+
endRun();
|
|
107
|
+
merged.push(child);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
run.push(child);
|
|
111
|
+
});
|
|
112
|
+
endRun();
|
|
113
|
+
return merged;
|
|
114
|
+
};
|
|
@@ -6,22 +6,29 @@ import { findChildrenByMark } from '@atlaskit/editor-prosemirror/utils';
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
/** Separates text from adjacent blocks so phrases cannot fuse across block boundaries. */
|
|
9
|
-
var BLOCK_SEPARATOR = ' ';
|
|
9
|
+
export var BLOCK_SEPARATOR = ' ';
|
|
10
10
|
var asText = function asText(value) {
|
|
11
11
|
return typeof value === 'string' ? value : '';
|
|
12
12
|
};
|
|
13
|
+
var nestedContentOf = function nestedContentOf(attrs) {
|
|
14
|
+
var _attrs$parameters;
|
|
15
|
+
return attrs === null || attrs === void 0 || (_attrs$parameters = attrs.parameters) === null || _attrs$parameters === void 0 ? void 0 : _attrs$parameters.nestedContent;
|
|
16
|
+
};
|
|
17
|
+
export var isExpandNode = function isExpandNode(node) {
|
|
18
|
+
return node.type.name === 'expand' || node.type.name === 'nestedExpand';
|
|
19
|
+
};
|
|
13
20
|
|
|
14
21
|
/**
|
|
15
|
-
*
|
|
22
|
+
* Reads the text out of raw ADF JSON.
|
|
16
23
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
24
|
+
* Some extensions keep a whole ADF subtree in `attrs.parameters.nestedContent` rather than in their
|
|
25
|
+
* node content. Confluence does this when the authored nesting is deeper than the schema allows —
|
|
26
|
+
* an expand more than two collapsible layers deep, for example. To ProseMirror the extension is a
|
|
27
|
+
* leaf, so walking the node tree never sees that subtree, even though a nested renderer puts it on
|
|
28
|
+
* the page. Recurses, because stashed content often stashes more content of its own.
|
|
22
29
|
*/
|
|
23
30
|
var _adfEntityText = function adfEntityText(entity) {
|
|
24
|
-
var _entity$
|
|
31
|
+
var _entity$content;
|
|
25
32
|
if (!entity) {
|
|
26
33
|
return '';
|
|
27
34
|
}
|
|
@@ -29,13 +36,13 @@ var _adfEntityText = function adfEntityText(entity) {
|
|
|
29
36
|
if (entity.type === 'text') {
|
|
30
37
|
parts.push(asText(entity.text));
|
|
31
38
|
}
|
|
32
|
-
var nestedContent = (
|
|
39
|
+
var nestedContent = nestedContentOf(entity.attrs);
|
|
33
40
|
if (nestedContent) {
|
|
34
41
|
// Prefer the real subtree over `attrs.text`, which is only a placeholder label for it.
|
|
35
42
|
parts.push(_adfEntityText(nestedContent));
|
|
36
43
|
} else {
|
|
37
|
-
var _entity$
|
|
38
|
-
parts.push(asText((_entity$
|
|
44
|
+
var _entity$attrs;
|
|
45
|
+
parts.push(asText((_entity$attrs = entity.attrs) === null || _entity$attrs === void 0 ? void 0 : _entity$attrs.text));
|
|
39
46
|
}
|
|
40
47
|
(_entity$content = entity.content) === null || _entity$content === void 0 || _entity$content.forEach(function (child) {
|
|
41
48
|
return parts.push(_adfEntityText(child));
|
|
@@ -44,16 +51,48 @@ var _adfEntityText = function adfEntityText(entity) {
|
|
|
44
51
|
};
|
|
45
52
|
|
|
46
53
|
/**
|
|
47
|
-
* `textBetween`
|
|
48
|
-
*
|
|
54
|
+
* `textBetween` skips every leaf node unless its spec declares `leafText`, and no ADF node
|
|
55
|
+
* does — so mentions, emoji, dates and statuses would all be lost. They keep their visible
|
|
56
|
+
* text in attrs, so read it from there.
|
|
49
57
|
*/
|
|
50
58
|
var leafText = function leafText(leaf) {
|
|
51
|
-
var _leaf$attrs
|
|
52
|
-
var nestedContent = (
|
|
59
|
+
var _leaf$attrs;
|
|
60
|
+
var nestedContent = nestedContentOf(leaf.attrs);
|
|
53
61
|
if (nestedContent) {
|
|
54
62
|
return _adfEntityText(nestedContent);
|
|
55
63
|
}
|
|
56
|
-
return asText((_leaf$
|
|
64
|
+
return asText((_leaf$attrs = leaf.attrs) === null || _leaf$attrs === void 0 ? void 0 : _leaf$attrs.text);
|
|
65
|
+
};
|
|
66
|
+
var isRevealable = function isRevealable(node) {
|
|
67
|
+
return isExpandNode(node) || Boolean(nestedContentOf(node.attrs));
|
|
68
|
+
};
|
|
69
|
+
var revealableCache = new WeakMap();
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Whether this block holds something that can be opened on its own: an expand, or an extension
|
|
73
|
+
* holding stashed ADF that a nested renderer will mount expands from.
|
|
74
|
+
*
|
|
75
|
+
* Those need a real element even while the expand around them is collapsed. The element is how we
|
|
76
|
+
* know find matched inside them rather than higher up: the beforematch event says nothing about
|
|
77
|
+
* where the match was, so the element the browser chose to reveal is the only clue.
|
|
78
|
+
*/
|
|
79
|
+
export var holdsRevealableContent = function holdsRevealableContent(node) {
|
|
80
|
+
var cached = revealableCache.get(node);
|
|
81
|
+
if (cached !== undefined) {
|
|
82
|
+
return cached;
|
|
83
|
+
}
|
|
84
|
+
var found = isRevealable(node);
|
|
85
|
+
if (!found) {
|
|
86
|
+
node.descendants(function (descendant) {
|
|
87
|
+
if (found) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
found = isRevealable(descendant);
|
|
91
|
+
return !found;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
revealableCache.set(node, found);
|
|
95
|
+
return found;
|
|
57
96
|
};
|
|
58
97
|
var hasInlineComment = function hasInlineComment(node) {
|
|
59
98
|
return findChildrenByMark(node, node.type.schema.marks.annotation, true).some(function (annotation) {
|
|
@@ -63,30 +102,31 @@ var hasInlineComment = function hasInlineComment(node) {
|
|
|
63
102
|
});
|
|
64
103
|
};
|
|
65
104
|
|
|
66
|
-
/** `null` is a cached "no
|
|
105
|
+
/** `null` is a cached "no search text for this node", as distinct from `undefined` for a miss. */
|
|
67
106
|
var searchTextCache = new WeakMap();
|
|
68
107
|
|
|
69
108
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
109
|
+
* The text shown in place of one block of a collapsed expand's body, so browser find can still
|
|
110
|
+
* reach the content without the block being rendered.
|
|
72
111
|
*
|
|
73
|
-
* Cached on the node. ProseMirror nodes
|
|
74
|
-
*
|
|
75
|
-
* serializer rebuilds that invalidate the module-level `memoizeOne` caches.
|
|
112
|
+
* Cached on the node. ProseMirror nodes never change, and the serializer reuses the same instances
|
|
113
|
+
* across renders, so the text only has to be worked out once per block.
|
|
76
114
|
*
|
|
77
|
-
* @returns the
|
|
115
|
+
* @returns the text, or `undefined` if this block has to be rendered instead.
|
|
78
116
|
*/
|
|
79
|
-
export var
|
|
117
|
+
export var getBlockSearchText = function getBlockSearchText(node) {
|
|
80
118
|
var cached = searchTextCache.get(node);
|
|
81
119
|
if (cached === undefined) {
|
|
82
120
|
cached = hasInlineComment(node) ?
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
// No text, which makes the block render instead. Comment navigation looks through the DOM
|
|
122
|
+
// for the commented text and scrolls to it, so that text has to be a real element —
|
|
123
|
+
// plain text carries no comment marks and cannot stand in for it. Without this, the
|
|
124
|
+
// arrow keys silently skip every comment inside a collapsed expand. We give up the
|
|
125
|
+
// saving for this block on purpose.
|
|
126
|
+
null :
|
|
127
|
+
// A leaf block is not inside its own range, so `textBetween` would return nothing and its
|
|
128
|
+
// text would be lost — a macro sitting straight in an expand body, for instance.
|
|
129
|
+
node.isLeaf ? leafText(node) : node.textBetween(0, node.content.size, BLOCK_SEPARATOR, leafText);
|
|
90
130
|
searchTextCache.set(node, cached);
|
|
91
131
|
}
|
|
92
132
|
return cached !== null && cached !== void 0 ? cached : undefined;
|
|
@@ -9,10 +9,11 @@ export interface ExpandProps {
|
|
|
9
9
|
localId?: string;
|
|
10
10
|
nestedHeaderIds?: Array<string>;
|
|
11
11
|
/**
|
|
12
|
-
* This expand's ProseMirror node.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* This expand's ProseMirror node. Passing it turns on lazy body loading: until the expand is
|
|
13
|
+
* first opened, the blocks the serializer wrapped show their text rather than rendering. After
|
|
14
|
+
* that they stay rendered, even if the reader closes the expand again (PGXT-9021). Leave it out
|
|
15
|
+
* to always render the body. Browsers without `hidden="until-found"` render it in full shortly
|
|
16
|
+
* after the page loads, since the text would not be findable there anyway.
|
|
16
17
|
*/
|
|
17
18
|
node?: PMNode;
|
|
18
19
|
nodeType: 'expand' | 'nestedExpand';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Provider, ReactNode } from 'react';
|
|
2
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
type ExpandBody = {
|
|
4
|
+
/**
|
|
5
|
+
* Opens this expand, then asks the expand above it to do the same.
|
|
6
|
+
*
|
|
7
|
+
* Browser find reveals the element holding the text it matched, and that element belongs to the
|
|
8
|
+
* expand the text is really in — even a deeply nested one, because an expand only ever replaces
|
|
9
|
+
* its own blocks with text, never a nested expand. So the expands to open are just that expand's
|
|
10
|
+
* ancestors.
|
|
11
|
+
*/
|
|
12
|
+
openWithAncestors: () => void;
|
|
13
|
+
/**
|
|
14
|
+
* True once this expand has been opened. While it is false, the blocks of the body show
|
|
15
|
+
* their text instead of rendering.
|
|
16
|
+
*
|
|
17
|
+
* Each block reads this and decides for itself. The expand does not swap its body out,
|
|
18
|
+
* because that would throw away and rebuild everything inside it — including a nested
|
|
19
|
+
* expand find had just opened, and any nested renderer that had already loaded.
|
|
20
|
+
*/
|
|
21
|
+
revealed: boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare const ExpandBodyProvider: Provider<ExpandBody | null>;
|
|
24
|
+
export declare const useExpandBody: () => ExpandBody | null;
|
|
25
|
+
type ExpandBodyBlockProps = {
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
/** The block's text, or `undefined` if it must always be rendered. */
|
|
28
|
+
searchText?: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* One or more neighbouring blocks of an expand's body. Shows their text until the expand is
|
|
32
|
+
* opened, then renders them. Falls back to rendering if there is no text, or if there is no
|
|
33
|
+
* expand above it.
|
|
34
|
+
*
|
|
35
|
+
* The text is rendered as-is, with no element around it. Nothing reads it but browser find,
|
|
36
|
+
* which only needs the characters to be in the DOM.
|
|
37
|
+
*/
|
|
38
|
+
export declare const ExpandBodyBlock: ({ children, searchText }: ExpandBodyBlockProps) => ReactNode;
|
|
39
|
+
/**
|
|
40
|
+
* Called for every node the serializer renders. If the node is a block of an expand's body,
|
|
41
|
+
* wraps it so it can show its text instead of rendering while that expand is collapsed.
|
|
42
|
+
* Everything else is returned untouched.
|
|
43
|
+
*
|
|
44
|
+
* A block holding an expand of its own is also returned untouched, so a table with a nested
|
|
45
|
+
* expand in it, or an extension holding stashed ADF, renders even while collapsed. That
|
|
46
|
+
* expand needs an element of its own, because the element browser find reveals is the only
|
|
47
|
+
* way we learn the match was inside it rather than higher up. We give up the saving on those
|
|
48
|
+
* blocks so the reader searches once instead of once per level.
|
|
49
|
+
*
|
|
50
|
+
* `ancestors` is the node's ancestor chain, nearest last.
|
|
51
|
+
*/
|
|
52
|
+
export declare const withExpandBodyBlock: (node: PMNode, ancestors: readonly PMNode[], index: number, serialized: JSX.Element | null) => JSX.Element | null;
|
|
53
|
+
/** Whatever the serializer produced for one child: an element, a string, an array of either. */
|
|
54
|
+
type SerializedChild = ReactNode;
|
|
55
|
+
/**
|
|
56
|
+
* Joins neighbouring blocks that are showing text into one, so a run of them is a single string in
|
|
57
|
+
* the DOM rather than one per block. A body of twenty paragraphs becomes one text node instead of
|
|
58
|
+
* twenty.
|
|
59
|
+
*
|
|
60
|
+
* A block that is being rendered — a nested expand, say — ends the run, because the text either
|
|
61
|
+
* side of it has to stay either side of it.
|
|
62
|
+
*/
|
|
63
|
+
export declare const mergeExpandBodyText: (children: SerializedChild[]) => SerializedChild[];
|
|
64
|
+
export {};
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
/** Separates text from adjacent blocks so phrases cannot fuse across block boundaries. */
|
|
3
|
+
export declare const BLOCK_SEPARATOR = " ";
|
|
4
|
+
export declare const isExpandNode: (node: PMNode) => boolean;
|
|
2
5
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
6
|
+
* Whether this block holds something that can be opened on its own: an expand, or an extension
|
|
7
|
+
* holding stashed ADF that a nested renderer will mount expands from.
|
|
5
8
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
+
* Those need a real element even while the expand around them is collapsed. The element is how we
|
|
10
|
+
* know find matched inside them rather than higher up: the beforematch event says nothing about
|
|
11
|
+
* where the match was, so the element the browser chose to reveal is the only clue.
|
|
12
|
+
*/
|
|
13
|
+
export declare const holdsRevealableContent: (node: PMNode) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The text shown in place of one block of a collapsed expand's body, so browser find can still
|
|
16
|
+
* reach the content without the block being rendered.
|
|
17
|
+
*
|
|
18
|
+
* Cached on the node. ProseMirror nodes never change, and the serializer reuses the same instances
|
|
19
|
+
* across renders, so the text only has to be worked out once per block.
|
|
9
20
|
*
|
|
10
|
-
* @returns the
|
|
21
|
+
* @returns the text, or `undefined` if this block has to be rendered instead.
|
|
11
22
|
*/
|
|
12
|
-
export declare const
|
|
23
|
+
export declare const getBlockSearchText: (node: PMNode) => string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/renderer",
|
|
3
|
-
"version": "137.1.
|
|
3
|
+
"version": "137.1.7",
|
|
4
4
|
"description": "Renderer component",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"@atlaskit/media-client-react": "^6.2.0",
|
|
59
59
|
"@atlaskit/media-common": "^14.5.0",
|
|
60
60
|
"@atlaskit/media-filmstrip": "^52.3.0",
|
|
61
|
-
"@atlaskit/media-ui": "^30.
|
|
62
|
-
"@atlaskit/media-viewer": "^54.
|
|
61
|
+
"@atlaskit/media-ui": "^30.14.0",
|
|
62
|
+
"@atlaskit/media-viewer": "^54.7.0",
|
|
63
63
|
"@atlaskit/platform-feature-experiments": "^0.3.0",
|
|
64
64
|
"@atlaskit/platform-feature-flags": "^2.1.0",
|
|
65
65
|
"@atlaskit/platform-feature-flags-react": "^1.1.0",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@atlaskit/status": "^5.8.0",
|
|
71
71
|
"@atlaskit/task-decision": "^21.8.0",
|
|
72
72
|
"@atlaskit/theme": "^28.1.0",
|
|
73
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
73
|
+
"@atlaskit/tmp-editor-statsig": "^164.0.0",
|
|
74
74
|
"@atlaskit/tokens": "^16.8.0",
|
|
75
75
|
"@atlaskit/tooltip": "^24.2.0",
|
|
76
76
|
"@atlaskit/visually-hidden": "^4.3.0",
|
|
@@ -229,9 +229,6 @@
|
|
|
229
229
|
"jfp-magma-ssr-iv-editor-links": {
|
|
230
230
|
"type": "boolean"
|
|
231
231
|
},
|
|
232
|
-
"hot-121622_lazy_load_expand_content": {
|
|
233
|
-
"type": "boolean"
|
|
234
|
-
},
|
|
235
232
|
"media-perf-uplift-mutation-fix": {
|
|
236
233
|
"type": "boolean"
|
|
237
234
|
},
|