@atlaskit/editor-plugin-block-controls 2.21.5 → 2.21.7

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/blockControlsPlugin.js +0 -2
  3. package/dist/cjs/editor-commands/move-node.js +2 -5
  4. package/dist/cjs/editor-commands/move-to-layout.js +186 -60
  5. package/dist/cjs/pm-plugins/decorations-anchor.js +1 -6
  6. package/dist/cjs/pm-plugins/decorations-drag-handle.js +1 -4
  7. package/dist/cjs/pm-plugins/decorations-drop-target.js +35 -20
  8. package/dist/cjs/pm-plugins/main.js +2 -10
  9. package/dist/cjs/pm-plugins/utils/check-fragment.js +26 -0
  10. package/dist/cjs/pm-plugins/utils/drag-handle-positions.js +1 -4
  11. package/dist/cjs/pm-plugins/utils/fire-analytics.js +1 -4
  12. package/dist/cjs/pm-plugins/utils/inline-drop-target.js +1 -4
  13. package/dist/cjs/pm-plugins/utils/remove-from-source.js +15 -6
  14. package/dist/cjs/pm-plugins/utils/update-column-widths.js +1 -4
  15. package/dist/cjs/pm-plugins/utils/validation.js +28 -3
  16. package/dist/es2019/blockControlsPlugin.js +0 -2
  17. package/dist/es2019/editor-commands/move-node.js +2 -5
  18. package/dist/es2019/editor-commands/move-to-layout.js +171 -50
  19. package/dist/es2019/pm-plugins/decorations-anchor.js +1 -6
  20. package/dist/es2019/pm-plugins/decorations-drag-handle.js +1 -4
  21. package/dist/es2019/pm-plugins/decorations-drop-target.js +36 -21
  22. package/dist/es2019/pm-plugins/main.js +2 -10
  23. package/dist/es2019/pm-plugins/utils/check-fragment.js +20 -0
  24. package/dist/es2019/pm-plugins/utils/drag-handle-positions.js +1 -4
  25. package/dist/es2019/pm-plugins/utils/fire-analytics.js +1 -4
  26. package/dist/es2019/pm-plugins/utils/inline-drop-target.js +1 -4
  27. package/dist/es2019/pm-plugins/utils/remove-from-source.js +15 -6
  28. package/dist/es2019/pm-plugins/utils/update-column-widths.js +1 -4
  29. package/dist/es2019/pm-plugins/utils/validation.js +27 -3
  30. package/dist/esm/blockControlsPlugin.js +0 -2
  31. package/dist/esm/editor-commands/move-node.js +2 -5
  32. package/dist/esm/editor-commands/move-to-layout.js +187 -61
  33. package/dist/esm/pm-plugins/decorations-anchor.js +1 -6
  34. package/dist/esm/pm-plugins/decorations-drag-handle.js +1 -4
  35. package/dist/esm/pm-plugins/decorations-drop-target.js +36 -21
  36. package/dist/esm/pm-plugins/main.js +2 -10
  37. package/dist/esm/pm-plugins/utils/check-fragment.js +20 -0
  38. package/dist/esm/pm-plugins/utils/drag-handle-positions.js +1 -4
  39. package/dist/esm/pm-plugins/utils/fire-analytics.js +1 -4
  40. package/dist/esm/pm-plugins/utils/inline-drop-target.js +1 -4
  41. package/dist/esm/pm-plugins/utils/remove-from-source.js +15 -6
  42. package/dist/esm/pm-plugins/utils/update-column-widths.js +1 -4
  43. package/dist/esm/pm-plugins/utils/validation.js +27 -3
  44. package/dist/types/pm-plugins/utils/check-fragment.d.ts +9 -0
  45. package/dist/types/pm-plugins/utils/remove-from-source.d.ts +1 -1
  46. package/dist/types/pm-plugins/utils/validation.d.ts +1 -0
  47. package/dist/types-ts4.5/pm-plugins/utils/check-fragment.d.ts +9 -0
  48. package/dist/types-ts4.5/pm-plugins/utils/remove-from-source.d.ts +1 -1
  49. package/dist/types-ts4.5/pm-plugins/utils/validation.d.ts +1 -0
  50. package/package.json +5 -5
