@atlaskit/editor-plugin-synced-block 12.0.4 → 12.0.6
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 +14 -0
- package/dist/cjs/editor-commands/index.js +8 -18
- package/dist/cjs/nodeviews/bodiedSyncedBlock.js +3 -7
- package/dist/cjs/nodeviews/syncedBlock.js +9 -15
- package/dist/cjs/pm-plugins/main.js +14 -42
- package/dist/cjs/pm-plugins/utils/handle-bodied-sync-block-removal.js +36 -54
- package/dist/cjs/syncedBlockPlugin.js +1 -4
- package/dist/cjs/ui/CreateSyncedBlockButton.js +23 -35
- package/dist/cjs/ui/CreateSyncedBlockDropdownItem.js +36 -47
- package/dist/cjs/ui/SyncBlockLabel.js +2 -13
- package/dist/cjs/ui/SyncBlockRendererWrapper.js +7 -9
- package/dist/cjs/ui/SyncedLocationDropdown.js +53 -116
- package/dist/cjs/ui/floating-toolbar.js +1 -2
- package/dist/es2019/editor-commands/index.js +8 -18
- package/dist/es2019/nodeviews/bodiedSyncedBlock.js +3 -7
- package/dist/es2019/nodeviews/syncedBlock.js +9 -15
- package/dist/es2019/pm-plugins/main.js +16 -41
- package/dist/es2019/pm-plugins/utils/handle-bodied-sync-block-removal.js +33 -51
- package/dist/es2019/syncedBlockPlugin.js +1 -4
- package/dist/es2019/ui/CreateSyncedBlockButton.js +25 -36
- package/dist/es2019/ui/CreateSyncedBlockDropdownItem.js +36 -47
- package/dist/es2019/ui/SyncBlockLabel.js +2 -13
- package/dist/es2019/ui/SyncBlockRendererWrapper.js +7 -9
- package/dist/es2019/ui/SyncedLocationDropdown.js +1 -58
- package/dist/es2019/ui/floating-toolbar.js +1 -2
- package/dist/esm/editor-commands/index.js +8 -18
- package/dist/esm/nodeviews/bodiedSyncedBlock.js +3 -7
- package/dist/esm/nodeviews/syncedBlock.js +9 -15
- package/dist/esm/pm-plugins/main.js +15 -43
- package/dist/esm/pm-plugins/utils/handle-bodied-sync-block-removal.js +36 -54
- package/dist/esm/syncedBlockPlugin.js +1 -4
- package/dist/esm/ui/CreateSyncedBlockButton.js +23 -35
- package/dist/esm/ui/CreateSyncedBlockDropdownItem.js +36 -47
- package/dist/esm/ui/SyncBlockLabel.js +2 -13
- package/dist/esm/ui/SyncBlockRendererWrapper.js +7 -9
- package/dist/esm/ui/SyncedLocationDropdown.js +53 -116
- package/dist/esm/ui/floating-toolbar.js +1 -2
- package/dist/types/pm-plugins/main.d.ts +2 -4
- package/package.json +4 -13
|
@@ -9,9 +9,8 @@ import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
|
9
9
|
import { NodeSelection, PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
10
10
|
import { ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
11
11
|
import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
12
|
-
import { convertPMNodesToSyncBlockNodes
|
|
12
|
+
import { convertPMNodesToSyncBlockNodes } from '@atlaskit/editor-synced-block-provider';
|
|
13
13
|
import { getSourceProductFromResourceIdSafe } from '@atlaskit/editor-synced-block-provider/utils';
|
|
14
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
15
14
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
16
15
|
import { bodiedSyncBlockNodeView } from '../nodeviews/bodiedSyncedBlock';
|
|
17
16
|
import { SyncBlock as SyncBlockView } from '../nodeviews/syncedBlock';
|
|
@@ -32,8 +31,7 @@ export const syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
|
|
|
32
31
|
* `deleteButton` (otherwise indistinguishable from a keyboard delete — both are
|
|
33
32
|
* plain ReplaceSteps). A separate key (rather than `syncedBlockPluginKey`, which
|
|
34
33
|
* carries plugin-state transitions read in `apply()`, or a bare string) keeps
|
|
35
|
-
* this transient signal uniquely namespaced and isolated.
|
|
36
|
-
* `platform_editor_blocks_patch_4`.
|
|
34
|
+
* this transient signal uniquely namespaced and isolated.
|
|
37
35
|
*/
|
|
38
36
|
export const deleteMechanismMetaKey = new PluginKey('syncedBlockDeleteMechanism');
|
|
39
37
|
const syncedBlockPromptedFeedbackMetaKey = new PluginKey('syncedBlockPromptedFeedback');
|
|
@@ -42,8 +40,7 @@ const syncedBlockPromptedFeedbackMetaKey = new PluginKey('syncedBlockPromptedFee
|
|
|
42
40
|
* Creation-type signals set by {@link createSyncedBlock} on the creating
|
|
43
41
|
* transaction. The async creation handler forwards them to the store manager,
|
|
44
42
|
* which attaches them to the `syncedBlockCreate` event. A dedicated key keeps
|
|
45
|
-
* this transient signal isolated from plugin state.
|
|
46
|
-
* `platform_editor_blocks_patch_4`.
|
|
43
|
+
* this transient signal isolated from plugin state.
|
|
47
44
|
*/
|
|
48
45
|
|
|
49
46
|
export const creationMetaKey = new PluginKey('syncedBlockCreationMeta');
|
|
@@ -288,17 +285,9 @@ const filterTransactionOnline = ({
|
|
|
288
285
|
});
|
|
289
286
|
});
|
|
290
287
|
if (bodiedSyncBlockRemoved.length > 0) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
// platform_editor_blocks_patch_4 is on we instead recompute the delete fresh
|
|
295
|
-
// from the live document on confirm (see handleBodiedSyncBlockRemoval), so
|
|
296
|
-
// there is no need to stash the transaction here. See EDITOR-7889.
|
|
297
|
-
// eslint-disable-next-line no-param-reassign
|
|
298
|
-
confirmationTransactionRef.current = tr;
|
|
299
|
-
}
|
|
300
|
-
// Only derive the mechanism behind the gate — discarded gate-off.
|
|
301
|
-
const mechanism = fg('platform_editor_blocks_patch_4') ? getDeleteMechanism(tr, state) : undefined;
|
|
288
|
+
// Recompute the delete fresh from the live document on confirm
|
|
289
|
+
// (see handleBodiedSyncBlockRemoval). See EDITOR-7889.
|
|
290
|
+
const mechanism = getDeleteMechanism(tr, state);
|
|
302
291
|
const feedbackContext = entryPoint && getDeleteReason(tr) !== 'source-block-unsynced' ? {
|
|
303
292
|
blockType: 'source',
|
|
304
293
|
entryPoint
|
|
@@ -330,7 +319,7 @@ const filterTransactionOnline = ({
|
|
|
330
319
|
// be wrong. The bodiedSyncBlock content will still be inserted but will
|
|
331
320
|
// not be registered as a source block in Block Service, which is safer
|
|
332
321
|
// than creating a zombie block under the wrong ARI.
|
|
333
|
-
if (isPasteOrDrop
|
|
322
|
+
if (isPasteOrDrop) {
|
|
334
323
|
return true;
|
|
335
324
|
}
|
|
336
325
|
|
|
@@ -635,8 +624,8 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
635
624
|
const syncBlockNodes = instance.doc.children.filter(syncBlockStore.referenceManager.isReferenceBlock);
|
|
636
625
|
syncBlockStore.referenceManager.fetchSyncBlocksData(convertPMNodesToSyncBlockNodes(syncBlockNodes));
|
|
637
626
|
|
|
638
|
-
// Populate source sync block cache from initial document
|
|
639
|
-
//
|
|
627
|
+
// Populate source sync block cache from initial document.
|
|
628
|
+
// This replaces the constructor call in the nodeview.
|
|
640
629
|
instance.doc.forEach(node => {
|
|
641
630
|
if (syncBlockStore.sourceManager.isSourceBlock(node)) {
|
|
642
631
|
syncBlockStore.sourceManager.updateSyncBlockData(node, false);
|
|
@@ -867,18 +856,12 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
867
856
|
return false;
|
|
868
857
|
},
|
|
869
858
|
cut: () => {
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
ctx.markCutEvent();
|
|
873
|
-
}
|
|
859
|
+
ctx.consumeCutEvent();
|
|
860
|
+
ctx.markCutEvent();
|
|
874
861
|
return false;
|
|
875
862
|
}
|
|
876
863
|
},
|
|
877
864
|
transformPasted: (slice, _view) => {
|
|
878
|
-
if (!fg('platform_synced_block_patch_13')) {
|
|
879
|
-
return slice;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
865
|
// Defense against bodiedSyncBlock nodes arriving via paste
|
|
883
866
|
// (e.g. drag-and-drop, cut-paste, or browser-level clipboard
|
|
884
867
|
// operations that bypass the transformCopied handler).
|
|
@@ -907,9 +890,8 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
907
890
|
schema
|
|
908
891
|
} = state;
|
|
909
892
|
const isCopy = ctx.consumeCopyEvent();
|
|
910
|
-
const isSyncedBlockPatch13Enabled = fg('platform_synced_block_patch_13');
|
|
911
893
|
const wasCut = ctx.consumeCutEvent();
|
|
912
|
-
const isCut =
|
|
894
|
+
const isCut = wasCut;
|
|
913
895
|
const isPromptedFeedbackCut = Boolean(syncBlockStore && (options === null || options === void 0 ? void 0 : options.onGiveFeedback) && wasCut) && expValEqualsNoExposure('platform_editor_sync_block_activation', 'isEnabled', true);
|
|
914
896
|
if (!syncBlockStore || !isCopy && !isCut && !isPromptedFeedbackCut) {
|
|
915
897
|
return slice;
|
|
@@ -947,7 +929,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
947
929
|
if (!sliceFullyContainsNode(slice, node)) {
|
|
948
930
|
return node.content;
|
|
949
931
|
}
|
|
950
|
-
if (
|
|
932
|
+
if (isCut) {
|
|
951
933
|
return node;
|
|
952
934
|
}
|
|
953
935
|
const newResourceId = syncBlockStore.referenceManager.generateResourceIdForReference(node.attrs.resourceId);
|
|
@@ -1094,16 +1076,9 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
1094
1076
|
});
|
|
1095
1077
|
}
|
|
1096
1078
|
|
|
1097
|
-
//
|
|
1098
|
-
//
|
|
1099
|
-
|
|
1100
|
-
if (!fg('platform_editor_blocks_patch_4')) {
|
|
1101
|
-
trs.filter(tr => tr.docChanged).forEach(tr => {
|
|
1102
|
-
if (confirmationTransactionRef.current) {
|
|
1103
|
-
confirmationTransactionRef.current = rebaseTransaction(confirmationTransactionRef.current, tr, newState);
|
|
1104
|
-
}
|
|
1105
|
-
});
|
|
1106
|
-
}
|
|
1079
|
+
// Delete is recomputed fresh from the live document on confirm.
|
|
1080
|
+
// See EDITOR-7889.
|
|
1081
|
+
|
|
1107
1082
|
for (const tr of trs) {
|
|
1108
1083
|
if (tr.getMeta(pmHistoryPluginKey)) {
|
|
1109
1084
|
const {
|
|
@@ -30,64 +30,46 @@ export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockSt
|
|
|
30
30
|
// See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
|
|
31
31
|
// proceed with deletion.
|
|
32
32
|
syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(bodiedSyncBlockRemoved.map(node => node.attrs), deletionReason, () => {
|
|
33
|
-
var _api$
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const isUnsync = fg('platform_editor_blocks_patch_6') && deletionReason === 'source-block-unsynced';
|
|
51
|
-
const recomputedTr = isUnsync ? recomputeUnsyncTransaction(tr, syncBlockStore.sourceManager.isSourceBlock, bodiedSyncBlockRemoved.map(node => node.attrs)) : recomputeDeleteTransaction(tr, syncBlockStore.sourceManager.isSourceBlock, bodiedSyncBlockRemoved.map(node => node.attrs));
|
|
33
|
+
var _api$core2;
|
|
34
|
+
// Recompute the delete fresh from the live document instead of
|
|
35
|
+
// replaying a stashed-and-rebased transaction. The stash/rebase
|
|
36
|
+
// approach produced stale, schema-invalid transactions when the
|
|
37
|
+
// document changed shape (local edits or remote collab) while the
|
|
38
|
+
// confirmation modal was open — the dominant signature being
|
|
39
|
+
// "Invalid content for node bodiedSyncBlock: <>". See EDITOR-7889.
|
|
40
|
+
api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(({
|
|
41
|
+
tr
|
|
42
|
+
}) => {
|
|
43
|
+
// EDITOR-8230: unsync must remove only the sync wrapper and keep the block's
|
|
44
|
+
// content inline, whereas delete removes the whole block. Before this branch,
|
|
45
|
+
// the confirm path always recomputed a full delete, so unsyncing a source block
|
|
46
|
+
// silently deleted its content. When the deletion was actually triggered by
|
|
47
|
+
// unsync, recompute an unwrap (replaceWith content) instead of a delete.
|
|
48
|
+
const isUnsync = fg('platform_editor_blocks_patch_6') && deletionReason === 'source-block-unsynced';
|
|
49
|
+
const recomputedTr = isUnsync ? recomputeUnsyncTransaction(tr, syncBlockStore.sourceManager.isSourceBlock, bodiedSyncBlockRemoved.map(node => node.attrs)) : recomputeDeleteTransaction(tr, syncBlockStore.sourceManager.isSourceBlock, bodiedSyncBlockRemoved.map(node => node.attrs));
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// bodiedSyncBlock deletion is expected to be permanent (cannot undo)
|
|
66
|
-
// For a normal deletion (not triggered by undo), remove it from history so that it cannot be undone
|
|
67
|
-
recomputedTr.setMeta('addToHistory', false);
|
|
68
|
-
}
|
|
69
|
-
return recomputedTr;
|
|
70
|
-
});
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
const confirmationTransaction = confirmationTransactionRef.current;
|
|
74
|
-
if (!confirmationTransaction) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(() => {
|
|
78
|
-
const trToDispatch = confirmationTransaction.setMeta('isConfirmedSyncBlockDeletion', true);
|
|
79
|
-
feedbackCallbacks === null || feedbackCallbacks === void 0 ? void 0 : feedbackCallbacks.onDeleteTransaction(trToDispatch);
|
|
80
|
-
if (!trToDispatch.getMeta(pmHistoryPluginKey)) {
|
|
51
|
+
// The target node(s) no longer exist in the live document (e.g.
|
|
52
|
+
// a remote collaborator already removed them). There is nothing
|
|
53
|
+
// to delete locally, so return `null` to skip the dispatch
|
|
54
|
+
// entirely — dispatching the untouched transaction would be a
|
|
55
|
+
// no-op that still runs the plugin's filter/append hooks. The
|
|
56
|
+
// backend deletion has already been issued by the store manager.
|
|
57
|
+
if (!recomputedTr) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
recomputedTr.setMeta('isConfirmedSyncBlockDeletion', true);
|
|
61
|
+
feedbackCallbacks === null || feedbackCallbacks === void 0 ? void 0 : feedbackCallbacks.onDeleteTransaction(recomputedTr);
|
|
62
|
+
if (!recomputedTr.getMeta(pmHistoryPluginKey)) {
|
|
81
63
|
// bodiedSyncBlock deletion is expected to be permanent (cannot undo)
|
|
82
64
|
// For a normal deletion (not triggered by undo), remove it from history so that it cannot be undone
|
|
83
|
-
|
|
65
|
+
recomputedTr.setMeta('addToHistory', false);
|
|
84
66
|
}
|
|
85
|
-
return
|
|
67
|
+
return recomputedTr;
|
|
86
68
|
});
|
|
87
69
|
}, success => {
|
|
88
|
-
var _api$
|
|
70
|
+
var _api$core3;
|
|
89
71
|
feedbackCallbacks === null || feedbackCallbacks === void 0 ? void 0 : feedbackCallbacks.onDeleteCompleted(success);
|
|
90
|
-
api === null || api === void 0 ? void 0 : (_api$
|
|
72
|
+
api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
|
|
91
73
|
tr
|
|
92
74
|
}) => {
|
|
93
75
|
let newState;
|
|
@@ -2,7 +2,6 @@ import React from 'react';
|
|
|
2
2
|
import { syncBlock, bodiedSyncBlock } from '@atlaskit/adf-schema';
|
|
3
3
|
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
4
4
|
import { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
|
|
5
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
6
5
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
7
6
|
import { flushBodiedSyncBlocks, flushSyncBlocks, discardUnpublishedSyncBlocks } from './editor-actions';
|
|
8
7
|
import { copySyncedBlockReferenceToClipboardEditorCommand, createSyncedBlock } from './editor-commands';
|
|
@@ -187,9 +186,7 @@ export const syncedBlockPlugin = ({
|
|
|
187
186
|
// Destroy the SyncBlockStoreManager on editor unmount to cancel
|
|
188
187
|
// pending timers, subscriptions, and in-flight fetches.
|
|
189
188
|
destroy() {
|
|
190
|
-
|
|
191
|
-
syncBlockStore.destroy();
|
|
192
|
-
}
|
|
189
|
+
syncBlockStore.destroy();
|
|
193
190
|
}
|
|
194
191
|
};
|
|
195
192
|
};
|
|
@@ -6,7 +6,6 @@ import { syncBlockMessages } from '@atlaskit/editor-common/messages';
|
|
|
6
6
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
7
7
|
import { ToolbarButton, ToolbarTooltip } from '@atlaskit/editor-toolbar';
|
|
8
8
|
import BlockSyncedIcon from '@atlaskit/icon-lab/core/block-synced';
|
|
9
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
10
9
|
import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
|
|
11
10
|
import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
|
|
12
11
|
export const CreateSyncedBlockButton = ({
|
|
@@ -30,45 +29,35 @@ export const CreateSyncedBlockButton = ({
|
|
|
30
29
|
const canInsertEmptyBlock = Boolean(selection === null || selection === void 0 ? void 0 : selection.empty);
|
|
31
30
|
const isDisabled = Boolean(isOfflineMode(mode) || !canBeConverted && !canInsertEmptyBlock);
|
|
32
31
|
const onClick = useCallback(() => {
|
|
33
|
-
var _api$
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
var _api$core;
|
|
33
|
+
// Insert the synced block and stop preserving the selection in a single
|
|
34
|
+
// transaction so the caret that insertSyncedBlock places inside the new
|
|
35
|
+
// block survives (EDITOR-7949). The toolbar is not normally opened via the
|
|
36
|
+
// block menu, but stopping preservation is defensive and keeps behaviour
|
|
37
|
+
// consistent with the block-menu create item should preservation ever be
|
|
38
|
+
// active (e.g. a block is selected via block-controls). Then re-focus so
|
|
39
|
+
// the caret is active, but only when the transaction was actually
|
|
40
|
+
// dispatched — otherwise a failed insertion would still steal DOM focus
|
|
41
|
+
// into the editor (mirrors CreateSyncedBlockDropdownItem).
|
|
42
|
+
const dispatched = api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
|
|
43
|
+
tr
|
|
44
|
+
}) => {
|
|
45
|
+
var _api$blockControls, _api$blockControls$co;
|
|
46
|
+
const result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.SYNCED_BLOCK_TB)({
|
|
46
47
|
tr
|
|
47
|
-
}) => {
|
|
48
|
-
var _api$blockControls, _api$blockControls$co;
|
|
49
|
-
const result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.SYNCED_BLOCK_TB)({
|
|
50
|
-
tr
|
|
51
|
-
});
|
|
52
|
-
if (!result) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : (_api$blockControls$co = _api$blockControls.commands) === null || _api$blockControls$co === void 0 ? void 0 : _api$blockControls$co.stopPreservingSelection()({
|
|
56
|
-
tr
|
|
57
|
-
});
|
|
58
|
-
return tr;
|
|
59
48
|
});
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.focus();
|
|
49
|
+
if (!result) {
|
|
50
|
+
return null;
|
|
63
51
|
}
|
|
64
|
-
|
|
52
|
+
api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : (_api$blockControls$co = _api$blockControls.commands) === null || _api$blockControls$co === void 0 ? void 0 : _api$blockControls$co.stopPreservingSelection()({
|
|
53
|
+
tr
|
|
54
|
+
});
|
|
55
|
+
return tr;
|
|
56
|
+
});
|
|
57
|
+
if (dispatched) {
|
|
58
|
+
var _api$core2;
|
|
59
|
+
api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.focus();
|
|
65
60
|
}
|
|
66
|
-
api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
|
|
67
|
-
tr
|
|
68
|
-
}) => api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.SYNCED_BLOCK_TB)({
|
|
69
|
-
tr
|
|
70
|
-
}));
|
|
71
|
-
api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.focus();
|
|
72
61
|
}, [api]);
|
|
73
62
|
const message = intl.formatMessage(syncBlockMessages.syncBlockLabel);
|
|
74
63
|
return /*#__PURE__*/React.createElement(ToolbarTooltip, {
|
|
@@ -39,54 +39,43 @@ const CreateSyncedBlockDropdownItem = ({
|
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
const onClick = () => {
|
|
42
|
-
var _api$
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
var _api$core;
|
|
43
|
+
// Insert the synced block, close the block menu, and stop preserving the
|
|
44
|
+
// selection — all in a single transaction, mirroring the block-menu delete
|
|
45
|
+
// item. Stopping preservation is required so the caret that
|
|
46
|
+
// insertSyncedBlock places inside the new block survives; otherwise
|
|
47
|
+
// block-controls restores a whole-node NodeSelection over it, hiding the
|
|
48
|
+
// caret (EDITOR-7949). Then re-focus the editor so the caret is active,
|
|
49
|
+
// but only when the transaction was actually dispatched — otherwise a
|
|
50
|
+
// failed insertion would still steal DOM focus into the editor.
|
|
51
|
+
const dispatched = api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
|
|
52
|
+
tr
|
|
53
|
+
}) => {
|
|
54
|
+
var _api$blockControls, _api$blockControls$co, _api$blockControls2, _api$blockControls2$c;
|
|
55
|
+
// If the insertion fails, bail out without closing the menu or
|
|
56
|
+
// stopping selection preservation — otherwise we would dispatch an
|
|
57
|
+
// effectively-empty transaction and close the menu despite nothing
|
|
58
|
+
// having been inserted.
|
|
59
|
+
const result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.BLOCK_MENU)({
|
|
54
60
|
tr
|
|
55
|
-
}) => {
|
|
56
|
-
var _api$blockControls, _api$blockControls$co, _api$blockControls2, _api$blockControls2$c;
|
|
57
|
-
// If the insertion fails, bail out without closing the menu or
|
|
58
|
-
// stopping selection preservation — otherwise we would dispatch an
|
|
59
|
-
// effectively-empty transaction and close the menu despite nothing
|
|
60
|
-
// having been inserted.
|
|
61
|
-
const result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.BLOCK_MENU)({
|
|
62
|
-
tr
|
|
63
|
-
});
|
|
64
|
-
if (!result) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : (_api$blockControls$co = _api$blockControls.commands) === null || _api$blockControls$co === void 0 ? void 0 : _api$blockControls$co.toggleBlockMenu({
|
|
68
|
-
closeMenu: true
|
|
69
|
-
})({
|
|
70
|
-
tr
|
|
71
|
-
});
|
|
72
|
-
api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : (_api$blockControls2$c = _api$blockControls2.commands) === null || _api$blockControls2$c === void 0 ? void 0 : _api$blockControls2$c.stopPreservingSelection()({
|
|
73
|
-
tr
|
|
74
|
-
});
|
|
75
|
-
return tr;
|
|
76
61
|
});
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.focus();
|
|
62
|
+
if (!result) {
|
|
63
|
+
return null;
|
|
80
64
|
}
|
|
81
|
-
|
|
65
|
+
api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : (_api$blockControls$co = _api$blockControls.commands) === null || _api$blockControls$co === void 0 ? void 0 : _api$blockControls$co.toggleBlockMenu({
|
|
66
|
+
closeMenu: true
|
|
67
|
+
})({
|
|
68
|
+
tr
|
|
69
|
+
});
|
|
70
|
+
api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : (_api$blockControls2$c = _api$blockControls2.commands) === null || _api$blockControls2$c === void 0 ? void 0 : _api$blockControls2$c.stopPreservingSelection()({
|
|
71
|
+
tr
|
|
72
|
+
});
|
|
73
|
+
return tr;
|
|
74
|
+
});
|
|
75
|
+
if (dispatched) {
|
|
76
|
+
var _api$core2;
|
|
77
|
+
api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.focus();
|
|
82
78
|
}
|
|
83
|
-
|
|
84
|
-
// Legacy behaviour: insert then close the block menu as two separate executes
|
|
85
|
-
// (batching them caused selection collisions — EDITOR-2751).
|
|
86
|
-
api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.BLOCK_MENU));
|
|
87
|
-
api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 ? void 0 : (_api$blockControls3$c = _api$blockControls3.commands) === null || _api$blockControls3$c === void 0 ? void 0 : _api$blockControls3$c.toggleBlockMenu({
|
|
88
|
-
closeMenu: true
|
|
89
|
-
}));
|
|
90
79
|
};
|
|
91
80
|
const isOffline = isOfflineMode(mode);
|
|
92
81
|
const lozenge = /*#__PURE__*/React.createElement(SyncedBlockNewLozenge, {
|
|
@@ -118,9 +107,9 @@ const CopySyncedBlockDropdownItem = ({
|
|
|
118
107
|
};
|
|
119
108
|
});
|
|
120
109
|
const onClick = () => {
|
|
121
|
-
var _api$
|
|
122
|
-
api === null || api === void 0 ? void 0 : (_api$
|
|
123
|
-
api === null || api === void 0 ? void 0 : (_api$
|
|
110
|
+
var _api$core3, _api$core4, _api$blockControls3, _api$blockControls3$c;
|
|
111
|
+
api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.copySyncedBlockReferenceToClipboard(INPUT_METHOD.BLOCK_MENU));
|
|
112
|
+
api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 ? void 0 : (_api$blockControls3$c = _api$blockControls3.commands) === null || _api$blockControls3$c === void 0 ? void 0 : _api$blockControls3$c.toggleBlockMenu({
|
|
124
113
|
closeMenu: true
|
|
125
114
|
}));
|
|
126
115
|
};
|
|
@@ -3,7 +3,6 @@ import { useIntl } from 'react-intl';
|
|
|
3
3
|
import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
4
4
|
import { SyncBlockLabelSharedCssClassName } from '@atlaskit/editor-common/sync-block';
|
|
5
5
|
import BlockSyncedIcon from '@atlaskit/icon-lab/core/block-synced';
|
|
6
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
6
|
import { Text } from '@atlaskit/primitives/compiled';
|
|
8
7
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
9
8
|
import Tooltip from '@atlaskit/tooltip';
|
|
@@ -32,7 +31,7 @@ const SyncBlockLabelComponent = ({
|
|
|
32
31
|
let tooltipContent = tooltipMessage;
|
|
33
32
|
if (contentUpdatedAt) {
|
|
34
33
|
const elapsedTime = formatElapsedTime(contentUpdatedAt, intl);
|
|
35
|
-
tooltipContent =
|
|
34
|
+
tooltipContent = /*#__PURE__*/React.createElement("div", null, title ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, {
|
|
36
35
|
size: "small",
|
|
37
36
|
color: "color.text.inverse",
|
|
38
37
|
weight: "bold"
|
|
@@ -49,17 +48,7 @@ const SyncBlockLabelComponent = ({
|
|
|
49
48
|
}, formatMessage(messages.referenceSyncBlockLastEdited)), /*#__PURE__*/React.createElement(Text, {
|
|
50
49
|
size: "small",
|
|
51
50
|
color: "color.text.inverse"
|
|
52
|
-
}, ' ', elapsedTime))
|
|
53
|
-
size: "small",
|
|
54
|
-
color: "color.text.inverse"
|
|
55
|
-
}, tooltipMessage), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement(Text, {
|
|
56
|
-
size: "small",
|
|
57
|
-
color: "color.text.inverse",
|
|
58
|
-
weight: "bold"
|
|
59
|
-
}, formatMessage(messages.referenceSyncBlockLastEdited)), /*#__PURE__*/React.createElement(Text, {
|
|
60
|
-
size: "small",
|
|
61
|
-
color: "color.text.inverse"
|
|
62
|
-
}, elapsedTime));
|
|
51
|
+
}, ' ', elapsedTime));
|
|
63
52
|
}
|
|
64
53
|
setTooltipContent(tooltipContent);
|
|
65
54
|
}, [contentUpdatedAt, formatMessage, intl, title, tooltipMessage]);
|
|
@@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useRef } from 'react';
|
|
|
2
2
|
import { bind } from 'bind-event-listener';
|
|
3
3
|
import { SyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
|
|
4
4
|
import { SyncBlockError, useFetchSyncBlockData, useFetchSyncBlockTitle } from '@atlaskit/editor-synced-block-provider';
|
|
5
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
6
5
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
7
6
|
import { SyncBlockLabel } from './SyncBlockLabel';
|
|
8
7
|
const SyncBlockRendererWrapperDataId = 'sync-block-plugin-renderer-wrapper';
|
|
@@ -20,7 +19,6 @@ const SyncBlockRendererWrapperComponent = ({
|
|
|
20
19
|
const contentUpdatedAt = syncBlockFetchResult === null || syncBlockFetchResult === void 0 ? void 0 : (_syncBlockFetchResult = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult === void 0 ? void 0 : (_syncBlockFetchResult2 = _syncBlockFetchResult.data) === null || _syncBlockFetchResult2 === void 0 ? void 0 : _syncBlockFetchResult2.contentUpdatedAt;
|
|
21
20
|
const isUnpublishedBlock = ((_syncBlockFetchResult3 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult3 === void 0 ? void 0 : (_syncBlockFetchResult4 = _syncBlockFetchResult3.data) === null || _syncBlockFetchResult4 === void 0 ? void 0 : _syncBlockFetchResult4.status) === 'unpublished';
|
|
22
21
|
const isUnsyncedBlock = isUnpublishedBlock || (syncBlockFetchResult === null || syncBlockFetchResult === void 0 ? void 0 : (_syncBlockFetchResult5 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult5 === void 0 ? void 0 : (_syncBlockFetchResult6 = _syncBlockFetchResult5.error) === null || _syncBlockFetchResult6 === void 0 ? void 0 : _syncBlockFetchResult6.type) === SyncBlockError.NotFound;
|
|
23
|
-
const isTextSelectionEnabled = fg('platform_synced_block_patch_14');
|
|
24
22
|
|
|
25
23
|
// Evaluated unconditionally so the experiment exposure is tracked correctly
|
|
26
24
|
// (recorded on every render, not lazily on first click).
|
|
@@ -43,7 +41,7 @@ const SyncBlockRendererWrapperComponent = ({
|
|
|
43
41
|
const rendererRef = useRef(null);
|
|
44
42
|
useEffect(() => {
|
|
45
43
|
const containerEl = rendererRef.current;
|
|
46
|
-
if (!containerEl || !
|
|
44
|
+
if (!containerEl || !isSyncBlockActivationEnabled) {
|
|
47
45
|
return;
|
|
48
46
|
}
|
|
49
47
|
const unbind = bind(containerEl, {
|
|
@@ -63,25 +61,25 @@ const SyncBlockRendererWrapperComponent = ({
|
|
|
63
61
|
}
|
|
64
62
|
});
|
|
65
63
|
return unbind;
|
|
66
|
-
}, [
|
|
64
|
+
}, [isSyncBlockActivationEnabled]);
|
|
67
65
|
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
|
68
66
|
ref: rendererRef,
|
|
69
67
|
"data-testid": SyncBlockRendererWrapperDataId
|
|
70
68
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
71
69
|
,
|
|
72
70
|
className: SyncBlockSharedCssClassName.renderer,
|
|
73
|
-
contentEditable:
|
|
71
|
+
contentEditable: true,
|
|
74
72
|
suppressContentEditableWarning: true
|
|
75
73
|
// Prevent the contentEditable div from being keyboard-focusable.
|
|
76
74
|
// It is only used to enable text selection, not as an input target.
|
|
77
75
|
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
78
76
|
,
|
|
79
|
-
tabIndex:
|
|
80
|
-
onBeforeInput:
|
|
81
|
-
onPaste:
|
|
77
|
+
tabIndex: -1,
|
|
78
|
+
onBeforeInput: preventInput,
|
|
79
|
+
onPaste: preventInput
|
|
82
80
|
// eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop
|
|
83
81
|
,
|
|
84
|
-
onDrop:
|
|
82
|
+
onDrop: preventInput
|
|
85
83
|
}, syncedBlockRenderer({
|
|
86
84
|
syncBlockFetchResult,
|
|
87
85
|
api
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/* SyncedLocationDropdown.tsx generated by @compiled/babel-plugin v2.0.0 */
|
|
2
|
-
import _extends from "@babel/runtime/helpers/extends";
|
|
3
2
|
import "./SyncedLocationDropdown.compiled.css";
|
|
4
3
|
import * as React from 'react';
|
|
5
4
|
import { ax, ix } from "@compiled/react/runtime";
|
|
6
5
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
6
|
import { cx } from '@compiled/react';
|
|
8
|
-
import
|
|
7
|
+
import { DropdownItem, DropdownItemGroup } from '@atlaskit/dropdown-menu';
|
|
9
8
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
10
9
|
import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
11
10
|
import { SYNCED_BLOCKS_DOCUMENTATION_URL } from '@atlaskit/editor-common/sync-block';
|
|
@@ -26,7 +25,6 @@ import SubtaskIcon from '@atlaskit/icon/core/subtasks';
|
|
|
26
25
|
import TaskIcon from '@atlaskit/icon/core/task';
|
|
27
26
|
import { ConfluenceIcon, JiraIcon, AtlassianIcon } from '@atlaskit/logo';
|
|
28
27
|
import Lozenge from '@atlaskit/lozenge';
|
|
29
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
30
28
|
import { Box, Text, Inline, Anchor, Stack } from '@atlaskit/primitives/compiled';
|
|
31
29
|
import Spinner from '@atlaskit/spinner';
|
|
32
30
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
@@ -297,16 +295,6 @@ export const SyncedLocationDropdown = ({
|
|
|
297
295
|
api,
|
|
298
296
|
floatingToolbarRenderContext
|
|
299
297
|
}) => {
|
|
300
|
-
if (!fg('platform_synced_block_patch_13')) {
|
|
301
|
-
return /*#__PURE__*/React.createElement(LegacySyncedLocationDropdown, {
|
|
302
|
-
syncBlockStore: syncBlockStore,
|
|
303
|
-
resourceId: resourceId,
|
|
304
|
-
intl: intl,
|
|
305
|
-
isSource: isSource,
|
|
306
|
-
localId: localId,
|
|
307
|
-
api: api
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
298
|
return /*#__PURE__*/React.createElement(EditorPositionedSyncedLocationDropdown, {
|
|
311
299
|
syncBlockStore: syncBlockStore,
|
|
312
300
|
resourceId: resourceId,
|
|
@@ -381,51 +369,6 @@ const EditorPositionedSyncedLocationDropdown = ({
|
|
|
381
369
|
}
|
|
382
370
|
}, content);
|
|
383
371
|
};
|
|
384
|
-
const LegacySyncedLocationDropdown = ({
|
|
385
|
-
syncBlockStore,
|
|
386
|
-
resourceId,
|
|
387
|
-
intl,
|
|
388
|
-
isSource,
|
|
389
|
-
localId,
|
|
390
|
-
api
|
|
391
|
-
}) => {
|
|
392
|
-
const triggerTitle = intl.formatMessage(messages.syncedLocationDropdownTitle);
|
|
393
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
394
|
-
const content = isOpen ? /*#__PURE__*/React.createElement(DropdownContent, {
|
|
395
|
-
syncBlockStore: syncBlockStore,
|
|
396
|
-
resourceId: resourceId,
|
|
397
|
-
intl: intl,
|
|
398
|
-
isSource: isSource,
|
|
399
|
-
localId: localId,
|
|
400
|
-
api: api
|
|
401
|
-
}) : null;
|
|
402
|
-
return /*#__PURE__*/React.createElement(DropdownMenu, {
|
|
403
|
-
isOpen: isOpen
|
|
404
|
-
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
|
|
405
|
-
,
|
|
406
|
-
onOpenChange: ({
|
|
407
|
-
isOpen
|
|
408
|
-
}) => setIsOpen(isOpen),
|
|
409
|
-
testId: SYNCED_LOCATIONS_DROPDOWN_TEST_ID
|
|
410
|
-
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
|
|
411
|
-
,
|
|
412
|
-
trigger: ({
|
|
413
|
-
triggerRef,
|
|
414
|
-
...triggerProps
|
|
415
|
-
}) => /*#__PURE__*/React.createElement(Button, _extends({
|
|
416
|
-
ref: triggerRef,
|
|
417
|
-
areAnyNewToolbarFlagsEnabled: true,
|
|
418
|
-
selected: isOpen,
|
|
419
|
-
iconAfter: /*#__PURE__*/React.createElement(ChevronDownIcon, {
|
|
420
|
-
color: "currentColor",
|
|
421
|
-
spacing: "spacious",
|
|
422
|
-
label: "",
|
|
423
|
-
size: "small"
|
|
424
|
-
})
|
|
425
|
-
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
426
|
-
}, triggerProps), triggerTitle)
|
|
427
|
-
}, content);
|
|
428
|
-
};
|
|
429
372
|
export const SyncedLocationDropdownWithCount = ({
|
|
430
373
|
syncBlockStore,
|
|
431
374
|
resourceId,
|