@atlaskit/editor-plugin-synced-block 15.0.10 → 16.0.0
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 +29 -0
- package/afm-products/tsconfig.json +3 -0
- package/dist/cjs/editor-commands/index.js +13 -4
- package/dist/cjs/pm-plugins/main.js +45 -12
- package/dist/cjs/pm-plugins/utils/handle-bodied-sync-block-creation.js +4 -4
- package/dist/cjs/pm-plugins/utils/handle-bodied-sync-block-removal.js +110 -24
- package/dist/cjs/pm-plugins/utils/recompute-delete-transaction.js +2 -2
- package/dist/es2019/editor-commands/index.js +10 -4
- package/dist/es2019/pm-plugins/main.js +45 -12
- package/dist/es2019/pm-plugins/utils/handle-bodied-sync-block-creation.js +4 -4
- package/dist/es2019/pm-plugins/utils/handle-bodied-sync-block-removal.js +103 -15
- package/dist/es2019/pm-plugins/utils/recompute-delete-transaction.js +1 -1
- package/dist/esm/editor-commands/index.js +13 -4
- package/dist/esm/pm-plugins/main.js +45 -12
- package/dist/esm/pm-plugins/utils/handle-bodied-sync-block-creation.js +4 -4
- package/dist/esm/pm-plugins/utils/handle-bodied-sync-block-removal.js +111 -25
- package/dist/esm/pm-plugins/utils/recompute-delete-transaction.js +1 -1
- package/dist/types/pm-plugins/main.d.ts +2 -1
- package/dist/types/pm-plugins/utils/handle-bodied-sync-block-creation.d.ts +1 -1
- package/dist/types/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +11 -3
- package/dist/types/pm-plugins/utils/recompute-delete-transaction.d.ts +10 -0
- package/dist/types/syncedBlockPluginType.d.ts +2 -0
- package/package.json +20 -22
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { pmHistoryPluginKey } from '@atlaskit/editor-common/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
3
3
|
import { FLAG_ID } from '../../types';
|
|
4
4
|
import { syncedBlockPluginKey } from '../main';
|
|
5
|
-
import { recomputeDeleteTransaction, recomputeUnsyncTransaction } from './recompute-delete-transaction';
|
|
5
|
+
import { matchesRequestedBlock, recomputeDeleteTransaction, recomputeUnsyncTransaction } from './recompute-delete-transaction';
|
|
6
6
|
const onRetry = (api, syncBlockStore) => () => {
|
|
7
7
|
var _api$core;
|
|
8
8
|
api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
|
|
@@ -21,7 +21,77 @@ const onDismissed = syncBlockStore => tr => {
|
|
|
21
21
|
bodiedSyncBlockDeletionStatus: 'none'
|
|
22
22
|
});
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
const topLevelSourceBlocks = (doc, isSourceBlock) => {
|
|
25
|
+
const blocks = [];
|
|
26
|
+
doc.forEach(node => {
|
|
27
|
+
if (isSourceBlock(node)) {
|
|
28
|
+
blocks.push(node);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return blocks;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether `tr` removes every block in `wanted` from the document. Identity follows
|
|
36
|
+
* `matchesRequestedBlock` (localId, falling back to resourceId) so this agrees with the
|
|
37
|
+
* recompute helpers.
|
|
38
|
+
*/
|
|
39
|
+
const removesRequestedBlocks = (tr, isSourceBlock, wanted) => {
|
|
40
|
+
const before = topLevelSourceBlocks(tr.before, isSourceBlock);
|
|
41
|
+
const after = topLevelSourceBlocks(tr.doc, isSourceBlock);
|
|
42
|
+
return wanted.every(block => before.some(node => matchesRequestedBlock(node, block)) && !after.some(node => matchesRequestedBlock(node, block)));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Replay the undo that removed `wanted` once the source deletion has been confirmed and
|
|
47
|
+
* persisted (EDITOR-8863).
|
|
48
|
+
*
|
|
49
|
+
* The original undo transaction was rejected by `filterTransaction` so the confirmation could
|
|
50
|
+
* run, which leaves the history event on top of the undo stack. Building the undo again from
|
|
51
|
+
* the live state (via the history plugin) restores whatever the block was converted from
|
|
52
|
+
* (e.g. a table), unlike a recomputed whole-node delete which drops that content.
|
|
53
|
+
*
|
|
54
|
+
* Guard: only dispatch if that undo still removes the same source block(s). If the user
|
|
55
|
+
* edited in the meantime the top of the stack is no longer that event, so return `false` and
|
|
56
|
+
* let the caller fall back to the recomputed unwrap.
|
|
57
|
+
*/
|
|
58
|
+
const replayConfirmedUndo = (api, isSourceBlock, wanted, onTransaction) => {
|
|
59
|
+
var _api$history;
|
|
60
|
+
const undo = api === null || api === void 0 ? void 0 : (_api$history = api.history) === null || _api$history === void 0 ? void 0 : _api$history.commands.undo;
|
|
61
|
+
if (!undo || !(api !== null && api !== void 0 && api.core)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
let applied = false;
|
|
65
|
+
api.core.actions.execute(({
|
|
66
|
+
tr
|
|
67
|
+
}) => {
|
|
68
|
+
const undoTr = undo({
|
|
69
|
+
tr
|
|
70
|
+
});
|
|
71
|
+
if (!undoTr || !removesRequestedBlocks(undoTr, isSourceBlock, wanted)) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
undoTr.setMeta('isConfirmedSyncBlockDeletion', true);
|
|
75
|
+
onTransaction(undoTr);
|
|
76
|
+
applied = true;
|
|
77
|
+
return undoTr;
|
|
78
|
+
});
|
|
79
|
+
return applied;
|
|
80
|
+
};
|
|
81
|
+
export const handleBodiedSyncBlockRemoval = ({
|
|
82
|
+
api,
|
|
83
|
+
confirmationTransactionRef,
|
|
84
|
+
deletionReason,
|
|
85
|
+
feedbackCallbacks,
|
|
86
|
+
mechanism,
|
|
87
|
+
removed,
|
|
88
|
+
syncBlockStore
|
|
89
|
+
}) => {
|
|
90
|
+
const removedAttrs = removed.map(node => node.attrs);
|
|
91
|
+
const {
|
|
92
|
+
isSourceBlock
|
|
93
|
+
} = syncBlockStore.sourceManager;
|
|
94
|
+
|
|
25
95
|
// Clear potential old pending deletion to retreat the deletion as first attempt
|
|
26
96
|
syncBlockStore.sourceManager.clearPendingDeletion();
|
|
27
97
|
|
|
@@ -29,8 +99,32 @@ export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockSt
|
|
|
29
99
|
// we block the transaction here, and wait for user confirmation to proceed with deletion.
|
|
30
100
|
// See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
|
|
31
101
|
// proceed with deletion.
|
|
32
|
-
syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(
|
|
102
|
+
syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(removedAttrs, deletionReason, () => {
|
|
33
103
|
var _api$core2;
|
|
104
|
+
const closeBlockMenu = confirmedTr => {
|
|
105
|
+
if (isExperimentEnabled('platform_editor_blocks_exp_patch_10')) {
|
|
106
|
+
var _api$blockControls, _api$blockControls$co;
|
|
107
|
+
// The original transaction's closeMenu meta was rejected along with it above,
|
|
108
|
+
// so re-apply it here to close the block menu once the change actually lands.
|
|
109
|
+
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({
|
|
110
|
+
closeMenu: true
|
|
111
|
+
})({
|
|
112
|
+
tr: confirmedTr
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Any undo that removes a source block (creation, paste, ...) must restore what was
|
|
118
|
+
// there before. A recomputed whole-node delete drops the converted content and leaves
|
|
119
|
+
// the event on the undo stack, so replay the undo instead (EDITOR-8863).
|
|
120
|
+
const shouldReplayUndo = mechanism === 'undo' && isExperimentEnabled('platform_editor_blocks_exp_patch_10');
|
|
121
|
+
if (shouldReplayUndo && replayConfirmedUndo(api, isSourceBlock, removedAttrs, undoTr => {
|
|
122
|
+
closeBlockMenu(undoTr);
|
|
123
|
+
feedbackCallbacks === null || feedbackCallbacks === void 0 ? void 0 : feedbackCallbacks.onDeleteTransaction(undoTr);
|
|
124
|
+
})) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
34
128
|
// Recompute the delete fresh from the live document instead of
|
|
35
129
|
// replaying a stashed-and-rebased transaction. The stash/rebase
|
|
36
130
|
// approach produced stale, schema-invalid transactions when the
|
|
@@ -45,7 +139,10 @@ export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockSt
|
|
|
45
139
|
// the confirm path always recomputed a full delete, so unsyncing a source block
|
|
46
140
|
// silently deleted its content. When the deletion was actually triggered by
|
|
47
141
|
// unsync, recompute an unwrap (replaceWith content) instead of a delete.
|
|
48
|
-
|
|
142
|
+
// The same applies when the undo could not be replayed (the undo stack changed
|
|
143
|
+
// while the backend delete was in flight): the backend delete has already
|
|
144
|
+
// succeeded, so drop the wrapper but keep the content (EDITOR-8863).
|
|
145
|
+
const recomputedTr = deletionReason === 'source-block-unsynced' || shouldReplayUndo ? recomputeUnsyncTransaction(tr, isSourceBlock, removedAttrs) : recomputeDeleteTransaction(tr, isSourceBlock, removedAttrs);
|
|
49
146
|
|
|
50
147
|
// The target node(s) no longer exist in the live document (e.g.
|
|
51
148
|
// a remote collaborator already removed them). There is nothing
|
|
@@ -56,16 +153,7 @@ export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockSt
|
|
|
56
153
|
if (!recomputedTr) {
|
|
57
154
|
return null;
|
|
58
155
|
}
|
|
59
|
-
|
|
60
|
-
var _api$blockControls, _api$blockControls$co;
|
|
61
|
-
// The original transaction's closeMenu meta was rejected along with it above,
|
|
62
|
-
// so re-apply it here to close the block menu once the delete actually lands.
|
|
63
|
-
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({
|
|
64
|
-
closeMenu: true
|
|
65
|
-
})({
|
|
66
|
-
tr: recomputedTr
|
|
67
|
-
});
|
|
68
|
-
}
|
|
156
|
+
closeBlockMenu(recomputedTr);
|
|
69
157
|
recomputedTr.setMeta('isConfirmedSyncBlockDeletion', true);
|
|
70
158
|
feedbackCallbacks === null || feedbackCallbacks === void 0 ? void 0 : feedbackCallbacks.onDeleteTransaction(recomputedTr);
|
|
71
159
|
if (!recomputedTr.getMeta(pmHistoryPluginKey)) {
|
|
@@ -8,7 +8,7 @@ import { Slice } from '@atlaskit/editor-prosemirror/model';
|
|
|
8
8
|
* requested block's `localId` first and only fall back to its `resourceId` when
|
|
9
9
|
* the `localId` has been regenerated (so it no longer matches the live node).
|
|
10
10
|
*/
|
|
11
|
-
const matchesRequestedBlock = (node, wanted) => {
|
|
11
|
+
export const matchesRequestedBlock = (node, wanted) => {
|
|
12
12
|
const {
|
|
13
13
|
localId,
|
|
14
14
|
resourceId
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
3
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
4
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
5
|
import { defaultSchema } from '@atlaskit/adf-schema/schema-default';
|
|
5
6
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
6
7
|
import { copyDomNodeWithResult, toDOM } from '@atlaskit/editor-common/copy-button';
|
|
8
|
+
import { getSourceNodesFromSelectionRange } from '@atlaskit/editor-common/selection';
|
|
7
9
|
import { DOMSerializer, Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
8
10
|
import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
9
11
|
import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
10
12
|
import { getSourceProductFromResourceIdSafe } from '@atlaskit/editor-synced-block-provider/utils';
|
|
13
|
+
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
11
14
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
12
15
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
13
16
|
import { creationMetaKey, deleteMechanismMetaKey, syncedBlockPluginKey } from '../pm-plugins/main';
|
|
@@ -83,6 +86,7 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
|
|
|
83
86
|
// set on the final transaction before returning (see below), since the
|
|
84
87
|
// typeahead path may reassign `tr` and drop meta set here.
|
|
85
88
|
var createdEmpty = tr.selection.empty;
|
|
89
|
+
var nodeTypes;
|
|
86
90
|
|
|
87
91
|
// If the selection is empty, we want to insert the sync block on a new line
|
|
88
92
|
if (tr.selection.empty) {
|
|
@@ -124,6 +128,11 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
|
|
|
124
128
|
});
|
|
125
129
|
return false;
|
|
126
130
|
}
|
|
131
|
+
if (isExperimentEnabled('platform_editor_sync_block_node_types')) {
|
|
132
|
+
nodeTypes = _toConsumableArray(new Set(getSourceNodesFromSelectionRange(tr, tr.selection).map(function (node) {
|
|
133
|
+
return node.type.name;
|
|
134
|
+
}))).sort();
|
|
135
|
+
}
|
|
127
136
|
var _attrs = syncBlockStore.sourceManager.generateBodiedSyncBlockAttrs();
|
|
128
137
|
var _newBodiedSyncBlockNode = bodiedSyncBlock.createAndFill(_attrs, conversionInfo.contentToInclude, fg('platform_editor_blocks_patch_8') && conversionInfo.breakoutMark ? [conversionInfo.breakoutMark] : undefined);
|
|
129
138
|
if (!_newBodiedSyncBlockNode) {
|
|
@@ -145,12 +154,12 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
|
|
|
145
154
|
tr = placeCaretInsideBodiedSyncBlock(tr, _newBodiedSyncBlockNode.attrs.localId);
|
|
146
155
|
}
|
|
147
156
|
|
|
148
|
-
// Stash creation-type signals on the final transaction
|
|
149
|
-
// reassignment
|
|
150
|
-
// the feature gate.
|
|
157
|
+
// Stash creation-type signals on the final transaction after any typeahead
|
|
158
|
+
// reassignment. nodeTypes is populated only for gated non-empty conversions.
|
|
151
159
|
tr.setMeta(creationMetaKey, {
|
|
152
160
|
createdEmpty: createdEmpty,
|
|
153
|
-
inputMethod: inputMethod
|
|
161
|
+
inputMethod: inputMethod,
|
|
162
|
+
nodeTypes: nodeTypes
|
|
154
163
|
});
|
|
155
164
|
return tr;
|
|
156
165
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
1
2
|
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
3
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
4
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
@@ -15,11 +16,13 @@ import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
|
|
|
15
16
|
import { BodiedSyncBlockSharedCssClassName, SyncBlockStateCssClassName } from '@atlaskit/editor-common/sync-block';
|
|
16
17
|
import { mapSlice, pmHistoryPluginKey } from '@atlaskit/editor-common/utils';
|
|
17
18
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
19
|
+
import { Slice } from '@atlaskit/editor-prosemirror/model';
|
|
18
20
|
import { NodeSelection, PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
19
21
|
import { ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
20
22
|
import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
21
23
|
import { convertPMNodesToSyncBlockNodes } from '@atlaskit/editor-synced-block-provider';
|
|
22
24
|
import { getSourceProductFromResourceIdSafe } from '@atlaskit/editor-synced-block-provider/utils';
|
|
25
|
+
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
23
26
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
24
27
|
import { bodiedSyncBlockNodeView } from '../nodeviews/bodiedSyncedBlock';
|
|
25
28
|
import { SyncBlock as SyncBlockView } from '../nodeviews/syncedBlock';
|
|
@@ -46,7 +49,7 @@ export var deleteMechanismMetaKey = new PluginKey('syncedBlockDeleteMechanism');
|
|
|
46
49
|
var syncedBlockPromptedFeedbackMetaKey = new PluginKey('syncedBlockPromptedFeedback');
|
|
47
50
|
|
|
48
51
|
/**
|
|
49
|
-
* Creation
|
|
52
|
+
* Creation analytics signals set by {@link createSyncedBlock} on the creating
|
|
50
53
|
* transaction. The async creation handler forwards them to the store manager,
|
|
51
54
|
* which attaches them to the `syncedBlockCreate` event. A dedicated key keeps
|
|
52
55
|
* this transient signal isolated from plugin state.
|
|
@@ -312,9 +315,10 @@ var filterTransactionOnline = function filterTransactionOnline(_ref3) {
|
|
|
312
315
|
blockType: 'source',
|
|
313
316
|
entryPoint: entryPoint
|
|
314
317
|
} : undefined;
|
|
318
|
+
var feedbackCallbacks;
|
|
315
319
|
if (feedbackContext) {
|
|
316
320
|
var sourceAttempt = ctx.queueSourceFeedback(feedbackContext);
|
|
317
|
-
|
|
321
|
+
feedbackCallbacks = {
|
|
318
322
|
onDeleteCompleted: function onDeleteCompleted(success) {
|
|
319
323
|
return ctx.completeSourceDeletion(sourceAttempt, success);
|
|
320
324
|
},
|
|
@@ -327,9 +331,17 @@ var filterTransactionOnline = function filterTransactionOnline(_ref3) {
|
|
|
327
331
|
onDestroy: function onDestroy() {
|
|
328
332
|
return ctx.clearSourceFeedback(sourceAttempt);
|
|
329
333
|
}
|
|
330
|
-
}
|
|
334
|
+
};
|
|
331
335
|
}
|
|
332
|
-
return handleBodiedSyncBlockRemoval(
|
|
336
|
+
return handleBodiedSyncBlockRemoval({
|
|
337
|
+
removed: bodiedSyncBlockRemoved,
|
|
338
|
+
syncBlockStore: syncBlockStore,
|
|
339
|
+
api: api,
|
|
340
|
+
confirmationTransactionRef: confirmationTransactionRef,
|
|
341
|
+
deletionReason: getDeleteReason(tr),
|
|
342
|
+
mechanism: mechanism,
|
|
343
|
+
feedbackCallbacks: feedbackCallbacks
|
|
344
|
+
});
|
|
333
345
|
}
|
|
334
346
|
if (bodiedSyncBlockAdded.length > 0) {
|
|
335
347
|
if (tr.getMeta(pmHistoryPluginKey)) {
|
|
@@ -349,7 +361,7 @@ var filterTransactionOnline = function filterTransactionOnline(_ref3) {
|
|
|
349
361
|
return true;
|
|
350
362
|
}
|
|
351
363
|
|
|
352
|
-
// Forward creation
|
|
364
|
+
// Forward creation analytics signals captured by createSyncedBlock onto the async
|
|
353
365
|
// `syncedBlockCreate` success event.
|
|
354
366
|
var creationMeta = tr.getMeta(creationMetaKey);
|
|
355
367
|
handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, state, api, creationMeta);
|
|
@@ -1134,14 +1146,35 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
|
|
|
1134
1146
|
var _trackSyncBlocks6 = trackSyncBlocks(syncBlockStore.sourceManager.isSourceBlock, tr, oldState),
|
|
1135
1147
|
added = _trackSyncBlocks6.added;
|
|
1136
1148
|
if (added.length > 0) {
|
|
1137
|
-
//
|
|
1138
|
-
//
|
|
1149
|
+
// A bodiedSyncBlock must not be (re)created from history, i.e. redo of a
|
|
1150
|
+
// creation (see filterTransaction above). Unwrap it instead of deleting it:
|
|
1151
|
+
// deleting the whole node also dropped the content the block was converted
|
|
1152
|
+
// from (e.g. a table), and undoing that delete re-added the block and hit
|
|
1153
|
+
// this guard again, so the content was unrecoverable (EDITOR-8863).
|
|
1139
1154
|
var _tr = newState.tr;
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1155
|
+
if (isExperimentEnabled('platform_editor_blocks_exp_patch_10')) {
|
|
1156
|
+
// Unwrap in reverse document order so earlier replacements keep later
|
|
1157
|
+
// positions valid.
|
|
1158
|
+
_toConsumableArray(added).sort(function (a, b) {
|
|
1159
|
+
var _b$from, _a$from;
|
|
1160
|
+
return ((_b$from = b.from) !== null && _b$from !== void 0 ? _b$from : 0) - ((_a$from = a.from) !== null && _a$from !== void 0 ? _a$from : 0);
|
|
1161
|
+
}).forEach(function (_ref12) {
|
|
1162
|
+
var node = _ref12.node,
|
|
1163
|
+
from = _ref12.from,
|
|
1164
|
+
to = _ref12.to;
|
|
1165
|
+
if (node && from !== undefined && to !== undefined) {
|
|
1166
|
+
// This is ProseMirror's Transaction.replace (a document edit), not String.replace.
|
|
1167
|
+
// eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
|
|
1168
|
+
_tr.replace(from, to, new Slice(node.content, 0, 0));
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
} else {
|
|
1172
|
+
added.forEach(function (node) {
|
|
1173
|
+
if (node.from !== undefined && node.to !== undefined) {
|
|
1174
|
+
_tr.delete(node.from, node.to);
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1145
1178
|
return {
|
|
1146
1179
|
v: _tr
|
|
1147
1180
|
};
|
|
@@ -5,7 +5,7 @@ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
|
5
5
|
import { FLAG_ID } from '../../types';
|
|
6
6
|
import { syncedBlockPluginKey } from '../main';
|
|
7
7
|
import { deferDispatch } from './utils';
|
|
8
|
-
var onRetry = function onRetry(api, resourceId) {
|
|
8
|
+
var onRetry = function onRetry(api, resourceId, enrichment) {
|
|
9
9
|
return function () {
|
|
10
10
|
var _api$core, _api$core2;
|
|
11
11
|
api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.focus();
|
|
@@ -21,7 +21,7 @@ var onRetry = function onRetry(api, resourceId) {
|
|
|
21
21
|
tr.setSelection(TextSelection.create(tr.doc, from, to)).setMeta(syncedBlockPluginKey, {
|
|
22
22
|
activeFlag: false
|
|
23
23
|
});
|
|
24
|
-
api === null || api === void 0 || (_api$syncedBlock2 = api.syncedBlock) === null || _api$syncedBlock2 === void 0 || _api$syncedBlock2.commands.insertSyncedBlock()({
|
|
24
|
+
api === null || api === void 0 || (_api$syncedBlock2 = api.syncedBlock) === null || _api$syncedBlock2 === void 0 || _api$syncedBlock2.commands.insertSyncedBlock(enrichment === null || enrichment === void 0 ? void 0 : enrichment.inputMethod)({
|
|
25
25
|
tr: tr
|
|
26
26
|
});
|
|
27
27
|
return tr;
|
|
@@ -69,7 +69,7 @@ var buildRevertCreationTr = function buildRevertCreationTr(tr, pos) {
|
|
|
69
69
|
*
|
|
70
70
|
* Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow.
|
|
71
71
|
*
|
|
72
|
-
* @param enrichment optional creation
|
|
72
|
+
* @param enrichment optional creation analytics signals from `createSyncedBlock`,
|
|
73
73
|
* forwarded to the store manager to attach to the `syncedBlockCreate` event.
|
|
74
74
|
*/
|
|
75
75
|
export var handleBodiedSyncBlockCreation = function handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, editorState, api, enrichment) {
|
|
@@ -120,7 +120,7 @@ export var handleBodiedSyncBlockCreation = function handleBodiedSyncBlockCreatio
|
|
|
120
120
|
return revertTr.setMeta('isConfirmedSyncBlockDeletion', true).setMeta('addToHistory', false).setMeta(syncedBlockPluginKey, {
|
|
121
121
|
activeFlag: {
|
|
122
122
|
id: FLAG_ID.CANNOT_CREATE_SYNC_BLOCK,
|
|
123
|
-
onRetry: onRetry(api, resourceId),
|
|
123
|
+
onRetry: onRetry(api, resourceId, enrichment),
|
|
124
124
|
onDismissed: function onDismissed(tr) {
|
|
125
125
|
return tr.setMeta(syncedBlockPluginKey, _objectSpread(_objectSpread({}, tr.getMeta(syncedBlockPluginKey)), {}, {
|
|
126
126
|
retryCreationPos: {
|
|
@@ -2,10 +2,10 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
2
2
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
3
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
4
|
import { pmHistoryPluginKey } from '@atlaskit/editor-common/utils';
|
|
5
|
-
import {
|
|
5
|
+
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
6
6
|
import { FLAG_ID } from '../../types';
|
|
7
7
|
import { syncedBlockPluginKey } from '../main';
|
|
8
|
-
import { recomputeDeleteTransaction, recomputeUnsyncTransaction } from './recompute-delete-transaction';
|
|
8
|
+
import { matchesRequestedBlock, recomputeDeleteTransaction, recomputeUnsyncTransaction } from './recompute-delete-transaction';
|
|
9
9
|
var onRetry = function onRetry(api, syncBlockStore) {
|
|
10
10
|
return function () {
|
|
11
11
|
var _api$core;
|
|
@@ -27,7 +27,81 @@ var onDismissed = function onDismissed(syncBlockStore) {
|
|
|
27
27
|
});
|
|
28
28
|
};
|
|
29
29
|
};
|
|
30
|
-
|
|
30
|
+
var topLevelSourceBlocks = function topLevelSourceBlocks(doc, isSourceBlock) {
|
|
31
|
+
var blocks = [];
|
|
32
|
+
doc.forEach(function (node) {
|
|
33
|
+
if (isSourceBlock(node)) {
|
|
34
|
+
blocks.push(node);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return blocks;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Whether `tr` removes every block in `wanted` from the document. Identity follows
|
|
42
|
+
* `matchesRequestedBlock` (localId, falling back to resourceId) so this agrees with the
|
|
43
|
+
* recompute helpers.
|
|
44
|
+
*/
|
|
45
|
+
var removesRequestedBlocks = function removesRequestedBlocks(tr, isSourceBlock, wanted) {
|
|
46
|
+
var before = topLevelSourceBlocks(tr.before, isSourceBlock);
|
|
47
|
+
var after = topLevelSourceBlocks(tr.doc, isSourceBlock);
|
|
48
|
+
return wanted.every(function (block) {
|
|
49
|
+
return before.some(function (node) {
|
|
50
|
+
return matchesRequestedBlock(node, block);
|
|
51
|
+
}) && !after.some(function (node) {
|
|
52
|
+
return matchesRequestedBlock(node, block);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Replay the undo that removed `wanted` once the source deletion has been confirmed and
|
|
59
|
+
* persisted (EDITOR-8863).
|
|
60
|
+
*
|
|
61
|
+
* The original undo transaction was rejected by `filterTransaction` so the confirmation could
|
|
62
|
+
* run, which leaves the history event on top of the undo stack. Building the undo again from
|
|
63
|
+
* the live state (via the history plugin) restores whatever the block was converted from
|
|
64
|
+
* (e.g. a table), unlike a recomputed whole-node delete which drops that content.
|
|
65
|
+
*
|
|
66
|
+
* Guard: only dispatch if that undo still removes the same source block(s). If the user
|
|
67
|
+
* edited in the meantime the top of the stack is no longer that event, so return `false` and
|
|
68
|
+
* let the caller fall back to the recomputed unwrap.
|
|
69
|
+
*/
|
|
70
|
+
var replayConfirmedUndo = function replayConfirmedUndo(api, isSourceBlock, wanted, onTransaction) {
|
|
71
|
+
var _api$history;
|
|
72
|
+
var undo = api === null || api === void 0 || (_api$history = api.history) === null || _api$history === void 0 ? void 0 : _api$history.commands.undo;
|
|
73
|
+
if (!undo || !(api !== null && api !== void 0 && api.core)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
var applied = false;
|
|
77
|
+
api.core.actions.execute(function (_ref2) {
|
|
78
|
+
var tr = _ref2.tr;
|
|
79
|
+
var undoTr = undo({
|
|
80
|
+
tr: tr
|
|
81
|
+
});
|
|
82
|
+
if (!undoTr || !removesRequestedBlocks(undoTr, isSourceBlock, wanted)) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
undoTr.setMeta('isConfirmedSyncBlockDeletion', true);
|
|
86
|
+
onTransaction(undoTr);
|
|
87
|
+
applied = true;
|
|
88
|
+
return undoTr;
|
|
89
|
+
});
|
|
90
|
+
return applied;
|
|
91
|
+
};
|
|
92
|
+
export var handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(_ref3) {
|
|
93
|
+
var api = _ref3.api,
|
|
94
|
+
confirmationTransactionRef = _ref3.confirmationTransactionRef,
|
|
95
|
+
deletionReason = _ref3.deletionReason,
|
|
96
|
+
feedbackCallbacks = _ref3.feedbackCallbacks,
|
|
97
|
+
mechanism = _ref3.mechanism,
|
|
98
|
+
removed = _ref3.removed,
|
|
99
|
+
syncBlockStore = _ref3.syncBlockStore;
|
|
100
|
+
var removedAttrs = removed.map(function (node) {
|
|
101
|
+
return node.attrs;
|
|
102
|
+
});
|
|
103
|
+
var isSourceBlock = syncBlockStore.sourceManager.isSourceBlock;
|
|
104
|
+
|
|
31
105
|
// Clear potential old pending deletion to retreat the deletion as first attempt
|
|
32
106
|
syncBlockStore.sourceManager.clearPendingDeletion();
|
|
33
107
|
|
|
@@ -35,28 +109,49 @@ export var handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(
|
|
|
35
109
|
// we block the transaction here, and wait for user confirmation to proceed with deletion.
|
|
36
110
|
// See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
|
|
37
111
|
// proceed with deletion.
|
|
38
|
-
syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(
|
|
39
|
-
return node.attrs;
|
|
40
|
-
}), deletionReason, function () {
|
|
112
|
+
syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(removedAttrs, deletionReason, function () {
|
|
41
113
|
var _api$core2;
|
|
114
|
+
var closeBlockMenu = function closeBlockMenu(confirmedTr) {
|
|
115
|
+
if (isExperimentEnabled('platform_editor_blocks_exp_patch_10')) {
|
|
116
|
+
var _api$blockControls;
|
|
117
|
+
// The original transaction's closeMenu meta was rejected along with it above,
|
|
118
|
+
// so re-apply it here to close the block menu once the change actually lands.
|
|
119
|
+
api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.commands) === null || _api$blockControls === void 0 || _api$blockControls.toggleBlockMenu({
|
|
120
|
+
closeMenu: true
|
|
121
|
+
})({
|
|
122
|
+
tr: confirmedTr
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Any undo that removes a source block (creation, paste, ...) must restore what was
|
|
128
|
+
// there before. A recomputed whole-node delete drops the converted content and leaves
|
|
129
|
+
// the event on the undo stack, so replay the undo instead (EDITOR-8863).
|
|
130
|
+
var shouldReplayUndo = mechanism === 'undo' && isExperimentEnabled('platform_editor_blocks_exp_patch_10');
|
|
131
|
+
if (shouldReplayUndo && replayConfirmedUndo(api, isSourceBlock, removedAttrs, function (undoTr) {
|
|
132
|
+
closeBlockMenu(undoTr);
|
|
133
|
+
feedbackCallbacks === null || feedbackCallbacks === void 0 || feedbackCallbacks.onDeleteTransaction(undoTr);
|
|
134
|
+
})) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
42
138
|
// Recompute the delete fresh from the live document instead of
|
|
43
139
|
// replaying a stashed-and-rebased transaction. The stash/rebase
|
|
44
140
|
// approach produced stale, schema-invalid transactions when the
|
|
45
141
|
// document changed shape (local edits or remote collab) while the
|
|
46
142
|
// confirmation modal was open — the dominant signature being
|
|
47
143
|
// "Invalid content for node bodiedSyncBlock: <>". See EDITOR-7889.
|
|
48
|
-
api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (
|
|
49
|
-
var tr =
|
|
144
|
+
api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (_ref4) {
|
|
145
|
+
var tr = _ref4.tr;
|
|
50
146
|
// EDITOR-8230: unsync must remove only the sync wrapper and keep the block's
|
|
51
147
|
// content inline, whereas delete removes the whole block. Before this branch,
|
|
52
148
|
// the confirm path always recomputed a full delete, so unsyncing a source block
|
|
53
149
|
// silently deleted its content. When the deletion was actually triggered by
|
|
54
150
|
// unsync, recompute an unwrap (replaceWith content) instead of a delete.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}));
|
|
151
|
+
// The same applies when the undo could not be replayed (the undo stack changed
|
|
152
|
+
// while the backend delete was in flight): the backend delete has already
|
|
153
|
+
// succeeded, so drop the wrapper but keep the content (EDITOR-8863).
|
|
154
|
+
var recomputedTr = deletionReason === 'source-block-unsynced' || shouldReplayUndo ? recomputeUnsyncTransaction(tr, isSourceBlock, removedAttrs) : recomputeDeleteTransaction(tr, isSourceBlock, removedAttrs);
|
|
60
155
|
|
|
61
156
|
// The target node(s) no longer exist in the live document (e.g.
|
|
62
157
|
// a remote collaborator already removed them). There is nothing
|
|
@@ -67,16 +162,7 @@ export var handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(
|
|
|
67
162
|
if (!recomputedTr) {
|
|
68
163
|
return null;
|
|
69
164
|
}
|
|
70
|
-
|
|
71
|
-
var _api$blockControls;
|
|
72
|
-
// The original transaction's closeMenu meta was rejected along with it above,
|
|
73
|
-
// so re-apply it here to close the block menu once the delete actually lands.
|
|
74
|
-
api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.commands) === null || _api$blockControls === void 0 || _api$blockControls.toggleBlockMenu({
|
|
75
|
-
closeMenu: true
|
|
76
|
-
})({
|
|
77
|
-
tr: recomputedTr
|
|
78
|
-
});
|
|
79
|
-
}
|
|
165
|
+
closeBlockMenu(recomputedTr);
|
|
80
166
|
recomputedTr.setMeta('isConfirmedSyncBlockDeletion', true);
|
|
81
167
|
feedbackCallbacks === null || feedbackCallbacks === void 0 || feedbackCallbacks.onDeleteTransaction(recomputedTr);
|
|
82
168
|
if (!recomputedTr.getMeta(pmHistoryPluginKey)) {
|
|
@@ -89,8 +175,8 @@ export var handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(
|
|
|
89
175
|
}, function (success) {
|
|
90
176
|
var _api$core3;
|
|
91
177
|
feedbackCallbacks === null || feedbackCallbacks === void 0 || feedbackCallbacks.onDeleteCompleted(success);
|
|
92
|
-
api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (
|
|
93
|
-
var tr =
|
|
178
|
+
api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref5) {
|
|
179
|
+
var tr = _ref5.tr;
|
|
94
180
|
var newState;
|
|
95
181
|
if (!success) {
|
|
96
182
|
newState = {
|
|
@@ -11,7 +11,7 @@ import { Slice } from '@atlaskit/editor-prosemirror/model';
|
|
|
11
11
|
* requested block's `localId` first and only fall back to its `resourceId` when
|
|
12
12
|
* the `localId` has been regenerated (so it no longer matches the live node).
|
|
13
13
|
*/
|
|
14
|
-
var matchesRequestedBlock = function matchesRequestedBlock(node, wanted) {
|
|
14
|
+
export var matchesRequestedBlock = function matchesRequestedBlock(node, wanted) {
|
|
15
15
|
var _ref = node.attrs,
|
|
16
16
|
localId = _ref.localId,
|
|
17
17
|
resourceId = _ref.resourceId;
|
|
@@ -19,7 +19,7 @@ export declare const syncedBlockPluginKey: PluginKey;
|
|
|
19
19
|
*/
|
|
20
20
|
export declare const deleteMechanismMetaKey: PluginKey<DeletionMechanism>;
|
|
21
21
|
/**
|
|
22
|
-
* Creation
|
|
22
|
+
* Creation analytics signals set by {@link createSyncedBlock} on the creating
|
|
23
23
|
* transaction. The async creation handler forwards them to the store manager,
|
|
24
24
|
* which attaches them to the `syncedBlockCreate` event. A dedicated key keeps
|
|
25
25
|
* this transient signal isolated from plugin state.
|
|
@@ -27,6 +27,7 @@ export declare const deleteMechanismMetaKey: PluginKey<DeletionMechanism>;
|
|
|
27
27
|
export type SyncedBlockCreationMeta = {
|
|
28
28
|
createdEmpty?: boolean;
|
|
29
29
|
inputMethod?: INPUT_METHOD;
|
|
30
|
+
nodeTypes?: string[];
|
|
30
31
|
};
|
|
31
32
|
export declare const creationMetaKey: PluginKey<SyncedBlockCreationMeta>;
|
|
32
33
|
type SyncedBlockPluginState = {
|
|
@@ -7,7 +7,7 @@ import type { SyncBlockInfo } from '../../types';
|
|
|
7
7
|
*
|
|
8
8
|
* Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow.
|
|
9
9
|
*
|
|
10
|
-
* @param enrichment optional creation
|
|
10
|
+
* @param enrichment optional creation analytics signals from `createSyncedBlock`,
|
|
11
11
|
* forwarded to the store manager to attach to the `syncedBlockCreate` event.
|
|
12
12
|
*/
|
|
13
13
|
export declare const handleBodiedSyncBlockCreation: (bodiedSyncBlockAdded: SyncBlockInfo[], editorState: EditorState, api: ExtractInjectionAPI<SyncedBlockPlugin> | undefined, enrichment?: CreateSuccessEnrichment) => void;
|
|
@@ -7,10 +7,18 @@ import type { SyncBlockInfo } from '../../types';
|
|
|
7
7
|
export type TransactionRef = {
|
|
8
8
|
current: Transaction | undefined;
|
|
9
9
|
};
|
|
10
|
-
type SourceFeedbackCallbacks = {
|
|
10
|
+
export type SourceFeedbackCallbacks = {
|
|
11
11
|
onDeleteCompleted: (success: boolean) => void;
|
|
12
12
|
onDeleteTransaction: (tr: Transaction) => void;
|
|
13
13
|
onDestroy: () => void;
|
|
14
14
|
};
|
|
15
|
-
export
|
|
16
|
-
|
|
15
|
+
export type HandleBodiedSyncBlockRemovalOptions = {
|
|
16
|
+
api: ExtractInjectionAPI<SyncedBlockPlugin> | undefined;
|
|
17
|
+
confirmationTransactionRef: TransactionRef;
|
|
18
|
+
deletionReason: DeletionReason;
|
|
19
|
+
feedbackCallbacks?: SourceFeedbackCallbacks;
|
|
20
|
+
mechanism?: DeletionMechanism;
|
|
21
|
+
removed: SyncBlockInfo[];
|
|
22
|
+
syncBlockStore: SyncBlockStoreManager;
|
|
23
|
+
};
|
|
24
|
+
export declare const handleBodiedSyncBlockRemoval: ({ api, confirmationTransactionRef, deletionReason, feedbackCallbacks, mechanism, removed, syncBlockStore, }: HandleBodiedSyncBlockRemovalOptions) => boolean;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
2
2
|
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
3
|
import type { SyncBlockAttrs } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* Whether `node` is the source block requested by `wanted`.
|
|
6
|
+
*
|
|
7
|
+
* Matching is done *per requested block* (rather than against global pools of
|
|
8
|
+
* every requested localId/resourceId) so a node cannot be matched by combining
|
|
9
|
+
* one block's `localId` with a different block's `resourceId`. We match on the
|
|
10
|
+
* requested block's `localId` first and only fall back to its `resourceId` when
|
|
11
|
+
* the `localId` has been regenerated (so it no longer matches the live node).
|
|
12
|
+
*/
|
|
13
|
+
export declare const matchesRequestedBlock: (node: PMNode, wanted: SyncBlockAttrs) => boolean;
|
|
4
14
|
/**
|
|
5
15
|
* Recompute a source `bodiedSyncBlock` delete from the live document.
|
|
6
16
|
*
|
|
@@ -12,6 +12,7 @@ import type { EditorDisabledPlugin } from '@atlaskit/editor-plugin-editor-disabl
|
|
|
12
12
|
import type { EditorViewModePlugin } from '@atlaskit/editor-plugin-editor-viewmode';
|
|
13
13
|
import type { FloatingToolbarPlugin } from '@atlaskit/editor-plugin-floating-toolbar';
|
|
14
14
|
import type { FocusPlugin } from '@atlaskit/editor-plugin-focus';
|
|
15
|
+
import type { HistoryPlugin } from '@atlaskit/editor-plugin-history';
|
|
15
16
|
import type { SelectionPlugin } from '@atlaskit/editor-plugin-selection';
|
|
16
17
|
import type { ToolbarPlugin } from '@atlaskit/editor-plugin-toolbar';
|
|
17
18
|
import type { UserIntentPlugin } from '@atlaskit/editor-plugin-user-intent';
|
|
@@ -136,6 +137,7 @@ export type SyncedBlockPlugin = NextEditorPlugin<'syncedBlock', {
|
|
|
136
137
|
OptionalPlugin<ContentFormatPlugin>,
|
|
137
138
|
OptionalPlugin<UserIntentPlugin>,
|
|
138
139
|
OptionalPlugin<FocusPlugin>,
|
|
140
|
+
OptionalPlugin<HistoryPlugin>,
|
|
139
141
|
OptionalPlugin<UiControlRegistryPlugin>
|
|
140
142
|
];
|
|
141
143
|
pluginConfiguration: SyncedBlockPluginOptions | undefined;
|