@atlaskit/editor-plugin-block-controls 2.23.1 → 2.24.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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 2.24.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#111465](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/111465)
8
+ [`c0cbae02ded12`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/c0cbae02ded12) -
9
+ [ux] Expand existing multi-node selection when dragging handle
10
+
11
+ ### Patch Changes
12
+
13
+ - [#109402](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/109402)
14
+ [`a8c334f52bb60`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/a8c334f52bb60) -
15
+ Fix multi-select bug when dragging onto the handle replaces text
16
+ - Updated dependencies
17
+
3
18
  ## 2.23.1
4
19
 
5
20
  ### Patch Changes
@@ -5,12 +5,17 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.blockControlsPlugin = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
8
9
  var _react = _interopRequireDefault(require("react"));
10
+ var _selection = require("@atlaskit/editor-common/selection");
11
+ var _state = require("@atlaskit/editor-prosemirror/state");
9
12
  var _moveNode = require("./editor-commands/move-node");
10
13
  var _moveToLayout = require("./editor-commands/move-to-layout");
11
14
  var _main = require("./pm-plugins/main");
12
15
  var _dragHandleMenu = require("./ui/drag-handle-menu");
13
16
  var _globalStyles = require("./ui/global-styles");
17
+ 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; }
18
+ 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) { (0, _defineProperty2.default)(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; }
14
19
  var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPlugin(_ref) {
15
20
  var api = _ref.api;
16
21
  return {
@@ -31,14 +36,15 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
31
36
  showDragHandleAt: function showDragHandleAt(pos, anchorName, nodeType, handleOptions) {
32
37
  return function (_ref3) {
33
38
  var tr = _ref3.tr;
34
- tr.setMeta(_main.key, {
39
+ var currMeta = tr.getMeta(_main.key);
40
+ tr.setMeta(_main.key, _objectSpread(_objectSpread({}, currMeta), {}, {
35
41
  activeNode: {
36
42
  pos: pos,
37
43
  anchorName: anchorName,
38
44
  nodeType: nodeType,
39
45
  handleOptions: handleOptions
40
46
  }
41
- });
47
+ }));
42
48
  return tr;
43
49
  };
44
50
  },
@@ -49,20 +55,52 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
49
55
  if (pos === undefined) {
50
56
  return tr;
51
57
  }
52
- tr.setMeta(_main.key, {
58
+ var currMeta = tr.getMeta(_main.key);
59
+ tr.setMeta(_main.key, _objectSpread(_objectSpread({}, currMeta), {}, {
53
60
  isDragging: true,
54
61
  activeNode: {
55
62
  pos: pos,
56
63
  anchorName: anchorName,
57
64
  nodeType: nodeType
58
65
  }
66
+ }));
67
+ return tr;
68
+ };
69
+ },
70
+ setMultiSelectPositions: function setMultiSelectPositions() {
71
+ return function (_ref5) {
72
+ var _api$selection;
73
+ var tr = _ref5.tr;
74
+ var _tr$selection = tr.selection,
75
+ userAnchor = _tr$selection.anchor,
76
+ userHead = _tr$selection.head;
77
+ var _expandSelectionBound = (0, _selection.expandSelectionBounds)(tr.selection.$anchor, tr.selection.$head),
78
+ expandedAnchor = _expandSelectionBound.$anchor,
79
+ expandedHead = _expandSelectionBound.$head;
80
+ api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || _api$selection.commands.setManualSelection(expandedAnchor.pos, expandedHead.pos)({
81
+ tr: tr
59
82
  });
83
+ // this is to normalise the selection's boundaries to inline positions, preventing it from collapsing
84
+ var expandedNormalisedSel = _state.TextSelection.between(expandedAnchor, expandedHead);
85
+ tr.setSelection(expandedNormalisedSel);
86
+ var multiSelectDnD = {
87
+ anchor: expandedAnchor.pos,
88
+ head: expandedHead.pos,
89
+ textAnchor: expandedNormalisedSel.anchor,
90
+ textHead: expandedNormalisedSel.head,
91
+ userAnchor: userAnchor,
92
+ userHead: userHead
93
+ };
94
+ var currMeta = tr.getMeta(_main.key);
95
+ tr.setMeta(_main.key, _objectSpread(_objectSpread({}, currMeta), {}, {
96
+ multiSelectDnD: multiSelectDnD
97
+ }));
60
98
  return tr;
61
99
  };
62
100
  }
63
101
  },
64
102
  getSharedState: function getSharedState(editorState) {
65
- var _key$getState$isMenuO, _key$getState, _key$getState$activeN, _key$getState2, _key$getState$isDragg, _key$getState3, _key$getState$isPMDra, _key$getState4;
103
+ var _key$getState$isMenuO, _key$getState, _key$getState$activeN, _key$getState2, _key$getState$isDragg, _key$getState3, _key$getState$isPMDra, _key$getState4, _key$getState$multiSe, _key$getState5;
66
104
  if (!editorState) {
67
105
  return undefined;
68
106
  }
@@ -70,7 +108,8 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
70
108
  isMenuOpen: (_key$getState$isMenuO = (_key$getState = _main.key.getState(editorState)) === null || _key$getState === void 0 ? void 0 : _key$getState.isMenuOpen) !== null && _key$getState$isMenuO !== void 0 ? _key$getState$isMenuO : false,
71
109
  activeNode: (_key$getState$activeN = (_key$getState2 = _main.key.getState(editorState)) === null || _key$getState2 === void 0 ? void 0 : _key$getState2.activeNode) !== null && _key$getState$activeN !== void 0 ? _key$getState$activeN : undefined,
72
110
  isDragging: (_key$getState$isDragg = (_key$getState3 = _main.key.getState(editorState)) === null || _key$getState3 === void 0 ? void 0 : _key$getState3.isDragging) !== null && _key$getState$isDragg !== void 0 ? _key$getState$isDragg : false,
73
- isPMDragging: (_key$getState$isPMDra = (_key$getState4 = _main.key.getState(editorState)) === null || _key$getState4 === void 0 ? void 0 : _key$getState4.isPMDragging) !== null && _key$getState$isPMDra !== void 0 ? _key$getState$isPMDra : false
111
+ isPMDragging: (_key$getState$isPMDra = (_key$getState4 = _main.key.getState(editorState)) === null || _key$getState4 === void 0 ? void 0 : _key$getState4.isPMDragging) !== null && _key$getState$isPMDra !== void 0 ? _key$getState$isPMDra : false,
112
+ multiSelectDnD: (_key$getState$multiSe = (_key$getState5 = _main.key.getState(editorState)) === null || _key$getState5 === void 0 ? void 0 : _key$getState5.multiSelectDnD) !== null && _key$getState$multiSe !== void 0 ? _key$getState$multiSe : undefined
74
113
  };
75
114
  },
76
115
  contentComponent: function contentComponent() {
@@ -24,6 +24,7 @@ var _fireAnalytics = require("../pm-plugins/utils/fire-analytics");
24
24
  var _getNestedNodePosition = require("../pm-plugins/utils/getNestedNodePosition");
25
25
  var _getSelection = require("../pm-plugins/utils/getSelection");
26
26
  var _removeFromSource = require("../pm-plugins/utils/remove-from-source");
27
+ var _selection2 = require("../pm-plugins/utils/selection");
27
28
  var _validation = require("../pm-plugins/utils/validation");
28
29
  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; }
29
30
  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) { (0, _defineProperty2.default)(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; }
@@ -220,26 +221,28 @@ var moveNode = exports.moveNode = function moveNode(api) {
220
221
  var formatMessage = arguments.length > 3 ? arguments[3] : undefined;
221
222
  return function (_ref6) {
222
223
  var tr = _ref6.tr;
223
- var isMultiSelect = (0, _experiments.editorExperiment)('platform_editor_element_drag_and_drop_multiselect', true, {
224
- exposure: true
225
- });
226
- var selection = tr.selection;
227
- var selectionFrom = selection.$from.pos;
228
- var selectionTo = selection.$to.pos;
224
+ if (!api) {
225
+ return tr;
226
+ }
229
227
  var handleNode = tr.doc.nodeAt(start);
230
228
  if (!handleNode) {
231
229
  return tr;
232
230
  }
233
231
  var sliceFrom = start;
234
232
  var sliceTo;
233
+ var isMultiSelect = (0, _experiments.editorExperiment)('platform_editor_element_drag_and_drop_multiselect', true, {
234
+ exposure: true
235
+ });
235
236
  if (isMultiSelect) {
236
237
  var _handleNode$nodeSize;
237
- // //If the handle position sits within the Editor selection, we will move all nodes that sit in that selection
238
- var useSelection = sliceFrom >= selectionFrom - 1 && sliceFrom <= selectionTo;
239
- sliceFrom = useSelection ? selectionFrom : start;
238
+ var _getMultiSelectionIfP = (0, _selection2.getMultiSelectionIfPosInside)(api, start),
239
+ anchor = _getMultiSelectionIfP.anchor,
240
+ head = _getMultiSelectionIfP.head;
241
+ var inSelection = anchor !== undefined && head !== undefined;
242
+ sliceFrom = inSelection ? Math.min(anchor, head) : start;
240
243
  var handleSize = (_handleNode$nodeSize = handleNode === null || handleNode === void 0 ? void 0 : handleNode.nodeSize) !== null && _handleNode$nodeSize !== void 0 ? _handleNode$nodeSize : 1;
241
244
  var handleEnd = sliceFrom + handleSize;
242
- sliceTo = useSelection ? selectionTo : handleEnd;
245
+ sliceTo = inSelection ? Math.max(anchor, head) : handleEnd;
243
246
  } else {
244
247
  var _handleNode$nodeSize2;
245
248
  var size = (_handleNode$nodeSize2 = handleNode === null || handleNode === void 0 ? void 0 : handleNode.nodeSize) !== null && _handleNode$nodeSize2 !== void 0 ? _handleNode$nodeSize2 : 1;
@@ -14,6 +14,7 @@ var _checkFragment = require("../pm-plugins/utils/check-fragment");
14
14
  var _consts = require("../pm-plugins/utils/consts");
15
15
  var _fireAnalytics = require("../pm-plugins/utils/fire-analytics");
16
16
  var _removeFromSource = require("../pm-plugins/utils/remove-from-source");
17
+ var _selection = require("../pm-plugins/utils/selection");
17
18
  var _updateColumnWidths = require("../pm-plugins/utils/update-column-widths");
18
19
  var _validation = require("../pm-plugins/utils/validation");
19
20
  var _consts2 = require("../ui/consts");
@@ -161,7 +162,7 @@ var insertToDestination = function insertToDestination(tr, to, sourceContent, to
161
162
  * Check if the node at `from` can be moved to node at `to` to create/expand a layout.
162
163
  * Returns the source and destination nodes and positions if it's a valid move, otherwise, undefined
163
164
  */
164
- var canMoveToLayout = function canMoveToLayout(from, to, tr) {
165
+ var canMoveToLayout = function canMoveToLayout(api, from, to, tr) {
165
166
  if (from === to) {
166
167
  return;
167
168
  }
@@ -193,17 +194,12 @@ var canMoveToLayout = function canMoveToLayout(from, to, tr) {
193
194
  var sourceFrom = from;
194
195
  var sourceTo = from + sourceContent.nodeSize;
195
196
  if (isMultiSelect) {
196
- var _tr$selection$$from$n;
197
- // Selection often starts from the content of the node (e.g. to show text selection properly),
198
- // so we need to trace back to the start of the node instead
199
- var contentStartPos = tr.selection.$from.nodeAfter && ((_tr$selection$$from$n = tr.selection.$from.nodeAfter) === null || _tr$selection$$from$n === void 0 ? void 0 : _tr$selection$$from$n.type.name) !== 'text' ? tr.selection.$from.pos : tr.selection.$from.before();
200
-
201
- // If the handle position sits within the Editor selection, we will move all nodes that sit in that selection
202
- // handle position is the same as `from` position
203
- var useSelection = from >= contentStartPos && from <= tr.selection.to;
204
- if (useSelection) {
205
- sourceFrom = contentStartPos;
206
- sourceTo = tr.selection.to;
197
+ var _getMultiSelectionIfP = (0, _selection.getMultiSelectionIfPosInside)(api, from),
198
+ anchor = _getMultiSelectionIfP.anchor,
199
+ head = _getMultiSelectionIfP.head;
200
+ if (anchor && head) {
201
+ sourceFrom = Math.min(anchor, head);
202
+ sourceTo = Math.max(anchor, head);
207
203
  sourceContent = tr.doc.slice(sourceFrom, sourceTo).content;
208
204
 
209
205
  // TODO: this might become expensive for large content, consider removing it if check has been done beforehand
@@ -291,7 +287,10 @@ var moveToLayout = exports.moveToLayout = function moveToLayout(api) {
291
287
  return function (from, to, options) {
292
288
  return function (_ref7) {
293
289
  var tr = _ref7.tr;
294
- var canMove = canMoveToLayout(from, to, tr);
290
+ if (!api) {
291
+ return tr;
292
+ }
293
+ var canMove = canMoveToLayout(api, from, to, tr);
295
294
  if (!canMove) {
296
295
  return tr;
297
296
  }
@@ -78,8 +78,24 @@ var destroyFn = function destroyFn(api, editorView) {
78
78
  }
79
79
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref3) {
80
80
  var tr = _ref3.tr;
81
- var _ref4 = source.data,
82
- start = _ref4.start;
81
+ if ((0, _experiments.editorExperiment)('platform_editor_element_drag_and_drop_multiselect', true)) {
82
+ var _api$blockControls, _api$selection;
83
+ var _ref4 = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {},
84
+ multiSelectDnD = _ref4.multiSelectDnD;
85
+ // Restore the users initial Editor selection when the drop completes
86
+ if (multiSelectDnD) {
87
+ // If the TextSelection between the drag start and end has changed, the document has changed, and we should not reapply the last selection
88
+ var expandedSelectionUnchanged = multiSelectDnD.textAnchor === tr.selection.anchor && multiSelectDnD.textHead === tr.selection.head;
89
+ if (expandedSelectionUnchanged) {
90
+ tr.setSelection(_state.TextSelection.create(tr.doc, multiSelectDnD.userAnchor, multiSelectDnD.userHead));
91
+ }
92
+ }
93
+ api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || _api$selection.commands.clearManualSelection()({
94
+ tr: tr
95
+ });
96
+ }
97
+ var _ref5 = source.data,
98
+ start = _ref5.start;
83
99
  // if no drop targets are rendered, assume that drop is invalid
84
100
  if (location.current.dropTargets.length === 0) {
85
101
  var _api$analytics2;
@@ -115,13 +131,15 @@ var initialState = {
115
131
  editorWidthRight: 0,
116
132
  isResizerResizing: false,
117
133
  isDocSizeLimitEnabled: null,
118
- isPMDragging: false
134
+ isPMDragging: false,
135
+ multiSelectDnD: undefined
119
136
  };
120
137
  var newApply = exports.newApply = function newApply(api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache) {
121
- var _meta$activeNode, _activeNode, _activeNode2, _meta$activeNode$hand, _meta$isDragging, _meta$isDragging2, _meta$editorHeight, _meta$editorWidthLeft, _meta$editorWidthRigh, _meta$isPMDragging;
138
+ var _meta$activeNode, _activeNode, _activeNode2, _meta$activeNode$hand, _meta$isDragging, _meta$isDragging2, _meta$editorHeight, _meta$editorWidthLeft, _meta$editorWidthRigh, _meta$isPMDragging, _meta$multiSelectDnD;
122
139
  var activeNode = currentState.activeNode,
123
140
  decorations = currentState.decorations,
124
- isResizerResizing = currentState.isResizerResizing;
141
+ isResizerResizing = currentState.isResizerResizing,
142
+ multiSelectDnD = currentState.multiSelectDnD;
125
143
  var editorHeight = currentState.editorHeight,
126
144
  editorWidthLeft = currentState.editorWidthLeft,
127
145
  editorWidthRight = currentState.editorWidthRight,
@@ -130,7 +148,7 @@ var newApply = exports.newApply = function newApply(api, formatMessage, tr, curr
130
148
  isPMDragging = currentState.isPMDragging;
131
149
  var isActiveNodeDeleted = false;
132
150
 
133
- // Remap existing decorations and activeNode when steps exist
151
+ // When steps exist, remap existing decorations, activeNode and multi select positions
134
152
  if (tr.docChanged) {
135
153
  decorations = decorations.map(tr.mapping, tr.doc);
136
154
  if (activeNode) {
@@ -142,10 +160,17 @@ var newApply = exports.newApply = function newApply(api, formatMessage, tr, curr
142
160
  nodeType: activeNode.nodeType
143
161
  };
144
162
  }
163
+ if (multiSelectDnD && flags.isMultiSelectEnabled) {
164
+ multiSelectDnD.anchor = tr.mapping.map(multiSelectDnD.anchor);
165
+ multiSelectDnD.head = tr.mapping.map(multiSelectDnD.head);
166
+ }
145
167
  }
146
168
  var meta = tr.getMeta(key);
147
169
  var resizerMeta = tr.getMeta('is-resizer-resizing');
148
170
  isResizerResizing = resizerMeta !== null && resizerMeta !== void 0 ? resizerMeta : isResizerResizing;
171
+ if (multiSelectDnD && flags.isMultiSelectEnabled) {
172
+ multiSelectDnD = (meta === null || meta === void 0 ? void 0 : meta.isDragging) === false ? undefined : multiSelectDnD;
173
+ }
149
174
  var _getTrMetadata = (0, _transactions.getTrMetadata)(tr),
150
175
  from = _getTrMetadata.from,
151
176
  to = _getTrMetadata.to,
@@ -245,7 +270,8 @@ var newApply = exports.newApply = function newApply(api, formatMessage, tr, curr
245
270
  editorWidthRight: (_meta$editorWidthRigh = meta === null || meta === void 0 ? void 0 : meta.editorWidthRight) !== null && _meta$editorWidthRigh !== void 0 ? _meta$editorWidthRigh : editorWidthRight,
246
271
  isResizerResizing: isResizerResizing,
247
272
  isDocSizeLimitEnabled: initialState.isDocSizeLimitEnabled,
248
- isPMDragging: (_meta$isPMDragging = meta === null || meta === void 0 ? void 0 : meta.isPMDragging) !== null && _meta$isPMDragging !== void 0 ? _meta$isPMDragging : isPMDragging
273
+ isPMDragging: (_meta$isPMDragging = meta === null || meta === void 0 ? void 0 : meta.isPMDragging) !== null && _meta$isPMDragging !== void 0 ? _meta$isPMDragging : isPMDragging,
274
+ multiSelectDnD: (_meta$multiSelectDnD = meta === null || meta === void 0 ? void 0 : meta.multiSelectDnD) !== null && _meta$multiSelectDnD !== void 0 ? _meta$multiSelectDnD : multiSelectDnD
249
275
  };
250
276
  };
251
277
  var oldApply = exports.oldApply = function oldApply(api, formatMessage, tr, currentState, oldState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache) {
@@ -289,8 +315,8 @@ var oldApply = exports.oldApply = function oldApply(api, formatMessage, tr, curr
289
315
  }
290
316
  var decsLength = isNestedEnabled ? decorations.find(undefined, undefined, function (spec) {
291
317
  return spec.type === 'node-decoration';
292
- }).length : decorations.find().filter(function (_ref5) {
293
- var spec = _ref5.spec;
318
+ }).length : decorations.find().filter(function (_ref6) {
319
+ var spec = _ref6.spec;
294
320
  return spec.type !== 'drag-handle';
295
321
  }).length;
296
322
  var isDecsMissing = false;
@@ -328,7 +354,7 @@ var oldApply = exports.oldApply = function oldApply(api, formatMessage, tr, curr
328
354
  var newNodeDecs = (0, _decorationsAnchor.nodeDecorations)(newState);
329
355
  decorations = decorations.add(newState.doc, (0, _toConsumableArray2.default)(newNodeDecs));
330
356
  if (activeNode && !(meta !== null && meta !== void 0 && meta.nodeMoved) && !isDecsMissing) {
331
- var _meta$activeNode$pos, _meta$activeNode3, _ref6, _meta$activeNode$anch, _meta$activeNode4, _decAtPos$spec, _ref7, _meta$activeNode$node, _meta$activeNode5, _decAtPos$spec2, _meta$activeNode6;
357
+ var _meta$activeNode$pos, _meta$activeNode3, _ref7, _meta$activeNode$anch, _meta$activeNode4, _decAtPos$spec, _ref8, _meta$activeNode$node, _meta$activeNode5, _decAtPos$spec2, _meta$activeNode6;
332
358
  var mappedPosisiton = tr.mapping.map(activeNode.pos);
333
359
  var prevMappedPos = oldState.tr.mapping.map(activeNode.pos);
334
360
 
@@ -347,7 +373,7 @@ var oldApply = exports.oldApply = function oldApply(api, formatMessage, tr, curr
347
373
  var decAtPos = newNodeDecs.find(function (dec) {
348
374
  return dec.from === mappedPosisiton;
349
375
  });
350
- var draghandleDec = (0, _decorationsDragHandle.dragHandleDecoration)(api, formatMessage, (_meta$activeNode$pos = meta === null || meta === void 0 || (_meta$activeNode3 = meta.activeNode) === null || _meta$activeNode3 === void 0 ? void 0 : _meta$activeNode3.pos) !== null && _meta$activeNode$pos !== void 0 ? _meta$activeNode$pos : mappedPosisiton, (_ref6 = (_meta$activeNode$anch = meta === null || meta === void 0 || (_meta$activeNode4 = meta.activeNode) === null || _meta$activeNode4 === void 0 ? void 0 : _meta$activeNode4.anchorName) !== null && _meta$activeNode$anch !== void 0 ? _meta$activeNode$anch : decAtPos === null || decAtPos === void 0 || (_decAtPos$spec = decAtPos.spec) === null || _decAtPos$spec === void 0 ? void 0 : _decAtPos$spec.anchorName) !== null && _ref6 !== void 0 ? _ref6 : activeNode === null || activeNode === void 0 ? void 0 : activeNode.anchorName, (_ref7 = (_meta$activeNode$node = meta === null || meta === void 0 || (_meta$activeNode5 = meta.activeNode) === null || _meta$activeNode5 === void 0 ? void 0 : _meta$activeNode5.nodeType) !== null && _meta$activeNode$node !== void 0 ? _meta$activeNode$node : decAtPos === null || decAtPos === void 0 || (_decAtPos$spec2 = decAtPos.spec) === null || _decAtPos$spec2 === void 0 ? void 0 : _decAtPos$spec2.nodeType) !== null && _ref7 !== void 0 ? _ref7 : activeNode === null || activeNode === void 0 ? void 0 : activeNode.nodeType, nodeViewPortalProviderAPI, meta === null || meta === void 0 || (_meta$activeNode6 = meta.activeNode) === null || _meta$activeNode6 === void 0 ? void 0 : _meta$activeNode6.handleOptions);
376
+ var draghandleDec = (0, _decorationsDragHandle.dragHandleDecoration)(api, formatMessage, (_meta$activeNode$pos = meta === null || meta === void 0 || (_meta$activeNode3 = meta.activeNode) === null || _meta$activeNode3 === void 0 ? void 0 : _meta$activeNode3.pos) !== null && _meta$activeNode$pos !== void 0 ? _meta$activeNode$pos : mappedPosisiton, (_ref7 = (_meta$activeNode$anch = meta === null || meta === void 0 || (_meta$activeNode4 = meta.activeNode) === null || _meta$activeNode4 === void 0 ? void 0 : _meta$activeNode4.anchorName) !== null && _meta$activeNode$anch !== void 0 ? _meta$activeNode$anch : decAtPos === null || decAtPos === void 0 || (_decAtPos$spec = decAtPos.spec) === null || _decAtPos$spec === void 0 ? void 0 : _decAtPos$spec.anchorName) !== null && _ref7 !== void 0 ? _ref7 : activeNode === null || activeNode === void 0 ? void 0 : activeNode.anchorName, (_ref8 = (_meta$activeNode$node = meta === null || meta === void 0 || (_meta$activeNode5 = meta.activeNode) === null || _meta$activeNode5 === void 0 ? void 0 : _meta$activeNode5.nodeType) !== null && _meta$activeNode$node !== void 0 ? _meta$activeNode$node : decAtPos === null || decAtPos === void 0 || (_decAtPos$spec2 = decAtPos.spec) === null || _decAtPos$spec2 === void 0 ? void 0 : _decAtPos$spec2.nodeType) !== null && _ref8 !== void 0 ? _ref8 : activeNode === null || activeNode === void 0 ? void 0 : activeNode.nodeType, nodeViewPortalProviderAPI, meta === null || meta === void 0 || (_meta$activeNode6 = meta.activeNode) === null || _meta$activeNode6 === void 0 ? void 0 : _meta$activeNode6.handleOptions);
351
377
  decorations = decorations.add(newState.doc, [draghandleDec]);
352
378
  }
353
379
  }
@@ -419,8 +445,10 @@ var createPlugin = exports.createPlugin = function createPlugin(api, getIntl, no
419
445
  var isAdvancedLayoutEnabled = (0, _experiments.editorExperiment)('advanced_layouts', true, {
420
446
  exposure: true
421
447
  });
448
+ var isMultiSelectEnabled = (0, _experiments.editorExperiment)('platform_editor_element_drag_and_drop_multiselect', true);
422
449
  var flags = {
423
- isNestedEnabled: isNestedEnabled
450
+ isNestedEnabled: isNestedEnabled,
451
+ isMultiSelectEnabled: isMultiSelectEnabled
424
452
  };
425
453
  var anchorRectCache;
426
454
  if (!(0, _anchorUtils.isAnchorSupported)()) {
@@ -483,8 +511,10 @@ var createPlugin = exports.createPlugin = function createPlugin(api, getIntl, no
483
511
  var domPos = Math.max(view.posAtDOM(nodeElement, 0) - 1, 0);
484
512
  var nodeTarget = state.doc.nodeAt(domPos);
485
513
  var isSameNode = !!(nodeTarget && draggable !== null && draggable !== void 0 && draggable.eq(nodeTarget));
486
- if (isSameNode) {
487
- // Prevent the default drop behavior if the position is within the activeNode
514
+ var isInSelection = domPos >= state.selection.$from.pos && domPos < state.selection.$to.pos;
515
+
516
+ // Prevent the default drop behavior if the position is within the activeNode or Editor selection
517
+ if (isSameNode || isInSelection && isMultiSelectEnabled) {
488
518
  event.preventDefault();
489
519
  return true;
490
520
  }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getMultiSelectionIfPosInside = void 0;
7
+ var getMultiSelectionIfPosInside = exports.getMultiSelectionIfPosInside = function getMultiSelectionIfPosInside(api, pos) {
8
+ var _api$blockControls;
9
+ var _ref = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {},
10
+ multiSelectDnD = _ref.multiSelectDnD;
11
+ if (multiSelectDnD && multiSelectDnD.anchor >= 0 && multiSelectDnD.head >= 0) {
12
+ var multiFrom = Math.min(multiSelectDnD.anchor, multiSelectDnD.head);
13
+ var multiTo = Math.max(multiSelectDnD.anchor, multiSelectDnD.head);
14
+
15
+ // We subtract one as the handle position is before the node
16
+ return pos >= multiFrom - 1 && pos <= multiTo ? {
17
+ anchor: multiSelectDnD.anchor,
18
+ head: multiSelectDnD.head
19
+ } : {};
20
+ }
21
+ return {};
22
+ };
@@ -256,39 +256,27 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
256
256
  return;
257
257
  }
258
258
  api === null || api === void 0 || (_api$core5 = api.core) === null || _api$core5 === void 0 || _api$core5.actions.execute(function (_ref6) {
259
- var _api$blockControls, _api$analytics3;
259
+ var _api$blockControls2, _api$analytics3;
260
260
  var tr = _ref6.tr;
261
261
  var isMultiSelect = (0, _experiments.editorExperiment)('platform_editor_element_drag_and_drop_multiselect', true, {
262
262
  exposure: true
263
263
  });
264
- var selectionStart = start;
265
264
  if (isMultiSelect) {
266
- var selection = tr.selection;
267
- var selectionFrom = selection.$from.pos;
268
- var selectionTo = selection.$to.pos;
269
- var $selectionFrom = tr.doc.resolve(selectionFrom);
270
- var $selectionTo = tr.doc.resolve(selectionTo);
271
- selectionStart = $selectionFrom.start();
272
- var selectionEnd = $selectionTo.end();
273
265
  var handlePos = getPos();
274
266
  if (typeof handlePos !== 'number') {
275
267
  return tr;
276
268
  }
277
- var posBeforeNode = $selectionFrom.pos ? $selectionFrom.start() - 1 : $selectionFrom.pos;
278
- var shouldExpandSelection = handlePos >= posBeforeNode && handlePos <= selectionEnd;
279
- if (shouldExpandSelection) {
280
- //TODO: What happens if not a text selection?
281
- var newSelection = _state.TextSelection.create(tr.doc, selectionStart, selectionEnd);
282
- tr.setSelection(newSelection);
283
- } else {
284
- var _$selectionFrom = tr.doc.resolve(handlePos + 1);
285
- (0, _getSelection.selectNode)(tr, handlePos, _$selectionFrom.node().type.name);
269
+ if (!tr.selection.empty && handlePos >= tr.selection.$from.start() - 1 && handlePos <= tr.selection.to) {
270
+ var _api$blockControls;
271
+ api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || _api$blockControls.commands.setMultiSelectPositions()({
272
+ tr: tr
273
+ });
286
274
  }
287
275
  }
288
- api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || _api$blockControls.commands.setNodeDragged(getPos, anchorName, nodeType)({
276
+ api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || _api$blockControls2.commands.setNodeDragged(getPos, anchorName, nodeType)({
289
277
  tr: tr
290
278
  });
291
- var resolvedMovingNode = tr.doc.resolve(selectionStart);
279
+ var resolvedMovingNode = tr.doc.resolve(start);
292
280
  var maybeNode = resolvedMovingNode.nodeAfter;
293
281
  tr.setMeta('scrollIntoView', false);
294
282
  api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 || _api$analytics3.actions.attachAnalyticsEvent({
@@ -436,6 +424,9 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
436
424
  var message = helpDescriptors.map(function (descriptor) {
437
425
  return descriptor.keymap ? [descriptor.description, (0, _keymaps.getAriaKeyshortcuts)(descriptor.keymap)] : [descriptor.description];
438
426
  }).join('. ');
427
+ var handleOnDrop = function handleOnDrop(event) {
428
+ (0, _experiments.editorExperiment)('platform_editor_element_drag_and_drop_multiselect', true) && event.stopPropagation();
429
+ };
439
430
  var renderButton = function renderButton() {
440
431
  return (
441
432
  // eslint-disable-next-line @atlaskit/design-system/no-html-button
@@ -451,7 +442,10 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
451
442
  style: positionStyles,
452
443
  onClick: handleOnClick,
453
444
  onMouseDown: handleMouseDown,
454
- onKeyDown: handleKeyDown,
445
+ onKeyDown: handleKeyDown
446
+ // eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop
447
+ ,
448
+ onDrop: handleOnDrop,
455
449
  "data-testid": "block-ctrl-drag-handle"
456
450
  }, (0, _react2.jsx)(_primitives.Box, {
457
451
  as: "span",
@@ -1,4 +1,6 @@
1
1
  import React from 'react';
2
+ import { expandSelectionBounds } from '@atlaskit/editor-common/selection';
3
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
2
4
  import { moveNode } from './editor-commands/move-node';
3
5
  import { moveToLayout } from './editor-commands/move-to-layout';
4
6
  import { createPlugin, key } from './pm-plugins/main';
@@ -23,7 +25,9 @@ export const blockControlsPlugin = ({
23
25
  showDragHandleAt: (pos, anchorName, nodeType, handleOptions) => ({
24
26
  tr
25
27
  }) => {
28
+ const currMeta = tr.getMeta(key);
26
29
  tr.setMeta(key, {
30
+ ...currMeta,
27
31
  activeNode: {
28
32
  pos,
29
33
  anchorName,
@@ -40,7 +44,9 @@ export const blockControlsPlugin = ({
40
44
  if (pos === undefined) {
41
45
  return tr;
42
46
  }
47
+ const currMeta = tr.getMeta(key);
43
48
  tr.setMeta(key, {
49
+ ...currMeta,
44
50
  isDragging: true,
45
51
  activeNode: {
46
52
  pos,
@@ -49,10 +55,43 @@ export const blockControlsPlugin = ({
49
55
  }
50
56
  });
51
57
  return tr;
58
+ },
59
+ setMultiSelectPositions: () => ({
60
+ tr
61
+ }) => {
62
+ var _api$selection;
63
+ const {
64
+ anchor: userAnchor,
65
+ head: userHead
66
+ } = tr.selection;
67
+ const {
68
+ $anchor: expandedAnchor,
69
+ $head: expandedHead
70
+ } = expandSelectionBounds(tr.selection.$anchor, tr.selection.$head);
71
+ api === null || api === void 0 ? void 0 : (_api$selection = api.selection) === null || _api$selection === void 0 ? void 0 : _api$selection.commands.setManualSelection(expandedAnchor.pos, expandedHead.pos)({
72
+ tr
73
+ });
74
+ // this is to normalise the selection's boundaries to inline positions, preventing it from collapsing
75
+ const expandedNormalisedSel = TextSelection.between(expandedAnchor, expandedHead);
76
+ tr.setSelection(expandedNormalisedSel);
77
+ const multiSelectDnD = {
78
+ anchor: expandedAnchor.pos,
79
+ head: expandedHead.pos,
80
+ textAnchor: expandedNormalisedSel.anchor,
81
+ textHead: expandedNormalisedSel.head,
82
+ userAnchor: userAnchor,
83
+ userHead: userHead
84
+ };
85
+ const currMeta = tr.getMeta(key);
86
+ tr.setMeta(key, {
87
+ ...currMeta,
88
+ multiSelectDnD
89
+ });
90
+ return tr;
52
91
  }
53
92
  },
54
93
  getSharedState(editorState) {
55
- var _key$getState$isMenuO, _key$getState, _key$getState$activeN, _key$getState2, _key$getState$isDragg, _key$getState3, _key$getState$isPMDra, _key$getState4;
94
+ var _key$getState$isMenuO, _key$getState, _key$getState$activeN, _key$getState2, _key$getState$isDragg, _key$getState3, _key$getState$isPMDra, _key$getState4, _key$getState$multiSe, _key$getState5;
56
95
  if (!editorState) {
57
96
  return undefined;
58
97
  }
@@ -60,7 +99,8 @@ export const blockControlsPlugin = ({
60
99
  isMenuOpen: (_key$getState$isMenuO = (_key$getState = key.getState(editorState)) === null || _key$getState === void 0 ? void 0 : _key$getState.isMenuOpen) !== null && _key$getState$isMenuO !== void 0 ? _key$getState$isMenuO : false,
61
100
  activeNode: (_key$getState$activeN = (_key$getState2 = key.getState(editorState)) === null || _key$getState2 === void 0 ? void 0 : _key$getState2.activeNode) !== null && _key$getState$activeN !== void 0 ? _key$getState$activeN : undefined,
62
101
  isDragging: (_key$getState$isDragg = (_key$getState3 = key.getState(editorState)) === null || _key$getState3 === void 0 ? void 0 : _key$getState3.isDragging) !== null && _key$getState$isDragg !== void 0 ? _key$getState$isDragg : false,
63
- isPMDragging: (_key$getState$isPMDra = (_key$getState4 = key.getState(editorState)) === null || _key$getState4 === void 0 ? void 0 : _key$getState4.isPMDragging) !== null && _key$getState$isPMDra !== void 0 ? _key$getState$isPMDra : false
102
+ isPMDragging: (_key$getState$isPMDra = (_key$getState4 = key.getState(editorState)) === null || _key$getState4 === void 0 ? void 0 : _key$getState4.isPMDragging) !== null && _key$getState$isPMDra !== void 0 ? _key$getState$isPMDra : false,
103
+ multiSelectDnD: (_key$getState$multiSe = (_key$getState5 = key.getState(editorState)) === null || _key$getState5 === void 0 ? void 0 : _key$getState5.multiSelectDnD) !== null && _key$getState$multiSe !== void 0 ? _key$getState$multiSe : undefined
64
104
  };
65
105
  },
66
106
  contentComponent() {
@@ -16,6 +16,7 @@ import { attachMoveNodeAnalytics } from '../pm-plugins/utils/fire-analytics';
16
16
  import { getNestedNodePosition } from '../pm-plugins/utils/getNestedNodePosition';
17
17
  import { selectNode, setCursorPositionAtMovedNode } from '../pm-plugins/utils/getSelection';
18
18
  import { removeFromSource } from '../pm-plugins/utils/remove-from-source';
19
+ import { getMultiSelectionIfPosInside } from '../pm-plugins/utils/selection';
19
20
  import { canMoveNodeToIndex, isInsideTable, transformSliceExpandToNestedExpand } from '../pm-plugins/utils/validation';
20
21
 
21
22
  /**
@@ -218,26 +219,29 @@ export const moveNodeViaShortcut = (api, direction, formatMessage) => {
218
219
  export const moveNode = api => (start, to, inputMethod = INPUT_METHOD.DRAG_AND_DROP, formatMessage) => ({
219
220
  tr
220
221
  }) => {
221
- const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
222
- exposure: true
223
- });
224
- const selection = tr.selection;
225
- const selectionFrom = selection.$from.pos;
226
- const selectionTo = selection.$to.pos;
222
+ if (!api) {
223
+ return tr;
224
+ }
227
225
  const handleNode = tr.doc.nodeAt(start);
228
226
  if (!handleNode) {
229
227
  return tr;
230
228
  }
231
229
  let sliceFrom = start;
232
230
  let sliceTo;
231
+ const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
232
+ exposure: true
233
+ });
233
234
  if (isMultiSelect) {
234
235
  var _handleNode$nodeSize;
235
- // //If the handle position sits within the Editor selection, we will move all nodes that sit in that selection
236
- const useSelection = sliceFrom >= selectionFrom - 1 && sliceFrom <= selectionTo;
237
- sliceFrom = useSelection ? selectionFrom : start;
236
+ const {
237
+ anchor,
238
+ head
239
+ } = getMultiSelectionIfPosInside(api, start);
240
+ const inSelection = anchor !== undefined && head !== undefined;
241
+ sliceFrom = inSelection ? Math.min(anchor, head) : start;
238
242
  const handleSize = (_handleNode$nodeSize = handleNode === null || handleNode === void 0 ? void 0 : handleNode.nodeSize) !== null && _handleNode$nodeSize !== void 0 ? _handleNode$nodeSize : 1;
239
243
  const handleEnd = sliceFrom + handleSize;
240
- sliceTo = useSelection ? selectionTo : handleEnd;
244
+ sliceTo = inSelection ? Math.max(anchor, head) : handleEnd;
241
245
  } else {
242
246
  var _handleNode$nodeSize2;
243
247
  const size = (_handleNode$nodeSize2 = handleNode === null || handleNode === void 0 ? void 0 : handleNode.nodeSize) !== null && _handleNode$nodeSize2 !== void 0 ? _handleNode$nodeSize2 : 1;
@@ -8,6 +8,7 @@ import { isFragmentOfType, containsNodeOfType } from '../pm-plugins/utils/check-
8
8
  import { maxLayoutColumnSupported } from '../pm-plugins/utils/consts';
9
9
  import { fireInsertLayoutAnalytics, attachMoveNodeAnalytics } from '../pm-plugins/utils/fire-analytics';
10
10
  import { removeFromSource } from '../pm-plugins/utils/remove-from-source';
11
+ import { getMultiSelectionIfPosInside } from '../pm-plugins/utils/selection';
11
12
  import { updateColumnWidths } from '../pm-plugins/utils/update-column-widths';
12
13
  import { isInSameLayout } from '../pm-plugins/utils/validation';
13
14
  import { DEFAULT_COLUMN_DISTRIBUTIONS } from '../ui/consts';
@@ -159,7 +160,7 @@ const insertToDestination = (tr, to, sourceContent, toLayout, toLayoutPos) => {
159
160
  * Check if the node at `from` can be moved to node at `to` to create/expand a layout.
160
161
  * Returns the source and destination nodes and positions if it's a valid move, otherwise, undefined
161
162
  */
162
- const canMoveToLayout = (from, to, tr) => {
163
+ const canMoveToLayout = (api, from, to, tr) => {
163
164
  if (from === to) {
164
165
  return;
165
166
  }
@@ -192,17 +193,13 @@ const canMoveToLayout = (from, to, tr) => {
192
193
  let sourceFrom = from;
193
194
  let sourceTo = from + sourceContent.nodeSize;
194
195
  if (isMultiSelect) {
195
- var _tr$selection$$from$n;
196
- // Selection often starts from the content of the node (e.g. to show text selection properly),
197
- // so we need to trace back to the start of the node instead
198
- const contentStartPos = tr.selection.$from.nodeAfter && ((_tr$selection$$from$n = tr.selection.$from.nodeAfter) === null || _tr$selection$$from$n === void 0 ? void 0 : _tr$selection$$from$n.type.name) !== 'text' ? tr.selection.$from.pos : tr.selection.$from.before();
199
-
200
- // If the handle position sits within the Editor selection, we will move all nodes that sit in that selection
201
- // handle position is the same as `from` position
202
- const useSelection = from >= contentStartPos && from <= tr.selection.to;
203
- if (useSelection) {
204
- sourceFrom = contentStartPos;
205
- sourceTo = tr.selection.to;
196
+ const {
197
+ anchor,
198
+ head
199
+ } = getMultiSelectionIfPosInside(api, from);
200
+ if (anchor && head) {
201
+ sourceFrom = Math.min(anchor, head);
202
+ sourceTo = Math.max(anchor, head);
206
203
  sourceContent = tr.doc.slice(sourceFrom, sourceTo).content;
207
204
 
208
205
  // TODO: this might become expensive for large content, consider removing it if check has been done beforehand
@@ -280,7 +277,10 @@ const getBreakoutMode = (content, breakout) => {
280
277
  export const moveToLayout = api => (from, to, options) => ({
281
278
  tr
282
279
  }) => {
283
- const canMove = canMoveToLayout(from, to, tr);
280
+ if (!api) {
281
+ return tr;
282
+ }
283
+ const canMove = canMoveToLayout(api, from, to, tr);
284
284
  if (!canMove) {
285
285
  return tr;
286
286
  }