@@ -1,8 +1,5 @@
1
1
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
- export const attachMoveNodeAnalytics = (tr, inputMethod, fromDepth, fromNodeType, toDepth, toNodeType, isSameParent, api
3
- // Ignored via go/ees005
4
- // eslint-disable-next-line @typescript-eslint/max-params
5
- ) => {
2
+ export const attachMoveNodeAnalytics = (tr, inputMethod, fromDepth, fromNodeType, toDepth, toNodeType, isSameParent, api) => {
6
3
  var _api$analytics, _api$analytics$action;
7
4
  return api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.attachAnalyticsEvent({
8
5
  eventType: EVENT_TYPE.TRACK,
@@ -6,10 +6,7 @@ export const shouldAllowInlineDropTarget = (isNested, node,
6
6
  /**
7
7
  * Is the active node in the same layout as the target node
8
8
  */
9
- isSameLayout = false, activeNode
10
- // Ignored via go/ees005
11
- // eslint-disable-next-line @typescript-eslint/max-params
12
- ) => {
9
+ isSameLayout = false, activeNode) => {
13
10
  if (editorExperiment('advanced_layouts', false) || isNested) {
14
11
  return false;
15
12
  }
@@ -1,16 +1,25 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
2
  import { findParentNodeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
3
3
  import { fg } from '@atlaskit/platform-feature-flags';
4
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
5
+ import { isFragmentOfType } from './check-fragment';
4
6
  import { MIN_LAYOUT_COLUMN } from './consts';
5
7
  import { updateColumnWidths } from './update-column-widths';
6
- export const removeFromSource = (tr, $from) => {
7
- const sourceNode = $from.nodeAfter;
8
- const sourceParent = $from.parent;
9
- if (!sourceNode) {
8
+ export const removeFromSource = (tr, $from, to) => {
9
+ var _sourceContent, _sourceContent2;
10
+ let sourceContent = $from.nodeAfter;
11
+ let isLayoutColumn = ((_sourceContent = sourceContent) === null || _sourceContent === void 0 ? void 0 : _sourceContent.type.name) === 'layoutColumn';
12
+ let sourceNodeEndPos = $from.pos + (((_sourceContent2 = sourceContent) === null || _sourceContent2 === void 0 ? void 0 : _sourceContent2.nodeSize) || 1);
13
+ if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
14
+ sourceContent = tr.doc.slice($from.pos, to).content;
15
+ isLayoutColumn = isFragmentOfType(sourceContent, 'layoutColumn');
16
+ sourceNodeEndPos = to === undefined ? $from.pos + sourceContent.size : to;
17
+ }
18
+ if (!sourceContent) {
10
19
  return tr;
11
20
  }
12
- const sourceNodeEndPos = $from.pos + sourceNode.nodeSize;
13
- if (sourceNode.type.name === 'layoutColumn') {
21
+ if (isLayoutColumn) {
22
+ const sourceParent = $from.parent;
14
23
  if (sourceParent.childCount === MIN_LAYOUT_COLUMN) {
15
24
  tr.delete($from.pos + 1, sourceNodeEndPos - 1);
16
25
 
@@ -1,8 +1,5 @@
1
1
  import { DEFAULT_COLUMN_DISTRIBUTIONS } from '../../ui/consts';
2
- export const updateColumnWidths = (tr, layoutNode, layoutNodePos, childCount
3
- // Ignored via go/ees005
4
- // eslint-disable-next-line @typescript-eslint/max-params
5
- ) => {
2
+ export const updateColumnWidths = (tr, layoutNode, layoutNodePos, childCount) => {
6
3
  const newColumnWidth = DEFAULT_COLUMN_DISTRIBUTIONS[childCount];
7
4
  if (newColumnWidth) {
8
5
  layoutNode.content.forEach((node, offset) => {
@@ -72,9 +72,6 @@ export const memoizedTransformExpandToNestedExpand = memoizeOne(node => {
72
72
  return null;
73
73
  }
74
74
  });
75
-
76
- // Ignored via go/ees005
77
- // eslint-disable-next-line @typescript-eslint/max-params
78
75
  export function canMoveNodeToIndex(destParent, indexIntoParent, srcNode, $destNodePos, destNode) {
79
76
  let srcNodeType = srcNode.type;
80
77
  const parentNodeType = destParent === null || destParent === void 0 ? void 0 : destParent.type.name;
@@ -123,4 +120,31 @@ export function canMoveNodeToIndex(destParent, indexIntoParent, srcNode, $destNo
123
120
  srcNodeType = srcNodeType.schema.nodes.expand;
124
121
  }
125
122
  return destParent.canReplaceWith(indexIntoParent, indexIntoParent, srcNodeType);
123
+ }
124
+ export function canMoveSliceToIndex(slice, sliceFromPos, doc, destParent, indexIntoParent, $destNodePos, destNode) {
125
+ let canMoveNodes = true;
126
+ const nodesPos = [];
127
+ for (let i = 0; i < slice.content.childCount; i++) {
128
+ const node = slice.content.maybeChild(i);
129
+ if (i === 0) {
130
+ nodesPos[i] = sliceFromPos;
131
+ } else {
132
+ var _slice$content$maybeC;
133
+ nodesPos[i] = nodesPos[i - 1] + (((_slice$content$maybeC = slice.content.maybeChild(i - 1)) === null || _slice$content$maybeC === void 0 ? void 0 : _slice$content$maybeC.nodeSize) || 0);
134
+ }
135
+ if (node && node.isInline) {
136
+ // If the node is an inline node, we need to find the parent node
137
+ // as passing in them into canMoveNodeToIndex will return false
138
+ const $nodePos = doc.resolve(nodesPos[i]);
139
+ const parentNode = $nodePos.parent;
140
+ if (!parentNode || parentNode && !canMoveNodeToIndex(destParent, indexIntoParent, parentNode, $destNodePos, destNode)) {
141
+ canMoveNodes = false;
142
+ break;
143
+ }
144
+ } else if (node && !canMoveNodeToIndex(destParent, indexIntoParent, node, $destNodePos, destNode)) {
145
+ canMoveNodes = false;
146
+ break;
147
+ }
148
+ }
149
+ return canMoveNodes;
126
150
  }
@@ -21,8 +21,6 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
21
21
  commands: {
22
22
  moveNode: moveNode(api),
23
23
  moveToLayout: moveToLayout(api),
24
- // Ignored via go/ees005
25
- // eslint-disable-next-line @typescript-eslint/max-params
26
24
  showDragHandleAt: function showDragHandleAt(pos, anchorName, nodeType, handleOptions) {
27
25
  return function (_ref3) {
28
26
  var tr = _ref3.tr;
@@ -154,10 +154,7 @@ export var moveNodeViaShortcut = function moveNodeViaShortcut(api, direction, fo
154
154
  export var moveNode = function moveNode(api) {
155
155
  return function (start, to) {
156
156
  var inputMethod = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INPUT_METHOD.DRAG_AND_DROP;
157
- var formatMessage
158
- // Ignored via go/ees005
159
- // eslint-disable-next-line @typescript-eslint/max-params
160
- = arguments.length > 3 ? arguments[3] : undefined;
157
+ var formatMessage = arguments.length > 3 ? arguments[3] : undefined;
161
158
  return function (_ref4) {
162
159
  var tr = _ref4.tr;
163
160
  var isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
@@ -202,7 +199,7 @@ export var moveNode = function moveNode(api) {
202
199
  if (sourceNode && isDragLayoutColumnToTopLevel($handlePos, $to)) {
203
200
  // need update after we support single column layout.
204
201
  var fragment = Fragment.from(sourceNode.content);
205
- removeFromSource(tr, $handlePos);
202
+ removeFromSource(tr, $handlePos, $handlePos.pos + sourceNode.nodeSize);
206
203
  var _mappedTo = tr.mapping.map(to);
207
204
  tr.insert(_mappedTo, fragment).setSelection(Selection.near(tr.doc.resolve(_mappedTo))).scrollIntoView();
208
205
  return tr;
@@ -1,7 +1,9 @@
1
1
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
- import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { Fragment, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
3
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
4
4
  import { fg } from '@atlaskit/platform-feature-flags';
5
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
6
+ import { isFragmentOfType, containsNodeOfType } from '../pm-plugins/utils/check-fragment';
5
7
  import { maxLayoutColumnSupported } from '../pm-plugins/utils/consts';
6
8
  import { fireInsertLayoutAnalytics, attachMoveNodeAnalytics } from '../pm-plugins/utils/fire-analytics';
7
9
  import { removeFromSource } from '../pm-plugins/utils/remove-from-source';
@@ -32,17 +34,27 @@ var createNewLayout = function createNewLayout(schema, layoutContents) {
32
34
  }
33
35
  return null;
34
36
  };
35
- var moveToExistingLayout = function moveToExistingLayout(toLayout, toLayoutPos, sourceNode, from, to, tr, $originalFrom, $originalTo, api
36
- // Ignored via go/ees005
37
- // eslint-disable-next-line @typescript-eslint/max-params
38
- ) {
37
+ var moveToExistingLayout = function moveToExistingLayout(toLayout, toLayoutPos, sourceContent, from, to, tr, $originalFrom, $originalTo, api) {
39
38
  var isSameLayout = isInSameLayout($originalFrom, $originalTo);
39
+ var sourceContentEndPos = -1;
40
+ if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
41
+ if (sourceContent instanceof Fragment) {
42
+ sourceContentEndPos = from + sourceContent.size;
43
+ }
44
+ } else {
45
+ if (sourceContent instanceof PMNode) {
46
+ sourceContentEndPos = from + sourceContent.nodeSize;
47
+ }
48
+ }
49
+ if (sourceContentEndPos === -1) {
50
+ return tr;
51
+ }
40
52
  if (isSameLayout) {
41
53
  var _$originalFrom$nodeAf;
42
54
  // reorder columns
43
- tr.delete(from, from + sourceNode.nodeSize);
55
+ tr.delete(from, sourceContentEndPos);
44
56
  var mappedTo = tr.mapping.map(to);
45
- tr.insert(mappedTo, sourceNode);
57
+ tr.insert(mappedTo, sourceContent);
46
58
  if (!fg('platform_editor_advanced_layouts_post_fix_patch_1')) {
47
59
  tr.setSelection(new NodeSelection(tr.doc.resolve(mappedTo))).scrollIntoView();
48
60
  }
@@ -50,12 +62,12 @@ var moveToExistingLayout = function moveToExistingLayout(toLayout, toLayoutPos,
50
62
  } else if (toLayout.childCount < maxLayoutColumnSupported()) {
51
63
  var _$originalFrom$nodeAf2;
52
64
  if (fg('platform_editor_advanced_layouts_post_fix_patch_1')) {
53
- removeFromSource(tr, tr.doc.resolve(from));
54
- insertToDestinationNoWidthUpdate(tr, tr.mapping.map(to), sourceNode);
65
+ removeFromSource(tr, tr.doc.resolve(from), sourceContentEndPos);
66
+ insertToDestinationNoWidthUpdate(tr, tr.mapping.map(to), sourceContent);
55
67
  } else {
56
- insertToDestination(tr, to, sourceNode, toLayout, toLayoutPos);
68
+ insertToDestination(tr, to, sourceContent, toLayout, toLayoutPos);
57
69
  var mappedFrom = tr.mapping.map(from);
58
- removeFromSource(tr, tr.doc.resolve(mappedFrom));
70
+ removeFromSource(tr, tr.doc.resolve(mappedFrom), tr.mapping.map(sourceContentEndPos));
59
71
  }
60
72
  attachMoveNodeAnalytics(tr, INPUT_METHOD.DRAG_AND_DROP, $originalFrom.depth, ((_$originalFrom$nodeAf2 = $originalFrom.nodeAfter) === null || _$originalFrom$nodeAf2 === void 0 ? void 0 : _$originalFrom$nodeAf2.type.name) || '', 1, 'layoutSection', false, api);
61
73
  }
@@ -70,27 +82,54 @@ var moveToExistingLayout = function moveToExistingLayout(toLayout, toLayoutPos,
70
82
  * @param sourceNode
71
83
  * @returns
72
84
  */
73
- var insertToDestinationNoWidthUpdate = function insertToDestinationNoWidthUpdate(tr, to, sourceNode) {
85
+ var insertToDestinationNoWidthUpdate = function insertToDestinationNoWidthUpdate(tr, to, sourceContent) {
74
86
  var _ref2 = tr.doc.type.schema.nodes || {},
75
87
  layoutColumn = _ref2.layoutColumn;
76
- var content = layoutColumn.createChecked({
77
- width: 0
78
- }, sourceNode.type.name === 'layoutColumn' ? sourceNode.content : sourceNode);
79
- tr.insert(to, content);
88
+ var content = null;
89
+ if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
90
+ if (sourceContent instanceof Fragment) {
91
+ var _sourceFragment$first;
92
+ var sourceFragment = sourceContent;
93
+ content = layoutColumn.createChecked({
94
+ width: 0
95
+ }, isFragmentOfType(sourceFragment, 'layoutColumn') ? (_sourceFragment$first = sourceFragment.firstChild) === null || _sourceFragment$first === void 0 ? void 0 : _sourceFragment$first.content : sourceFragment);
96
+ }
97
+ } else {
98
+ if (sourceContent instanceof PMNode) {
99
+ var sourceNode = sourceContent;
100
+ content = layoutColumn.createChecked({
101
+ width: 0
102
+ }, sourceNode.type.name === 'layoutColumn' ? sourceNode.content : sourceNode);
103
+ }
104
+ }
105
+ if (content) {
106
+ tr.insert(to, content);
107
+ }
80
108
  return tr;
81
109
  };
82
- var insertToDestination = function insertToDestination(tr, to, sourceNode, toLayout, toLayoutPos
83
- // Ignored via go/ees005
84
- // eslint-disable-next-line @typescript-eslint/max-params
85
- ) {
110
+ var insertToDestination = function insertToDestination(tr, to, sourceContent, toLayout, toLayoutPos) {
86
111
  var _ref3 = updateColumnWidths(tr, toLayout, toLayoutPos, toLayout.childCount + 1) || {},
87
112
  newColumnWidth = _ref3.newColumnWidth;
88
113
  var _ref4 = tr.doc.type.schema.nodes || {},
89
114
  layoutColumn = _ref4.layoutColumn;
90
- var content = layoutColumn.createChecked({
91
- width: newColumnWidth
92
- }, sourceNode.type.name === 'layoutColumn' ? sourceNode.content : sourceNode);
93
- tr.insert(to, content);
115
+ var content = null;
116
+ if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
117
+ if (sourceContent instanceof Fragment) {
118
+ var _sourceContent$firstC;
119
+ content = layoutColumn.createChecked({
120
+ width: newColumnWidth
121
+ }, isFragmentOfType(sourceContent, 'layoutColumn') ? (_sourceContent$firstC = sourceContent.firstChild) === null || _sourceContent$firstC === void 0 ? void 0 : _sourceContent$firstC.content : sourceContent);
122
+ }
123
+ } else {
124
+ if (sourceContent instanceof PMNode) {
125
+ content = layoutColumn.createChecked({
126
+ width: newColumnWidth
127
+ }, sourceContent.type.name === 'layoutColumn' ? sourceContent.content : sourceContent);
128
+ }
129
+ }
130
+ if (content) {
131
+ tr.insert(to, content);
132
+ }
94
133
  if (!fg('platform_editor_advanced_layouts_post_fix_patch_1')) {
95
134
  tr.setSelection(new NodeSelection(tr.doc.resolve(to))).scrollIntoView();
96
135
  }
@@ -121,67 +160,148 @@ var canMoveToLayout = function canMoveToLayout(from, to, tr) {
121
160
  return;
122
161
  }
123
162
  var $from = tr.doc.resolve(from);
163
+ var isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
164
+ exposure: true
165
+ });
124
166
 
125
167
  // invalid from position or dragging a layout
126
168
  if (!$from.nodeAfter || $from.nodeAfter.type === layoutSection) {
127
169
  return;
128
170
  }
171
+ var sourceContent = $from.nodeAfter;
172
+ var sourceFrom = from;
173
+ var sourceTo = from + sourceContent.nodeSize;
174
+ if (isMultiSelect) {
175
+ var _tr$selection$$from$n;
176
+ // Selection often starts from the content of the node (e.g. to show text selection properly),
177
+ // so we need to trace back to the start of the node instead
178
+ 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();
179
+
180
+ // If the handle position sits within the Editor selection, we will move all nodes that sit in that selection
181
+ // handle position is the same as `from` position
182
+ var useSelection = from >= contentStartPos && from <= tr.selection.to;
183
+ if (useSelection) {
184
+ sourceFrom = contentStartPos;
185
+ sourceTo = tr.selection.to;
186
+ sourceContent = tr.doc.slice(sourceFrom, sourceTo).content;
187
+
188
+ // TODO: this might become expensive for large content, consider removing it if check has been done beforehand
189
+ if (containsNodeOfType(sourceContent, 'layoutSection')) {
190
+ return;
191
+ }
192
+ } else {
193
+ sourceContent = Fragment.from($from.nodeAfter);
194
+ }
195
+ }
129
196
  var toNode = $to.nodeAfter;
130
- var fromNode = $from.nodeAfter;
131
197
  return {
132
198
  toNode: toNode,
133
- fromNode: fromNode,
134
- $from: $from,
135
- $to: $to
199
+ $to: $to,
200
+ sourceContent: sourceContent,
201
+ $sourceFrom: tr.doc.resolve(sourceFrom),
202
+ sourceTo: sourceTo
136
203
  };
137
204
  };
205
+ var removeBreakoutMarks = function removeBreakoutMarks(tr, $from, to) {
206
+ var fromContentWithoutBreakout = $from.nodeAfter;
207
+ var _ref6 = tr.doc.type.schema.marks || {},
208
+ breakout = _ref6.breakout;
209
+ if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
210
+ tr.doc.nodesBetween($from.pos, to, function (node, pos, parent) {
211
+ // breakout doesn't exist on nested nodes
212
+ if ((parent === null || parent === void 0 ? void 0 : parent.type.name) === 'doc' && node.marks.some(function (m) {
213
+ return m.type === breakout;
214
+ })) {
215
+ tr.removeNodeMark(pos, breakout);
216
+ }
217
+
218
+ // descending is not needed as breakout doesn't exist on nested nodes
219
+ return false;
220
+ });
221
+ // resolve again the source content after node updated (remove breakout marks)
222
+ fromContentWithoutBreakout = tr.doc.slice($from.pos, to).content;
223
+ } else {
224
+ if (breakout && $from.nodeAfter && $from.nodeAfter.marks.some(function (m) {
225
+ return m.type === breakout;
226
+ })) {
227
+ tr.removeNodeMark($from.pos, breakout);
228
+ // resolve again the source node after node updated (remove breakout marks)
229
+ fromContentWithoutBreakout = tr.doc.resolve($from.pos).nodeAfter;
230
+ }
231
+ }
232
+ return fromContentWithoutBreakout;
233
+ };
234
+ var getBreakoutMode = function getBreakoutMode(content, breakout) {
235
+ if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
236
+ if (content instanceof PMNode) {
237
+ var _content$marks$find;
238
+ return (_content$marks$find = content.marks.find(function (m) {
239
+ return m.type === breakout;
240
+ })) === null || _content$marks$find === void 0 ? void 0 : _content$marks$find.attrs;
241
+ } else if (content instanceof Fragment) {
242
+ // Find the first breakout mode in the fragment
243
+ var firstBreakoutMode;
244
+ for (var i = 0; i < content.childCount; i++) {
245
+ var child = content.child(i);
246
+ var breakoutMark = child.marks.find(function (m) {
247
+ return m.type === breakout;
248
+ });
249
+ if (breakoutMark) {
250
+ firstBreakoutMode = breakoutMark.attrs.mode;
251
+ break;
252
+ }
253
+ }
254
+ return firstBreakoutMode;
255
+ }
256
+ } else {
257
+ // Without multi-select support, we can assume source content is of type PMNode
258
+ if (content instanceof PMNode) {
259
+ var _content$marks$find2;
260
+ return (_content$marks$find2 = content.marks.find(function (m) {
261
+ return m.type === breakout;
262
+ })) === null || _content$marks$find2 === void 0 ? void 0 : _content$marks$find2.attrs.mode;
263
+ }
264
+ }
265
+ };
266
+
267
+ // TODO: As part of platform_editor_element_drag_and_drop_multiselect clean up,
268
+ // source content variable that has type of `PMNode | Fragment` should be updated to `Fragment` only
138
269
  export var moveToLayout = function moveToLayout(api) {
139
270
  return function (from, to, options) {
140
- return function (_ref6) {
141
- var tr = _ref6.tr;
271
+ return function (_ref7) {
272
+ var tr = _ref7.tr;
142
273
  var canMove = canMoveToLayout(from, to, tr);
143
274
  if (!canMove) {
144
275
  return tr;
145
276
  }
146
277
  var toNode = canMove.toNode,
147
- fromNode = canMove.fromNode,
148
- $from = canMove.$from,
149
- $to = canMove.$to;
150
- var _ref7 = tr.doc.type.schema.nodes || {},
151
- layoutSection = _ref7.layoutSection,
152
- layoutColumn = _ref7.layoutColumn;
153
- var _ref8 = tr.doc.type.schema.marks || {},
154
- breakout = _ref8.breakout;
155
- var fromNodeWithoutBreakout = fromNode;
156
- var getBreakoutMode = function getBreakoutMode(node) {
157
- var _node$marks$find;
158
- return (_node$marks$find = node.marks.find(function (m) {
159
- return m.type === breakout;
160
- })) === null || _node$marks$find === void 0 ? void 0 : _node$marks$find.attrs.mode;
161
- };
278
+ $to = canMove.$to,
279
+ sourceContent = canMove.sourceContent,
280
+ $sourceFrom = canMove.$sourceFrom,
281
+ sourceTo = canMove.sourceTo;
282
+ var _ref8 = tr.doc.type.schema.nodes || {},
283
+ layoutSection = _ref8.layoutSection,
284
+ layoutColumn = _ref8.layoutColumn;
285
+ var _ref9 = tr.doc.type.schema.marks || {},
286
+ breakout = _ref9.breakout;
287
+
162
288
  // get breakout mode from destination node,
163
289
  // if not found, get from source node,
164
- var breakoutMode = getBreakoutMode(toNode) || getBreakoutMode(fromNode);
290
+ var breakoutMode = getBreakoutMode(toNode, breakout) || getBreakoutMode(sourceContent, breakout);
165
291
 
166
- // remove breakout from node;
167
- if (breakout && $from.nodeAfter && $from.nodeAfter.marks.some(function (m) {
168
- return m.type === breakout;
169
- })) {
170
- tr.removeNodeMark(from, breakout);
171
- // resolve again the source node after node updated (remove breakout marks)
172
- fromNodeWithoutBreakout = tr.doc.resolve(from).nodeAfter;
173
- }
174
- if (!fromNodeWithoutBreakout) {
292
+ // remove breakout from source content
293
+ var fromContentWithoutBreakout = removeBreakoutMarks(tr, $sourceFrom, sourceTo);
294
+ if (!fromContentWithoutBreakout) {
175
295
  return tr;
176
296
  }
177
297
  if (toNode.type === layoutSection) {
178
298
  var toPos = options !== null && options !== void 0 && options.moveToEnd ? to + toNode.nodeSize - 1 : to + 1;
179
- return moveToExistingLayout(toNode, to, fromNodeWithoutBreakout, from, toPos, tr, $from, $to, api);
299
+ return moveToExistingLayout(toNode, to, fromContentWithoutBreakout, $sourceFrom.pos, toPos, tr, $sourceFrom, $to, api);
180
300
  } else if (toNode.type === layoutColumn) {
181
301
  var toLayout = $to.parent;
182
302
  var toLayoutPos = to - $to.parentOffset - 1;
183
303
  var _toPos = options !== null && options !== void 0 && options.moveToEnd ? to + toNode.nodeSize : to;
184
- return moveToExistingLayout(toLayout, toLayoutPos, fromNodeWithoutBreakout, from, _toPos, tr, $from, $to, api);
304
+ return moveToExistingLayout(toLayout, toLayoutPos, fromContentWithoutBreakout, $sourceFrom.pos, _toPos, tr, $sourceFrom, $to, api);
185
305
  } else {
186
306
  var toNodeWithoutBreakout = toNode;
187
307
 
@@ -193,13 +313,19 @@ export var moveToLayout = function moveToLayout(api) {
193
313
  // resolve again the source node after node updated (remove breakout marks)
194
314
  toNodeWithoutBreakout = tr.doc.resolve(to).nodeAfter || toNode;
195
315
  }
196
- if (fromNodeWithoutBreakout.type.name === 'layoutColumn') {
197
- fromNodeWithoutBreakout = fromNodeWithoutBreakout.content;
316
+ if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
317
+ if (isFragmentOfType(fromContentWithoutBreakout, 'layoutColumn') && fromContentWithoutBreakout.firstChild) {
318
+ fromContentWithoutBreakout = fromContentWithoutBreakout.firstChild.content;
319
+ }
320
+ } else {
321
+ if (fromContentWithoutBreakout instanceof PMNode && fromContentWithoutBreakout.type.name === 'layoutColumn') {
322
+ fromContentWithoutBreakout = fromContentWithoutBreakout.content;
323
+ }
198
324
  }
199
- var layoutContents = options !== null && options !== void 0 && options.moveToEnd ? [toNodeWithoutBreakout, fromNodeWithoutBreakout] : [fromNodeWithoutBreakout, toNodeWithoutBreakout];
325
+ var layoutContents = options !== null && options !== void 0 && options.moveToEnd ? [toNodeWithoutBreakout, fromContentWithoutBreakout] : [fromContentWithoutBreakout, toNodeWithoutBreakout];
200
326
  var newLayout = createNewLayout(tr.doc.type.schema, layoutContents);
201
327
  if (newLayout) {
202
- tr = removeFromSource(tr, $from);
328
+ tr = removeFromSource(tr, $sourceFrom, sourceTo);
203
329
  var mappedTo = tr.mapping.map(to);
204
330
  tr.delete(mappedTo, mappedTo + toNodeWithoutBreakout.nodeSize).insert(mappedTo, newLayout);
205
331
  if (!fg('platform_editor_advanced_layouts_post_fix_patch_1')) {
@@ -20,10 +20,7 @@ export var shouldDescendIntoNode = function shouldDescendIntoNode(node) {
20
20
  }
21
21
  return !IGNORE_NODE_DESCENDANTS.includes(node.type.name);
22
22
  };
23
- var shouldIgnoreNode = function shouldIgnoreNode(node, ignore_nodes, depth, parent
24
- // Ignored via go/ees005
25
- // eslint-disable-next-line @typescript-eslint/max-params
26
- ) {
23
+ var shouldIgnoreNode = function shouldIgnoreNode(node, ignore_nodes, depth, parent) {
27
24
  var isEmbedCard = node.type.name === 'embedCard';
28
25
  var isMediaSingle = node.type.name === 'mediaSingle';
29
26
  var isFirstTableRow = (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'table' && depth === 1 && node === parent.firstChild && 'tableRow' === node.type.name && editorExperiment('advanced_layouts', true);
@@ -67,8 +64,6 @@ export var nodeDecorations = function nodeDecorations(newState, from, to) {
67
64
  var docFrom = from === undefined || from < 0 ? 0 : from;
68
65
  var docTo = to === undefined || to > newState.doc.nodeSize - 2 ? newState.doc.nodeSize - 2 : to;
69
66
  var ignore_nodes = editorExperiment('advanced_layouts', true) ? IGNORE_NODES_NEXT : IGNORE_NODES;
70
- // Ignored via go/ees005
71
- // eslint-disable-next-line @typescript-eslint/max-params
72
67
  newState.doc.nodesBetween(docFrom, docTo, function (node, pos, parent, index) {
73
68
  var depth = 0;
74
69
  var anchorName;
@@ -22,10 +22,7 @@ export var findHandleDec = function findHandleDec(decorations, from, to) {
22
22
  return spec.type === TYPE_HANDLE_DEC;
23
23
  });
24
24
  };
25
- export var dragHandleDecoration = function dragHandleDecoration(api, formatMessage, pos, anchorName, nodeType, nodeViewPortalProviderAPI, handleOptions
26
- // Ignored via go/ees005
27
- // eslint-disable-next-line @typescript-eslint/max-params
28
- ) {
25
+ export var dragHandleDecoration = function dragHandleDecoration(api, formatMessage, pos, anchorName, nodeType, nodeViewPortalProviderAPI, handleOptions) {
29
26
  unmountDecorations(nodeViewPortalProviderAPI, 'data-blocks-drag-handle-container', 'data-blocks-drag-handle-key');
30
27
  var unbind;
31
28
  var key = uuid();
@@ -13,7 +13,7 @@ import { DropTarget, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP, EDITOR_BLOCK_CONT
13
13
  import { DropTargetLayout } from '../ui/drop-target-layout';
14
14
  import { getNestedDepth, TYPE_DROP_TARGET_DEC, unmountDecorations } from './decorations-common';
15
15
  import { maxLayoutColumnSupported } from './utils/consts';
16
- import { canMoveNodeToIndex, isInSameLayout } from './utils/validation';
16
+ import { canMoveNodeToIndex, canMoveSliceToIndex, isInSameLayout } from './utils/validation';
17
17
  var IGNORE_NODES = ['tableCell', 'tableHeader', 'tableRow', 'layoutColumn', 'listItem', 'caption'];
18
18
  var PARENT_WITH_END_DROP_TARGET = ['tableCell', 'tableHeader', 'panel', 'layoutColumn', 'expand', 'nestedExpand', 'bodiedExtension'];
19
19
  var DISABLE_CHILD_DROP_TARGET = ['orderedList', 'bulletList'];
@@ -76,10 +76,7 @@ export var findDropTargetDecs = function findDropTargetDecs(decorations, from, t
76
76
  return spec.type === TYPE_DROP_TARGET_DEC;
77
77
  });
78
78
  };
79
- export var createDropTargetDecoration = function createDropTargetDecoration(pos, props, nodeViewPortalProviderAPI, side, anchorRectCache, isSameLayout
80
- // Ignored via go/ees005
81
- // eslint-disable-next-line @typescript-eslint/max-params
82
- ) {
79
+ export var createDropTargetDecoration = function createDropTargetDecoration(pos, props, nodeViewPortalProviderAPI, side, anchorRectCache, isSameLayout) {
83
80
  var key = uuid();
84
81
  return Decoration.widget(pos, function (_, getPosUnsafe) {
85
82
  var getPos = function getPos() {
@@ -120,10 +117,7 @@ export var createDropTargetDecoration = function createDropTargetDecoration(pos,
120
117
  side: side
121
118
  });
122
119
  };
123
- export var createLayoutDropTargetDecoration = function createLayoutDropTargetDecoration(pos, props, nodeViewPortalProviderAPI, anchorRectCache
124
- // Ignored via go/ees005
125
- // eslint-disable-next-line @typescript-eslint/max-params
126
- ) {
120
+ export var createLayoutDropTargetDecoration = function createLayoutDropTargetDecoration(pos, props, nodeViewPortalProviderAPI, anchorRectCache) {
127
121
  var key = uuid();
128
122
  return Decoration.widget(pos, function (_, getPosUnsafe) {
129
123
  var getPos = function getPos() {
@@ -155,10 +149,7 @@ export var createLayoutDropTargetDecoration = function createLayoutDropTargetDec
155
149
  type: TYPE_DROP_TARGET_DEC
156
150
  });
157
151
  };
158
- export var dropTargetDecorations = function dropTargetDecorations(newState, api, formatMessage, nodeViewPortalProviderAPI, activeNode, anchorRectCache, from, to
159
- // Ignored via go/ees005
160
- // eslint-disable-next-line @typescript-eslint/max-params
161
- ) {
152
+ export var dropTargetDecorations = function dropTargetDecorations(newState, api, formatMessage, nodeViewPortalProviderAPI, activeNode, anchorRectCache, from, to) {
162
153
  unmountDecorations(nodeViewPortalProviderAPI, 'data-blocks-drop-target-container', 'data-blocks-drop-target-key');
163
154
  var decs = [];
164
155
  var POS_END_OF_DOC = newState.doc.nodeSize - 2;
@@ -167,6 +158,9 @@ export var dropTargetDecorations = function dropTargetDecorations(newState, api,
167
158
  var activeNodePos = activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos;
168
159
  var $activeNodePos = typeof activeNodePos === 'number' && newState.doc.resolve(activeNodePos);
169
160
  var activePMNode = $activeNodePos && $activeNodePos.nodeAfter;
161
+ var isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
162
+ exposure: true
163
+ });
170
164
  anchorRectCache === null || anchorRectCache === void 0 || anchorRectCache.clear();
171
165
  var prevNodeStack = [];
172
166
  var popNodeStack = function popNodeStack(depth) {
@@ -182,9 +176,6 @@ export var dropTargetDecorations = function dropTargetDecorations(newState, api,
182
176
  prevNodeStack.push(node);
183
177
  };
184
178
  var isAdvancedLayoutsPreRelease2 = editorExperiment('advanced_layouts', true);
185
-
186
- // Ignored via go/ees005
187
- // eslint-disable-next-line @typescript-eslint/max-params
188
179
  newState.doc.nodesBetween(docFrom, docTo, function (node, pos, parent, index) {
189
180
  var depth = 0;
190
181
  // drop target deco at the end position
@@ -216,12 +207,36 @@ export var dropTargetDecorations = function dropTargetDecorations(newState, api,
216
207
  pushNodeStack(node, depth);
217
208
  return shouldDescend(node); //skip over, don't consider it a valid depth
218
209
  }
219
- var canDrop = activePMNode && canMoveNodeToIndex(parent, index, activePMNode, $pos, node);
220
210
 
221
- //NOTE: This will block drop targets showing for nodes that are valid after transformation (i.e. expand -> nestedExpand)
222
- if (!canDrop) {
223
- pushNodeStack(node, depth);
224
- return false; //not valid pos, so nested not valid either
211
+ // When multi select is on, validate all the nodes in the selection instead of just the handle node
212
+ if (isMultiSelect) {
213
+ var selection = newState.selection;
214
+ var selectionFrom = selection.$from.pos;
215
+ var selectionTo = selection.$to.pos;
216
+ var handleInsideSelection = activeNodePos !== undefined && activeNodePos >= selectionFrom - 1 && activeNodePos <= selectionTo;
217
+ var selectionSlice = newState.doc.slice(selectionFrom, selectionTo, false);
218
+ var selectionSliceChildCount = selectionSlice.content.childCount;
219
+ var canDropSingleNode = true;
220
+ var canDropMultipleNodes = true;
221
+
222
+ // when there is only one node in the slice, use the same logic as when multi select is not on
223
+ if (selectionSliceChildCount > 1 && handleInsideSelection) {
224
+ canDropMultipleNodes = canMoveSliceToIndex(selectionSlice, selectionFrom, newState.doc, parent, index, $pos);
225
+ } else {
226
+ canDropSingleNode = !!(activePMNode && canMoveNodeToIndex(parent, index, activePMNode, $pos, node));
227
+ }
228
+ if (!canDropMultipleNodes || !canDropSingleNode) {
229
+ pushNodeStack(node, depth);
230
+ return false; //not valid pos, so nested not valid either
231
+ }
232
+ } else {
233
+ var canDrop = activePMNode && canMoveNodeToIndex(parent, index, activePMNode, $pos, node);
234
+
235
+ //NOTE: This will block drop targets showing for nodes that are valid after transformation (i.e. expand -> nestedExpand)
236
+ if (!canDrop) {
237
+ pushNodeStack(node, depth);
238
+ return false; //not valid pos, so nested not valid either
239
+ }
225
240
  }
226
241
  if (parent.lastChild === node && !isEmptyParagraph(node) && PARENT_WITH_END_DROP_TARGET.includes(parent.type.name)) {
227
242
  endPos = pos + node.nodeSize;
@@ -110,10 +110,7 @@ var initialState = {
110
110
  isDocSizeLimitEnabled: null,
111
111
  isPMDragging: false
112
112
  };
113
- export var newApply = function newApply(api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache
114
- // Ignored via go/ees005
115
- // eslint-disable-next-line @typescript-eslint/max-params
116
- ) {
113
+ export var newApply = function newApply(api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache) {
117
114
  var _meta$activeNode, _activeNode, _activeNode2, _meta$activeNode$hand, _meta$isDragging, _meta$isDragging2, _meta$editorHeight, _meta$editorWidthLeft, _meta$editorWidthRigh, _meta$isPMDragging;
118
115
  var activeNode = currentState.activeNode,
119
116
  decorations = currentState.decorations,
@@ -244,10 +241,7 @@ export var newApply = function newApply(api, formatMessage, tr, currentState, ne
244
241
  isPMDragging: (_meta$isPMDragging = meta === null || meta === void 0 ? void 0 : meta.isPMDragging) !== null && _meta$isPMDragging !== void 0 ? _meta$isPMDragging : isPMDragging
245
242
  };
246
243
  };
247
- export var oldApply = function oldApply(api, formatMessage, tr, currentState, oldState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache
248
- // Ignored via go/ees005
249
- // eslint-disable-next-line @typescript-eslint/max-params
250
- ) {
244
+ export var oldApply = function oldApply(api, formatMessage, tr, currentState, oldState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache) {
251
245
  var _meta$activeNode2, _meta$activeNode$hand2, _meta$activeNode8, _meta$isDragging4, _meta$editorHeight2, _meta$editorWidthLeft2, _meta$editorWidthRigh2, _meta$isPMDragging2;
252
246
  var isNestedEnabled = flags.isNestedEnabled;
253
247
  var activeNode = currentState.activeNode,
@@ -431,8 +425,6 @@ export var createPlugin = function createPlugin(api, getIntl, nodeViewPortalProv
431
425
  init: function init() {
432
426
  return initialState;
433
427
  },
434
- // Ignored via go/ees005
435
- // eslint-disable-next-line @typescript-eslint/max-params
436
428
  apply: function apply(tr, currentState, oldState, newState) {
437
429
  if (isNestedEnabled) {
438
430
  return newApply(api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache);