@atlaskit/editor-plugin-block-controls 3.0.1 → 3.1.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/blockControlsPlugin.js +27 -14
  3. package/dist/cjs/editor-commands/move-node.js +15 -18
  4. package/dist/cjs/pm-plugins/main.js +73 -34
  5. package/dist/cjs/pm-plugins/utils/getSelection.js +1 -1
  6. package/dist/cjs/pm-plugins/utils/selection.js +34 -4
  7. package/dist/cjs/ui/consts.js +9 -1
  8. package/dist/cjs/ui/drag-handle.js +68 -35
  9. package/dist/es2019/blockControlsPlugin.js +27 -14
  10. package/dist/es2019/editor-commands/move-node.js +17 -20
  11. package/dist/es2019/pm-plugins/main.js +70 -23
  12. package/dist/es2019/pm-plugins/utils/getSelection.js +1 -1
  13. package/dist/es2019/pm-plugins/utils/selection.js +33 -3
  14. package/dist/es2019/ui/consts.js +8 -0
  15. package/dist/es2019/ui/drag-handle.js +58 -29
  16. package/dist/esm/blockControlsPlugin.js +27 -14
  17. package/dist/esm/editor-commands/move-node.js +16 -19
  18. package/dist/esm/pm-plugins/main.js +71 -32
  19. package/dist/esm/pm-plugins/utils/getSelection.js +1 -1
  20. package/dist/esm/pm-plugins/utils/selection.js +33 -3
  21. package/dist/esm/ui/consts.js +8 -0
  22. package/dist/esm/ui/drag-handle.js +69 -36
  23. package/dist/types/blockControlsPluginType.d.ts +2 -0
  24. package/dist/types/pm-plugins/main.d.ts +2 -1
  25. package/dist/types/pm-plugins/utils/selection.d.ts +13 -1
  26. package/dist/types/ui/consts.d.ts +7 -0
  27. package/dist/types-ts4.5/blockControlsPluginType.d.ts +2 -0
  28. package/dist/types-ts4.5/pm-plugins/main.d.ts +2 -1
  29. package/dist/types-ts4.5/pm-plugins/utils/selection.d.ts +13 -1
  30. package/dist/types-ts4.5/ui/consts.d.ts +7 -0
  31. package/package.json +2 -2
@@ -1,3 +1,4 @@
1
+ import { TextSelection, NodeSelection } from '@atlaskit/editor-prosemirror/state';
1
2
  import { key } from '../main';
