@atlaskit/editor-plugin-synced-block 8.2.9 → 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.
@@ -1,6 +1,8 @@
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';
5
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
4
6
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
5
7
  import { flushBodiedSyncBlocks as _flushBodiedSyncBlocks, flushSyncBlocks, discardUnpublishedSyncBlocks as _discardUnpublishedSyncBlocks } from './editor-actions';
6
8
  import { copySyncedBlockReferenceToClipboardEditorCommand, createSyncedBlock } from './editor-commands';
@@ -14,14 +16,51 @@ import { getToolbarConfig } from './ui/floating-toolbar';
14
16
  import { getQuickInsertConfig } from './ui/quick-insert';
15
17
  import { SyncBlockRefresher } from './ui/SyncBlockRefresher';
16
18
  import { getToolbarComponents } from './ui/toolbar-components';
17
- export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
18
- var _api$editorViewMode, _api$analytics, _api$blockMenu, _config$enableSourceC, _api$toolbar, _config$enableSourceC2;
19
- var config = _ref.config,
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,
20
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;
21
52
  var refs = {};
22
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;
23
54
  var syncBlockStore = new SyncBlockStoreManager(config === null || config === void 0 ? void 0 : config.syncBlockDataProvider, viewMode, config === null || config === void 0 ? void 0 : config.__livePage);
55
+ var isPerfExperimentOn = expValEquals('editor_synced_block_perf', 'isEnabled', true);
24
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;
25
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));
26
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));
27
66
  return {
@@ -63,9 +102,9 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
63
102
  return copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, inputMethod, api);
64
103
  },
65
104
  insertSyncedBlock: function insertSyncedBlock() {
66
- return function (_ref2) {
105
+ return function (_ref3) {
67
106
  var _api$analytics3;
68
- var tr = _ref2.tr;
107
+ var tr = _ref3.tr;
69
108
  if (!(config !== null && config !== void 0 && config.enableSourceCreation)) {
70
109
  return null;
71
110
  }
@@ -91,38 +130,49 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
91
130
  pluginsOptions: {
92
131
  quickInsert: getQuickInsertConfig(config, api, syncBlockStore),
93
132
  floatingToolbar: function floatingToolbar(state, intl) {
133
+ var _syncedBlockPluginKey;
134
+ // When the experiment is ON and the document has no synced blocks,
135
+ // skip the toolbar config entirely to avoid the per-selection-change
136
+ // cost of findSyncBlockOrBodiedSyncBlock (EDITOR-6931).
137
+ // Save the expValEquals('editor_synced_block_perf', 'isEnabled', true) in a const
138
+ // because floatingToolbar is called on every selection change.
139
+ // computing it once at plugin initialisation is more efficient.
140
+ if (!((_syncedBlockPluginKey = syncedBlockPluginKey.getState(state)) !== null && _syncedBlockPluginKey !== void 0 && _syncedBlockPluginKey.hasSyncedBlocks) && isPerfExperimentOn) {
141
+ return undefined;
142
+ }
94
143
  return getToolbarConfig(state, intl, api, syncBlockStore);
95
144
  }
96
145
  },
97
- contentComponent: function contentComponent(_ref3) {
98
- var containerElement = _ref3.containerElement,
99
- wrapperElement = _ref3.wrapperElement,
100
- popupsMountPoint = _ref3.popupsMountPoint;
146
+ contentComponent: function contentComponent(_ref4) {
147
+ var containerElement = _ref4.containerElement,
148
+ wrapperElement = _ref4.wrapperElement,
149
+ popupsMountPoint = _ref4.popupsMountPoint;
101
150
  refs.containerElement = containerElement || undefined;
102
151
  refs.popupsMountPoint = popupsMountPoint || undefined;
103
152
  refs.wrapperElement = wrapperElement || undefined;
104
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SyncBlockRefresher, {
105
- syncBlockStoreManager: syncBlockStore,
153
+ return /*#__PURE__*/React.createElement(LazySyncedBlockUI, {
154
+ syncBlockStore: syncBlockStore,
106
155
  api: api
107
- }), /*#__PURE__*/React.createElement(DeleteConfirmationModal, {
108
- syncBlockStoreManager: syncBlockStore,
109
- api: api
110
- }), /*#__PURE__*/React.createElement(Flag, {
111
- api: api
112
- }));
156
+ });
113
157
  },
114
158
  getSharedState: function getSharedState(editorState) {
115
159
  if (!editorState) {
116
160
  return;
117
161
  }
118
- var _syncedBlockPluginKey = syncedBlockPluginKey.getState(editorState),
119
- activeFlag = _syncedBlockPluginKey.activeFlag,
120
- currentSyncBlockStore = _syncedBlockPluginKey.syncBlockStore,
121
- bodiedSyncBlockDeletionStatus = _syncedBlockPluginKey.bodiedSyncBlockDeletionStatus,
122
- retryCreationPosMap = _syncedBlockPluginKey.retryCreationPosMap,
123
- hasSyncedBlocks = _syncedBlockPluginKey.hasSyncedBlocks,
124
- hasUnsavedBodiedSyncBlockChanges = _syncedBlockPluginKey.hasUnsavedBodiedSyncBlockChanges;
125
- return {
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 = {
126
176
  activeFlag: activeFlag,
127
177
  syncBlockStore: currentSyncBlockStore,
128
178
  bodiedSyncBlockDeletionStatus: bodiedSyncBlockDeletionStatus,
@@ -130,6 +180,8 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
130
180
  hasSyncedBlocks: hasSyncedBlocks,
131
181
  hasUnsavedBodiedSyncBlockChanges: hasUnsavedBodiedSyncBlockChanges
132
182
  };
183
+ cachedSharedState = nextSharedState;
184
+ return nextSharedState;
133
185
  }
134
186
  };
135
187
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-synced-block",
3
- "version": "8.2.9",
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",
@@ -54,8 +54,8 @@
54
54
  "@atlaskit/platform-feature-flags": "^1.1.0",
55
55
  "@atlaskit/primitives": "^19.0.0",
56
56
  "@atlaskit/spinner": "19.1.2",
57
- "@atlaskit/tmp-editor-statsig": "^77.1.0",
58
- "@atlaskit/tokens": "13.0.3",
57
+ "@atlaskit/tmp-editor-statsig": "^77.3.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",
@@ -64,7 +64,7 @@
64
64
  "date-fns": "^2.17.0"
65
65
  },
66
66
  "peerDependencies": {
67
- "@atlaskit/editor-common": "^114.21.0",
67
+ "@atlaskit/editor-common": "^114.26.0",
68
68
  "react": "^18.2.0",
69
69
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
70
70
  },