@atlaskit/editor-plugin-synced-block 10.8.5 → 11.0.1
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/AGENTS.md +3 -3
- package/CHANGELOG.md +19 -0
- package/dist/cjs/nodeviews/bodiedSyncedBlock.js +25 -193
- package/dist/cjs/nodeviews/syncedBlock.js +4 -25
- package/dist/cjs/pm-plugins/main.js +64 -130
- package/dist/cjs/pm-plugins/menu-and-toolbar-experiences.js +2 -7
- package/dist/cjs/pm-plugins/utils/has-synced-blocks.js +2 -2
- package/dist/cjs/syncedBlockPlugin.js +3 -12
- package/dist/es2019/nodeviews/bodiedSyncedBlock.js +9 -150
- package/dist/es2019/nodeviews/syncedBlock.js +3 -17
- package/dist/es2019/pm-plugins/main.js +65 -133
- package/dist/es2019/pm-plugins/menu-and-toolbar-experiences.js +2 -7
- package/dist/es2019/pm-plugins/utils/has-synced-blocks.js +2 -2
- package/dist/es2019/syncedBlockPlugin.js +3 -12
- package/dist/esm/nodeviews/bodiedSyncedBlock.js +24 -192
- package/dist/esm/nodeviews/syncedBlock.js +4 -25
- package/dist/esm/pm-plugins/main.js +65 -131
- package/dist/esm/pm-plugins/menu-and-toolbar-experiences.js +2 -7
- package/dist/esm/pm-plugins/utils/has-synced-blocks.js +2 -2
- package/dist/esm/syncedBlockPlugin.js +3 -12
- package/dist/types/nodeviews/bodiedSyncedBlock.d.ts +1 -13
- package/dist/types/pm-plugins/main.d.ts +6 -10
- package/dist/types/pm-plugins/utils/has-synced-blocks.d.ts +2 -2
- package/dist/types/types/index.d.ts +1 -2
- package/package.json +15 -15
- package/dist/cjs/ui/BodiedSyncBlockWrapper.js +0 -25
- package/dist/es2019/ui/BodiedSyncBlockWrapper.js +0 -19
- package/dist/esm/ui/BodiedSyncBlockWrapper.js +0 -18
- package/dist/types/ui/BodiedSyncBlockWrapper.d.ts +0 -9
|
@@ -2,153 +2,12 @@ import React from 'react';
|
|
|
2
2
|
import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
|
3
3
|
import { isSSR, isSSRStreaming } from '@atlaskit/editor-common/core-utils';
|
|
4
4
|
import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary';
|
|
5
|
-
import ReactNodeView from '@atlaskit/editor-common/react-node-view';
|
|
6
5
|
import { BodiedSyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
|
|
7
6
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
8
7
|
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
9
8
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
10
|
-
import { BodiedSyncBlockWrapper } from '../ui/BodiedSyncBlockWrapper';
|
|
11
9
|
import { SyncBlockLabel } from '../ui/SyncBlockLabel';
|
|
12
10
|
import { isEmptySourceSyncBlock } from './isEmptySourceSyncBlock';
|
|
13
|
-
const toDOMOld = () => ['div', {
|
|
14
|
-
class: BodiedSyncBlockSharedCssClassName.content,
|
|
15
|
-
contenteditable: true
|
|
16
|
-
}, 0];
|
|
17
|
-
class BodiedSyncBlockOld extends ReactNodeView {
|
|
18
|
-
constructor(props) {
|
|
19
|
-
super(props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props);
|
|
20
|
-
this.api = props.api;
|
|
21
|
-
this.syncBlockStore = props.syncBlockStore;
|
|
22
|
-
this.handleConnectivityModeChange();
|
|
23
|
-
this.handleViewModeChange();
|
|
24
|
-
}
|
|
25
|
-
updateContentEditable({
|
|
26
|
-
contentDOM,
|
|
27
|
-
nextConnectivityMode,
|
|
28
|
-
nextViewMode
|
|
29
|
-
}) {
|
|
30
|
-
var _this$api, _this$api$connectivit, _this$api$connectivit2, _this$api$connectivit3, _this$api2, _this$api2$editorView, _this$api2$editorView2, _this$api2$editorView3;
|
|
31
|
-
const connectivityMode = nextConnectivityMode !== null && nextConnectivityMode !== void 0 ? nextConnectivityMode : (_this$api = this.api) === null || _this$api === void 0 ? void 0 : (_this$api$connectivit = _this$api.connectivity) === null || _this$api$connectivit === void 0 ? void 0 : (_this$api$connectivit2 = _this$api$connectivit.sharedState) === null || _this$api$connectivit2 === void 0 ? void 0 : (_this$api$connectivit3 = _this$api$connectivit2.currentState()) === null || _this$api$connectivit3 === void 0 ? void 0 : _this$api$connectivit3.mode;
|
|
32
|
-
const viewMode = nextViewMode !== null && nextViewMode !== void 0 ? nextViewMode : (_this$api2 = this.api) === null || _this$api2 === void 0 ? void 0 : (_this$api2$editorView = _this$api2.editorViewMode) === null || _this$api2$editorView === void 0 ? void 0 : (_this$api2$editorView2 = _this$api2$editorView.sharedState) === null || _this$api2$editorView2 === void 0 ? void 0 : (_this$api2$editorView3 = _this$api2$editorView2.currentState()) === null || _this$api2$editorView3 === void 0 ? void 0 : _this$api2$editorView3.mode;
|
|
33
|
-
const isOnline = !isOfflineMode(connectivityMode);
|
|
34
|
-
const isEditMode = viewMode !== 'view';
|
|
35
|
-
const shouldBeEditable = isOnline && isEditMode;
|
|
36
|
-
contentDOM === null || contentDOM === void 0 ? void 0 : contentDOM.setAttribute('contenteditable', shouldBeEditable ? 'true' : 'false');
|
|
37
|
-
}
|
|
38
|
-
handleConnectivityModeChange() {
|
|
39
|
-
var _this$api3;
|
|
40
|
-
if ((_this$api3 = this.api) !== null && _this$api3 !== void 0 && _this$api3.connectivity) {
|
|
41
|
-
this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(({
|
|
42
|
-
nextSharedState
|
|
43
|
-
}) => {
|
|
44
|
-
this.updateContentEditable({
|
|
45
|
-
contentDOM: this.contentDOM,
|
|
46
|
-
nextConnectivityMode: nextSharedState.mode
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
handleViewModeChange() {
|
|
52
|
-
var _this$api4;
|
|
53
|
-
if ((_this$api4 = this.api) !== null && _this$api4 !== void 0 && _this$api4.editorViewMode) {
|
|
54
|
-
this.cleanupViewModeListener = this.api.editorViewMode.sharedState.onChange(({
|
|
55
|
-
nextSharedState
|
|
56
|
-
}) => {
|
|
57
|
-
this.updateContentEditable({
|
|
58
|
-
contentDOM: this.contentDOM,
|
|
59
|
-
nextViewMode: nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.mode
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
createDomRef() {
|
|
65
|
-
// eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- NodeView DOM must be created against active runtime document
|
|
66
|
-
const domRef = document.createElement('div');
|
|
67
|
-
domRef.classList.add(BodiedSyncBlockSharedCssClassName.prefix);
|
|
68
|
-
domRef.classList.toggle(BodiedSyncBlockSharedCssClassName.empty, isEmptySourceSyncBlock(this.node));
|
|
69
|
-
return domRef;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Keeps the empty marker class in sync so the source placeholder can be shown purely from
|
|
74
|
-
* document state. See `isEmptySourceSyncBlock` for why the DOM is not used to decide this.
|
|
75
|
-
*/
|
|
76
|
-
update(node, decorations) {
|
|
77
|
-
const updated = super.update(node, decorations);
|
|
78
|
-
if (updated) {
|
|
79
|
-
// `ReactNodeView`'s `dom` getter throws rather than returning nullish once the view is
|
|
80
|
-
// destroyed, so optional chaining here would be misleading. `super.update()` only
|
|
81
|
-
// returns true while the view is still mounted, so the ref is guaranteed.
|
|
82
|
-
this.dom.classList.toggle(BodiedSyncBlockSharedCssClassName.empty, isEmptySourceSyncBlock(this.node));
|
|
83
|
-
}
|
|
84
|
-
return updated;
|
|
85
|
-
}
|
|
86
|
-
render(_props, forwardRef) {
|
|
87
|
-
var _this$api$syncedBlock, _this$api5, _this$api5$syncedBloc, _this$api5$syncedBloc2, _this$api6, _this$api6$analytics;
|
|
88
|
-
// Use passed syncBlockStore for SSR where sharedState.currentState() is delayed
|
|
89
|
-
const syncBlockStore = (_this$api$syncedBlock = (_this$api5 = this.api) === null || _this$api5 === void 0 ? void 0 : (_this$api5$syncedBloc = _this$api5.syncedBlock.sharedState) === null || _this$api5$syncedBloc === void 0 ? void 0 : (_this$api5$syncedBloc2 = _this$api5$syncedBloc.currentState()) === null || _this$api5$syncedBloc2 === void 0 ? void 0 : _this$api5$syncedBloc2.syncBlockStore) !== null && _this$api$syncedBlock !== void 0 ? _this$api$syncedBlock : this.syncBlockStore;
|
|
90
|
-
if (!syncBlockStore) {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
return /*#__PURE__*/React.createElement(ErrorBoundary, {
|
|
94
|
-
component: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
95
|
-
dispatchAnalyticsEvent: (_this$api6 = this.api) === null || _this$api6 === void 0 ? void 0 : (_this$api6$analytics = _this$api6.analytics) === null || _this$api6$analytics === void 0 ? void 0 : _this$api6$analytics.actions.fireAnalyticsEvent,
|
|
96
|
-
fallbackComponent: null
|
|
97
|
-
}, /*#__PURE__*/React.createElement(BodiedSyncBlockWrapper, {
|
|
98
|
-
ref: forwardRef,
|
|
99
|
-
syncBlockStore: syncBlockStore,
|
|
100
|
-
node: this.node
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
|
-
getContentDOM() {
|
|
104
|
-
// eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- NodeView serialization must target active runtime document
|
|
105
|
-
const {
|
|
106
|
-
dom,
|
|
107
|
-
contentDOM
|
|
108
|
-
} = DOMSerializer.renderSpec(document, toDOMOld());
|
|
109
|
-
// In SSR, the first check won't work, so fallback to nodeType check
|
|
110
|
-
if (dom instanceof HTMLElement || dom.nodeType === 1) {
|
|
111
|
-
this.updateContentEditable({
|
|
112
|
-
contentDOM
|
|
113
|
-
});
|
|
114
|
-
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
115
|
-
return {
|
|
116
|
-
dom: dom,
|
|
117
|
-
contentDOM
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
return undefined;
|
|
121
|
-
}
|
|
122
|
-
destroy() {
|
|
123
|
-
if (this.cleanupConnectivityModeListener) {
|
|
124
|
-
this.cleanupConnectivityModeListener();
|
|
125
|
-
}
|
|
126
|
-
if (this.cleanupViewModeListener) {
|
|
127
|
-
this.cleanupViewModeListener();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
export const bodiedSyncBlockNodeViewOld = ({
|
|
132
|
-
pluginOptions,
|
|
133
|
-
pmPluginFactoryParams,
|
|
134
|
-
api,
|
|
135
|
-
syncBlockStore
|
|
136
|
-
}) => (node, view, getPos) => {
|
|
137
|
-
const {
|
|
138
|
-
portalProviderAPI,
|
|
139
|
-
eventDispatcher
|
|
140
|
-
} = pmPluginFactoryParams;
|
|
141
|
-
return new BodiedSyncBlockOld({
|
|
142
|
-
api,
|
|
143
|
-
pluginOptions,
|
|
144
|
-
node,
|
|
145
|
-
view,
|
|
146
|
-
getPos,
|
|
147
|
-
portalProviderAPI,
|
|
148
|
-
eventDispatcher,
|
|
149
|
-
syncBlockStore
|
|
150
|
-
}).init();
|
|
151
|
-
};
|
|
152
11
|
const toDOM = node => ['div', {
|
|
153
12
|
class: `${BodiedSyncBlockSharedCssClassName.prefix} bodiedSyncBlockView-content-wrap`,
|
|
154
13
|
localid: node.attrs.localId,
|
|
@@ -192,11 +51,11 @@ export class BodiedSyncBlock {
|
|
|
192
51
|
}
|
|
193
52
|
this.labelKey = crypto.randomUUID();
|
|
194
53
|
this.nodeViewPortalProviderAPI.render(() => {
|
|
195
|
-
var _this$
|
|
54
|
+
var _this$api, _this$api$analytics;
|
|
196
55
|
return /*#__PURE__*/React.createElement(ErrorBoundary, {
|
|
197
56
|
component: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
198
57
|
componentId: ACTION_SUBJECT_ID.SYNCED_BLOCK_LABEL,
|
|
199
|
-
dispatchAnalyticsEvent: (_this$
|
|
58
|
+
dispatchAnalyticsEvent: (_this$api = this.api) === null || _this$api === void 0 ? void 0 : (_this$api$analytics = _this$api.analytics) === null || _this$api$analytics === void 0 ? void 0 : _this$api$analytics.actions.fireAnalyticsEvent,
|
|
200
59
|
fallbackComponent: null
|
|
201
60
|
}, /*#__PURE__*/React.createElement(SyncBlockLabel, {
|
|
202
61
|
isSource: true,
|
|
@@ -215,17 +74,17 @@ export class BodiedSyncBlock {
|
|
|
215
74
|
nextConnectivityMode,
|
|
216
75
|
nextViewMode
|
|
217
76
|
}) {
|
|
218
|
-
var _this$
|
|
219
|
-
const connectivityMode = nextConnectivityMode !== null && nextConnectivityMode !== void 0 ? nextConnectivityMode : (_this$
|
|
220
|
-
const viewMode = nextViewMode !== null && nextViewMode !== void 0 ? nextViewMode : (_this$
|
|
77
|
+
var _this$api2, _this$api2$connectivi, _this$api2$connectivi2, _this$api2$connectivi3, _this$api3, _this$api3$editorView, _this$api3$editorView2, _this$api3$editorView3;
|
|
78
|
+
const connectivityMode = nextConnectivityMode !== null && nextConnectivityMode !== void 0 ? nextConnectivityMode : (_this$api2 = this.api) === null || _this$api2 === void 0 ? void 0 : (_this$api2$connectivi = _this$api2.connectivity) === null || _this$api2$connectivi === void 0 ? void 0 : (_this$api2$connectivi2 = _this$api2$connectivi.sharedState) === null || _this$api2$connectivi2 === void 0 ? void 0 : (_this$api2$connectivi3 = _this$api2$connectivi2.currentState()) === null || _this$api2$connectivi3 === void 0 ? void 0 : _this$api2$connectivi3.mode;
|
|
79
|
+
const viewMode = nextViewMode !== null && nextViewMode !== void 0 ? nextViewMode : (_this$api3 = this.api) === null || _this$api3 === void 0 ? void 0 : (_this$api3$editorView = _this$api3.editorViewMode) === null || _this$api3$editorView === void 0 ? void 0 : (_this$api3$editorView2 = _this$api3$editorView.sharedState) === null || _this$api3$editorView2 === void 0 ? void 0 : (_this$api3$editorView3 = _this$api3$editorView2.currentState()) === null || _this$api3$editorView3 === void 0 ? void 0 : _this$api3$editorView3.mode;
|
|
221
80
|
const isOnline = !isOfflineMode(connectivityMode);
|
|
222
81
|
const isEditMode = viewMode !== 'view';
|
|
223
82
|
const shouldBeEditable = isOnline && isEditMode;
|
|
224
83
|
this.contentDOM.setAttribute('contenteditable', shouldBeEditable ? 'true' : 'false');
|
|
225
84
|
}
|
|
226
85
|
handleConnectivityModeChange() {
|
|
227
|
-
var _this$
|
|
228
|
-
if ((_this$
|
|
86
|
+
var _this$api4;
|
|
87
|
+
if ((_this$api4 = this.api) !== null && _this$api4 !== void 0 && _this$api4.connectivity) {
|
|
229
88
|
this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(({
|
|
230
89
|
nextSharedState
|
|
231
90
|
}) => {
|
|
@@ -236,8 +95,8 @@ export class BodiedSyncBlock {
|
|
|
236
95
|
}
|
|
237
96
|
}
|
|
238
97
|
handleViewModeChange() {
|
|
239
|
-
var _this$
|
|
240
|
-
if ((_this$
|
|
98
|
+
var _this$api5;
|
|
99
|
+
if ((_this$api5 = this.api) !== null && _this$api5 !== void 0 && _this$api5.editorViewMode) {
|
|
241
100
|
this.cleanupViewModeListener = this.api.editorViewMode.sharedState.onChange(({
|
|
242
101
|
nextSharedState
|
|
243
102
|
}) => {
|
|
@@ -6,7 +6,6 @@ import ReactNodeView from '@atlaskit/editor-common/react-node-view';
|
|
|
6
6
|
import { SyncBlockSharedCssClassName, SyncBlockLabelSharedCssClassName, SyncBlockActionsProvider } from '@atlaskit/editor-common/sync-block';
|
|
7
7
|
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
8
8
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
9
|
-
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
10
9
|
import { removeSyncedBlockAtPos } from '../editor-commands';
|
|
11
10
|
import { SyncBlockRendererWrapper } from '../ui/SyncBlockRendererWrapper';
|
|
12
11
|
import { SyncBlockSSRReactContextsProvider } from '../ui/SyncBlockSSRReactContextsProvider';
|
|
@@ -184,10 +183,6 @@ export class SyncBlock extends ReactNodeView {
|
|
|
184
183
|
return null;
|
|
185
184
|
}
|
|
186
185
|
|
|
187
|
-
// Use expValEqualsNoExposure — the exposure is already fired once at plugin
|
|
188
|
-
// creation time in syncedBlockPlugin.tsx and main.ts createPlugin().
|
|
189
|
-
const isPerfEnabled = expValEqualsNoExposure('editor_synced_block_perf', 'isEnabled', true);
|
|
190
|
-
|
|
191
186
|
// get document node from data provider
|
|
192
187
|
return /*#__PURE__*/React.createElement(SyncBlockSSRReactContextsProvider, {
|
|
193
188
|
intl: this.intl
|
|
@@ -195,18 +190,9 @@ export class SyncBlock extends ReactNodeView {
|
|
|
195
190
|
component: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
196
191
|
dispatchAnalyticsEvent: (_this$api3 = this.api) === null || _this$api3 === void 0 ? void 0 : (_this$api3$analytics = _this$api3.analytics) === null || _this$api3$analytics === void 0 ? void 0 : _this$api3$analytics.actions.fireAnalyticsEvent,
|
|
197
192
|
fallbackComponent: null
|
|
198
|
-
}, /*#__PURE__*/React.createElement(SyncBlockActionsProvider
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
removeSyncBlock: isPerfEnabled ? this.removeSyncBlockStable : () => {
|
|
202
|
-
const pos = getPos();
|
|
203
|
-
if (pos !== undefined) {
|
|
204
|
-
removeSyncedBlockAtPos(this.api, pos);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
|
|
208
|
-
,
|
|
209
|
-
fetchSyncBlockSourceInfo: isPerfEnabled ? this.fetchSyncBlockSourceInfoStable : sourceAri => syncBlockStore.referenceManager.fetchSyncBlockSourceInfoBySourceAri(sourceAri)
|
|
193
|
+
}, /*#__PURE__*/React.createElement(SyncBlockActionsProvider, {
|
|
194
|
+
removeSyncBlock: this.removeSyncBlockStable,
|
|
195
|
+
fetchSyncBlockSourceInfo: this.fetchSyncBlockSourceInfoStable
|
|
210
196
|
}, /*#__PURE__*/React.createElement(SyncBlockRendererWrapper, {
|
|
211
197
|
localId: localId,
|
|
212
198
|
resourceId: resourceId,
|
|
@@ -12,10 +12,8 @@ import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
|
12
12
|
import { convertPMNodesToSyncBlockNodes, rebaseTransaction } from '@atlaskit/editor-synced-block-provider';
|
|
13
13
|
import { getSourceProductFromResourceIdSafe } from '@atlaskit/editor-synced-block-provider/utils';
|
|
14
14
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
15
|
-
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
16
15
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
17
|
-
import {
|
|
18
|
-
import { bodiedSyncBlockNodeView, bodiedSyncBlockNodeViewOld } from '../nodeviews/bodiedSyncedBlock';
|
|
16
|
+
import { bodiedSyncBlockNodeView } from '../nodeviews/bodiedSyncedBlock';
|
|
19
17
|
import { SyncBlock as SyncBlockView } from '../nodeviews/syncedBlock';
|
|
20
18
|
import { FLAG_ID } from '../types';
|
|
21
19
|
import { handleBodiedSyncBlockCreation } from './utils/handle-bodied-sync-block-creation';
|
|
@@ -49,13 +47,13 @@ const syncedBlockPromptedFeedbackMetaKey = new PluginKey('syncedBlockPromptedFee
|
|
|
49
47
|
*/
|
|
50
48
|
|
|
51
49
|
export const creationMetaKey = new PluginKey('syncedBlockCreationMeta');
|
|
52
|
-
const mapRetryCreationPosMap = (oldMap, newRetryCreationPos, mapPos
|
|
50
|
+
const mapRetryCreationPosMap = (oldMap, newRetryCreationPos, mapPos) => {
|
|
53
51
|
const resourceId = newRetryCreationPos === null || newRetryCreationPos === void 0 ? void 0 : newRetryCreationPos.resourceId;
|
|
54
52
|
|
|
55
53
|
// Fast path: no new entry and nothing to remap — return the same reference.
|
|
56
54
|
// This is critical for PR-E (EDITOR-6929) which relies on reference equality
|
|
57
55
|
// to short-circuit SharedStateAPI deep-equality checks.
|
|
58
|
-
if (
|
|
56
|
+
if (!resourceId && oldMap.size === 0) {
|
|
59
57
|
return oldMap;
|
|
60
58
|
}
|
|
61
59
|
const newMap = new Map(oldMap);
|
|
@@ -574,12 +572,6 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
574
572
|
const {
|
|
575
573
|
useLongPressSelection = false
|
|
576
574
|
} = options || {};
|
|
577
|
-
|
|
578
|
-
// Cache the experiment value once at plugin creation time.
|
|
579
|
-
// This fires the exposure event exactly once (correct per Exposure Events 101)
|
|
580
|
-
// and avoids ~10 redundant Statsig SDK evaluations per keystroke in hot paths
|
|
581
|
-
// (apply, filterTransaction, appendTransaction, decorations).
|
|
582
|
-
const isPerfExperimentOn = expValEquals('editor_synced_block_perf', 'isEnabled', true);
|
|
583
575
|
const ctx = new SyncedBlockPluginContext(options === null || options === void 0 ? void 0 : options.onGiveFeedback, options === null || options === void 0 ? void 0 : options.shouldShowFeedbackPrompt, api);
|
|
584
576
|
const confirmationTransactionRef = ctx.confirmationTransactionRef;
|
|
585
577
|
const unpublishedFlagShown = ctx.unpublishedFlagShown;
|
|
@@ -634,11 +626,11 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
634
626
|
state: {
|
|
635
627
|
init(_, instance) {
|
|
636
628
|
var _api$connectivity2, _api$connectivity2$sh, _api$editorViewMode, _api$editorViewMode$s, _api$userIntent, _api$userIntent$share;
|
|
637
|
-
// When
|
|
638
|
-
//
|
|
639
|
-
//
|
|
640
|
-
//
|
|
641
|
-
const docHasSyncedBlocks =
|
|
629
|
+
// When the document has no synced blocks, we skip the eager fetch +
|
|
630
|
+
// cache walks. They will be re-run lazily by `apply` the first time a
|
|
631
|
+
// synced block enters the document (paste, collab insert, or
|
|
632
|
+
// programmatic insert).
|
|
633
|
+
const docHasSyncedBlocks = hasSyncedBlocks(instance.doc);
|
|
642
634
|
if (docHasSyncedBlocks) {
|
|
643
635
|
const syncBlockNodes = instance.doc.children.filter(syncBlockStore.referenceManager.isReferenceBlock);
|
|
644
636
|
syncBlockStore.referenceManager.fetchSyncBlocksData(convertPMNodesToSyncBlockNodes(syncBlockNodes));
|
|
@@ -664,7 +656,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
664
656
|
// When the perf gate is ON and the doc has synced blocks we do a
|
|
665
657
|
// single traversal here; afterwards `apply()` will map or rebuild
|
|
666
658
|
// only when a status signal changes.
|
|
667
|
-
const initStatusDecorationSet = docHasSyncedBlocks
|
|
659
|
+
const initStatusDecorationSet = docHasSyncedBlocks ? buildStatusDecorations(instance.doc, syncBlockStore, initIsOffline, initIsViewMode, initIsDragging) : DecorationSet.empty;
|
|
668
660
|
return {
|
|
669
661
|
selectionDecorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema),
|
|
670
662
|
activeFlag: false,
|
|
@@ -681,8 +673,6 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
681
673
|
apply: (tr, currentPluginState, oldEditorState) => {
|
|
682
674
|
var _api$userIntent2, _api$userIntent2$shar, _api$connectivity3, _api$connectivity3$sh, _api$editorViewMode2, _api$editorViewMode2$, _meta$activeFlag, _meta$bodiedSyncBlock;
|
|
683
675
|
const meta = tr.getMeta(syncedBlockPluginKey);
|
|
684
|
-
// isPerfExperimentOn is cached at createPlugin() level — see above
|
|
685
|
-
|
|
686
676
|
const {
|
|
687
677
|
activeFlag,
|
|
688
678
|
selectionDecorationSet,
|
|
@@ -714,14 +704,14 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
714
704
|
// status decoration rebuilds (e.g. drag border). Without this check the
|
|
715
705
|
// fast path would swallow drag-start transactions and the synced block
|
|
716
706
|
// border would never appear (see VR test: synced-block-drag-selection).
|
|
717
|
-
if (
|
|
707
|
+
if (!meta && !tr.docChanged && tr.selection.eq(oldEditorState.selection) && nextHasUnsavedBodiedSyncBlockChanges === currentPluginState.hasUnsavedBodiedSyncBlockChanges && (api === null || api === void 0 ? void 0 : (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : (_api$userIntent2$shar = _api$userIntent2.sharedState.currentState()) === null || _api$userIntent2$shar === void 0 ? void 0 : _api$userIntent2$shar.currentUserIntent) === 'dragging' === prevDragging && isOfflineMode(api === null || api === void 0 ? void 0 : (_api$connectivity3 = api.connectivity) === null || _api$connectivity3 === void 0 ? void 0 : (_api$connectivity3$sh = _api$connectivity3.sharedState.currentState()) === null || _api$connectivity3$sh === void 0 ? void 0 : _api$connectivity3$sh.mode) === prevOffline && (api === null || api === void 0 ? void 0 : (_api$editorViewMode2 = api.editorViewMode) === null || _api$editorViewMode2 === void 0 ? void 0 : (_api$editorViewMode2$ = _api$editorViewMode2.sharedState.currentState()) === null || _api$editorViewMode2$ === void 0 ? void 0 : _api$editorViewMode2$.mode) === 'view' === prevViewMode) {
|
|
718
708
|
return currentPluginState;
|
|
719
709
|
}
|
|
720
710
|
|
|
721
711
|
// Lazy-init bookkeeping: once a synced block enters the document we
|
|
722
712
|
// flip `hasSyncedBlocks` to `true` for the lifetime of this editor
|
|
723
713
|
let nextHasSyncedBlocks = prevHasSyncedBlocks;
|
|
724
|
-
if (!prevHasSyncedBlocks && tr.docChanged
|
|
714
|
+
if (!prevHasSyncedBlocks && tr.docChanged) {
|
|
725
715
|
if (transactionInsertsSyncedBlock(tr)) {
|
|
726
716
|
nextHasSyncedBlocks = true;
|
|
727
717
|
}
|
|
@@ -749,42 +739,40 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
749
739
|
let nextIsOffline = prevOffline;
|
|
750
740
|
let nextIsViewMode = prevViewMode;
|
|
751
741
|
let nextIsDragging = prevDragging;
|
|
752
|
-
if (
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
nextIsDragging = (api === null || api === void 0 ? void 0 : (_api$userIntent3 = api.userIntent) === null || _api$userIntent3 === void 0 ? void 0 : (_api$userIntent3$shar = _api$userIntent3.sharedState.currentState()) === null || _api$userIntent3$shar === void 0 ? void 0 : _api$userIntent3$shar.currentUserIntent) === 'dragging';
|
|
742
|
+
if (!nextHasSyncedBlocks) {
|
|
743
|
+
// No synced blocks → keep empty status decorations
|
|
744
|
+
nextStatusDecorationSet = DecorationSet.empty;
|
|
745
|
+
} else {
|
|
746
|
+
var _api$connectivity4, _api$connectivity4$sh, _api$editorViewMode3, _api$editorViewMode3$, _api$userIntent3, _api$userIntent3$shar;
|
|
747
|
+
// Read current shared-state signals
|
|
748
|
+
nextIsOffline = isOfflineMode(api === null || api === void 0 ? void 0 : (_api$connectivity4 = api.connectivity) === null || _api$connectivity4 === void 0 ? void 0 : (_api$connectivity4$sh = _api$connectivity4.sharedState.currentState()) === null || _api$connectivity4$sh === void 0 ? void 0 : _api$connectivity4$sh.mode);
|
|
749
|
+
nextIsViewMode = (api === null || api === void 0 ? void 0 : (_api$editorViewMode3 = api.editorViewMode) === null || _api$editorViewMode3 === void 0 ? void 0 : (_api$editorViewMode3$ = _api$editorViewMode3.sharedState.currentState()) === null || _api$editorViewMode3$ === void 0 ? void 0 : _api$editorViewMode3$.mode) === 'view';
|
|
750
|
+
nextIsDragging = (api === null || api === void 0 ? void 0 : (_api$userIntent3 = api.userIntent) === null || _api$userIntent3 === void 0 ? void 0 : (_api$userIntent3$shar = _api$userIntent3.sharedState.currentState()) === null || _api$userIntent3$shar === void 0 ? void 0 : _api$userIntent3$shar.currentUserIntent) === 'dragging';
|
|
762
751
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
// else: nothing changed, keep same reference
|
|
752
|
+
// Determine whether we need a full rebuild or a cheap map
|
|
753
|
+
const hasSyncedBlocksJustFlipped = nextHasSyncedBlocks && !prevHasSyncedBlocks;
|
|
754
|
+
const statusSignalChanged = nextIsOffline !== prevOffline || nextIsViewMode !== prevViewMode || nextIsDragging !== prevDragging;
|
|
755
|
+
// Meta-driven status changes (e.g. pending creation
|
|
756
|
+
// completed, retry creation pos updated)
|
|
757
|
+
const hasMetaStatusChange = !!(meta !== null && meta !== void 0 && meta.retryCreationPos) || !!(meta !== null && meta !== void 0 && meta.activeFlag);
|
|
758
|
+
if (hasSyncedBlocksJustFlipped || statusSignalChanged || hasMetaStatusChange) {
|
|
759
|
+
// Full rebuild — a status signal changed
|
|
760
|
+
nextStatusDecorationSet = buildStatusDecorations(tr.doc, syncBlockStore, nextIsOffline, nextIsViewMode, nextIsDragging);
|
|
761
|
+
} else if (tr.docChanged) {
|
|
762
|
+
// Cheap map — positions shifted but status unchanged
|
|
763
|
+
nextStatusDecorationSet = prevStatusDecorationSet.map(tr.mapping, tr.doc);
|
|
777
764
|
}
|
|
765
|
+
// else: nothing changed, keep same reference
|
|
778
766
|
}
|
|
779
767
|
const newPosEntry = meta === null || meta === void 0 ? void 0 : meta.retryCreationPos;
|
|
780
|
-
const newRetryCreationPosMap = mapRetryCreationPosMap(retryCreationPosMap, newPosEntry, tr.mapping.map.bind(tr.mapping)
|
|
768
|
+
const newRetryCreationPosMap = mapRetryCreationPosMap(retryCreationPosMap, newPosEntry, tr.mapping.map.bind(tr.mapping));
|
|
781
769
|
const nextActiveFlag = (_meta$activeFlag = meta === null || meta === void 0 ? void 0 : meta.activeFlag) !== null && _meta$activeFlag !== void 0 ? _meta$activeFlag : activeFlag;
|
|
782
770
|
const nextBodiedSyncBlockDeletionStatus = (_meta$bodiedSyncBlock = meta === null || meta === void 0 ? void 0 : meta.bodiedSyncBlockDeletionStatus) !== null && _meta$bodiedSyncBlock !== void 0 ? _meta$bodiedSyncBlock : bodiedSyncBlockDeletionStatus;
|
|
783
771
|
|
|
784
772
|
// --- Reference equality (EDITOR-6929): return the same object
|
|
785
773
|
// when ALL fields are reference-equal to avoid SharedStateAPI
|
|
786
774
|
// notifying subscribers and triggering React re-renders. ---
|
|
787
|
-
if (nextActiveFlag === activeFlag && newDecorationSet === selectionDecorationSet && newRetryCreationPosMap === retryCreationPosMap && nextHasSyncedBlocks === prevHasSyncedBlocks && nextBodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && nextHasUnsavedBodiedSyncBlockChanges === currentPluginState.hasUnsavedBodiedSyncBlockChanges && nextStatusDecorationSet === prevStatusDecorationSet && nextIsOffline === prevOffline && nextIsViewMode === prevViewMode && nextIsDragging === prevDragging
|
|
775
|
+
if (nextActiveFlag === activeFlag && newDecorationSet === selectionDecorationSet && newRetryCreationPosMap === retryCreationPosMap && nextHasSyncedBlocks === prevHasSyncedBlocks && nextBodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && nextHasUnsavedBodiedSyncBlockChanges === currentPluginState.hasUnsavedBodiedSyncBlockChanges && nextStatusDecorationSet === prevStatusDecorationSet && nextIsOffline === prevOffline && nextIsViewMode === prevViewMode && nextIsDragging === prevDragging) {
|
|
788
776
|
return currentPluginState;
|
|
789
777
|
}
|
|
790
778
|
return {
|
|
@@ -820,14 +808,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
820
808
|
syncBlockStore: syncBlockStore,
|
|
821
809
|
intl: pmPluginFactoryParams.getIntl()
|
|
822
810
|
}).init(),
|
|
823
|
-
bodiedSyncBlock:
|
|
824
|
-
exposure: true
|
|
825
|
-
}) ? bodiedSyncBlockNodeView({
|
|
826
|
-
pluginOptions: options,
|
|
827
|
-
pmPluginFactoryParams,
|
|
828
|
-
api,
|
|
829
|
-
syncBlockStore
|
|
830
|
-
}) : bodiedSyncBlockNodeViewOld({
|
|
811
|
+
bodiedSyncBlock: bodiedSyncBlockNodeView({
|
|
831
812
|
pluginOptions: options,
|
|
832
813
|
pmPluginFactoryParams,
|
|
833
814
|
api,
|
|
@@ -835,6 +816,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
835
816
|
})
|
|
836
817
|
},
|
|
837
818
|
decorations: state => {
|
|
819
|
+
var _api$focus$sharedStat, _api$focus, _api$focus$sharedStat2, _api$focus$sharedStat3;
|
|
838
820
|
const currentPluginState = syncedBlockPluginKey.getState(state);
|
|
839
821
|
if (!currentPluginState) {
|
|
840
822
|
return DecorationSet.empty;
|
|
@@ -850,70 +832,24 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
850
832
|
// `apply()`. The `decorations` prop is now an O(1) merge of
|
|
851
833
|
// the two cached sets — no `doc.descendants()` walk, no
|
|
852
834
|
// shared-state reads.
|
|
853
|
-
if (
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
return selectionDecorationSet;
|
|
857
|
-
}
|
|
835
|
+
if (!docHasSyncedBlocks) {
|
|
836
|
+
return selectionDecorationSet;
|
|
837
|
+
}
|
|
858
838
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
839
|
+
// Focus state is read live here (single cheap read) because
|
|
840
|
+
// it only gates whether selection decorations are included —
|
|
841
|
+
// it does not affect the status decoration set and can change
|
|
842
|
+
// within the same transaction cycle.
|
|
843
|
+
const hasFocus = (_api$focus$sharedStat = api === null || api === void 0 ? void 0 : (_api$focus = api.focus) === null || _api$focus === void 0 ? void 0 : (_api$focus$sharedStat2 = _api$focus.sharedState) === null || _api$focus$sharedStat2 === void 0 ? void 0 : (_api$focus$sharedStat3 = _api$focus$sharedStat2.currentState()) === null || _api$focus$sharedStat3 === void 0 ? void 0 : _api$focus$sharedStat3.hasFocus) !== null && _api$focus$sharedStat !== void 0 ? _api$focus$sharedStat : true;
|
|
864
844
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
} else {
|
|
872
|
-
return hasFocus ? selectionDecorationSet.add(state.doc, statusDecorations) : statusDecorationSet;
|
|
873
|
-
}
|
|
845
|
+
// Merge selection + status decorations.
|
|
846
|
+
// When the editor is unfocused,
|
|
847
|
+
// omit selection decorations (matches old behaviour).
|
|
848
|
+
const statusDecorations = statusDecorationSet.find();
|
|
849
|
+
if (statusDecorations.length === 0) {
|
|
850
|
+
return hasFocus ? selectionDecorationSet : DecorationSet.empty;
|
|
874
851
|
} else {
|
|
875
|
-
|
|
876
|
-
// --- Legacy path (perf gate OFF) ---
|
|
877
|
-
// Full `doc.descendants()` walk every transaction. Preserved
|
|
878
|
-
// for safe rollback.
|
|
879
|
-
const syncBlockStore = currentPluginState.syncBlockStore;
|
|
880
|
-
const {
|
|
881
|
-
doc
|
|
882
|
-
} = state;
|
|
883
|
-
const isOffline = isOfflineMode(api === null || api === void 0 ? void 0 : (_api$connectivity5 = api.connectivity) === null || _api$connectivity5 === void 0 ? void 0 : (_api$connectivity5$sh = _api$connectivity5.sharedState.currentState()) === null || _api$connectivity5$sh === void 0 ? void 0 : _api$connectivity5$sh.mode);
|
|
884
|
-
const isViewMode = (api === null || api === void 0 ? void 0 : (_api$editorViewMode4 = api.editorViewMode) === null || _api$editorViewMode4 === void 0 ? void 0 : (_api$editorViewMode4$ = _api$editorViewMode4.sharedState.currentState()) === null || _api$editorViewMode4$ === void 0 ? void 0 : _api$editorViewMode4$.mode) === 'view';
|
|
885
|
-
const isDragging = (api === null || api === void 0 ? void 0 : (_api$userIntent4 = api.userIntent) === null || _api$userIntent4 === void 0 ? void 0 : (_api$userIntent4$shar = _api$userIntent4.sharedState.currentState()) === null || _api$userIntent4$shar === void 0 ? void 0 : _api$userIntent4$shar.currentUserIntent) === 'dragging';
|
|
886
|
-
const offlineDecorations = [];
|
|
887
|
-
const viewModeDecorations = [];
|
|
888
|
-
const loadingDecorations = [];
|
|
889
|
-
const dragDecorations = [];
|
|
890
|
-
state.doc.descendants((node, pos) => {
|
|
891
|
-
if (node.type.name === 'bodiedSyncBlock' && isOffline) {
|
|
892
|
-
offlineDecorations.push(Decoration.node(pos, pos + node.nodeSize, {
|
|
893
|
-
class: SyncBlockStateCssClassName.disabledClassName
|
|
894
|
-
}));
|
|
895
|
-
}
|
|
896
|
-
if (syncBlockStore.isSyncBlock(node) && isViewMode) {
|
|
897
|
-
viewModeDecorations.push(Decoration.node(pos, pos + node.nodeSize, {
|
|
898
|
-
class: SyncBlockStateCssClassName.viewModeClassName
|
|
899
|
-
}));
|
|
900
|
-
}
|
|
901
|
-
if (node.type.name === 'bodiedSyncBlock' && syncBlockStore.sourceManager.isPendingCreation(node.attrs.resourceId)) {
|
|
902
|
-
loadingDecorations.push(Decoration.node(pos, pos + node.nodeSize, {
|
|
903
|
-
class: SyncBlockStateCssClassName.creationLoadingClassName
|
|
904
|
-
}));
|
|
905
|
-
}
|
|
906
|
-
if (isDragging && syncBlockStore.isSyncBlock(node)) {
|
|
907
|
-
dragDecorations.push(Decoration.node(pos, pos + node.nodeSize, {
|
|
908
|
-
class: SyncBlockStateCssClassName.draggingClassName
|
|
909
|
-
}));
|
|
910
|
-
}
|
|
911
|
-
});
|
|
912
|
-
if (api !== null && api !== void 0 && (_api$focus2 = api.focus) !== null && _api$focus2 !== void 0 && (_api$focus2$sharedSta = _api$focus2.sharedState) !== null && _api$focus2$sharedSta !== void 0 && (_api$focus2$sharedSta2 = _api$focus2$sharedSta.currentState()) !== null && _api$focus2$sharedSta2 !== void 0 && _api$focus2$sharedSta2.hasFocus) {
|
|
913
|
-
return selectionDecorationSet.add(doc, offlineDecorations).add(doc, viewModeDecorations).add(doc, loadingDecorations).add(doc, dragDecorations);
|
|
914
|
-
} else {
|
|
915
|
-
return DecorationSet.empty.add(doc, offlineDecorations).add(doc, viewModeDecorations).add(doc, loadingDecorations).add(doc, dragDecorations);
|
|
916
|
-
}
|
|
852
|
+
return hasFocus ? selectionDecorationSet.add(state.doc, statusDecorations) : statusDecorationSet;
|
|
917
853
|
}
|
|
918
854
|
},
|
|
919
855
|
handleClickOn: createSelectionClickHandler(['bodiedSyncBlock'], target => !!target.closest(`.${BodiedSyncBlockSharedCssClassName.prefix}`), {
|
|
@@ -1037,22 +973,20 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
1037
973
|
}
|
|
1038
974
|
},
|
|
1039
975
|
filterTransaction: (tr, state) => {
|
|
1040
|
-
var _api$
|
|
976
|
+
var _api$editorViewMode4, _api$editorViewMode4$, _api$connectivity5, _api$connectivity5$sh;
|
|
1041
977
|
// Lazy-init: when no synced block currently exists in the doc and the
|
|
1042
978
|
// transaction does not insert one, all downstream filter logic is a
|
|
1043
979
|
// no-op. Avoid both the shared-state reads and the `trackSyncBlocks`
|
|
1044
980
|
// walks for the ~99.97% of pages that have no synced blocks.
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
return true;
|
|
1049
|
-
}
|
|
981
|
+
const pluginState = syncedBlockPluginKey.getState(state);
|
|
982
|
+
if (pluginState && !pluginState.hasSyncedBlocks && !transactionInsertsSyncedBlock(tr)) {
|
|
983
|
+
return true;
|
|
1050
984
|
}
|
|
1051
|
-
const viewMode = api === null || api === void 0 ? void 0 : (_api$
|
|
985
|
+
const viewMode = api === null || api === void 0 ? void 0 : (_api$editorViewMode4 = api.editorViewMode) === null || _api$editorViewMode4 === void 0 ? void 0 : (_api$editorViewMode4$ = _api$editorViewMode4.sharedState.currentState()) === null || _api$editorViewMode4$ === void 0 ? void 0 : _api$editorViewMode4$.mode;
|
|
1052
986
|
if (viewMode === 'view') {
|
|
1053
987
|
return true;
|
|
1054
988
|
}
|
|
1055
|
-
const isOffline = isOfflineMode(api === null || api === void 0 ? void 0 : (_api$
|
|
989
|
+
const isOffline = isOfflineMode(api === null || api === void 0 ? void 0 : (_api$connectivity5 = api.connectivity) === null || _api$connectivity5 === void 0 ? void 0 : (_api$connectivity5$sh = _api$connectivity5.sharedState.currentState()) === null || _api$connectivity5$sh === void 0 ? void 0 : _api$connectivity5$sh.mode);
|
|
1056
990
|
const isConfirmedSyncBlockDeletion = Boolean(tr.getMeta('isConfirmedSyncBlockDeletion'));
|
|
1057
991
|
|
|
1058
992
|
// Track newly added reference sync blocks before processing the transaction
|
|
@@ -1120,20 +1054,18 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
1120
1054
|
});
|
|
1121
1055
|
},
|
|
1122
1056
|
appendTransaction: (trs, oldState, newState) => {
|
|
1123
|
-
var _api$
|
|
1057
|
+
var _api$editorViewMode5, _api$editorViewMode5$;
|
|
1124
1058
|
// Lazy-init: when neither the previous nor the new state contains a
|
|
1125
1059
|
// synced block (and none of the dispatched transactions inserts one),
|
|
1126
1060
|
// skip all downstream work. This is the hot path on the ~99.97% of
|
|
1127
1061
|
// pages that don't use synced blocks (see EDITOR-6586).
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
return null;
|
|
1134
|
-
}
|
|
1062
|
+
const oldPluginState = syncedBlockPluginKey.getState(oldState);
|
|
1063
|
+
const newPluginState = syncedBlockPluginKey.getState(newState);
|
|
1064
|
+
const hadOrHasSyncedBlocks = !!(oldPluginState !== null && oldPluginState !== void 0 && oldPluginState.hasSyncedBlocks) || !!(newPluginState !== null && newPluginState !== void 0 && newPluginState.hasSyncedBlocks);
|
|
1065
|
+
if (!hadOrHasSyncedBlocks) {
|
|
1066
|
+
return null;
|
|
1135
1067
|
}
|
|
1136
|
-
const viewMode = api === null || api === void 0 ? void 0 : (_api$
|
|
1068
|
+
const viewMode = api === null || api === void 0 ? void 0 : (_api$editorViewMode5 = api.editorViewMode) === null || _api$editorViewMode5 === void 0 ? void 0 : (_api$editorViewMode5$ = _api$editorViewMode5.sharedState.currentState()) === null || _api$editorViewMode5$ === void 0 ? void 0 : _api$editorViewMode5$.mode;
|
|
1137
1069
|
if (viewMode === 'view') {
|
|
1138
1070
|
return null;
|
|
1139
1071
|
}
|
|
@@ -3,7 +3,6 @@ import { ACTION, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
|
|
3
3
|
import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getNodeQuery, getPopupContainerFromEditorView, popupWithNestedElement, getSelectionAncestorDOM } from '@atlaskit/editor-common/experiences';
|
|
4
4
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
5
5
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
6
|
-
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
7
6
|
import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
|
|
8
7
|
import { syncedBlockPluginKey } from './main';
|
|
9
8
|
const TIMEOUT_DURATION = 30000;
|
|
@@ -14,10 +13,6 @@ export const getMenuAndToolbarExperiencesPlugin = ({
|
|
|
14
13
|
refs,
|
|
15
14
|
dispatchAnalyticsEvent
|
|
16
15
|
}) => {
|
|
17
|
-
// Cache the experiment value once at plugin creation time.
|
|
18
|
-
// This fires the exposure event exactly once (correct per Exposure Events 101)
|
|
19
|
-
// and avoids redundant Statsig SDK evaluations in the view() and update() hooks.
|
|
20
|
-
const isPerfExperimentOn = expValEquals('editor_synced_block_perf', 'isEnabled', true);
|
|
21
16
|
let popupsTargetEl;
|
|
22
17
|
const editorViewRef = {
|
|
23
18
|
current: undefined
|
|
@@ -180,13 +175,13 @@ export const getMenuAndToolbarExperiencesPlugin = ({
|
|
|
180
175
|
unbindClickListener = unbinders.unbindClickListener;
|
|
181
176
|
unbindKeydownListener = unbinders.unbindKeydownListener;
|
|
182
177
|
};
|
|
183
|
-
if ((_syncedBlockPluginKey = syncedBlockPluginKey.getState(view.state)) !== null && _syncedBlockPluginKey !== void 0 && _syncedBlockPluginKey.hasSyncedBlocks
|
|
178
|
+
if ((_syncedBlockPluginKey = syncedBlockPluginKey.getState(view.state)) !== null && _syncedBlockPluginKey !== void 0 && _syncedBlockPluginKey.hasSyncedBlocks) {
|
|
184
179
|
ensureListenersBound();
|
|
185
180
|
}
|
|
186
181
|
return {
|
|
187
182
|
update: (view, prevState) => {
|
|
188
183
|
var _syncedBlockPluginKey2;
|
|
189
|
-
if (!listenersBound && view.state.doc !== prevState.doc && (_syncedBlockPluginKey2 = syncedBlockPluginKey.getState(view.state)) !== null && _syncedBlockPluginKey2 !== void 0 && _syncedBlockPluginKey2.hasSyncedBlocks
|
|
184
|
+
if (!listenersBound && view.state.doc !== prevState.doc && (_syncedBlockPluginKey2 = syncedBlockPluginKey.getState(view.state)) !== null && _syncedBlockPluginKey2 !== void 0 && _syncedBlockPluginKey2.hasSyncedBlocks) {
|
|
190
185
|
// Bind listeners now that synced blocks are present.
|
|
191
186
|
ensureListenersBound();
|
|
192
187
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Both `syncBlock` and `bodiedSyncBlock` are top-level nodes in the schema, so
|
|
6
6
|
* a single shallow scan is sufficient — no need for a recursive `descendants`
|
|
7
|
-
* walk. This is used
|
|
8
|
-
*
|
|
7
|
+
* walk. This is used to avoid spinning up the synced block subsystem on
|
|
8
|
+
* documents that don't contain any
|
|
9
9
|
* synced blocks (which is ~99.97% of pages, see EDITOR-6586).
|
|
10
10
|
*/
|
|
11
11
|
export const hasSyncedBlocks = doc => {
|