2
3
  export const getMultiSelectionIfPosInside = (api, pos, tr) => {
3
4
  var _api$blockControls, _pluginState$multiSel, _tr$getMeta;
@@ -10,7 +11,7 @@ export const getMultiSelectionIfPosInside = (api, pos, tr) => {
10
11
  const multiTo = Math.max(multiSelectDnD.anchor, multiSelectDnD.head);
11
12
 
12
13
  // We subtract one as the handle position is before the node
13
- return pos >= multiFrom - 1 && pos <= multiTo ? {
14
+ return pos >= multiFrom - 1 && pos < multiTo ? {
14
15
  anchor: multiSelectDnD.anchor,
15
16
  head: multiSelectDnD.head
16
17
  } : {};
@@ -29,12 +30,41 @@ export const getSelectedSlicePosition = (handlePos, tr, api) => {
29
30
  head
30
31
  } = getMultiSelectionIfPosInside(api, handlePos, tr);
31
32
  const inSelection = anchor !== undefined && head !== undefined;
32
- const from = inSelection ? Math.min(anchor, head) : handlePos;
33
+ const from = inSelection ? Math.min(anchor || 0, head || 0) : handlePos;
33
34
  const activeNode = tr.doc.nodeAt(handlePos);
34
35
  const activeNodeEndPos = handlePos + ((_activeNode$nodeSize = activeNode === null || activeNode === void 0 ? void 0 : activeNode.nodeSize) !== null && _activeNode$nodeSize !== void 0 ? _activeNode$nodeSize : 1);
35
- const to = inSelection ? Math.max(anchor, head) : activeNodeEndPos;
36
+ const to = inSelection ? Math.max(anchor || 0, head || 0) : activeNodeEndPos;
36
37
  return {
37
38
  from,
38
39
  to
39
40
  };
41
+ };
42
+
43
+ /**
44
+ * Takes a position and expands the selection to encompass the node at that position. Ignores empty or out of range selections.
45
+ * Ignores positions that are in text blocks (i.e. not start of a node)
46
+ * @returns TextSelection if expanded, otherwise returns Selection that was passed in.
47
+ */
48
+ export const expandSelectionHeadToNodeAtPos = (selection, nodePos) => {
49
+ const doc = selection.$anchor.doc;
50
+ if (nodePos < 0 || nodePos > doc.nodeSize - 2 || selection.empty) {
51
+ return selection;
52
+ }
53
+ const $pos = doc.resolve(nodePos);
54
+ const node = $pos.nodeAfter;
55
+ if ($pos.node().isTextblock || !node) {
56
+ return selection;
57
+ }
58
+ const $newHead = nodePos < selection.anchor ? $pos : doc.resolve(node.nodeSize + nodePos);
59
+ const textSelection = new TextSelection(selection.$anchor, $newHead);
60
+ return textSelection;
61
+ };
62
+
63
+ /**
64
+ * This swaps the anchor/head for NodeSelections when its anchor > pos.
65
+ * This is because NodeSelection always has an anchor at the start of the node,
66
+ * which may not align with the existing selection.
67
+ */
68
+ export const alignAnchorHeadInDirectionOfPos = (selection, pos) => {
69
+ return selection instanceof NodeSelection && Math.max(pos, selection.anchor) === selection.anchor ? new TextSelection(selection.$head, selection.$anchor) : selection;
40
70
  };
@@ -18,6 +18,14 @@ export const DRAG_HANDLE_H5_TOP_ADJUSTMENT = 3;
18
18
  export const DRAG_HANDLE_H6_TOP_ADJUSTMENT = 3;
19
19
  export const DRAG_HANDLE_LAYOUT_SECTION_TOP_ADJUSTMENT = 8;
20
20
  export const DRAG_HANDLE_PARAGRAPH_TOP_ADJUSTMENT = 2;
21
+
22
+ /** We only want to shift-select nodes that are at the top level of a document.
23
+ * This is because funky things happen when selecting inside of tableCells, but we
24
+ * also want to avoid heavily nested cases to descope potential corner cases.
25
+ * Various top level nodes have their selection 'from' at depths other than 0,
26
+ * so we allow for some leniency to capture them all. e.g. Table is depth 3.
27
+ */
28
+ export const DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH = 3;
21
29
  const nodeTypeExcludeList = ['embedCard', 'mediaSingle', 'table'];
22
30
  export const dragHandleGap = (nodeType, parentNodeType) => {
23
31
  if (nodeType === 'layoutSection' && fg('platform_editor_advanced_layouts_post_fix_patch_2')) {
@@ -25,7 +25,8 @@ import { key } from '../pm-plugins/main';
25
25
  import { getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics';
26
26
  import { getLeftPosition, getTopPosition } from '../pm-plugins/utils/drag-handle-positions';
27
27
  import { isHandleCorrelatedToSelection, selectNode } from '../pm-plugins/utils/getSelection';
28
- import { DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_WIDTH, DRAG_HANDLE_ZINDEX, dragHandleGap, nodeMargins, spacingBetweenNodesForPreview, topPositionAdjustment } from './consts';
28
+ import { alignAnchorHeadInDirectionOfPos, expandSelectionHeadToNodeAtPos } from '../pm-plugins/utils/selection';
29
+ import { DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH, DRAG_HANDLE_WIDTH, DRAG_HANDLE_ZINDEX, dragHandleGap, nodeMargins, spacingBetweenNodesForPreview, topPositionAdjustment } from './consts';
29
30
  import { dragPreview } from './drag-preview';
30
31
  const iconWrapperStyles = xcss({
31
32
  display: 'flex',
@@ -61,6 +62,13 @@ const dragHandleButtonStyles = css({
61
62
  },
62
63
  '&:focus': {
63
64
  outline: `2px solid ${"var(--ds-border-focused, #388BFF)"}`
65
+ },
66
+ '&:disabled': {
67
+ color: "var(--ds-icon-disabled, #8993A4)",
68
+ backgroundColor: 'transparent'
69
+ },
70
+ '&:hover:disabled': {
71
+ backgroundColor: "var(--ds-background-disabled, transparent)"
64
72
  }
65
73
  });
66
74
  const dragHandleMultiLineSelectionFixFirefox = css({
@@ -116,15 +124,18 @@ export const DragHandle = ({
116
124
  handleOptions,
117
125
  isTopLevelNode = true
118
126
  }) => {
119
- var _api$core2, _api$analytics2, _api$core4;
127
+ var _api$core2, _api$analytics2, _api$blockControls3, _api$core4;
120
128
  const start = getPos();
121
129
  const buttonRef = useRef(null);
122
130
  const [blockCardWidth, setBlockCardWidth] = useState(768);
123
131
  const [dragHandleSelected, setDragHandleSelected] = useState(false);
132
+ const [dragHandleDisabled, setDragHandleDisabled] = useState(false);
124
133
  const {
125
134
  featureFlagsState
126
135
  } = useSharedPluginState(api, ['featureFlags']);
127
136
  const selection = useSharedPluginStateSelector(api, 'selection.selection');
137
+ const isShiftDown = useSharedPluginStateSelector(api, 'blockControls.isShiftDown');
138
+ const multiSelectDnD = useSharedPluginStateSelector(api, 'blockControls.multiSelectDnD');
128
139
  const isLayoutColumn = nodeType === 'layoutColumn';
129
140
  const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
130
141
  exposure: true
@@ -144,9 +155,11 @@ export const DragHandle = ({
144
155
  }
145
156
  }
146
157
  }, [anchorName, nodeType, view.dom]);
147
- const handleOnClick = useCallback(() => {
158
+ const handleOnClick = useCallback(e => {
148
159
  var _api$core;
149
- setDragHandleSelected(!dragHandleSelected);
160
+ if (!isMultiSelect) {
161
+ setDragHandleSelected(!dragHandleSelected);
162
+ }
150
163
  api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
151
164
  tr
152
165
  }) => {
@@ -155,7 +168,24 @@ export const DragHandle = ({
155
168
  if (startPos === undefined) {
156
169
  return tr;
157
170
  }
158
- tr = selectNode(tr, startPos, nodeType);
171
+ const $anchor = (multiSelectDnD === null || multiSelectDnD === void 0 ? void 0 : multiSelectDnD.anchor) !== undefined ? tr.doc.resolve(multiSelectDnD === null || multiSelectDnD === void 0 ? void 0 : multiSelectDnD.anchor) : tr.selection.$anchor;
172
+ if (!isMultiSelect || tr.selection.empty || !e.shiftKey) {
173
+ tr = selectNode(tr, startPos, nodeType);
174
+ if (editorExperiment('platform_editor_controls', 'variant1')) {
175
+ var _api$blockControls;
176
+ api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.commands.toggleBlockMenu()({
177
+ tr
178
+ });
179
+ }
180
+ } else if (isTopLevelNode && $anchor.depth <= DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH && e.shiftKey) {
181
+ var _api$blockControls2;
182
+ const alignAnchorHeadToSel = alignAnchorHeadInDirectionOfPos(tr.selection, startPos);
183
+ const selectionWithExpandedHead = expandSelectionHeadToNodeAtPos(alignAnchorHeadToSel, startPos);
184
+ tr.setSelection(selectionWithExpandedHead);
185
+ api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.commands.setMultiSelectPositions()({
186
+ tr
187
+ });
188
+ }
159
189
  const resolvedMovingNode = tr.doc.resolve(startPos);
160
190
  const maybeNode = resolvedMovingNode.nodeAfter;
161
191
  tr.setMeta('scrollIntoView', false);
@@ -172,14 +202,7 @@ export const DragHandle = ({
172
202
  return tr;
173
203
  });
174
204
  view.focus();
175
- if (editorExperiment('platform_editor_controls', 'variant1')) {
176
- const startPos = getPos();
177
- if (startPos === undefined) {
178
- return false;
179
- }
180
- api === null || api === void 0 ? void 0 : api.core.actions.execute(api.blockControls.commands.toggleBlockMenu());
181
- }
182
- }, [dragHandleSelected, api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions, api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, api === null || api === void 0 ? void 0 : api.blockControls.commands, view, getPos, nodeType]);
205
+ }, [isMultiSelect, api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions, api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, api === null || api === void 0 ? void 0 : (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 ? void 0 : _api$blockControls3.commands, view, dragHandleSelected, getPos, multiSelectDnD === null || multiSelectDnD === void 0 ? void 0 : multiSelectDnD.anchor, isTopLevelNode, nodeType]);
183
206
 
184
207
  // TODO - This needs to be investigated further. Drag preview generation is not always working
185
208
  // as expected with a node selection. This workaround sets the selection to the node on mouseDown,
@@ -199,7 +222,6 @@ export const DragHandle = ({
199
222
  const handleKeyDown = useCallback(e => {
200
223
  if (fg('platform_editor_element_drag_and_drop_ed_23873')) {
201
224
  // allow user to use spacebar to select the node
202
-
203
225
  if (!e.repeat && e.key === ' ') {
204
226
  var _api$core3;
205
227
  const startPos = getPos();
@@ -216,9 +238,9 @@ export const DragHandle = ({
216
238
  const $startPos = tr.doc.resolve(startPos + node.nodeSize);
217
239
  const selection = new TextSelection($startPos);
218
240
  tr.setSelection(selection);
219
- tr.setMeta(key, {
241
+ !isMultiSelect && tr.setMeta(key, {
220
242
  pos: startPos
221
- }); ////WHERE IS THIS USED?
243
+ });
222
244
  return tr;
223
245
  });
224
246
  } else if (![e.altKey, e.ctrlKey, e.shiftKey].some(pressed => pressed)) {
@@ -227,7 +249,7 @@ export const DragHandle = ({
227
249
  view.focus();
228
250
  }
229
251
  }
230
- }, [getPos, api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions, view]);
252
+ }, [getPos, api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions, isMultiSelect, view]);
231
253
  useEffect(() => {
232
254
  const element = buttonRef.current;
233
255
  if (!element) {
@@ -242,7 +264,6 @@ export const DragHandle = ({
242
264
  onGenerateDragPreview: ({
243
265
  nativeSetDragImage
244
266
  }) => {
245
- var _api$blockControls2;
246
267
  if (isMultiSelect) {
247
268
  var _api$core5;
248
269
  api === null || api === void 0 ? void 0 : (_api$core5 = api.core) === null || _api$core5 === void 0 ? void 0 : _api$core5.actions.execute(({
@@ -253,8 +274,8 @@ export const DragHandle = ({
253
274
  return tr;
254
275
  }
255
276
  if (!tr.selection.empty && handlePos >= tr.selection.$from.start() - 1 && handlePos <= tr.selection.to) {
256
- var _api$blockControls;
257
- api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.commands.setMultiSelectPositions()({
277
+ var _api$blockControls4;
278
+ api === null || api === void 0 ? void 0 : (_api$blockControls4 = api.blockControls) === null || _api$blockControls4 === void 0 ? void 0 : _api$blockControls4.commands.setMultiSelectPositions()({
258
279
  tr
259
280
  });
260
281
  }
@@ -267,9 +288,6 @@ export const DragHandle = ({
267
288
  doc,
268
289
  selection
269
290
  } = state;
270
- const {
271
- multiSelectDnD
272
- } = (api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.sharedState.currentState()) || {};
273
291
  let sliceFrom = selection.from;
274
292
  let sliceTo = selection.to;
275
293
  if (multiSelectDnD) {
@@ -281,7 +299,7 @@ export const DragHandle = ({
281
299
  sliceTo = Math.max(anchor, head);
282
300
  }
283
301
  const expandedSlice = doc.slice(sliceFrom, sliceTo);
284
- const isDraggingMultiLine = isMultiSelect && startPos !== undefined && startPos >= sliceFrom && startPos <= sliceTo && expandedSlice.content.childCount > 1;
302
+ const isDraggingMultiLine = isMultiSelect && startPos !== undefined && startPos >= sliceFrom && startPos < sliceTo && expandedSlice.content.childCount > 1;
285
303
  setCustomNativeDragPreview({
286
304
  getOffset: () => {
287
305
  if (!isDraggingMultiLine) {
@@ -369,11 +387,10 @@ export const DragHandle = ({
369
387
  api === null || api === void 0 ? void 0 : (_api$core6 = api.core) === null || _api$core6 === void 0 ? void 0 : _api$core6.actions.execute(({
370
388
  tr
371
389
  }) => {
372
- var _tr$getMeta, _api$blockControls3, _api$analytics3;
390
+ var _api$blockControls5, _api$analytics3;
373
391
  let nodeTypes, hasSelectedMultipleNodes;
374
392
  const resolvedMovingNode = tr.doc.resolve(start);
375
393
  const maybeNode = resolvedMovingNode.nodeAfter;
376
- const multiSelectDnD = (_tr$getMeta = tr.getMeta(key)) === null || _tr$getMeta === void 0 ? void 0 : _tr$getMeta.multiSelectDnD;
377
394
  if (multiSelectDnD) {
378
395
  const attributes = getMultiSelectAnalyticsAttributes(tr, multiSelectDnD.anchor, multiSelectDnD.head);
379
396
  nodeTypes = attributes.nodeTypes;
@@ -382,7 +399,7 @@ export const DragHandle = ({
382
399
  nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name;
383
400
  hasSelectedMultipleNodes = false;
384
401
  }
385
- api === null || api === void 0 ? void 0 : (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 ? void 0 : _api$blockControls3.commands.setNodeDragged(getPos, anchorName, nodeType)({
402
+ api === null || api === void 0 ? void 0 : (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 ? void 0 : _api$blockControls5.commands.setNodeDragged(getPos, anchorName, nodeType)({
386
403
  tr
387
404
  });
388
405
  tr.setMeta('scrollIntoView', false);
@@ -405,7 +422,7 @@ export const DragHandle = ({
405
422
  view.focus();
406
423
  }
407
424
  });
408
- }, [anchorName, api, getPos, isMultiSelect, nodeType, start, view]);
425
+ }, [anchorName, api, getPos, isMultiSelect, multiSelectDnD, nodeType, start, view]);
409
426
  const macroInteractionUpdates = featureFlagsState === null || featureFlagsState === void 0 ? void 0 : featureFlagsState.macroInteractionUpdates;
410
427
  const calculatePosition = useCallback(() => {
411
428
  let parentNodeType;
@@ -492,6 +509,17 @@ export const DragHandle = ({
492
509
  }
493
510
  setDragHandleSelected(isHandleCorrelatedToSelection(view.state, selection, start));
494
511
  }, [start, selection, view.state, isMultiSelect]);
512
+ useEffect(() => {
513
+ if (!isMultiSelect || isShiftDown === undefined || view.state.selection.empty) {
514
+ return;
515
+ }
516
+ const $anchor = (multiSelectDnD === null || multiSelectDnD === void 0 ? void 0 : multiSelectDnD.anchor) !== undefined ? view.state.doc.resolve(multiSelectDnD === null || multiSelectDnD === void 0 ? void 0 : multiSelectDnD.anchor) : view.state.selection.$anchor;
517
+ if (isShiftDown && (!isTopLevelNode || isTopLevelNode && $anchor.depth > DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH)) {
518
+ setDragHandleDisabled(true);
519
+ } else {
520
+ setDragHandleDisabled(false);
521
+ }
522
+ }, [isMultiSelect, isShiftDown, isTopLevelNode, multiSelectDnD === null || multiSelectDnD === void 0 ? void 0 : multiSelectDnD.anchor, view.state.doc, view.state.selection]);
495
523
  let helpDescriptors = isTopLevelNode && fg('platform_editor_advanced_layouts_accessibility') ? [{
496
524
  description: formatMessage(blockControlsMessages.dragToMove)
497
525
  }, {
@@ -578,6 +606,7 @@ export const DragHandle = ({
578
606
  // eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop
579
607
  ,
580
608
  onDrop: handleOnDrop,
609
+ disabled: dragHandleDisabled,
581
610
  "data-testid": "block-ctrl-drag-handle"
582
611
  }, jsx(Box, {
583
612
  as: "span",
@@ -589,7 +618,7 @@ export const DragHandle = ({
589
618
  label: "",
590
619
  size: "medium"
591
620
  })));
592
- return fg('platform_editor_element_drag_and_drop_ed_23873') ? jsx(Tooltip, {
621
+ return !dragHandleDisabled && fg('platform_editor_element_drag_and_drop_ed_23873') ? jsx(Tooltip, {
593
622
  content: jsx(TooltipContentWithMultipleShortcuts, {
594
623
  helpDescriptors: helpDescriptors
595
624
  }),
@@ -8,6 +8,7 @@ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
8
8
  import { moveNode } from './editor-commands/move-node';
9
9
  import { moveToLayout } from './editor-commands/move-to-layout';
10
10
  import { createPlugin, key } from './pm-plugins/main';
11
+ import { selectNode } from './pm-plugins/utils/getSelection';
11
12
  import BlockMenu from './ui/block-menu';
12
13
  import { DragHandleMenu } from './ui/drag-handle-menu';
13
14
  import { GlobalStylesWrapper } from './ui/global-styles';
@@ -81,29 +82,40 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
81
82
  },
82
83
  setMultiSelectPositions: function setMultiSelectPositions(anchor, head) {
83
84
  return function (_ref6) {
84
- var _api$selection;
85
+ var _api$selection, _$to$nodeBefore, _$from$nodeAfter;
85
86
  var tr = _ref6.tr;
86
87
  var _tr$selection = tr.selection,
87
88
  userAnchor = _tr$selection.anchor,
88
89
  userHead = _tr$selection.head;
89
- var expandedAnchor, expandedHead;
90
+ var $expandedAnchor, $expandedHead;
90
91
  if (anchor !== undefined && head !== undefined) {
91
- expandedAnchor = tr.doc.resolve(anchor);
92
- expandedHead = tr.doc.resolve(head);
92
+ $expandedAnchor = tr.doc.resolve(anchor);
93
+ $expandedHead = tr.doc.resolve(head);
93
94
  } else {
94
95
  var expandedSelection = expandSelectionBounds(tr.selection.$anchor, tr.selection.$head);
95
- expandedAnchor = expandedSelection.$anchor;
96
- expandedHead = expandedSelection.$head;
96
+ $expandedAnchor = expandedSelection.$anchor;
97
+ $expandedHead = expandedSelection.$head;
97
98
  }
98
- api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || _api$selection.commands.setManualSelection(expandedAnchor.pos, expandedHead.pos)({
99
+ api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || _api$selection.commands.setManualSelection($expandedAnchor.pos, $expandedHead.pos)({
99
100
  tr: tr
100
101
  });
101
- // this is to normalise the selection's boundaries to inline positions, preventing it from collapsing
102
- var expandedNormalisedSel = TextSelection.between(expandedAnchor, expandedHead);
103
- tr.setSelection(expandedNormalisedSel);
102
+ var $from = $expandedAnchor.min($expandedHead);
103
+ var $to = $expandedAnchor.max($expandedHead);
104
+ var expandedNormalisedSel;
105
+ if ($from.nodeAfter === $to.nodeBefore) {
106
+ selectNode(tr, $from.pos, $expandedAnchor.node().type.name);
107
+ expandedNormalisedSel = tr.selection;
108
+ } else if (((_$to$nodeBefore = $to.nodeBefore) === null || _$to$nodeBefore === void 0 ? void 0 : _$to$nodeBefore.type.name) === 'mediaSingle' || ((_$from$nodeAfter = $from.nodeAfter) === null || _$from$nodeAfter === void 0 ? void 0 : _$from$nodeAfter.type.name) === 'mediaSingle') {
109
+ expandedNormalisedSel = new TextSelection($expandedAnchor, $expandedHead);
110
+ tr.setSelection(expandedNormalisedSel);
111
+ } else {
112
+ // this is to normalise the selection's boundaries to inline positions, preventing it from collapsing
113
+ expandedNormalisedSel = TextSelection.between($expandedAnchor, $expandedHead);
114
+ tr.setSelection(expandedNormalisedSel);
115
+ }
104
116
  var multiSelectDnD = {
105
- anchor: expandedAnchor.pos,
106
- head: expandedHead.pos,
117
+ anchor: $expandedAnchor.pos,
118
+ head: $expandedHead.pos,
107
119
  textAnchor: expandedNormalisedSel.anchor,
108
120
  textHead: expandedNormalisedSel.head,
109
121
  userAnchor: userAnchor,
@@ -118,7 +130,7 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
118
130
  }
119
131
  },
120
132
  getSharedState: function getSharedState(editorState) {
121
- 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;
133
+ 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, _key$getState$isShift, _key$getState6;
122
134
  if (!editorState) {
123
135
  return undefined;
124
136
  }
@@ -127,7 +139,8 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
127
139
  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,
128
140
  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,
129
141
  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,
130
- 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
142
+ 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,
143
+ isShiftDown: (_key$getState$isShift = (_key$getState6 = key.getState(editorState)) === null || _key$getState6 === void 0 ? void 0 : _key$getState6.isShiftDown) !== null && _key$getState$isShift !== void 0 ? _key$getState$isShift : undefined
131
144
  };
132
145
  },
133
146
  contentComponent: function contentComponent(_ref7) {
@@ -4,7 +4,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
4
4
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
5
5
  import { expandedState } from '@atlaskit/editor-common/expand';
6
6
  import { blockControlsMessages } from '@atlaskit/editor-common/messages';
7
- import { GapCursorSelection, expandSelectionBounds } from '@atlaskit/editor-common/selection';
7
+ import { expandSelectionBounds, GapCursorSelection } from '@atlaskit/editor-common/selection';
8
8
  import { transformSliceNestedExpandToExpand } from '@atlaskit/editor-common/transforms';
9
9
  import { isEmptyParagraph } from '@atlaskit/editor-common/utils';
10
10
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
@@ -54,16 +54,14 @@ var isDragLayoutColumnToTopLevel = function isDragLayoutColumnToTopLevel($from,
54
54
  * @returns the start position of a node if the node can be moved, otherwise -1
55
55
  */
56
56
  var getCurrentNodePos = function getCurrentNodePos(state) {
57
- var _activeNode$handleOpt;
58
57
  var selection = state.selection;
59
- var _ref = key.getState(state) || {},
60
- activeNode = _ref.activeNode;
61
58
  var currentNodePos = -1;
62
59
 
63
60
  // There are 3 cases when a node can be moved
64
- if (activeNode && (_activeNode$handleOpt = activeNode.handleOptions) !== null && _activeNode$handleOpt !== void 0 && _activeNode$handleOpt.isFocused) {
61
+ var focusedHandle = getFocusedHandle(state);
62
+ if (focusedHandle) {
65
63
  // 1. drag handle of the node is focused
66
- currentNodePos = activeNode.pos;
64
+ currentNodePos = focusedHandle.pos;
67
65
  } else if (isInTable(state)) {
68
66
  if (isTableSelected(selection)) {
69
67
  var _findTable$pos, _findTable;
@@ -81,6 +79,12 @@ var getCurrentNodePos = function getCurrentNodePos(state) {
81
79
  }
82
80
  return currentNodePos;
83
81
  };
82
+ var getFocusedHandle = function getFocusedHandle(state) {
83
+ var _activeNode$handleOpt;
84
+ var _ref = key.getState(state) || {},
85
+ activeNode = _ref.activeNode;
86
+ return activeNode && (_activeNode$handleOpt = activeNode.handleOptions) !== null && _activeNode$handleOpt !== void 0 && _activeNode$handleOpt.isFocused ? activeNode : undefined;
87
+ };
84
88
  export var moveNodeViaShortcut = function moveNodeViaShortcut(api, direction, formatMessage) {
85
89
  return function (state) {
86
90
  var isParentNodeOfTypeLayout;
@@ -90,20 +94,14 @@ export var moveNodeViaShortcut = function moveNodeViaShortcut(api, direction, fo
90
94
  isParentNodeOfTypeLayout = !!findParentNodeOfType([state.schema.nodes.layoutSection])(state.selection);
91
95
  }
92
96
  var isMultiSelectEnabled = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true);
93
- var expandedAnchor, expandedHead;
94
- var pluginState = api === null || api === void 0 ? void 0 : api.blockControls.sharedState.currentState();
95
- if (pluginState !== null && pluginState !== void 0 && pluginState.multiSelectDnD) {
96
- expandedAnchor = pluginState.multiSelectDnD.anchor;
97
- expandedHead = pluginState.multiSelectDnD.head;
98
- } else {
99
- var expandedSelection = expandSelectionBounds(selection.$anchor, selection.$head);
100
- expandedAnchor = expandedSelection.$anchor.pos;
101
- expandedHead = expandedSelection.$head.pos;
102
- }
103
- var currentNodePos = isMultiSelectEnabled ? Math.min(expandedAnchor, expandedHead) : getCurrentNodePos(state);
97
+ var expandedSelection = expandSelectionBounds(selection.$anchor, selection.$head);
98
+ var expandedAnchor = expandedSelection.$anchor.pos;
99
+ var expandedHead = expandedSelection.$head.pos;
100
+ var currentNodePos = isMultiSelectEnabled && !getFocusedHandle(state) ? Math.min(expandedAnchor, expandedHead) : getCurrentNodePos(state);
104
101
  if (currentNodePos > -1) {
105
102
  var _state$doc$nodeAt;
106
103
  var $pos = state.doc.resolve(currentNodePos);
104
+ var nodeAfterPos = isMultiSelectEnabled && !getFocusedHandle(state) ? Math.max(expandedAnchor, expandedHead) : $pos.posAtIndex($pos.index() + 1);
107
105
  var isTopLevelNode = $pos.depth === 0;
108
106
  var moveToPos = -1;
109
107
  var nodeIndex = $pos.index();
@@ -211,7 +209,6 @@ export var moveNodeViaShortcut = function moveNodeViaShortcut(api, direction, fo
211
209
  }
212
210
  } else {
213
211
  var _endOfDoc = $pos.end();
214
- var nodeAfterPos = isMultiSelectEnabled ? Math.max(expandedAnchor, expandedHead) : $pos.posAtIndex($pos.index() + 1);
215
212
  if (nodeAfterPos > _endOfDoc) {
216
213
  return false;
217
214
  }
@@ -290,8 +287,8 @@ export var moveNode = function moveNode(api) {
290
287
  var isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
291
288
  exposure: true
292
289
  });
290
+ var slicePosition = getSelectedSlicePosition(start, tr, api);
293
291
  if (isMultiSelect) {
294
- var slicePosition = getSelectedSlicePosition(start, tr, api);
295
292
  sliceFrom = slicePosition.from;
296
293
  sliceTo = slicePosition.to;
297
294
  var attributes = getMultiSelectAnalyticsAttributes(tr, sliceFrom, sliceTo);
@@ -121,10 +121,10 @@ var destroyFn = function destroyFn(api, editorView) {
121
121
  })
122
122
  })(tr);
123
123
  }
124
- return tr.setMeta(key, {
124
+ return tr.setMeta(key, _objectSpread(_objectSpread({}, tr.getMeta(key)), {}, {
125
125
  isDragging: false,
126
126
  isPMDragging: false
127
- });
127
+ }));
128
128
  });
129
129
  }
130
130
  }));
@@ -148,7 +148,7 @@ export var getDecorations = function getDecorations(state) {
148
148
  return (_key$getState = key.getState(state)) === null || _key$getState === void 0 ? void 0 : _key$getState.decorations;
149
149
  };
150
150
  export var newApply = function newApply(api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache) {
151
- var _meta$activeNode, _activeNode, _activeNode2, _meta$activeNode$hand, _meta$isDragging, _meta$isDragging2, _meta$editorHeight, _meta$editorWidthLeft, _meta$editorWidthRigh, _meta$isPMDragging, _meta$multiSelectDnD;
151
+ var _meta$multiSelectDnD, _meta$activeNode, _activeNode, _activeNode2, _meta$activeNode$hand, _meta$isDragging, _meta$isDragging2, _meta$editorHeight, _meta$editorWidthLeft, _meta$editorWidthRigh, _meta$isPMDragging, _meta$isShiftDown;
152
152
  var activeNode = currentState.activeNode,
153
153
  decorations = currentState.decorations,
154
154
  isResizerResizing = currentState.isResizerResizing,
@@ -158,7 +158,8 @@ export var newApply = function newApply(api, formatMessage, tr, currentState, ne
158
158
  editorWidthRight = currentState.editorWidthRight,
159
159
  isDragging = currentState.isDragging,
160
160
  isMenuOpen = currentState.isMenuOpen,
161
- isPMDragging = currentState.isPMDragging;
161
+ isPMDragging = currentState.isPMDragging,
162
+ isShiftDown = currentState.isShiftDown;
162
163
  var isActiveNodeDeleted = false;
163
164
 
164
165
  // When steps exist, remap existing decorations, activeNode and multi select positions
@@ -181,10 +182,9 @@ export var newApply = function newApply(api, formatMessage, tr, currentState, ne
181
182
  var meta = tr.getMeta(key);
182
183
  var resizerMeta = tr.getMeta('is-resizer-resizing');
183
184
  isResizerResizing = resizerMeta !== null && resizerMeta !== void 0 ? resizerMeta : isResizerResizing;
185
+ multiSelectDnD = (_meta$multiSelectDnD = meta === null || meta === void 0 ? void 0 : meta.multiSelectDnD) !== null && _meta$multiSelectDnD !== void 0 ? _meta$multiSelectDnD : multiSelectDnD;
184
186
  if (multiSelectDnD && flags.isMultiSelectEnabled) {
185
- multiSelectDnD = (meta === null || meta === void 0 ? void 0 : meta.isDragging) === false ||
186
- // For move node with shortcut, only reset when the selection changes
187
- tr.selection.anchor !== multiSelectDnD.textAnchor || tr.selection.head !== multiSelectDnD.textHead ? undefined : multiSelectDnD;
187
+ multiSelectDnD = (meta === null || meta === void 0 ? void 0 : meta.isDragging) === false || tr.selection.empty ? undefined : multiSelectDnD;
188
188
  }
189
189
  var _getTrMetadata = getTrMetadata(tr),
190
190
  from = _getTrMetadata.from,
@@ -287,7 +287,8 @@ export var newApply = function newApply(api, formatMessage, tr, currentState, ne
287
287
  isResizerResizing: isResizerResizing,
288
288
  isDocSizeLimitEnabled: initialState.isDocSizeLimitEnabled,
289
289
  isPMDragging: (_meta$isPMDragging = meta === null || meta === void 0 ? void 0 : meta.isPMDragging) !== null && _meta$isPMDragging !== void 0 ? _meta$isPMDragging : isPMDragging,
290
- multiSelectDnD: (_meta$multiSelectDnD = meta === null || meta === void 0 ? void 0 : meta.multiSelectDnD) !== null && _meta$multiSelectDnD !== void 0 ? _meta$multiSelectDnD : multiSelectDnD
290
+ multiSelectDnD: multiSelectDnD,
291
+ isShiftDown: (_meta$isShiftDown = meta === null || meta === void 0 ? void 0 : meta.isShiftDown) !== null && _meta$isShiftDown !== void 0 ? _meta$isShiftDown : isShiftDown
291
292
  };
292
293
  };
293
294
  export var oldApply = function oldApply(api, formatMessage, tr, currentState, oldState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache) {
@@ -503,9 +504,9 @@ export var createPlugin = function createPlugin(api, getIntl, nodeViewPortalProv
503
504
  dragging = view.dragging;
504
505
  var pluginState = key.getState(state);
505
506
  if (pluginState !== null && pluginState !== void 0 && pluginState.isPMDragging) {
506
- dispatch(state.tr.setMeta(key, {
507
+ dispatch(state.tr.setMeta(key, _objectSpread(_objectSpread({}, state.tr.getMeta(key)), {}, {
507
508
  isPMDragging: false
508
- }));
509
+ })));
509
510
  }
510
511
  if (!(event.target instanceof HTMLElement) || !(pluginState !== null && pluginState !== void 0 && pluginState.activeNode)) {
511
512
  return false;
@@ -558,18 +559,18 @@ export var createPlugin = function createPlugin(api, getIntl, nodeViewPortalProv
558
559
  defaultActiveAnchorTracker.reset();
559
560
  }
560
561
  (_anchorRectCache = anchorRectCache) === null || _anchorRectCache === void 0 || _anchorRectCache.setEditorView(view);
561
- view.dispatch(view.state.tr.setMeta(key, {
562
+ view.dispatch(view.state.tr.setMeta(key, _objectSpread(_objectSpread({}, view.state.tr.getMeta(key)), {}, {
562
563
  isPMDragging: true
563
- }));
564
+ })));
564
565
  },
565
566
  dragend: function dragend(view) {
566
567
  var _key$getState3;
567
568
  var state = view.state,
568
569
  dispatch = view.dispatch;
569
570
  if ((_key$getState3 = key.getState(state)) !== null && _key$getState3 !== void 0 && _key$getState3.isPMDragging) {
570
- dispatch(state.tr.setMeta(key, {
571
+ dispatch(state.tr.setMeta(key, _objectSpread(_objectSpread({}, state.tr.getMeta(key)), {}, {
571
572
  isPMDragging: false
572
- }));
573
+ })));
573
574
  }
574
575
  },
575
576
  mouseover: function mouseover(view, event) {
@@ -577,27 +578,65 @@ export var createPlugin = function createPlugin(api, getIntl, nodeViewPortalProv
577
578
  return false;
578
579
  },
579
580
  keydown: function keydown(view, event) {
580
- // Command + Shift + ArrowUp to select was broken with the plugin enabled so this manually sets the selection
581
- var _view$state = view.state,
582
- selection = _view$state.selection,
583
- doc = _view$state.doc,
584
- tr = _view$state.tr;
585
- var metaKey = browser.mac ? event.metaKey : event.ctrlKey;
586
- if (event.key === 'ArrowUp' && event.shiftKey && metaKey) {
587
- if (selection instanceof TextSelection || selection instanceof NodeSelection) {
588
- var newSelection = TextSelection.create(doc, selection.head, 1);
589
- view.dispatch(tr.setSelection(newSelection));
590
- return true;
581
+ if (isMultiSelectEnabled) {
582
+ if (event.shiftKey && event.ctrlKey) {
583
+ //prevent holding down key combo from firing repeatedly
584
+ if (!event.repeat && boundKeydownHandler(api, formatMessage)(view, event)) {
585
+ event.preventDefault();
586
+ return true;
587
+ }
591
588
  }
592
- }
593
- if (event.shiftKey && event.ctrlKey) {
594
- //prevent holding down key combo from firing repeatedly
595
- if (!event.repeat && boundKeydownHandler(api, formatMessage)(view, event)) {
596
- event.preventDefault();
589
+
590
+ // Command + Shift + ArrowUp to select was broken with the plugin enabled so this manually sets the selection
591
+ var _view$state = view.state,
592
+ selection = _view$state.selection,
593
+ doc = _view$state.doc,
594
+ tr = _view$state.tr;
595
+ var metaKey = browser.mac ? event.metaKey : event.ctrlKey;
596
+ if (event.key === 'ArrowUp' && event.shiftKey && metaKey) {
597
+ if (selection instanceof TextSelection || selection instanceof NodeSelection) {
598
+ var newSelection = TextSelection.create(doc, selection.head, 1);
599
+ view.dispatch(tr.setSelection(newSelection));
600
+ return true;
601
+ }
602
+ }
603
+ if (!event.repeat && event.shiftKey) {
604
+ view.dispatch(view.state.tr.setMeta(key, _objectSpread(_objectSpread({}, view.state.tr.getMeta(key)), {}, {
605
+ isShiftDown: true
606
+ })));
597
607
  return true;
598
608
  }
609
+ return false;
610
+ } else {
611
+ // Command + Shift + ArrowUp to select was broken with the plugin enabled so this manually sets the selection
612
+ var _view$state2 = view.state,
613
+ _selection = _view$state2.selection,
614
+ _doc = _view$state2.doc,
615
+ _tr = _view$state2.tr;
616
+ var _metaKey = browser.mac ? event.metaKey : event.ctrlKey;
617
+ if (event.key === 'ArrowUp' && event.shiftKey && _metaKey) {
618
+ if (_selection instanceof TextSelection || _selection instanceof NodeSelection) {
619
+ var _newSelection = TextSelection.create(_doc, _selection.head, 1);
620
+ view.dispatch(_tr.setSelection(_newSelection));
621
+ return true;
622
+ }
623
+ }
624
+ if (event.shiftKey && event.ctrlKey) {
625
+ //prevent holding down key combo from firing repeatedly
626
+ if (!event.repeat && boundKeydownHandler(api, formatMessage)(view, event)) {
627
+ event.preventDefault();
628
+ return true;
629
+ }
630
+ }
599
631
  }
600
632
  return false;
633
+ },
634
+ keyup: function keyup(view, event) {
635
+ if (!event.repeat && event.key === 'Shift') {
636
+ view.dispatch(view.state.tr.setMeta(key, _objectSpread(_objectSpread({}, view.state.tr.getMeta(key)), {}, {
637
+ isShiftDown: false
638
+ })));
639
+ }
601
640
  }
602
641
  }
603
642
  },
@@ -622,10 +661,10 @@ export var createPlugin = function createPlugin(api, getIntl, nodeViewPortalProv
622
661
  // Ignored via go/ees005
623
662
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
624
663
  var editorWidthLeft = _editorContentArea.getBoundingClientRect().left;
625
- transaction.setMeta(key, {
664
+ transaction.setMeta(key, _objectSpread(_objectSpread({}, transaction.getMeta(key)), {}, {
626
665
  editorWidthLeft: editorWidthLeft,
627
666
  editorWidthRight: editorWidthRight
628
- });
667
+ }));
629
668
  }
630
669
  editorView.dispatch(transaction);
631
670
  }