@atlaskit/editor-plugin-synced-block 8.2.10 → 8.2.11
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 +11 -0
- package/dist/cjs/pm-plugins/main.js +25 -6
- package/dist/cjs/syncedBlockPlugin.js +64 -25
- package/dist/es2019/pm-plugins/main.js +25 -6
- package/dist/es2019/syncedBlockPlugin.js +52 -10
- package/dist/esm/pm-plugins/main.js +25 -6
- package/dist/esm/syncedBlockPlugin.js +65 -25
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-synced-block
|
|
2
2
|
|
|
3
|
+
## 8.2.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`a160344820ea5`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a160344820ea5) -
|
|
8
|
+
EDITOR-6929: Fix React re-render cascade by (1) returning same plugin state reference from apply()
|
|
9
|
+
when nothing changed, (2) memoizing getSharedState to return a stable reference, and (3) guarding
|
|
10
|
+
contentComponent to skip rendering when hasSyncedBlocks is false. All gated behind
|
|
11
|
+
editor_synced_block_perf experiment.
|
|
12
|
+
- Updated dependencies
|
|
13
|
+
|
|
3
14
|
## 8.2.10
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
|
@@ -357,7 +357,7 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
|
|
|
357
357
|
// When the perf gate is ON and the doc has synced blocks we do a
|
|
358
358
|
// single traversal here; afterwards `apply()` will map or rebuild
|
|
359
359
|
// only when a status signal changes.
|
|
360
|
-
var initStatusDecorationSet = (0, _expValEquals.expValEquals)('editor_synced_block_perf', 'isEnabled', true)
|
|
360
|
+
var initStatusDecorationSet = docHasSyncedBlocks && (0, _expValEquals.expValEquals)('editor_synced_block_perf', 'isEnabled', true) ? buildStatusDecorations(instance.doc, syncBlockStore, initIsOffline, initIsViewMode, initIsDragging) : _view.DecorationSet.empty;
|
|
361
361
|
return {
|
|
362
362
|
selectionDecorationSet: (0, _selectionDecorations.calculateDecorations)(instance.doc, instance.selection, instance.schema),
|
|
363
363
|
activeFlag: false,
|
|
@@ -374,6 +374,7 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
|
|
|
374
374
|
apply: function apply(tr, currentPluginState, oldEditorState) {
|
|
375
375
|
var _meta$activeFlag, _meta$bodiedSyncBlock;
|
|
376
376
|
var meta = tr.getMeta(syncedBlockPluginKey);
|
|
377
|
+
var isPerfExperimentOn = (0, _expValEquals.expValEquals)('editor_synced_block_perf', 'isEnabled', true);
|
|
377
378
|
var activeFlag = currentPluginState.activeFlag,
|
|
378
379
|
selectionDecorationSet = currentPluginState.selectionDecorationSet,
|
|
379
380
|
bodiedSyncBlockDeletionStatus = currentPluginState.bodiedSyncBlockDeletionStatus,
|
|
@@ -387,11 +388,19 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
|
|
|
387
388
|
// Lazy-init bookkeeping: once a synced block enters the document we
|
|
388
389
|
// flip `hasSyncedBlocks` to `true` for the lifetime of this editor
|
|
389
390
|
var nextHasSyncedBlocks = prevHasSyncedBlocks;
|
|
390
|
-
if (!prevHasSyncedBlocks && tr.docChanged &&
|
|
391
|
+
if (!prevHasSyncedBlocks && tr.docChanged && isPerfExperimentOn) {
|
|
391
392
|
if ((0, _transactionInsertsSyncedBlock.transactionInsertsSyncedBlock)(tr)) {
|
|
392
393
|
nextHasSyncedBlocks = true;
|
|
393
394
|
}
|
|
394
395
|
}
|
|
396
|
+
|
|
397
|
+
// --- Fast path (EDITOR-6929): when `hasSyncedBlocks` is false,
|
|
398
|
+
// no meta is set, and the selection/doc haven't changed in a way
|
|
399
|
+
// that affects our state, return the SAME object reference so
|
|
400
|
+
// SharedStateAPI skips notifying subscribers. ---
|
|
401
|
+
if (!nextHasSyncedBlocks && !meta && !tr.docChanged && tr.selection.eq(oldEditorState.selection) && isPerfExperimentOn) {
|
|
402
|
+
return currentPluginState;
|
|
403
|
+
}
|
|
395
404
|
var newDecorationSet = tr.docChanged ? selectionDecorationSet.map(tr.mapping, tr.doc) // only map if document changed
|
|
396
405
|
: selectionDecorationSet;
|
|
397
406
|
if (!tr.selection.eq(oldEditorState.selection)) {
|
|
@@ -415,7 +424,7 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
|
|
|
415
424
|
var nextIsOffline = prevOffline;
|
|
416
425
|
var nextIsViewMode = prevViewMode;
|
|
417
426
|
var nextIsDragging = prevDragging;
|
|
418
|
-
if (
|
|
427
|
+
if (isPerfExperimentOn) {
|
|
419
428
|
if (!nextHasSyncedBlocks) {
|
|
420
429
|
// No synced blocks → keep empty status decorations
|
|
421
430
|
nextStatusDecorationSet = _view.DecorationSet.empty;
|
|
@@ -444,14 +453,24 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
|
|
|
444
453
|
}
|
|
445
454
|
var newPosEntry = meta === null || meta === void 0 ? void 0 : meta.retryCreationPos;
|
|
446
455
|
var newRetryCreationPosMap = mapRetryCreationPosMap(retryCreationPosMap, newPosEntry, tr.mapping.map.bind(tr.mapping));
|
|
456
|
+
var nextActiveFlag = (_meta$activeFlag = meta === null || meta === void 0 ? void 0 : meta.activeFlag) !== null && _meta$activeFlag !== void 0 ? _meta$activeFlag : activeFlag;
|
|
457
|
+
var nextBodiedSyncBlockDeletionStatus = (_meta$bodiedSyncBlock = meta === null || meta === void 0 ? void 0 : meta.bodiedSyncBlockDeletionStatus) !== null && _meta$bodiedSyncBlock !== void 0 ? _meta$bodiedSyncBlock : bodiedSyncBlockDeletionStatus;
|
|
458
|
+
var nextHasUnsavedBodiedSyncBlockChanges = syncBlockStore.sourceManager.hasUnsavedChanges();
|
|
459
|
+
|
|
460
|
+
// --- Reference equality (EDITOR-6929): return the same object
|
|
461
|
+
// when ALL fields are reference-equal to avoid SharedStateAPI
|
|
462
|
+
// notifying subscribers and triggering React re-renders. ---
|
|
463
|
+
if (nextActiveFlag === activeFlag && newDecorationSet === selectionDecorationSet && newRetryCreationPosMap === retryCreationPosMap && nextHasSyncedBlocks === prevHasSyncedBlocks && nextBodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && nextHasUnsavedBodiedSyncBlockChanges === currentPluginState.hasUnsavedBodiedSyncBlockChanges && nextStatusDecorationSet === prevStatusDecorationSet && nextIsOffline === prevOffline && nextIsViewMode === prevViewMode && nextIsDragging === prevDragging && isPerfExperimentOn) {
|
|
464
|
+
return currentPluginState;
|
|
465
|
+
}
|
|
447
466
|
return {
|
|
448
|
-
activeFlag:
|
|
467
|
+
activeFlag: nextActiveFlag,
|
|
449
468
|
selectionDecorationSet: newDecorationSet,
|
|
450
469
|
syncBlockStore: syncBlockStore,
|
|
451
470
|
retryCreationPosMap: newRetryCreationPosMap,
|
|
452
471
|
hasSyncedBlocks: nextHasSyncedBlocks,
|
|
453
|
-
bodiedSyncBlockDeletionStatus:
|
|
454
|
-
hasUnsavedBodiedSyncBlockChanges:
|
|
472
|
+
bodiedSyncBlockDeletionStatus: nextBodiedSyncBlockDeletionStatus,
|
|
473
|
+
hasUnsavedBodiedSyncBlockChanges: nextHasUnsavedBodiedSyncBlockChanges,
|
|
455
474
|
statusDecorationSet: nextStatusDecorationSet,
|
|
456
475
|
prevIsOffline: nextIsOffline,
|
|
457
476
|
prevIsViewMode: nextIsViewMode,
|
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.syncedBlockPlugin = void 0;
|
|
8
8
|
var _react = _interopRequireDefault(require("react"));
|
|
9
9
|
var _adfSchema = require("@atlaskit/adf-schema");
|
|
10
|
+
var _hooks = require("@atlaskit/editor-common/hooks");
|
|
10
11
|
var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider");
|
|
11
12
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
12
13
|
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
|
|
@@ -22,15 +23,50 @@ var _floatingToolbar = require("./ui/floating-toolbar");
|
|
|
22
23
|
var _quickInsert = require("./ui/quick-insert");
|
|
23
24
|
var _SyncBlockRefresher = require("./ui/SyncBlockRefresher");
|
|
24
25
|
var _toolbarComponents = require("./ui/toolbar-components");
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* EDITOR-6929 / PR-G: Guard contentComponent rendering.
|
|
28
|
+
* When `hasSyncedBlocks` is false return null
|
|
29
|
+
* to avoid mounting SyncBlockRefresher, DeleteConfirmationModal, and Flag —
|
|
30
|
+
* their hooks (useSharedPluginStateWithSelector) would execute selectors on
|
|
31
|
+
* every transaction for no benefit on the ~99.98% of pages with zero synced
|
|
32
|
+
* blocks.
|
|
33
|
+
*/
|
|
34
|
+
var LazySyncedBlockUI = function LazySyncedBlockUI(_ref) {
|
|
35
|
+
var syncBlockStoreManager = _ref.syncBlockStore,
|
|
28
36
|
api = _ref.api;
|
|
37
|
+
var hasSyncBlocks = (0, _hooks.useSharedPluginStateWithSelector)(api, ['syncedBlock'], function (states) {
|
|
38
|
+
var _states$syncedBlockSt;
|
|
39
|
+
return (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.hasSyncedBlocks;
|
|
40
|
+
});
|
|
41
|
+
if (!hasSyncBlocks && (0, _expValEquals.expValEquals)('editor_synced_block_perf', 'isEnabled', true)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_SyncBlockRefresher.SyncBlockRefresher, {
|
|
45
|
+
syncBlockStoreManager: syncBlockStoreManager,
|
|
46
|
+
api: api
|
|
47
|
+
}), /*#__PURE__*/_react.default.createElement(_DeleteConfirmationModal.DeleteConfirmationModal, {
|
|
48
|
+
syncBlockStoreManager: syncBlockStoreManager,
|
|
49
|
+
api: api
|
|
50
|
+
}), /*#__PURE__*/_react.default.createElement(_Flag.Flag, {
|
|
51
|
+
api: api
|
|
52
|
+
}));
|
|
53
|
+
};
|
|
54
|
+
var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_ref2) {
|
|
55
|
+
var _api$editorViewMode, _api$analytics, _api$blockMenu, _config$enableSourceC, _api$toolbar, _config$enableSourceC2;
|
|
56
|
+
var config = _ref2.config,
|
|
57
|
+
api = _ref2.api;
|
|
29
58
|
var refs = {};
|
|
30
59
|
var viewMode = api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode;
|
|
31
60
|
var syncBlockStore = new _editorSyncedBlockProvider.SyncBlockStoreManager(config === null || config === void 0 ? void 0 : config.syncBlockDataProvider, viewMode, config === null || config === void 0 ? void 0 : config.__livePage);
|
|
32
61
|
var isPerfExperimentOn = (0, _expValEquals.expValEquals)('editor_synced_block_perf', 'isEnabled', true);
|
|
33
62
|
syncBlockStore.setFireAnalyticsEvent(api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 ? void 0 : _api$analytics.fireAnalyticsEvent);
|
|
63
|
+
|
|
64
|
+
// --- Memoized getSharedState (EDITOR-6929 / PR-F) ---
|
|
65
|
+
// Cache the last returned shared state object. On each call, perform a
|
|
66
|
+
// shallow comparison of all fields against the cached value. If nothing
|
|
67
|
+
// changed, return the cached reference so SharedStateAPI subscribers
|
|
68
|
+
// (React components) skip re-rendering.
|
|
69
|
+
var cachedSharedState;
|
|
34
70
|
api === null || api === void 0 || (_api$blockMenu = api.blockMenu) === null || _api$blockMenu === void 0 || _api$blockMenu.actions.registerBlockMenuComponents((0, _blockMenuComponents.getBlockMenuComponents)(api, (_config$enableSourceC = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC !== void 0 ? _config$enableSourceC : false));
|
|
35
71
|
api === null || api === void 0 || (_api$toolbar = api.toolbar) === null || _api$toolbar === void 0 || _api$toolbar.actions.registerComponents((0, _toolbarComponents.getToolbarComponents)(api, (_config$enableSourceC2 = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC2 !== void 0 ? _config$enableSourceC2 : false));
|
|
36
72
|
return {
|
|
@@ -72,9 +108,9 @@ var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_
|
|
|
72
108
|
return (0, _editorCommands.copySyncedBlockReferenceToClipboardEditorCommand)(syncBlockStore, inputMethod, api);
|
|
73
109
|
},
|
|
74
110
|
insertSyncedBlock: function insertSyncedBlock() {
|
|
75
|
-
return function (
|
|
111
|
+
return function (_ref3) {
|
|
76
112
|
var _api$analytics3;
|
|
77
|
-
var tr =
|
|
113
|
+
var tr = _ref3.tr;
|
|
78
114
|
if (!(config !== null && config !== void 0 && config.enableSourceCreation)) {
|
|
79
115
|
return null;
|
|
80
116
|
}
|
|
@@ -113,35 +149,36 @@ var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_
|
|
|
113
149
|
return (0, _floatingToolbar.getToolbarConfig)(state, intl, api, syncBlockStore);
|
|
114
150
|
}
|
|
115
151
|
},
|
|
116
|
-
contentComponent: function contentComponent(
|
|
117
|
-
var containerElement =
|
|
118
|
-
wrapperElement =
|
|
119
|
-
popupsMountPoint =
|
|
152
|
+
contentComponent: function contentComponent(_ref4) {
|
|
153
|
+
var containerElement = _ref4.containerElement,
|
|
154
|
+
wrapperElement = _ref4.wrapperElement,
|
|
155
|
+
popupsMountPoint = _ref4.popupsMountPoint;
|
|
120
156
|
refs.containerElement = containerElement || undefined;
|
|
121
157
|
refs.popupsMountPoint = popupsMountPoint || undefined;
|
|
122
158
|
refs.wrapperElement = wrapperElement || undefined;
|
|
123
|
-
return /*#__PURE__*/_react.default.createElement(
|
|
124
|
-
|
|
125
|
-
api: api
|
|
126
|
-
}), /*#__PURE__*/_react.default.createElement(_DeleteConfirmationModal.DeleteConfirmationModal, {
|
|
127
|
-
syncBlockStoreManager: syncBlockStore,
|
|
159
|
+
return /*#__PURE__*/_react.default.createElement(LazySyncedBlockUI, {
|
|
160
|
+
syncBlockStore: syncBlockStore,
|
|
128
161
|
api: api
|
|
129
|
-
})
|
|
130
|
-
api: api
|
|
131
|
-
}));
|
|
162
|
+
});
|
|
132
163
|
},
|
|
133
164
|
getSharedState: function getSharedState(editorState) {
|
|
134
165
|
if (!editorState) {
|
|
135
166
|
return;
|
|
136
167
|
}
|
|
137
|
-
var
|
|
138
|
-
|
|
139
|
-
currentSyncBlockStore =
|
|
140
|
-
bodiedSyncBlockDeletionStatus =
|
|
141
|
-
retryCreationPosMap =
|
|
142
|
-
hasSyncedBlocks =
|
|
143
|
-
hasUnsavedBodiedSyncBlockChanges =
|
|
144
|
-
|
|
168
|
+
var pluginState = _main.syncedBlockPluginKey.getState(editorState);
|
|
169
|
+
var activeFlag = pluginState.activeFlag,
|
|
170
|
+
currentSyncBlockStore = pluginState.syncBlockStore,
|
|
171
|
+
bodiedSyncBlockDeletionStatus = pluginState.bodiedSyncBlockDeletionStatus,
|
|
172
|
+
retryCreationPosMap = pluginState.retryCreationPosMap,
|
|
173
|
+
hasSyncedBlocks = pluginState.hasSyncedBlocks,
|
|
174
|
+
hasUnsavedBodiedSyncBlockChanges = pluginState.hasUnsavedBodiedSyncBlockChanges;
|
|
175
|
+
|
|
176
|
+
// --- EDITOR-6929 / PR-F: return a stable reference when all
|
|
177
|
+
// fields are unchanged to prevent unnecessary React re-renders. ---
|
|
178
|
+
if (cachedSharedState !== undefined && cachedSharedState.activeFlag === activeFlag && cachedSharedState.syncBlockStore === currentSyncBlockStore && cachedSharedState.bodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && cachedSharedState.retryCreationPosMap === retryCreationPosMap && cachedSharedState.hasSyncedBlocks === hasSyncedBlocks && cachedSharedState.hasUnsavedBodiedSyncBlockChanges === hasUnsavedBodiedSyncBlockChanges && (0, _expValEquals.expValEquals)('editor_synced_block_perf', 'isEnabled', true)) {
|
|
179
|
+
return cachedSharedState;
|
|
180
|
+
}
|
|
181
|
+
var nextSharedState = {
|
|
145
182
|
activeFlag: activeFlag,
|
|
146
183
|
syncBlockStore: currentSyncBlockStore,
|
|
147
184
|
bodiedSyncBlockDeletionStatus: bodiedSyncBlockDeletionStatus,
|
|
@@ -149,6 +186,8 @@ var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_
|
|
|
149
186
|
hasSyncedBlocks: hasSyncedBlocks,
|
|
150
187
|
hasUnsavedBodiedSyncBlockChanges: hasUnsavedBodiedSyncBlockChanges
|
|
151
188
|
};
|
|
189
|
+
cachedSharedState = nextSharedState;
|
|
190
|
+
return nextSharedState;
|
|
152
191
|
}
|
|
153
192
|
};
|
|
154
193
|
};
|
|
@@ -324,7 +324,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
324
324
|
// When the perf gate is ON and the doc has synced blocks we do a
|
|
325
325
|
// single traversal here; afterwards `apply()` will map or rebuild
|
|
326
326
|
// only when a status signal changes.
|
|
327
|
-
const initStatusDecorationSet = expValEquals('editor_synced_block_perf', 'isEnabled', true)
|
|
327
|
+
const initStatusDecorationSet = docHasSyncedBlocks && expValEquals('editor_synced_block_perf', 'isEnabled', true) ? buildStatusDecorations(instance.doc, syncBlockStore, initIsOffline, initIsViewMode, initIsDragging) : DecorationSet.empty;
|
|
328
328
|
return {
|
|
329
329
|
selectionDecorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema),
|
|
330
330
|
activeFlag: false,
|
|
@@ -341,6 +341,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
341
341
|
apply: (tr, currentPluginState, oldEditorState) => {
|
|
342
342
|
var _meta$activeFlag, _meta$bodiedSyncBlock;
|
|
343
343
|
const meta = tr.getMeta(syncedBlockPluginKey);
|
|
344
|
+
const isPerfExperimentOn = expValEquals('editor_synced_block_perf', 'isEnabled', true);
|
|
344
345
|
const {
|
|
345
346
|
activeFlag,
|
|
346
347
|
selectionDecorationSet,
|
|
@@ -356,11 +357,19 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
356
357
|
// Lazy-init bookkeeping: once a synced block enters the document we
|
|
357
358
|
// flip `hasSyncedBlocks` to `true` for the lifetime of this editor
|
|
358
359
|
let nextHasSyncedBlocks = prevHasSyncedBlocks;
|
|
359
|
-
if (!prevHasSyncedBlocks && tr.docChanged &&
|
|
360
|
+
if (!prevHasSyncedBlocks && tr.docChanged && isPerfExperimentOn) {
|
|
360
361
|
if (transactionInsertsSyncedBlock(tr)) {
|
|
361
362
|
nextHasSyncedBlocks = true;
|
|
362
363
|
}
|
|
363
364
|
}
|
|
365
|
+
|
|
366
|
+
// --- Fast path (EDITOR-6929): when `hasSyncedBlocks` is false,
|
|
367
|
+
// no meta is set, and the selection/doc haven't changed in a way
|
|
368
|
+
// that affects our state, return the SAME object reference so
|
|
369
|
+
// SharedStateAPI skips notifying subscribers. ---
|
|
370
|
+
if (!nextHasSyncedBlocks && !meta && !tr.docChanged && tr.selection.eq(oldEditorState.selection) && isPerfExperimentOn) {
|
|
371
|
+
return currentPluginState;
|
|
372
|
+
}
|
|
364
373
|
let newDecorationSet = tr.docChanged ? selectionDecorationSet.map(tr.mapping, tr.doc) // only map if document changed
|
|
365
374
|
: selectionDecorationSet;
|
|
366
375
|
if (!tr.selection.eq(oldEditorState.selection)) {
|
|
@@ -384,7 +393,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
384
393
|
let nextIsOffline = prevOffline;
|
|
385
394
|
let nextIsViewMode = prevViewMode;
|
|
386
395
|
let nextIsDragging = prevDragging;
|
|
387
|
-
if (
|
|
396
|
+
if (isPerfExperimentOn) {
|
|
388
397
|
if (!nextHasSyncedBlocks) {
|
|
389
398
|
// No synced blocks → keep empty status decorations
|
|
390
399
|
nextStatusDecorationSet = DecorationSet.empty;
|
|
@@ -413,14 +422,24 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
413
422
|
}
|
|
414
423
|
const newPosEntry = meta === null || meta === void 0 ? void 0 : meta.retryCreationPos;
|
|
415
424
|
const newRetryCreationPosMap = mapRetryCreationPosMap(retryCreationPosMap, newPosEntry, tr.mapping.map.bind(tr.mapping));
|
|
425
|
+
const nextActiveFlag = (_meta$activeFlag = meta === null || meta === void 0 ? void 0 : meta.activeFlag) !== null && _meta$activeFlag !== void 0 ? _meta$activeFlag : activeFlag;
|
|
426
|
+
const nextBodiedSyncBlockDeletionStatus = (_meta$bodiedSyncBlock = meta === null || meta === void 0 ? void 0 : meta.bodiedSyncBlockDeletionStatus) !== null && _meta$bodiedSyncBlock !== void 0 ? _meta$bodiedSyncBlock : bodiedSyncBlockDeletionStatus;
|
|
427
|
+
const nextHasUnsavedBodiedSyncBlockChanges = syncBlockStore.sourceManager.hasUnsavedChanges();
|
|
428
|
+
|
|
429
|
+
// --- Reference equality (EDITOR-6929): return the same object
|
|
430
|
+
// when ALL fields are reference-equal to avoid SharedStateAPI
|
|
431
|
+
// notifying subscribers and triggering React re-renders. ---
|
|
432
|
+
if (nextActiveFlag === activeFlag && newDecorationSet === selectionDecorationSet && newRetryCreationPosMap === retryCreationPosMap && nextHasSyncedBlocks === prevHasSyncedBlocks && nextBodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && nextHasUnsavedBodiedSyncBlockChanges === currentPluginState.hasUnsavedBodiedSyncBlockChanges && nextStatusDecorationSet === prevStatusDecorationSet && nextIsOffline === prevOffline && nextIsViewMode === prevViewMode && nextIsDragging === prevDragging && isPerfExperimentOn) {
|
|
433
|
+
return currentPluginState;
|
|
434
|
+
}
|
|
416
435
|
return {
|
|
417
|
-
activeFlag:
|
|
436
|
+
activeFlag: nextActiveFlag,
|
|
418
437
|
selectionDecorationSet: newDecorationSet,
|
|
419
438
|
syncBlockStore: syncBlockStore,
|
|
420
439
|
retryCreationPosMap: newRetryCreationPosMap,
|
|
421
440
|
hasSyncedBlocks: nextHasSyncedBlocks,
|
|
422
|
-
bodiedSyncBlockDeletionStatus:
|
|
423
|
-
hasUnsavedBodiedSyncBlockChanges:
|
|
441
|
+
bodiedSyncBlockDeletionStatus: nextBodiedSyncBlockDeletionStatus,
|
|
442
|
+
hasUnsavedBodiedSyncBlockChanges: nextHasUnsavedBodiedSyncBlockChanges,
|
|
424
443
|
statusDecorationSet: nextStatusDecorationSet,
|
|
425
444
|
prevIsOffline: nextIsOffline,
|
|
426
445
|
prevIsViewMode: nextIsViewMode,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { syncBlock, bodiedSyncBlock } from '@atlaskit/adf-schema';
|
|
3
|
+
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
3
4
|
import { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
|
|
4
5
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
5
6
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
@@ -15,6 +16,36 @@ import { getToolbarConfig } from './ui/floating-toolbar';
|
|
|
15
16
|
import { getQuickInsertConfig } from './ui/quick-insert';
|
|
16
17
|
import { SyncBlockRefresher } from './ui/SyncBlockRefresher';
|
|
17
18
|
import { getToolbarComponents } from './ui/toolbar-components';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* EDITOR-6929 / PR-G: Guard contentComponent rendering.
|
|
22
|
+
* When `hasSyncedBlocks` is false return null
|
|
23
|
+
* to avoid mounting SyncBlockRefresher, DeleteConfirmationModal, and Flag —
|
|
24
|
+
* their hooks (useSharedPluginStateWithSelector) would execute selectors on
|
|
25
|
+
* every transaction for no benefit on the ~99.98% of pages with zero synced
|
|
26
|
+
* blocks.
|
|
27
|
+
*/
|
|
28
|
+
const LazySyncedBlockUI = ({
|
|
29
|
+
syncBlockStore: syncBlockStoreManager,
|
|
30
|
+
api
|
|
31
|
+
}) => {
|
|
32
|
+
const hasSyncBlocks = useSharedPluginStateWithSelector(api, ['syncedBlock'], states => {
|
|
33
|
+
var _states$syncedBlockSt;
|
|
34
|
+
return (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.hasSyncedBlocks;
|
|
35
|
+
});
|
|
36
|
+
if (!hasSyncBlocks && expValEquals('editor_synced_block_perf', 'isEnabled', true)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SyncBlockRefresher, {
|
|
40
|
+
syncBlockStoreManager: syncBlockStoreManager,
|
|
41
|
+
api: api
|
|
42
|
+
}), /*#__PURE__*/React.createElement(DeleteConfirmationModal, {
|
|
43
|
+
syncBlockStoreManager: syncBlockStoreManager,
|
|
44
|
+
api: api
|
|
45
|
+
}), /*#__PURE__*/React.createElement(Flag, {
|
|
46
|
+
api: api
|
|
47
|
+
}));
|
|
48
|
+
};
|
|
18
49
|
export const syncedBlockPlugin = ({
|
|
19
50
|
config,
|
|
20
51
|
api
|
|
@@ -25,6 +56,13 @@ export const syncedBlockPlugin = ({
|
|
|
25
56
|
const syncBlockStore = new SyncBlockStoreManager(config === null || config === void 0 ? void 0 : config.syncBlockDataProvider, viewMode, config === null || config === void 0 ? void 0 : config.__livePage);
|
|
26
57
|
const isPerfExperimentOn = expValEquals('editor_synced_block_perf', 'isEnabled', true);
|
|
27
58
|
syncBlockStore.setFireAnalyticsEvent(api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.fireAnalyticsEvent);
|
|
59
|
+
|
|
60
|
+
// --- Memoized getSharedState (EDITOR-6929 / PR-F) ---
|
|
61
|
+
// Cache the last returned shared state object. On each call, perform a
|
|
62
|
+
// shallow comparison of all fields against the cached value. If nothing
|
|
63
|
+
// changed, return the cached reference so SharedStateAPI subscribers
|
|
64
|
+
// (React components) skip re-rendering.
|
|
65
|
+
let cachedSharedState;
|
|
28
66
|
api === null || api === void 0 ? void 0 : (_api$blockMenu = api.blockMenu) === null || _api$blockMenu === void 0 ? void 0 : _api$blockMenu.actions.registerBlockMenuComponents(getBlockMenuComponents(api, (_config$enableSourceC = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC !== void 0 ? _config$enableSourceC : false));
|
|
29
67
|
api === null || api === void 0 ? void 0 : (_api$toolbar = api.toolbar) === null || _api$toolbar === void 0 ? void 0 : _api$toolbar.actions.registerComponents(getToolbarComponents(api, (_config$enableSourceC2 = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC2 !== void 0 ? _config$enableSourceC2 : false));
|
|
30
68
|
return {
|
|
@@ -108,20 +146,16 @@ export const syncedBlockPlugin = ({
|
|
|
108
146
|
refs.containerElement = containerElement || undefined;
|
|
109
147
|
refs.popupsMountPoint = popupsMountPoint || undefined;
|
|
110
148
|
refs.wrapperElement = wrapperElement || undefined;
|
|
111
|
-
return /*#__PURE__*/React.createElement(
|
|
112
|
-
|
|
113
|
-
api: api
|
|
114
|
-
}), /*#__PURE__*/React.createElement(DeleteConfirmationModal, {
|
|
115
|
-
syncBlockStoreManager: syncBlockStore,
|
|
149
|
+
return /*#__PURE__*/React.createElement(LazySyncedBlockUI, {
|
|
150
|
+
syncBlockStore: syncBlockStore,
|
|
116
151
|
api: api
|
|
117
|
-
})
|
|
118
|
-
api: api
|
|
119
|
-
}));
|
|
152
|
+
});
|
|
120
153
|
},
|
|
121
154
|
getSharedState: editorState => {
|
|
122
155
|
if (!editorState) {
|
|
123
156
|
return;
|
|
124
157
|
}
|
|
158
|
+
const pluginState = syncedBlockPluginKey.getState(editorState);
|
|
125
159
|
const {
|
|
126
160
|
activeFlag,
|
|
127
161
|
syncBlockStore: currentSyncBlockStore,
|
|
@@ -129,8 +163,14 @@ export const syncedBlockPlugin = ({
|
|
|
129
163
|
retryCreationPosMap,
|
|
130
164
|
hasSyncedBlocks,
|
|
131
165
|
hasUnsavedBodiedSyncBlockChanges
|
|
132
|
-
} =
|
|
133
|
-
|
|
166
|
+
} = pluginState;
|
|
167
|
+
|
|
168
|
+
// --- EDITOR-6929 / PR-F: return a stable reference when all
|
|
169
|
+
// fields are unchanged to prevent unnecessary React re-renders. ---
|
|
170
|
+
if (cachedSharedState !== undefined && cachedSharedState.activeFlag === activeFlag && cachedSharedState.syncBlockStore === currentSyncBlockStore && cachedSharedState.bodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && cachedSharedState.retryCreationPosMap === retryCreationPosMap && cachedSharedState.hasSyncedBlocks === hasSyncedBlocks && cachedSharedState.hasUnsavedBodiedSyncBlockChanges === hasUnsavedBodiedSyncBlockChanges && expValEquals('editor_synced_block_perf', 'isEnabled', true)) {
|
|
171
|
+
return cachedSharedState;
|
|
172
|
+
}
|
|
173
|
+
const nextSharedState = {
|
|
134
174
|
activeFlag,
|
|
135
175
|
syncBlockStore: currentSyncBlockStore,
|
|
136
176
|
bodiedSyncBlockDeletionStatus,
|
|
@@ -138,6 +178,8 @@ export const syncedBlockPlugin = ({
|
|
|
138
178
|
hasSyncedBlocks,
|
|
139
179
|
hasUnsavedBodiedSyncBlockChanges
|
|
140
180
|
};
|
|
181
|
+
cachedSharedState = nextSharedState;
|
|
182
|
+
return nextSharedState;
|
|
141
183
|
}
|
|
142
184
|
};
|
|
143
185
|
};
|
|
@@ -350,7 +350,7 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
|
|
|
350
350
|
// When the perf gate is ON and the doc has synced blocks we do a
|
|
351
351
|
// single traversal here; afterwards `apply()` will map or rebuild
|
|
352
352
|
// only when a status signal changes.
|
|
353
|
-
var initStatusDecorationSet = expValEquals('editor_synced_block_perf', 'isEnabled', true)
|
|
353
|
+
var initStatusDecorationSet = docHasSyncedBlocks && expValEquals('editor_synced_block_perf', 'isEnabled', true) ? buildStatusDecorations(instance.doc, syncBlockStore, initIsOffline, initIsViewMode, initIsDragging) : DecorationSet.empty;
|
|
354
354
|
return {
|
|
355
355
|
selectionDecorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema),
|
|
356
356
|
activeFlag: false,
|
|
@@ -367,6 +367,7 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
|
|
|
367
367
|
apply: function apply(tr, currentPluginState, oldEditorState) {
|
|
368
368
|
var _meta$activeFlag, _meta$bodiedSyncBlock;
|
|
369
369
|
var meta = tr.getMeta(syncedBlockPluginKey);
|
|
370
|
+
var isPerfExperimentOn = expValEquals('editor_synced_block_perf', 'isEnabled', true);
|
|
370
371
|
var activeFlag = currentPluginState.activeFlag,
|
|
371
372
|
selectionDecorationSet = currentPluginState.selectionDecorationSet,
|
|
372
373
|
bodiedSyncBlockDeletionStatus = currentPluginState.bodiedSyncBlockDeletionStatus,
|
|
@@ -380,11 +381,19 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
|
|
|
380
381
|
// Lazy-init bookkeeping: once a synced block enters the document we
|
|
381
382
|
// flip `hasSyncedBlocks` to `true` for the lifetime of this editor
|
|
382
383
|
var nextHasSyncedBlocks = prevHasSyncedBlocks;
|
|
383
|
-
if (!prevHasSyncedBlocks && tr.docChanged &&
|
|
384
|
+
if (!prevHasSyncedBlocks && tr.docChanged && isPerfExperimentOn) {
|
|
384
385
|
if (transactionInsertsSyncedBlock(tr)) {
|
|
385
386
|
nextHasSyncedBlocks = true;
|
|
386
387
|
}
|
|
387
388
|
}
|
|
389
|
+
|
|
390
|
+
// --- Fast path (EDITOR-6929): when `hasSyncedBlocks` is false,
|
|
391
|
+
// no meta is set, and the selection/doc haven't changed in a way
|
|
392
|
+
// that affects our state, return the SAME object reference so
|
|
393
|
+
// SharedStateAPI skips notifying subscribers. ---
|
|
394
|
+
if (!nextHasSyncedBlocks && !meta && !tr.docChanged && tr.selection.eq(oldEditorState.selection) && isPerfExperimentOn) {
|
|
395
|
+
return currentPluginState;
|
|
396
|
+
}
|
|
388
397
|
var newDecorationSet = tr.docChanged ? selectionDecorationSet.map(tr.mapping, tr.doc) // only map if document changed
|
|
389
398
|
: selectionDecorationSet;
|
|
390
399
|
if (!tr.selection.eq(oldEditorState.selection)) {
|
|
@@ -408,7 +417,7 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
|
|
|
408
417
|
var nextIsOffline = prevOffline;
|
|
409
418
|
var nextIsViewMode = prevViewMode;
|
|
410
419
|
var nextIsDragging = prevDragging;
|
|
411
|
-
if (
|
|
420
|
+
if (isPerfExperimentOn) {
|
|
412
421
|
if (!nextHasSyncedBlocks) {
|
|
413
422
|
// No synced blocks → keep empty status decorations
|
|
414
423
|
nextStatusDecorationSet = DecorationSet.empty;
|
|
@@ -437,14 +446,24 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
|
|
|
437
446
|
}
|
|
438
447
|
var newPosEntry = meta === null || meta === void 0 ? void 0 : meta.retryCreationPos;
|
|
439
448
|
var newRetryCreationPosMap = mapRetryCreationPosMap(retryCreationPosMap, newPosEntry, tr.mapping.map.bind(tr.mapping));
|
|
449
|
+
var nextActiveFlag = (_meta$activeFlag = meta === null || meta === void 0 ? void 0 : meta.activeFlag) !== null && _meta$activeFlag !== void 0 ? _meta$activeFlag : activeFlag;
|
|
450
|
+
var nextBodiedSyncBlockDeletionStatus = (_meta$bodiedSyncBlock = meta === null || meta === void 0 ? void 0 : meta.bodiedSyncBlockDeletionStatus) !== null && _meta$bodiedSyncBlock !== void 0 ? _meta$bodiedSyncBlock : bodiedSyncBlockDeletionStatus;
|
|
451
|
+
var nextHasUnsavedBodiedSyncBlockChanges = syncBlockStore.sourceManager.hasUnsavedChanges();
|
|
452
|
+
|
|
453
|
+
// --- Reference equality (EDITOR-6929): return the same object
|
|
454
|
+
// when ALL fields are reference-equal to avoid SharedStateAPI
|
|
455
|
+
// notifying subscribers and triggering React re-renders. ---
|
|
456
|
+
if (nextActiveFlag === activeFlag && newDecorationSet === selectionDecorationSet && newRetryCreationPosMap === retryCreationPosMap && nextHasSyncedBlocks === prevHasSyncedBlocks && nextBodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && nextHasUnsavedBodiedSyncBlockChanges === currentPluginState.hasUnsavedBodiedSyncBlockChanges && nextStatusDecorationSet === prevStatusDecorationSet && nextIsOffline === prevOffline && nextIsViewMode === prevViewMode && nextIsDragging === prevDragging && isPerfExperimentOn) {
|
|
457
|
+
return currentPluginState;
|
|
458
|
+
}
|
|
440
459
|
return {
|
|
441
|
-
activeFlag:
|
|
460
|
+
activeFlag: nextActiveFlag,
|
|
442
461
|
selectionDecorationSet: newDecorationSet,
|
|
443
462
|
syncBlockStore: syncBlockStore,
|
|
444
463
|
retryCreationPosMap: newRetryCreationPosMap,
|
|
445
464
|
hasSyncedBlocks: nextHasSyncedBlocks,
|
|
446
|
-
bodiedSyncBlockDeletionStatus:
|
|
447
|
-
hasUnsavedBodiedSyncBlockChanges:
|
|
465
|
+
bodiedSyncBlockDeletionStatus: nextBodiedSyncBlockDeletionStatus,
|
|
466
|
+
hasUnsavedBodiedSyncBlockChanges: nextHasUnsavedBodiedSyncBlockChanges,
|
|
448
467
|
statusDecorationSet: nextStatusDecorationSet,
|
|
449
468
|
prevIsOffline: nextIsOffline,
|
|
450
469
|
prevIsViewMode: nextIsViewMode,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { syncBlock, bodiedSyncBlock } from '@atlaskit/adf-schema';
|
|
3
|
+
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
3
4
|
import { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
|
|
4
5
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
5
6
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
@@ -15,15 +16,51 @@ import { getToolbarConfig } from './ui/floating-toolbar';
|
|
|
15
16
|
import { getQuickInsertConfig } from './ui/quick-insert';
|
|
16
17
|
import { SyncBlockRefresher } from './ui/SyncBlockRefresher';
|
|
17
18
|
import { getToolbarComponents } from './ui/toolbar-components';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* EDITOR-6929 / PR-G: Guard contentComponent rendering.
|
|
22
|
+
* When `hasSyncedBlocks` is false return null
|
|
23
|
+
* to avoid mounting SyncBlockRefresher, DeleteConfirmationModal, and Flag —
|
|
24
|
+
* their hooks (useSharedPluginStateWithSelector) would execute selectors on
|
|
25
|
+
* every transaction for no benefit on the ~99.98% of pages with zero synced
|
|
26
|
+
* blocks.
|
|
27
|
+
*/
|
|
28
|
+
var LazySyncedBlockUI = function LazySyncedBlockUI(_ref) {
|
|
29
|
+
var syncBlockStoreManager = _ref.syncBlockStore,
|
|
21
30
|
api = _ref.api;
|
|
31
|
+
var hasSyncBlocks = useSharedPluginStateWithSelector(api, ['syncedBlock'], function (states) {
|
|
32
|
+
var _states$syncedBlockSt;
|
|
33
|
+
return (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.hasSyncedBlocks;
|
|
34
|
+
});
|
|
35
|
+
if (!hasSyncBlocks && expValEquals('editor_synced_block_perf', 'isEnabled', true)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SyncBlockRefresher, {
|
|
39
|
+
syncBlockStoreManager: syncBlockStoreManager,
|
|
40
|
+
api: api
|
|
41
|
+
}), /*#__PURE__*/React.createElement(DeleteConfirmationModal, {
|
|
42
|
+
syncBlockStoreManager: syncBlockStoreManager,
|
|
43
|
+
api: api
|
|
44
|
+
}), /*#__PURE__*/React.createElement(Flag, {
|
|
45
|
+
api: api
|
|
46
|
+
}));
|
|
47
|
+
};
|
|
48
|
+
export var syncedBlockPlugin = function syncedBlockPlugin(_ref2) {
|
|
49
|
+
var _api$editorViewMode, _api$analytics, _api$blockMenu, _config$enableSourceC, _api$toolbar, _config$enableSourceC2;
|
|
50
|
+
var config = _ref2.config,
|
|
51
|
+
api = _ref2.api;
|
|
22
52
|
var refs = {};
|
|
23
53
|
var viewMode = api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode;
|
|
24
54
|
var syncBlockStore = new SyncBlockStoreManager(config === null || config === void 0 ? void 0 : config.syncBlockDataProvider, viewMode, config === null || config === void 0 ? void 0 : config.__livePage);
|
|
25
55
|
var isPerfExperimentOn = expValEquals('editor_synced_block_perf', 'isEnabled', true);
|
|
26
56
|
syncBlockStore.setFireAnalyticsEvent(api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 ? void 0 : _api$analytics.fireAnalyticsEvent);
|
|
57
|
+
|
|
58
|
+
// --- Memoized getSharedState (EDITOR-6929 / PR-F) ---
|
|
59
|
+
// Cache the last returned shared state object. On each call, perform a
|
|
60
|
+
// shallow comparison of all fields against the cached value. If nothing
|
|
61
|
+
// changed, return the cached reference so SharedStateAPI subscribers
|
|
62
|
+
// (React components) skip re-rendering.
|
|
63
|
+
var cachedSharedState;
|
|
27
64
|
api === null || api === void 0 || (_api$blockMenu = api.blockMenu) === null || _api$blockMenu === void 0 || _api$blockMenu.actions.registerBlockMenuComponents(getBlockMenuComponents(api, (_config$enableSourceC = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC !== void 0 ? _config$enableSourceC : false));
|
|
28
65
|
api === null || api === void 0 || (_api$toolbar = api.toolbar) === null || _api$toolbar === void 0 || _api$toolbar.actions.registerComponents(getToolbarComponents(api, (_config$enableSourceC2 = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC2 !== void 0 ? _config$enableSourceC2 : false));
|
|
29
66
|
return {
|
|
@@ -65,9 +102,9 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
|
|
|
65
102
|
return copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, inputMethod, api);
|
|
66
103
|
},
|
|
67
104
|
insertSyncedBlock: function insertSyncedBlock() {
|
|
68
|
-
return function (
|
|
105
|
+
return function (_ref3) {
|
|
69
106
|
var _api$analytics3;
|
|
70
|
-
var tr =
|
|
107
|
+
var tr = _ref3.tr;
|
|
71
108
|
if (!(config !== null && config !== void 0 && config.enableSourceCreation)) {
|
|
72
109
|
return null;
|
|
73
110
|
}
|
|
@@ -106,35 +143,36 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
|
|
|
106
143
|
return getToolbarConfig(state, intl, api, syncBlockStore);
|
|
107
144
|
}
|
|
108
145
|
},
|
|
109
|
-
contentComponent: function contentComponent(
|
|
110
|
-
var containerElement =
|
|
111
|
-
wrapperElement =
|
|
112
|
-
popupsMountPoint =
|
|
146
|
+
contentComponent: function contentComponent(_ref4) {
|
|
147
|
+
var containerElement = _ref4.containerElement,
|
|
148
|
+
wrapperElement = _ref4.wrapperElement,
|
|
149
|
+
popupsMountPoint = _ref4.popupsMountPoint;
|
|
113
150
|
refs.containerElement = containerElement || undefined;
|
|
114
151
|
refs.popupsMountPoint = popupsMountPoint || undefined;
|
|
115
152
|
refs.wrapperElement = wrapperElement || undefined;
|
|
116
|
-
return /*#__PURE__*/React.createElement(
|
|
117
|
-
|
|
118
|
-
api: api
|
|
119
|
-
}), /*#__PURE__*/React.createElement(DeleteConfirmationModal, {
|
|
120
|
-
syncBlockStoreManager: syncBlockStore,
|
|
153
|
+
return /*#__PURE__*/React.createElement(LazySyncedBlockUI, {
|
|
154
|
+
syncBlockStore: syncBlockStore,
|
|
121
155
|
api: api
|
|
122
|
-
})
|
|
123
|
-
api: api
|
|
124
|
-
}));
|
|
156
|
+
});
|
|
125
157
|
},
|
|
126
158
|
getSharedState: function getSharedState(editorState) {
|
|
127
159
|
if (!editorState) {
|
|
128
160
|
return;
|
|
129
161
|
}
|
|
130
|
-
var
|
|
131
|
-
|
|
132
|
-
currentSyncBlockStore =
|
|
133
|
-
bodiedSyncBlockDeletionStatus =
|
|
134
|
-
retryCreationPosMap =
|
|
135
|
-
hasSyncedBlocks =
|
|
136
|
-
hasUnsavedBodiedSyncBlockChanges =
|
|
137
|
-
|
|
162
|
+
var pluginState = syncedBlockPluginKey.getState(editorState);
|
|
163
|
+
var activeFlag = pluginState.activeFlag,
|
|
164
|
+
currentSyncBlockStore = pluginState.syncBlockStore,
|
|
165
|
+
bodiedSyncBlockDeletionStatus = pluginState.bodiedSyncBlockDeletionStatus,
|
|
166
|
+
retryCreationPosMap = pluginState.retryCreationPosMap,
|
|
167
|
+
hasSyncedBlocks = pluginState.hasSyncedBlocks,
|
|
168
|
+
hasUnsavedBodiedSyncBlockChanges = pluginState.hasUnsavedBodiedSyncBlockChanges;
|
|
169
|
+
|
|
170
|
+
// --- EDITOR-6929 / PR-F: return a stable reference when all
|
|
171
|
+
// fields are unchanged to prevent unnecessary React re-renders. ---
|
|
172
|
+
if (cachedSharedState !== undefined && cachedSharedState.activeFlag === activeFlag && cachedSharedState.syncBlockStore === currentSyncBlockStore && cachedSharedState.bodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && cachedSharedState.retryCreationPosMap === retryCreationPosMap && cachedSharedState.hasSyncedBlocks === hasSyncedBlocks && cachedSharedState.hasUnsavedBodiedSyncBlockChanges === hasUnsavedBodiedSyncBlockChanges && expValEquals('editor_synced_block_perf', 'isEnabled', true)) {
|
|
173
|
+
return cachedSharedState;
|
|
174
|
+
}
|
|
175
|
+
var nextSharedState = {
|
|
138
176
|
activeFlag: activeFlag,
|
|
139
177
|
syncBlockStore: currentSyncBlockStore,
|
|
140
178
|
bodiedSyncBlockDeletionStatus: bodiedSyncBlockDeletionStatus,
|
|
@@ -142,6 +180,8 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
|
|
|
142
180
|
hasSyncedBlocks: hasSyncedBlocks,
|
|
143
181
|
hasUnsavedBodiedSyncBlockChanges: hasUnsavedBodiedSyncBlockChanges
|
|
144
182
|
};
|
|
183
|
+
cachedSharedState = nextSharedState;
|
|
184
|
+
return nextSharedState;
|
|
145
185
|
}
|
|
146
186
|
};
|
|
147
187
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-synced-block",
|
|
3
|
-
"version": "8.2.
|
|
3
|
+
"version": "8.2.11",
|
|
4
4
|
"description": "SyncedBlock plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@atlaskit/primitives": "^19.0.0",
|
|
56
56
|
"@atlaskit/spinner": "19.1.2",
|
|
57
57
|
"@atlaskit/tmp-editor-statsig": "^77.3.0",
|
|
58
|
-
"@atlaskit/tokens": "13.0.
|
|
58
|
+
"@atlaskit/tokens": "13.0.4",
|
|
59
59
|
"@atlaskit/tooltip": "^22.0.0",
|
|
60
60
|
"@atlaskit/visually-hidden": "^3.1.0",
|
|
61
61
|
"@babel/runtime": "^7.0.0",
|