@atlaskit/renderer 137.1.2 → 137.1.4
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 +60 -0
- package/dist/cjs/react/index.js +7 -19
- package/dist/cjs/react/nodes/extension.js +41 -3
- package/dist/cjs/react/utils/EditorMediaClientProvider.js +1 -1
- package/dist/cjs/ui/Expand.js +65 -81
- package/dist/cjs/ui/Renderer/RendererStyleContainer.js +15 -1
- package/dist/cjs/ui/Renderer/index.js +1 -1
- package/dist/cjs/ui/utils/expand-search-text.js +98 -0
- package/dist/es2019/react/index.js +8 -18
- package/dist/es2019/react/nodes/extension.js +41 -3
- package/dist/es2019/react/utils/EditorMediaClientProvider.js +1 -1
- package/dist/es2019/ui/Expand.js +42 -61
- package/dist/es2019/ui/Renderer/RendererStyleContainer.js +21 -1
- package/dist/es2019/ui/Renderer/index.js +1 -1
- package/dist/es2019/ui/utils/expand-search-text.js +84 -0
- package/dist/esm/react/index.js +8 -20
- package/dist/esm/react/nodes/extension.js +41 -3
- package/dist/esm/react/utils/EditorMediaClientProvider.js +1 -1
- package/dist/esm/ui/Expand.js +65 -81
- package/dist/esm/ui/Renderer/RendererStyleContainer.js +15 -1
- package/dist/esm/ui/Renderer/index.js +1 -1
- package/dist/esm/ui/utils/expand-search-text.js +93 -0
- package/dist/types/ui/Expand.d.ts +8 -2
- package/dist/types/ui/utils/expand-search-text.d.ts +12 -0
- package/package.json +8 -7
|
@@ -16,6 +16,13 @@ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-e
|
|
|
16
16
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
17
17
|
import { calcBreakoutWidthCss } from '../utils/breakout';
|
|
18
18
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
19
|
+
const FORGE_EXTENSION_TYPE = 'com.atlassian.ecosystem';
|
|
20
|
+
/**
|
|
21
|
+
* Mirrors `FORGE_INLINE_BODIED_PARAM` in `@atlassian/xen-editor-provider`. Duplicated rather than
|
|
22
|
+
* imported: the renderer must not depend on a Forge package, and this is a stored parameter name,
|
|
23
|
+
* so it is part of the document contract rather than that package's API.
|
|
24
|
+
*/
|
|
25
|
+
const FORGE_INLINE_BODIED_PARAM = 'atlassianForgeInlineBodied';
|
|
19
26
|
const viewportSizes = ['small', 'medium', 'default', 'large', 'xlarge'];
|
|
20
27
|
// Mirrors sizes from https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/forge/xen-editor-provider/src/render/renderers/ForgeUIExtension.tsx
|
|
21
28
|
const macroHeights = {
|
|
@@ -62,6 +69,7 @@ const FireExtensionAsInlineAnalytics = ({
|
|
|
62
69
|
return null;
|
|
63
70
|
};
|
|
64
71
|
export const renderExtension = (content, layout, options = {}, removeOverflow, extensionId, extensionViewportSizes, nodeHeight, localId, shouldDisplayExtensionAsInline, node, isInsideOfInlineExtension) => {
|
|
72
|
+
var _node$parameters, _node$parameters$gues;
|
|
65
73
|
const overflowContainerClass = !removeOverflow ? RendererCssClassName.EXTENSION_OVERFLOW_CONTAINER : '';
|
|
66
74
|
|
|
67
75
|
// by default, we assume the extension is at top level, (direct child of doc node)
|
|
@@ -84,8 +92,36 @@ export const renderExtension = (content, layout, options = {}, removeOverflow, e
|
|
|
84
92
|
*/
|
|
85
93
|
const viewportSize = getViewportSize(extensionId, extensionViewportSizes);
|
|
86
94
|
const extensionHeight = nodeHeight || viewportSize;
|
|
87
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Scoped to nodes inserted by an app declaring `outputType: inline`, which is what writes
|
|
97
|
+
* `atlassianForgeInlineBodied`. The output-type marker alone would also match migrated Connect
|
|
98
|
+
* content — it carries the same marker, and after an upgrade plus a storage round trip in the
|
|
99
|
+
* same shape — so keying on it would change how existing content renders. The renderer only
|
|
100
|
+
* ever sees the stored node, never the manifest, so a parameter this code writes is the only
|
|
101
|
+
* available signal.
|
|
102
|
+
*
|
|
103
|
+
* Evaluated once and shared with `isNativeForgeInline` below, so the gate is read a single time
|
|
104
|
+
* per render, and the cheap checks stay in front of it so anything ineligible short-circuits
|
|
105
|
+
* without firing an exposure it can never act on.
|
|
106
|
+
*/
|
|
107
|
+
const isForgeInlineBodiedEnabled = Boolean((node === null || node === void 0 ? void 0 : node.extensionType) === FORGE_EXTENSION_TYPE && (node === null || node === void 0 ? void 0 : node.content) && (node === null || node === void 0 ? void 0 : (_node$parameters = node.parameters) === null || _node$parameters === void 0 ? void 0 : (_node$parameters$gues = _node$parameters.guestParams) === null || _node$parameters$gues === void 0 ? void 0 : _node$parameters$gues[FORGE_INLINE_BODIED_PARAM]) === 'true' && fg('platform_forge_inline_bodied_macro'));
|
|
108
|
+
/**
|
|
109
|
+
* The pass that marks the sibling textblocks around an inline extension resolves their
|
|
110
|
+
* positions without a depth term, so it only ever matches at the top level. Inlining a nested
|
|
111
|
+
* container without joining its neighbours leaves a shrink-wrapped box alone on its own line,
|
|
112
|
+
* which is worse than leaving it a block — so keep nested inline-bodied Forge macros as
|
|
113
|
+
* blocks until the sibling marking works at depth.
|
|
114
|
+
*/
|
|
115
|
+
const isNestedForgeInlineBodied = !isTopLevel && isForgeInlineBodiedEnabled;
|
|
116
|
+
const isInline = (shouldDisplayExtensionAsInline === null || shouldDisplayExtensionAsInline === void 0 ? void 0 : shouldDisplayExtensionAsInline(node)) && expValEquals('platform_editor_render_bodied_extension_as_inline', 'isEnabled', true) && !isNestedForgeInlineBodied;
|
|
88
117
|
const inlineClassName = isInline ? RendererCssClassName.EXTENSION_AS_INLINE : '';
|
|
118
|
+
/**
|
|
119
|
+
* A native Forge macro does not have its body rendered by the product — the body ADF is
|
|
120
|
+
* sent to the app over the bridge and the app renders it with its own nested renderer.
|
|
121
|
+
* The inline styling above stops at the outer wrapper, so mark these nodes to let the
|
|
122
|
+
* nested document's block spacing be collapsed too.
|
|
123
|
+
*/
|
|
124
|
+
const isNativeForgeInline = Boolean(isInline && isForgeInlineBodiedEnabled);
|
|
89
125
|
const asInlineAnalytics = isInline && fireAnalyticsEvent && node ? jsx(FireExtensionAsInlineAnalytics, {
|
|
90
126
|
fireAnalyticsEvent: fireAnalyticsEvent,
|
|
91
127
|
node: node
|
|
@@ -107,7 +143,8 @@ export const renderExtension = (content, layout, options = {}, removeOverflow, e
|
|
|
107
143
|
"data-local-id": localId,
|
|
108
144
|
"data-testid": "extension--wrapper",
|
|
109
145
|
"data-node-type": "extension",
|
|
110
|
-
"data-top-level": isTopLevel || undefined
|
|
146
|
+
"data-top-level": isTopLevel || undefined,
|
|
147
|
+
"data-forge-inline": isNativeForgeInline || undefined
|
|
111
148
|
}, jsx("div", {
|
|
112
149
|
tabIndex: options.tabIndex
|
|
113
150
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
@@ -138,7 +175,8 @@ export const renderExtension = (content, layout, options = {}, removeOverflow, e
|
|
|
138
175
|
},
|
|
139
176
|
"data-layout": layout,
|
|
140
177
|
"data-local-id": localId,
|
|
141
|
-
"data-top-level": isTopLevel || undefined
|
|
178
|
+
"data-top-level": isTopLevel || undefined,
|
|
179
|
+
"data-forge-inline": isNativeForgeInline || undefined
|
|
142
180
|
}, jsx("div", {
|
|
143
181
|
tabIndex: options.tabIndex
|
|
144
182
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
@@ -6,7 +6,7 @@ import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
|
6
6
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
7
7
|
const getMediaClientConfigForRenderer = provider => {
|
|
8
8
|
// eslint-disable-next-line @atlaskit/platform/no-preconditioning
|
|
9
|
-
return provider.viewAndUploadMediaClientConfig && fg('platform_media_video_captions')
|
|
9
|
+
return provider.viewAndUploadMediaClientConfig && fg('platform_media_video_captions') ? provider.viewAndUploadMediaClientConfig : provider.viewMediaClientConfig;
|
|
10
10
|
};
|
|
11
11
|
export const EditorMediaClientProvider = ({
|
|
12
12
|
children,
|
package/dist/es2019/ui/Expand.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
6
6
|
import { css, jsx } from '@emotion/react';
|
|
7
|
-
import React, { useCallback,
|
|
7
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
8
8
|
import { bind } from 'bind-event-listener';
|
|
9
9
|
import { getDocument } from '@atlaskit/browser-apis';
|
|
10
10
|
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
@@ -12,8 +12,9 @@ import { ExpandIconWrapper, ExpandLayoutWrapperWithRef, expandMessages, WidthPro
|
|
|
12
12
|
import { akEditorLineHeight, akEditorSwoopCubicBezier, akLayoutGutterOffset } from '@atlaskit/editor-shared-styles';
|
|
13
13
|
import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
|
|
14
14
|
import Tooltip from '@atlaskit/tooltip/Tooltip';
|
|
15
|
+
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
15
16
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
16
|
-
import {
|
|
17
|
+
import { getExpandSearchText } from './utils/expand-search-text';
|
|
17
18
|
import _uniqueId from 'lodash/uniqueId';
|
|
18
19
|
import { injectIntl } from 'react-intl';
|
|
19
20
|
import { MODE, PLATFORM } from '../analytics/events';
|
|
@@ -153,17 +154,6 @@ const clearNextSiblingMarginTopStyle = css({
|
|
|
153
154
|
marginTop: '0 !important'
|
|
154
155
|
}
|
|
155
156
|
});
|
|
156
|
-
|
|
157
|
-
// Lazy-loaded children component
|
|
158
|
-
const LazyChildren = /*#__PURE__*/lazy(() => {
|
|
159
|
-
return Promise.resolve({
|
|
160
|
-
default: ({
|
|
161
|
-
children
|
|
162
|
-
}) => {
|
|
163
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, children);
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
157
|
const Container = props => {
|
|
168
158
|
return jsx("div", {
|
|
169
159
|
css: [containerStyles, props['data-node-type'] === 'expand' && containerStylesDataNodeTypeExpand, props.expanded && containerStylesExpanded, props.focused && containerStylesFocused],
|
|
@@ -223,12 +213,19 @@ function Expand({
|
|
|
223
213
|
localId,
|
|
224
214
|
nestedHeaderIds,
|
|
225
215
|
rendererContentMode,
|
|
226
|
-
|
|
227
|
-
searchText
|
|
216
|
+
node
|
|
228
217
|
}) {
|
|
229
|
-
const [expanded, setExpanded] =
|
|
230
|
-
const [focused, setFocused] =
|
|
231
|
-
|
|
218
|
+
const [expanded, setExpanded] = useState(false);
|
|
219
|
+
const [focused, setFocused] = useState(false);
|
|
220
|
+
/**
|
|
221
|
+
* PGXT-9021: latches on the first open so the body stays mounted once rendered, including after
|
|
222
|
+
* the expand is collapsed again. Remounting it would re-run every macro and Forge data fetch
|
|
223
|
+
* inside on each reopen, which is both slower and visibly reloads content the reader has already
|
|
224
|
+
* seen. The saving this gate exists for is at initial load, where the body has never been opened,
|
|
225
|
+
* so nothing is given up by keeping it after that. Never set on the server, so the server and the
|
|
226
|
+
* client's first render agree and hydration cannot desync.
|
|
227
|
+
*/
|
|
228
|
+
const [hasBeenExpanded, setHasBeenExpanded] = useState(false);
|
|
232
229
|
const isMobile = false;
|
|
233
230
|
const label = intl.formatMessage(expanded ? expandMessages.collapseNode : expandMessages.expandNode);
|
|
234
231
|
const {
|
|
@@ -238,16 +235,12 @@ function Expand({
|
|
|
238
235
|
const contentWrapperRef = useRef(null);
|
|
239
236
|
const handleFocus = useCallback(() => setFocused(true), []);
|
|
240
237
|
const handleBlur = useCallback(() => setFocused(false), []);
|
|
241
|
-
|
|
242
|
-
|
|
238
|
+
// Both state updates happen in the same handler so they land in one render, rather than the
|
|
239
|
+
// latch reacting to `expanded` from an effect.
|
|
240
|
+
const openBody = useCallback(() => {
|
|
243
241
|
setExpanded(true);
|
|
242
|
+
setHasBeenExpanded(true);
|
|
244
243
|
}, []);
|
|
245
|
-
const shouldRenderLazyChildren = hasLoadedChildren || loadBodyContent;
|
|
246
|
-
// Only render the lightweight text placeholder when lazy load is ON and
|
|
247
|
-
// children haven't been loaded yet. When lazy load is OFF, children are
|
|
248
|
-
// always in the DOM — hidden="until-found" on the wrapper already makes
|
|
249
|
-
// them searchable by browser find, so no placeholder is needed.
|
|
250
|
-
const shouldRenderBrowserFindText = !shouldRenderLazyChildren && searchText && fg('hot-121622_lazy_load_expand_content') && expValEquals('platform_editor_close_expand_find', 'isEnabled', true);
|
|
251
244
|
|
|
252
245
|
// Feature-detect hidden="until-found" support via the beforematch event.
|
|
253
246
|
// Chrome 102+ and Firefox 130+ support it; Safari does not yet.
|
|
@@ -256,8 +249,8 @@ function Expand({
|
|
|
256
249
|
// Initialised as false and set in useEffect to avoid SSR/client hydration mismatch —
|
|
257
250
|
// useMemo would return true on the client's first render in supported browsers,
|
|
258
251
|
// differing from the server snapshot which always produces false.
|
|
259
|
-
const [supportsHiddenUntilFound, setSupportsHiddenUntilFound] =
|
|
260
|
-
|
|
252
|
+
const [supportsHiddenUntilFound, setSupportsHiddenUntilFound] = useState(false);
|
|
253
|
+
useEffect(() => {
|
|
261
254
|
const doc = getDocument();
|
|
262
255
|
setSupportsHiddenUntilFound(doc !== null && doc !== void 0 && doc.body ? 'onbeforematch' in doc.body : false);
|
|
263
256
|
}, []);
|
|
@@ -273,13 +266,13 @@ function Expand({
|
|
|
273
266
|
// In unsupported browsers (Safari), we skip this entirely and fall back to the normal
|
|
274
267
|
// CSS hiding (visibility:hidden + height:0), which doesn't support find-in-page but
|
|
275
268
|
// still works correctly for expand/collapse.
|
|
276
|
-
|
|
269
|
+
useEffect(() => {
|
|
277
270
|
const contentContainer = contentContainerRef.current;
|
|
278
271
|
const contentWrapper = contentWrapperRef.current;
|
|
279
272
|
if (!contentWrapper) {
|
|
280
273
|
return;
|
|
281
274
|
}
|
|
282
|
-
if (supportsHiddenUntilFound &&
|
|
275
|
+
if (supportsHiddenUntilFound && isExperimentEnabled('platform_editor_close_expand_find') && !expanded) {
|
|
283
276
|
contentWrapper.setAttribute('hidden', 'until-found');
|
|
284
277
|
// Override the CSS visibility:hidden from contentContainerStyles — hidden="until-found"
|
|
285
278
|
// now handles hiding via content-visibility:hidden, which allows browser find to index
|
|
@@ -293,31 +286,27 @@ function Expand({
|
|
|
293
286
|
contentWrapper.style.visibility = '';
|
|
294
287
|
}
|
|
295
288
|
}, [expanded, supportsHiddenUntilFound]);
|
|
296
|
-
|
|
297
|
-
if (!
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
if (!isExperimentEnabled('platform_editor_close_expand_find') || expanded) {
|
|
298
291
|
return;
|
|
299
292
|
}
|
|
300
293
|
const contentWrapper = contentWrapperRef.current;
|
|
301
294
|
const unbindWrapperBeforeMatch = contentWrapper && supportsHiddenUntilFound ? bind(contentWrapper, {
|
|
302
295
|
type: 'beforematch',
|
|
303
|
-
listener:
|
|
296
|
+
listener: openBody
|
|
304
297
|
}) : undefined;
|
|
305
298
|
return () => {
|
|
306
299
|
unbindWrapperBeforeMatch === null || unbindWrapperBeforeMatch === void 0 ? void 0 : unbindWrapperBeforeMatch();
|
|
307
300
|
};
|
|
308
|
-
}, [
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
expandContent = jsx(Suspense, {
|
|
318
|
-
fallback: jsx("div", null, intl.formatMessage(expandMessages.loading))
|
|
319
|
-
}, jsx(LazyChildren, null, children));
|
|
320
|
-
}
|
|
301
|
+
}, [expanded, openBody, supportsHiddenUntilFound]);
|
|
302
|
+
|
|
303
|
+
// Until the expand is first opened, stand in a plain-text mirror of the body so
|
|
304
|
+
// hidden="until-found" keeps the content findable by browser find without mounting the real
|
|
305
|
+
// subtree. The beforematch listener above expands before the browser scrolls to its match.
|
|
306
|
+
// Computed lazily: a nested expand is not mounted until its ancestor opens, so its mirror is
|
|
307
|
+
// never built on initial load.
|
|
308
|
+
const searchText = useMemo(() => node && fg('hot-121622_lazy_load_expand_content') ? getExpandSearchText(node) : undefined, [node]);
|
|
309
|
+
const expandContent = searchText && !expanded && !hasBeenExpanded ? jsx("span", null, searchText) : children;
|
|
321
310
|
return jsx(Container, {
|
|
322
311
|
"data-testid": `expand-container-${nodeType}-${id}`,
|
|
323
312
|
"data-node-type": nodeType,
|
|
@@ -327,15 +316,8 @@ function Expand({
|
|
|
327
316
|
expanded: expanded,
|
|
328
317
|
focused: focused
|
|
329
318
|
}, nestedHeaderIds && nestedHeaderIds.length > 0 ? jsx(ActiveHeaderIdConsumer, {
|
|
330
|
-
nestedHeaderIds: nestedHeaderIds
|
|
331
|
-
|
|
332
|
-
,
|
|
333
|
-
onNestedHeaderIdMatch: () => {
|
|
334
|
-
if (!hasLoadedChildren) {
|
|
335
|
-
setHasLoadedChildren(true);
|
|
336
|
-
}
|
|
337
|
-
setExpanded(true);
|
|
338
|
-
}
|
|
319
|
+
nestedHeaderIds: nestedHeaderIds,
|
|
320
|
+
onNestedHeaderIdMatch: openBody
|
|
339
321
|
}) : null, jsx(TitleContainer
|
|
340
322
|
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
|
|
341
323
|
, {
|
|
@@ -343,12 +325,11 @@ function Expand({
|
|
|
343
325
|
e.preventDefault();
|
|
344
326
|
e.stopPropagation();
|
|
345
327
|
fireExpandToggleAnalytics(nodeType, expanded, fireAnalyticsEvent);
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
328
|
+
if (expanded) {
|
|
329
|
+
setExpanded(false);
|
|
330
|
+
} else {
|
|
331
|
+
openBody();
|
|
350
332
|
}
|
|
351
|
-
setExpanded(!expanded);
|
|
352
333
|
e.persist();
|
|
353
334
|
// @ts-ignore detail doesn't exist on type
|
|
354
335
|
e.detail ? handleBlur() : handleFocus();
|
|
@@ -383,7 +364,7 @@ function Expand({
|
|
|
383
364
|
id: id
|
|
384
365
|
}, title || intl.formatMessage(expandMessages.expandDefaultTitle))), jsx(ContentContainer, {
|
|
385
366
|
expanded: expanded,
|
|
386
|
-
enableBrowserFind: supportsHiddenUntilFound &&
|
|
367
|
+
enableBrowserFind: supportsHiddenUntilFound && isExperimentEnabled('platform_editor_close_expand_find'),
|
|
387
368
|
contentRef: contentContainerRef
|
|
388
369
|
}, jsx("div", {
|
|
389
370
|
className: `${nodeType}-content-wrapper`,
|
|
@@ -1027,6 +1027,26 @@ const extensionAsInlineStyle = css({
|
|
|
1027
1027
|
display: 'inline-block'
|
|
1028
1028
|
}
|
|
1029
1029
|
});
|
|
1030
|
+
const forgeInlineBodiedSpacingStyle = css({
|
|
1031
|
+
[`.${RendererCssClassName.DOCUMENT} .${RendererCssClassName.EXTENSION_AS_INLINE}[data-forge-inline]`]: {
|
|
1032
|
+
marginBottom: 0,
|
|
1033
|
+
verticalAlign: 'baseline',
|
|
1034
|
+
maxWidth: '100%'
|
|
1035
|
+
},
|
|
1036
|
+
[[`.${RendererCssClassName.DOCUMENT} [data-forge-inline] + [data-as-inline="on"]`, `.${RendererCssClassName.DOCUMENT} [data-as-inline="on"]:has(+ [data-forge-inline])`].join(', ')]: {
|
|
1037
|
+
display: 'inline'
|
|
1038
|
+
},
|
|
1039
|
+
[`.${RendererCssClassName.EXTENSION_AS_INLINE}[data-forge-inline] *`]: {
|
|
1040
|
+
maxWidth: '100%'
|
|
1041
|
+
},
|
|
1042
|
+
[`.${RendererCssClassName.EXTENSION_AS_INLINE}[data-forge-inline] .${RendererCssClassName.EXTENSION_OVERFLOW_CONTAINER}`]: {
|
|
1043
|
+
overflowX: 'auto'
|
|
1044
|
+
},
|
|
1045
|
+
[`.${RendererCssClassName.EXTENSION_AS_INLINE}[data-forge-inline] .${RendererCssClassName.DOCUMENT} > p`]: {
|
|
1046
|
+
display: 'inline',
|
|
1047
|
+
margin: 0
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1030
1050
|
|
|
1031
1051
|
// Removes the blockNodesVerticalMargin styling for inline extensions, i.e. borderless excerpt-include
|
|
1032
1052
|
const inlineExtensionRendererMarginFix = css({
|
|
@@ -2915,7 +2935,7 @@ export const RendererStyleContainer = props => {
|
|
|
2915
2935
|
// eslint-disable-next-line @atlaskit/platform/no-preconditioning
|
|
2916
2936
|
fg('editor_inline_comments_on_inline_nodes') && rendererAnnotationStylesCommentHeightFix, expValEquals('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? baseOtherStyles : baseOtherStylesDuplicateAnchor,
|
|
2917
2937
|
// this should be placed after baseOtherStyles
|
|
2918
|
-
expValEquals('platform_editor_render_bodied_extension_as_inline', 'isEnabled', true) && (expValEquals('platform_editor_remove_important_in_render_ext', 'isEnabled', true) ? extensionAsInlineStyle : oldExtensionAsInlineStyle), inlineExtensionRendererMarginFix, allowNestedHeaderLinks && (expValEquals('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? alignedHeadingAnchorStyle : alignedHeadingAnchorStyleDuplicateAnchor), mediaSingleSharedStyle,
|
|
2938
|
+
expValEquals('platform_editor_render_bodied_extension_as_inline', 'isEnabled', true) && (expValEquals('platform_editor_remove_important_in_render_ext', 'isEnabled', true) ? extensionAsInlineStyle : oldExtensionAsInlineStyle), forgeInlineBodiedSpacingStyle, inlineExtensionRendererMarginFix, allowNestedHeaderLinks && (expValEquals('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? alignedHeadingAnchorStyle : alignedHeadingAnchorStyleDuplicateAnchor), mediaSingleSharedStyle,
|
|
2919
2939
|
// merge firstWrappedMediaStyles with mediaSingleSharedStyle when clean up platform_editor_fix_media_in_renderer
|
|
2920
2940
|
fg('platform_editor_fix_media_in_renderer') && firstWrappedMediaStyles, tableSharedStyle, expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true) && roundedTableOuterBorderOverlayStyles, expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true) && !isExperimentEnabled('platform_editor_table_q4_patch_7') && roundedTableLegacyClipPathStyles, expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true) && roundedTableRemixBlockHighlightStyles, expValEquals('platform_editor_table_fit_to_content_auto_convert', 'isEnabled', true) && tableContentModeScopedStyles, expValEquals('platform_editor_table_fit_to_content_auto_convert', 'isEnabled', true) && tableContentModeNestedTableStyles, tableRendererHeaderStylesForTableCellOnly, fg('platform_editor_bordered_panel_nested_in_table') && tableRendererNestedPanelStyles, isBackgroundClipBrowserFixNeeded() && tableStylesBackGroundClipForGeckoForTableCellOnly, firstNodeWithNotMarginTopWithNestedDnD, rendererTableStyles, isStickyScrollbarOn && stickyScrollbarStyles, isStickyScrollbarOn && expValEquals('platform_editor_table_css_overflow_shadow', 'isEnabled', true) && stickyScrollbarOverflowShadowFixStyles, rendererTableHeaderEqualHeightStylesForTableCellOnly, allowColumnSorting && rendererTableSortableColumnStyles, allowColumnSorting && expValEqualsNoExposure('platform_editor_table_menu_updates', 'isEnabled', true) && rendererTableSortableColumnValignStyles, allowColumnSorting && allowNestedHeaderLinks && (expValEquals('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? rendererTableHeaderEqualHeightStylesAllowNestedHeaderLinks : rendererTableHeaderEqualHeightStylesAllowNestedHeaderLinksDuplicateAnchor), rendererTableColumnStyles, stickyHeaderStyles, codeBlockAndLayoutStyles, columnLayoutSharedStyle, isAdvancedLayoutsOn && columnLayoutResponsiveSharedStyle, isAdvancedLayoutsOn && columnLayoutResponsiveRendererStyles, isAdvancedLayoutsOn && layoutSectionForAdvancedLayoutsStyles, !useBlockRenderForCodeBlock && gridRenderForCodeBlockStyles, browser.safari && codeBlockInListSafariFixStyles, appearance === 'full-page' && !isPreviewPanelResponsivenessOn && responsiveBreakoutWidth, appearance === 'full-page' && isPreviewPanelResponsivenessOn && responsiveBreakoutWidthWithReducedPadding, (appearance === 'full-width' || appearance === 'max' && (expValEquals('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEquals('confluence_max_width_content_appearance', 'isEnabled', true))) && responsiveBreakoutWidthFullWidth, expValEquals('platform_editor_lovability_emoji_scaling', 'isEnabled', true) ? contentMode === 'compact' ? scaledDenseEmojiStyles : scaledEmojiStyles : contentMode === 'compact' ? denseStyles : undefined, contentMode === 'compact' ? scaledDenseUnicodeEmojiStylesNew : scaledUnicodeEmojiStylesNew, syncBlockStyles, centerWrapperStyles, isInsideSyncBlock ? syncBlockRendererStyles : null, isInsideSyncBlock && tableFakeBorderStyles, isInsideSyncBlock && expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true) ? roundedTableFakeBorderOverlayStyles : null, expValEquals('platform_editor_hide_extension_renderer_support', 'isEnabled', true) && hideExtensionStyles],
|
|
2921
2941
|
"data-testid": testId
|
|
@@ -60,7 +60,7 @@ export const DEGRADED_SEVERITY_THRESHOLD = 3000;
|
|
|
60
60
|
const TABLE_INFO_TIMEOUT = 10000;
|
|
61
61
|
const RENDER_EVENT_SAMPLE_RATE = 0.1;
|
|
62
62
|
const packageName = "@atlaskit/renderer";
|
|
63
|
-
const packageVersion = "137.1.
|
|
63
|
+
const packageVersion = "137.1.3";
|
|
64
64
|
const setAsQueryContainerStyles = css({
|
|
65
65
|
containerName: 'ak-renderer-wrapper',
|
|
66
66
|
containerType: 'inline-size'
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { findChildrenByMark } from '@atlaskit/editor-prosemirror/utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal structural view of an ADF entity. `parameters.nestedContent` holds raw ADF JSON rather
|
|
5
|
+
* than ProseMirror nodes, so it cannot be walked with the model API.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Separates text from adjacent blocks so phrases cannot fuse across block boundaries. */
|
|
9
|
+
const BLOCK_SEPARATOR = ' ';
|
|
10
|
+
const asText = value => typeof value === 'string' ? value : '';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extracts text from raw ADF JSON.
|
|
14
|
+
*
|
|
15
|
+
* Needed because some extensions stash a whole ADF subtree in `attrs.parameters.nestedContent`
|
|
16
|
+
* instead of in their node content — Confluence does this when it coerces nesting the schema
|
|
17
|
+
* cannot represent (an expand more than two collapsible layers deep, say). Such a node is a
|
|
18
|
+
* ProseMirror leaf, so the model walk cannot see the subtree even though it renders on the page
|
|
19
|
+
* via a nested renderer. Recurses, because nestedContent routinely holds further nested content.
|
|
20
|
+
*/
|
|
21
|
+
const adfEntityText = entity => {
|
|
22
|
+
var _entity$attrs, _entity$attrs$paramet, _entity$content;
|
|
23
|
+
if (!entity) {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
const parts = [];
|
|
27
|
+
if (entity.type === 'text') {
|
|
28
|
+
parts.push(asText(entity.text));
|
|
29
|
+
}
|
|
30
|
+
const nestedContent = (_entity$attrs = entity.attrs) === null || _entity$attrs === void 0 ? void 0 : (_entity$attrs$paramet = _entity$attrs.parameters) === null || _entity$attrs$paramet === void 0 ? void 0 : _entity$attrs$paramet.nestedContent;
|
|
31
|
+
if (nestedContent) {
|
|
32
|
+
// Prefer the real subtree over `attrs.text`, which is only a placeholder label for it.
|
|
33
|
+
parts.push(adfEntityText(nestedContent));
|
|
34
|
+
} else {
|
|
35
|
+
var _entity$attrs2;
|
|
36
|
+
parts.push(asText((_entity$attrs2 = entity.attrs) === null || _entity$attrs2 === void 0 ? void 0 : _entity$attrs2.text));
|
|
37
|
+
}
|
|
38
|
+
(_entity$content = entity.content) === null || _entity$content === void 0 ? void 0 : _entity$content.forEach(child => parts.push(adfEntityText(child)));
|
|
39
|
+
return parts.filter(Boolean).join(BLOCK_SEPARATOR);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* `textBetween` drops every non-text leaf unless the node spec declares `leafText`, and no ADF
|
|
44
|
+
* node does. Leaves carry their visible text in attrs, so recover it here.
|
|
45
|
+
*/
|
|
46
|
+
const leafText = leaf => {
|
|
47
|
+
var _leaf$attrs, _leaf$attrs$parameter, _leaf$attrs2;
|
|
48
|
+
const nestedContent = (_leaf$attrs = leaf.attrs) === null || _leaf$attrs === void 0 ? void 0 : (_leaf$attrs$parameter = _leaf$attrs.parameters) === null || _leaf$attrs$parameter === void 0 ? void 0 : _leaf$attrs$parameter.nestedContent;
|
|
49
|
+
if (nestedContent) {
|
|
50
|
+
return adfEntityText(nestedContent);
|
|
51
|
+
}
|
|
52
|
+
return asText((_leaf$attrs2 = leaf.attrs) === null || _leaf$attrs2 === void 0 ? void 0 : _leaf$attrs2.text);
|
|
53
|
+
};
|
|
54
|
+
const hasInlineComment = node => findChildrenByMark(node, node.type.schema.marks.annotation, true).some(annotation => annotation.node.marks.some(mark => mark.attrs.annotationType === 'inlineComment'));
|
|
55
|
+
|
|
56
|
+
/** `null` is a cached "no mirror for this node", as distinct from `undefined` for a cache miss. */
|
|
57
|
+
const searchTextCache = new WeakMap();
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Plain-text mirror of an expand's body, rendered in its place while collapsed so browser
|
|
61
|
+
* find-in-page can still reach the content.
|
|
62
|
+
*
|
|
63
|
+
* Cached on the node. ProseMirror nodes are immutable and `render-document` memoises
|
|
64
|
+
* `nodeFromJSON`, so the same node instance is reused across renders — including across the
|
|
65
|
+
* serializer rebuilds that invalidate the module-level `memoizeOne` caches.
|
|
66
|
+
*
|
|
67
|
+
* @returns the mirror text, or `undefined` if this expand must keep its body rendered.
|
|
68
|
+
*/
|
|
69
|
+
export const getExpandSearchText = node => {
|
|
70
|
+
var _cached;
|
|
71
|
+
let cached = searchTextCache.get(node);
|
|
72
|
+
if (cached === undefined) {
|
|
73
|
+
cached = hasInlineComment(node) ?
|
|
74
|
+
// Withhold the mirror, which makes Expand keep rendering `children`. Comment
|
|
75
|
+
// navigation walks the DOM to find the annotated node and scrolls it into view, so
|
|
76
|
+
// the annotation has to exist as a real element — a flat text mirror carries no
|
|
77
|
+
// annotation marks and cannot stand in for it. Without this, stepping through
|
|
78
|
+
// comments with the arrow keys silently fails to reach any comment inside a
|
|
79
|
+
// collapsed expand. Costs the lazy-load win for these expands, deliberately.
|
|
80
|
+
null : node.textBetween(0, node.content.size, BLOCK_SEPARATOR, leafText);
|
|
81
|
+
searchTextCache.set(node, cached);
|
|
82
|
+
}
|
|
83
|
+
return (_cached = cached) !== null && _cached !== void 0 ? _cached : undefined;
|
|
84
|
+
};
|
package/dist/esm/react/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import TextWrapperComponent from './nodes/text-wrapper';
|
|
|
16
16
|
import { isNestedHeaderLinksEnabled } from './utils/links';
|
|
17
17
|
import { getColumnWidths } from '@atlaskit/editor-common/utils';
|
|
18
18
|
import { getMarksByOrder, isSameMark } from '@atlaskit/editor-common/validator';
|
|
19
|
-
import {
|
|
19
|
+
import { findChildrenByType } from '@atlaskit/editor-prosemirror/utils';
|
|
20
20
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
21
21
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
22
22
|
import { getText } from '../utils';
|
|
@@ -729,25 +729,14 @@ var ReactSerializer = /*#__PURE__*/function () {
|
|
|
729
729
|
value: function getExpandProps(node) {
|
|
730
730
|
var _this7 = this;
|
|
731
731
|
var _path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
loadBodyContent = annotations.some(function (annotation) {
|
|
738
|
-
return annotation.node.marks.some(function (mark) {
|
|
739
|
-
return mark.attrs.annotationType === 'inlineComment';
|
|
740
|
-
});
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// Only compute searchText when the browser find experiment is enabled,
|
|
745
|
-
// to avoid unnecessary node.textContent string allocations per expand node.
|
|
746
|
-
var searchText = expValEquals('platform_editor_close_expand_find', 'isEnabled', true) ? node.textContent : undefined;
|
|
732
|
+
// Expand receives the node rather than a precomputed string so it can derive the collapsed
|
|
733
|
+
// text mirror only when it actually renders one — a nested expand is not mounted until its
|
|
734
|
+
// ancestor opens — and so that derivation can reach content the model walk cannot, such as
|
|
735
|
+
// the ADF stashed in a coerced extension's `parameters.nestedContent`. Expand owns the
|
|
736
|
+
// feature gate check.
|
|
747
737
|
if (!isNestedHeaderLinksEnabled(this.allowHeadingAnchorLinks)) {
|
|
748
738
|
return _objectSpread(_objectSpread({}, this.getProps(node)), {}, {
|
|
749
|
-
|
|
750
|
-
searchText: searchText
|
|
739
|
+
node: node
|
|
751
740
|
});
|
|
752
741
|
}
|
|
753
742
|
var nestedHeaderIds = findChildrenByType(node, node.type.schema.nodes.heading).map(function (_ref4) {
|
|
@@ -756,8 +745,7 @@ var ReactSerializer = /*#__PURE__*/function () {
|
|
|
756
745
|
});
|
|
757
746
|
return _objectSpread(_objectSpread({}, this.getProps(node)), {}, {
|
|
758
747
|
nestedHeaderIds: nestedHeaderIds,
|
|
759
|
-
|
|
760
|
-
searchText: searchText
|
|
748
|
+
node: node
|
|
761
749
|
});
|
|
762
750
|
}
|
|
763
751
|
|
|
@@ -16,6 +16,13 @@ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-e
|
|
|
16
16
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
17
17
|
import { calcBreakoutWidthCss } from '../utils/breakout';
|
|
18
18
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
19
|
+
var FORGE_EXTENSION_TYPE = 'com.atlassian.ecosystem';
|
|
20
|
+
/**
|
|
21
|
+
* Mirrors `FORGE_INLINE_BODIED_PARAM` in `@atlassian/xen-editor-provider`. Duplicated rather than
|
|
22
|
+
* imported: the renderer must not depend on a Forge package, and this is a stored parameter name,
|
|
23
|
+
* so it is part of the document contract rather than that package's API.
|
|
24
|
+
*/
|
|
25
|
+
var FORGE_INLINE_BODIED_PARAM = 'atlassianForgeInlineBodied';
|
|
19
26
|
var viewportSizes = ['small', 'medium', 'default', 'large', 'xlarge'];
|
|
20
27
|
// Mirrors sizes from https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/forge/xen-editor-provider/src/render/renderers/ForgeUIExtension.tsx
|
|
21
28
|
var macroHeights = {
|
|
@@ -63,6 +70,7 @@ var FireExtensionAsInlineAnalytics = function FireExtensionAsInlineAnalytics(_re
|
|
|
63
70
|
return null;
|
|
64
71
|
};
|
|
65
72
|
export var renderExtension = function renderExtension(content, layout) {
|
|
73
|
+
var _node$parameters;
|
|
66
74
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
67
75
|
var removeOverflow = arguments.length > 3 ? arguments[3] : undefined;
|
|
68
76
|
var extensionId = arguments.length > 4 ? arguments[4] : undefined;
|
|
@@ -95,8 +103,36 @@ export var renderExtension = function renderExtension(content, layout) {
|
|
|
95
103
|
*/
|
|
96
104
|
var viewportSize = getViewportSize(extensionId, extensionViewportSizes);
|
|
97
105
|
var extensionHeight = nodeHeight || viewportSize;
|
|
98
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Scoped to nodes inserted by an app declaring `outputType: inline`, which is what writes
|
|
108
|
+
* `atlassianForgeInlineBodied`. The output-type marker alone would also match migrated Connect
|
|
109
|
+
* content — it carries the same marker, and after an upgrade plus a storage round trip in the
|
|
110
|
+
* same shape — so keying on it would change how existing content renders. The renderer only
|
|
111
|
+
* ever sees the stored node, never the manifest, so a parameter this code writes is the only
|
|
112
|
+
* available signal.
|
|
113
|
+
*
|
|
114
|
+
* Evaluated once and shared with `isNativeForgeInline` below, so the gate is read a single time
|
|
115
|
+
* per render, and the cheap checks stay in front of it so anything ineligible short-circuits
|
|
116
|
+
* without firing an exposure it can never act on.
|
|
117
|
+
*/
|
|
118
|
+
var isForgeInlineBodiedEnabled = Boolean((node === null || node === void 0 ? void 0 : node.extensionType) === FORGE_EXTENSION_TYPE && (node === null || node === void 0 ? void 0 : node.content) && (node === null || node === void 0 || (_node$parameters = node.parameters) === null || _node$parameters === void 0 || (_node$parameters = _node$parameters.guestParams) === null || _node$parameters === void 0 ? void 0 : _node$parameters[FORGE_INLINE_BODIED_PARAM]) === 'true' && fg('platform_forge_inline_bodied_macro'));
|
|
119
|
+
/**
|
|
120
|
+
* The pass that marks the sibling textblocks around an inline extension resolves their
|
|
121
|
+
* positions without a depth term, so it only ever matches at the top level. Inlining a nested
|
|
122
|
+
* container without joining its neighbours leaves a shrink-wrapped box alone on its own line,
|
|
123
|
+
* which is worse than leaving it a block — so keep nested inline-bodied Forge macros as
|
|
124
|
+
* blocks until the sibling marking works at depth.
|
|
125
|
+
*/
|
|
126
|
+
var isNestedForgeInlineBodied = !isTopLevel && isForgeInlineBodiedEnabled;
|
|
127
|
+
var isInline = (shouldDisplayExtensionAsInline === null || shouldDisplayExtensionAsInline === void 0 ? void 0 : shouldDisplayExtensionAsInline(node)) && expValEquals('platform_editor_render_bodied_extension_as_inline', 'isEnabled', true) && !isNestedForgeInlineBodied;
|
|
99
128
|
var inlineClassName = isInline ? RendererCssClassName.EXTENSION_AS_INLINE : '';
|
|
129
|
+
/**
|
|
130
|
+
* A native Forge macro does not have its body rendered by the product — the body ADF is
|
|
131
|
+
* sent to the app over the bridge and the app renders it with its own nested renderer.
|
|
132
|
+
* The inline styling above stops at the outer wrapper, so mark these nodes to let the
|
|
133
|
+
* nested document's block spacing be collapsed too.
|
|
134
|
+
*/
|
|
135
|
+
var isNativeForgeInline = Boolean(isInline && isForgeInlineBodiedEnabled);
|
|
100
136
|
var asInlineAnalytics = isInline && fireAnalyticsEvent && node ? jsx(FireExtensionAsInlineAnalytics, {
|
|
101
137
|
fireAnalyticsEvent: fireAnalyticsEvent,
|
|
102
138
|
node: node
|
|
@@ -118,7 +154,8 @@ export var renderExtension = function renderExtension(content, layout) {
|
|
|
118
154
|
"data-local-id": localId,
|
|
119
155
|
"data-testid": "extension--wrapper",
|
|
120
156
|
"data-node-type": "extension",
|
|
121
|
-
"data-top-level": isTopLevel || undefined
|
|
157
|
+
"data-top-level": isTopLevel || undefined,
|
|
158
|
+
"data-forge-inline": isNativeForgeInline || undefined
|
|
122
159
|
}, jsx("div", {
|
|
123
160
|
tabIndex: options.tabIndex
|
|
124
161
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
@@ -148,7 +185,8 @@ export var renderExtension = function renderExtension(content, layout) {
|
|
|
148
185
|
},
|
|
149
186
|
"data-layout": layout,
|
|
150
187
|
"data-local-id": localId,
|
|
151
|
-
"data-top-level": isTopLevel || undefined
|
|
188
|
+
"data-top-level": isTopLevel || undefined,
|
|
189
|
+
"data-forge-inline": isNativeForgeInline || undefined
|
|
152
190
|
}, jsx("div", {
|
|
153
191
|
tabIndex: options.tabIndex
|
|
154
192
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
@@ -7,7 +7,7 @@ import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
|
7
7
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
8
|
var getMediaClientConfigForRenderer = function getMediaClientConfigForRenderer(provider) {
|
|
9
9
|
// eslint-disable-next-line @atlaskit/platform/no-preconditioning
|
|
10
|
-
return provider.viewAndUploadMediaClientConfig && fg('platform_media_video_captions')
|
|
10
|
+
return provider.viewAndUploadMediaClientConfig && fg('platform_media_video_captions') ? provider.viewAndUploadMediaClientConfig : provider.viewMediaClientConfig;
|
|
11
11
|
};
|
|
12
12
|
export var EditorMediaClientProvider = function EditorMediaClientProvider(_ref) {
|
|
13
13
|
var children = _ref.children,
|