@atlaskit/editor-plugin-block-controls 2.27.0 → 2.27.4

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 (46) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/cjs/blockControlsPlugin.js +35 -6
  3. package/dist/cjs/pm-plugins/decorations-drop-target.js +11 -5
  4. package/dist/cjs/pm-plugins/main.js +2 -1
  5. package/dist/cjs/pm-plugins/utils/getSelection.js +2 -2
  6. package/dist/cjs/pm-plugins/utils/validation.js +2 -1
  7. package/dist/cjs/ui/block-menu-items.js +103 -0
  8. package/dist/cjs/ui/block-menu.js +77 -0
  9. package/dist/cjs/ui/consts.js +101 -2
  10. package/dist/cjs/ui/drag-handle.js +147 -41
  11. package/dist/cjs/ui/drag-preview.js +83 -34
  12. package/dist/es2019/blockControlsPlugin.js +34 -3
  13. package/dist/es2019/pm-plugins/decorations-drop-target.js +12 -5
  14. package/dist/es2019/pm-plugins/main.js +2 -1
  15. package/dist/es2019/pm-plugins/utils/getSelection.js +1 -1
  16. package/dist/es2019/pm-plugins/utils/validation.js +2 -1
  17. package/dist/es2019/ui/block-menu-items.js +92 -0
  18. package/dist/es2019/ui/block-menu.js +75 -0
  19. package/dist/es2019/ui/consts.js +100 -1
  20. package/dist/es2019/ui/drag-handle.js +152 -40
  21. package/dist/es2019/ui/drag-preview.js +83 -34
  22. package/dist/esm/blockControlsPlugin.js +35 -6
  23. package/dist/esm/pm-plugins/decorations-drop-target.js +11 -5
  24. package/dist/esm/pm-plugins/main.js +2 -1
  25. package/dist/esm/pm-plugins/utils/getSelection.js +1 -1
  26. package/dist/esm/pm-plugins/utils/validation.js +2 -1
  27. package/dist/esm/ui/block-menu-items.js +92 -0
  28. package/dist/esm/ui/block-menu.js +70 -0
  29. package/dist/esm/ui/consts.js +100 -1
  30. package/dist/esm/ui/drag-handle.js +149 -43
  31. package/dist/esm/ui/drag-preview.js +82 -34
  32. package/dist/types/blockControlsPluginType.d.ts +3 -0
  33. package/dist/types/pm-plugins/utils/getSelection.d.ts +3 -3
  34. package/dist/types/pm-plugins/utils/validation.d.ts +1 -1
  35. package/dist/types/ui/block-menu-items.d.ts +17 -0
  36. package/dist/types/ui/block-menu.d.ts +16 -0
  37. package/dist/types/ui/consts.d.ts +7 -0
  38. package/dist/types/ui/drag-preview.d.ts +9 -1
  39. package/dist/types-ts4.5/blockControlsPluginType.d.ts +3 -0
  40. package/dist/types-ts4.5/pm-plugins/utils/getSelection.d.ts +3 -3
  41. package/dist/types-ts4.5/pm-plugins/utils/validation.d.ts +1 -1
  42. package/dist/types-ts4.5/ui/block-menu-items.d.ts +17 -0
  43. package/dist/types-ts4.5/ui/block-menu.d.ts +16 -0
  44. package/dist/types-ts4.5/ui/consts.d.ts +7 -0
  45. package/dist/types-ts4.5/ui/drag-preview.d.ts +9 -1
  46. package/package.json +5 -5
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+ import { injectIntl } from 'react-intl-next';
3
+ import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
4
+ import { Popup } from '@atlaskit/editor-common/ui';
5
+ import { ArrowKeyNavigationType, DropdownMenu } from '@atlaskit/editor-common/ui-menu';
6
+ import { akEditorFloatingOverlapPanelZIndex } from '@atlaskit/editor-shared-styles';
7
+ import { getBlockMenuItems, menuItemsCallback } from './block-menu-items';
8
+ import { BLOCK_MENU_WIDTH } from './consts';
9
+ const dragHandleSelector = '[data-blocks-drag-handle-container="true"] button';
10
+ const BlockMenu = ({
11
+ editorView,
12
+ mountPoint,
13
+ boundariesElement,
14
+ scrollableElement,
15
+ api,
16
+ intl: {
17
+ formatMessage
18
+ }
19
+ }) => {
20
+ const {
21
+ blockControlsState
22
+ } = useSharedPluginState(api, ['blockControls']);
23
+ if (!(blockControlsState !== null && blockControlsState !== void 0 && blockControlsState.isMenuOpen)) {
24
+ return null;
25
+ }
26
+ const targetHandleRef = document.querySelector(dragHandleSelector);
27
+ const items = getBlockMenuItems(formatMessage);
28
+ const handleOpenChange = payload => {
29
+ if (!(payload !== null && payload !== void 0 && payload.isOpen)) {
30
+ api === null || api === void 0 ? void 0 : api.core.actions.execute(api === null || api === void 0 ? void 0 : api.blockControls.commands.toggleBlockMenu({
31
+ closeMenu: true
32
+ }));
33
+ }
34
+ };
35
+ const onMenuItemActivated = ({
36
+ item
37
+ }) => {
38
+ if (editorView) {
39
+ var _menuItemsCallback, _menuItemsCallback$ca;
40
+ (_menuItemsCallback = menuItemsCallback[item.value.name]) === null || _menuItemsCallback === void 0 ? void 0 : (_menuItemsCallback$ca = _menuItemsCallback.call(menuItemsCallback, api, formatMessage)) === null || _menuItemsCallback$ca === void 0 ? void 0 : _menuItemsCallback$ca(editorView.state, editorView.dispatch, editorView);
41
+ }
42
+ };
43
+ return /*#__PURE__*/React.createElement(Popup, {
44
+ alignX: 'right',
45
+ alignY: 'start'
46
+ // Ignored via go/ees005
47
+ // eslint-disable-next-line @atlaskit/editor/no-as-casting
48
+ ,
49
+ target: targetHandleRef,
50
+ mountTo: undefined,
51
+ zIndex: akEditorFloatingOverlapPanelZIndex,
52
+ forcePlacement: true,
53
+ stick: true,
54
+ offset: [-18, 8]
55
+ }, /*#__PURE__*/React.createElement(DropdownMenu, {
56
+ mountTo: mountPoint,
57
+ boundariesElement: boundariesElement,
58
+ scrollableElement: scrollableElement
59
+ //This needs be removed when the a11y is completely handled
60
+ //Disabling key navigation now as it works only partially
61
+ ,
62
+ arrowKeyNavigationProviderOptions: {
63
+ type: ArrowKeyNavigationType.MENU
64
+ },
65
+ items: items,
66
+ isOpen: true,
67
+ fitWidth: BLOCK_MENU_WIDTH,
68
+ section: {
69
+ hasSeparator: true
70
+ },
71
+ onOpenChange: handleOpenChange,
72
+ onItemActivated: onMenuItemActivated
73
+ }));
74
+ };
75
+ export default injectIntl(BlockMenu);
@@ -120,6 +120,104 @@ Array.from({
120
120
  }, 1000);
121
121
  return [currKeyValue, dropTargetMarginMap[value]];
122
122
  }));
123
+ export const spacingBetweenNodesForPreview = {
124
+ paragraph: {
125
+ top: '0.75rem',
126
+ bottom: '0'
127
+ },
128
+ heading1: {
129
+ top: '1.45833em',
130
+ bottom: '0'
131
+ },
132
+ heading2: {
133
+ top: '1.4em',
134
+ bottom: '0'
135
+ },
136
+ heading3: {
137
+ top: '1.31249em',
138
+ bottom: '0'
139
+ },
140
+ heading4: {
141
+ top: '1.25em',
142
+ bottom: '0'
143
+ },
144
+ heading5: {
145
+ top: '1.45833em',
146
+ bottom: '0'
147
+ },
148
+ heading6: {
149
+ top: '1.59091em',
150
+ bottom: '0'
151
+ },
152
+ table: {
153
+ top: '0',
154
+ bottom: '0'
155
+ },
156
+ bulletList: {
157
+ top: '10px',
158
+ bottom: '0'
159
+ },
160
+ orderedList: {
161
+ top: '10px',
162
+ bottom: '0'
163
+ },
164
+ decisionList: {
165
+ top: '0.5rem',
166
+ bottom: '0'
167
+ },
168
+ taskList: {
169
+ top: '0.75rem',
170
+ bottom: '0'
171
+ },
172
+ codeBlock: {
173
+ top: '0.75rem',
174
+ bottom: '0'
175
+ },
176
+ panel: {
177
+ top: '0.75rem',
178
+ bottom: '0'
179
+ },
180
+ rule: {
181
+ top: '1.5rem',
182
+ bottom: '1.5rem'
183
+ },
184
+ mediaSingle: {
185
+ top: '24px',
186
+ bottom: '24px'
187
+ },
188
+ media: {
189
+ top: '24px',
190
+ bottom: '24px'
191
+ },
192
+ bodiedExtension: {
193
+ top: '0',
194
+ bottom: '0'
195
+ },
196
+ extension: {
197
+ top: '0',
198
+ bottom: '0'
199
+ },
200
+ layoutSection: {
201
+ top: '0',
202
+ bottom: '0'
203
+ },
204
+ blockquote: {
205
+ top: '0',
206
+ bottom: '0'
207
+ },
208
+ embedCard: {
209
+ top: '24px',
210
+ bottom: '24px'
211
+ },
212
+ blockCard: {
213
+ top: '0.75rem',
214
+ bottom: '0'
215
+ },
216
+ default: {
217
+ top: '0',
218
+ bottom: '0'
219
+ }
220
+ };
123
221
 
124
222
  // This table contains the "margins" of different nodes
125
223
  // Note this is not the actually margin of the DOM elements,
@@ -221,4 +319,5 @@ export const DEFAULT_COLUMN_DISTRIBUTIONS = {
221
319
  3: 33.33,
222
320
  4: 25,
223
321
  5: 20
224
- };
322
+ };
323
+ export const BLOCK_MENU_WIDTH = 220;
@@ -13,6 +13,7 @@ import { dragToMoveDown, dragToMoveLeft, dragToMoveRight, dragToMoveUp, getAriaK
13
13
  import { blockControlsMessages } from '@atlaskit/editor-common/messages';
14
14
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
15
15
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
16
+ import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
16
17
  import DragHandlerIcon from '@atlaskit/icon/glyph/drag-handler';
17
18
  import { fg } from '@atlaskit/platform-feature-flags';
18
19
  import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
@@ -23,9 +24,8 @@ import Tooltip from '@atlaskit/tooltip';
23
24
  import { key } from '../pm-plugins/main';
24
25
  import { getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics';
25
26
  import { getLeftPosition, getTopPosition } from '../pm-plugins/utils/drag-handle-positions';
26
- import { getNestedNodePosition } from '../pm-plugins/utils/getNestedNodePosition';
27
- import { isHandleInSelection, selectNode } from '../pm-plugins/utils/getSelection';
28
- import { DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_WIDTH, DRAG_HANDLE_ZINDEX, dragHandleGap, topPositionAdjustment } from './consts';
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';
29
29
  import { dragPreview } from './drag-preview';
30
30
  const iconWrapperStyles = xcss({
31
31
  display: 'flex',
@@ -96,6 +96,16 @@ const handleIconDragStart = e => {
96
96
  (_e$target$closest = e.target.closest('button')) === null || _e$target$closest === void 0 ? void 0 : _e$target$closest.dispatchEvent(dragEvent);
97
97
  }
98
98
  };
99
+ const getNodeSpacingForPreview = node => {
100
+ if (!node) {
101
+ return spacingBetweenNodesForPreview['default'];
102
+ }
103
+ const nodeTypeName = node.type.name;
104
+ if (nodeTypeName === 'heading') {
105
+ return spacingBetweenNodesForPreview[`heading${node.attrs.level}`] || spacingBetweenNodesForPreview['default'];
106
+ }
107
+ return spacingBetweenNodesForPreview[nodeTypeName] || spacingBetweenNodesForPreview['default'];
108
+ };
99
109
  export const DragHandle = ({
100
110
  view,
101
111
  api,
@@ -116,6 +126,9 @@ export const DragHandle = ({
116
126
  } = useSharedPluginState(api, ['featureFlags']);
117
127
  const selection = useSharedPluginStateSelector(api, 'selection.selection');
118
128
  const isLayoutColumn = nodeType === 'layoutColumn';
129
+ const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
130
+ exposure: true
131
+ });
119
132
  useEffect(() => {
120
133
  // blockCard/datasource width is rendered correctly after this decoraton does. We need to observe for changes.
121
134
  if (nodeType === 'blockCard') {
@@ -159,7 +172,14 @@ export const DragHandle = ({
159
172
  return tr;
160
173
  });
161
174
  view.focus();
162
- }, [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, view, getPos, nodeType]);
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]);
163
183
 
164
184
  // TODO - This needs to be investigated further. Drag preview generation is not always working
165
185
  // as expected with a node selection. This workaround sets the selection to the node on mouseDown,
@@ -222,7 +242,92 @@ export const DragHandle = ({
222
242
  onGenerateDragPreview: ({
223
243
  nativeSetDragImage
224
244
  }) => {
245
+ var _api$blockControls2;
246
+ if (isMultiSelect) {
247
+ var _api$core5;
248
+ api === null || api === void 0 ? void 0 : (_api$core5 = api.core) === null || _api$core5 === void 0 ? void 0 : _api$core5.actions.execute(({
249
+ tr
250
+ }) => {
251
+ const handlePos = getPos();
252
+ if (typeof handlePos !== 'number') {
253
+ return tr;
254
+ }
255
+ 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()({
258
+ tr
259
+ });
260
+ }
261
+ return tr;
262
+ });
263
+ }
264
+ const startPos = getPos();
265
+ const state = view.state;
266
+ const {
267
+ doc,
268
+ selection
269
+ } = 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
+ let sliceFrom = selection.from;
274
+ let sliceTo = selection.to;
275
+ if (multiSelectDnD) {
276
+ const {
277
+ anchor,
278
+ head
279
+ } = multiSelectDnD;
280
+ sliceFrom = Math.min(anchor, head);
281
+ sliceTo = Math.max(anchor, head);
282
+ }
283
+ const expandedSlice = doc.slice(sliceFrom, sliceTo);
284
+ const isDraggingMultiLine = isMultiSelect && startPos !== undefined && startPos >= sliceFrom && startPos <= sliceTo && expandedSlice.content.childCount > 1;
225
285
  setCustomNativeDragPreview({
286
+ getOffset: () => {
287
+ if (!isDraggingMultiLine) {
288
+ return {
289
+ x: 0,
290
+ y: 0
291
+ };
292
+ } else {
293
+ // Calculate the offset of the preview container,
294
+ // So when drag multiple nodes, the preview align with the position of the selected nodes
295
+ const domAtPos = view.domAtPos.bind(view);
296
+ let domElementsHeightBeforeHandle = 0;
297
+ const nodesStartPos = [];
298
+ const nodesEndPos = [];
299
+ let activeNodeMarginTop = 0;
300
+ for (let i = 0; i < expandedSlice.content.childCount; i++) {
301
+ if (i === 0) {
302
+ var _expandedSlice$conten;
303
+ nodesStartPos[i] = sliceFrom;
304
+ nodesEndPos[i] = sliceFrom + (((_expandedSlice$conten = expandedSlice.content.maybeChild(i)) === null || _expandedSlice$conten === void 0 ? void 0 : _expandedSlice$conten.nodeSize) || 0);
305
+ } else {
306
+ var _expandedSlice$conten2;
307
+ nodesStartPos[i] = nodesEndPos[i - 1];
308
+ nodesEndPos[i] = nodesStartPos[i] + (((_expandedSlice$conten2 = expandedSlice.content.maybeChild(i)) === null || _expandedSlice$conten2 === void 0 ? void 0 : _expandedSlice$conten2.nodeSize) || 0);
309
+ }
310
+
311
+ // when the node is before the handle, calculate the height of the node
312
+ if (nodesEndPos[i] <= startPos) {
313
+ // eslint-disable-next-line @atlaskit/editor/no-as-casting
314
+ const currentNodeElement = findDomRefAtPos(nodesStartPos[i], domAtPos);
315
+ const maybeCurrentNode = expandedSlice.content.maybeChild(i);
316
+ const currentNodeSpacing = maybeCurrentNode ? nodeMargins[maybeCurrentNode.type.name].top + nodeMargins[maybeCurrentNode.type.name].bottom : 0;
317
+ domElementsHeightBeforeHandle = domElementsHeightBeforeHandle + currentNodeElement.offsetHeight + currentNodeSpacing;
318
+ } else {
319
+ // when the node is after the handle, calculate the top margin of the active node
320
+ const maybeNextNode = expandedSlice.content.maybeChild(i);
321
+ activeNodeMarginTop = maybeNextNode ? nodeMargins[maybeNextNode.type.name].top : 0;
322
+ break;
323
+ }
324
+ }
325
+ return {
326
+ x: 0,
327
+ y: domElementsHeightBeforeHandle + activeNodeMarginTop
328
+ };
329
+ }
330
+ },
226
331
  render: ({
227
332
  container
228
333
  }) => {
@@ -230,49 +335,54 @@ export const DragHandle = ({
230
335
  if (!dom) {
231
336
  return;
232
337
  }
233
- return dragPreview(container, dom, nodeType);
338
+ if (!isDraggingMultiLine) {
339
+ return dragPreview(container, {
340
+ dom,
341
+ nodeType
342
+ });
343
+ } else {
344
+ const domAtPos = view.domAtPos.bind(view);
345
+ const previewContent = [];
346
+ expandedSlice.content.descendants((node, pos, parent, index) => {
347
+ // Get the dom element of the node
348
+ //eslint-disable-next-line @atlaskit/editor/no-as-casting
349
+ const nodeDomElement = findDomRefAtPos(sliceFrom + pos, domAtPos);
350
+ const currentNodeSpacing = getNodeSpacingForPreview(node);
351
+ previewContent.push({
352
+ dom: nodeDomElement,
353
+ nodeType: node.type.name,
354
+ nodeSpacing: currentNodeSpacing
355
+ });
356
+ return false; // Only iterate through the first level of nodes
357
+ });
358
+ return dragPreview(container, previewContent);
359
+ }
234
360
  },
235
361
  nativeSetDragImage
236
362
  });
237
363
  },
238
364
  onDragStart() {
239
- var _api$core5;
365
+ var _api$core6;
240
366
  if (start === undefined) {
241
367
  return;
242
368
  }
243
- api === null || api === void 0 ? void 0 : (_api$core5 = api.core) === null || _api$core5 === void 0 ? void 0 : _api$core5.actions.execute(({
369
+ api === null || api === void 0 ? void 0 : (_api$core6 = api.core) === null || _api$core6 === void 0 ? void 0 : _api$core6.actions.execute(({
244
370
  tr
245
371
  }) => {
246
- var _api$blockControls2, _api$analytics3;
247
- const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
248
- exposure: true
249
- });
372
+ var _tr$getMeta, _api$blockControls3, _api$analytics3;
250
373
  let nodeTypes, hasSelectedMultipleNodes;
251
374
  const resolvedMovingNode = tr.doc.resolve(start);
252
375
  const maybeNode = resolvedMovingNode.nodeAfter;
253
- if (isMultiSelect) {
254
- var _tr$getMeta;
255
- const handlePos = getPos();
256
- if (typeof handlePos !== 'number') {
257
- return tr;
258
- }
259
- if (!tr.selection.empty && handlePos >= tr.selection.$from.start() - 1 && handlePos <= tr.selection.to) {
260
- var _api$blockControls;
261
- api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.commands.setMultiSelectPositions()({
262
- tr
263
- });
264
- }
265
- const multiSelectDnD = (_tr$getMeta = tr.getMeta(key)) === null || _tr$getMeta === void 0 ? void 0 : _tr$getMeta.multiSelectDnD;
266
- if (multiSelectDnD) {
267
- const attributes = getMultiSelectAnalyticsAttributes(tr, multiSelectDnD.anchor, multiSelectDnD.head);
268
- nodeTypes = attributes.nodeTypes;
269
- hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
270
- } else {
271
- nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name;
272
- hasSelectedMultipleNodes = false;
273
- }
376
+ const multiSelectDnD = (_tr$getMeta = tr.getMeta(key)) === null || _tr$getMeta === void 0 ? void 0 : _tr$getMeta.multiSelectDnD;
377
+ if (multiSelectDnD) {
378
+ const attributes = getMultiSelectAnalyticsAttributes(tr, multiSelectDnD.anchor, multiSelectDnD.head);
379
+ nodeTypes = attributes.nodeTypes;
380
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
381
+ } else {
382
+ nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name;
383
+ hasSelectedMultipleNodes = false;
274
384
  }
275
- api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.commands.setNodeDragged(getPos, anchorName, nodeType)({
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)({
276
386
  tr
277
387
  });
278
388
  tr.setMeta('scrollIntoView', false);
@@ -295,7 +405,7 @@ export const DragHandle = ({
295
405
  view.focus();
296
406
  }
297
407
  });
298
- }, [anchorName, api, getPos, nodeType, start, view]);
408
+ }, [anchorName, api, getPos, isMultiSelect, nodeType, start, view]);
299
409
  const macroInteractionUpdates = featureFlagsState === null || featureFlagsState === void 0 ? void 0 : featureFlagsState.macroInteractionUpdates;
300
410
  const calculatePosition = useCallback(() => {
301
411
  let parentNodeType;
@@ -377,14 +487,11 @@ export const DragHandle = ({
377
487
  }
378
488
  }, [buttonRef, handleOptions === null || handleOptions === void 0 ? void 0 : handleOptions.isFocused, view]);
379
489
  useEffect(() => {
380
- const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
381
- exposure: true
382
- });
383
490
  if (!isMultiSelect || typeof start !== 'number' || !selection) {
384
491
  return;
385
492
  }
386
- setDragHandleSelected(isHandleInSelection(view.state, selection, start));
387
- }, [start, selection, view.state]);
493
+ setDragHandleSelected(isHandleCorrelatedToSelection(view.state, selection, start));
494
+ }, [start, selection, view.state, isMultiSelect]);
388
495
  let helpDescriptors = isTopLevelNode && fg('platform_editor_advanced_layouts_accessibility') ? [{
389
496
  description: formatMessage(blockControlsMessages.dragToMove)
390
497
  }, {
@@ -410,7 +517,12 @@ export const DragHandle = ({
410
517
  }];
411
518
  let isParentNodeOfTypeLayout;
412
519
  if (!isTopLevelNode && (fg('platform_editor_advanced_layouts_accessibility') || handleOptions !== null && handleOptions !== void 0 && handleOptions.isFocused) && editorExperiment('nested-dnd', true)) {
413
- isParentNodeOfTypeLayout = nodeType === 'layoutSection' || view.state.doc.resolve(getNestedNodePosition(view.state)).node().type.name === 'layoutColumn';
520
+ const pos = getPos();
521
+ if (typeof pos === 'number') {
522
+ var _$pos$parent;
523
+ const $pos = view.state.doc.resolve(pos);
524
+ isParentNodeOfTypeLayout = ($pos === null || $pos === void 0 ? void 0 : (_$pos$parent = $pos.parent) === null || _$pos$parent === void 0 ? void 0 : _$pos$parent.type.name) === 'layoutColumn';
525
+ }
414
526
  if (isParentNodeOfTypeLayout) {
415
527
  helpDescriptors = [...helpDescriptors, {
416
528
  description: formatMessage(blockControlsMessages.moveLeft),
@@ -15,14 +15,8 @@ const previewStyleOld = {
15
15
  borderWidth: "var(--ds-border-width-outline, 2px)",
16
16
  backgroundColor: "var(--ds-blanket-selected, #388BFF14)"
17
17
  };
18
- export const dragPreview = (container, dom, nodeType) => {
18
+ const getPreviewContainerDimensionsForSingle = (dom, nodeType) => {
19
19
  let nodeContainer = dom;
20
- container.style.pointerEvents = 'none';
21
- const parent = document.createElement('div');
22
- // ProseMirror class is required to make sure the cloned dom is styled correctly
23
- parent.classList.add('ProseMirror', 'block-ctrl-drag-preview');
24
- const embedCard = dom.querySelector('.embedCardView-content-wrap');
25
- let shouldBeGenericPreview = nodeType === 'embedCard' || nodeType === 'extension' || !!embedCard;
26
20
  if (fg('platform_editor_elements_drag_and_drop_ed_23189')) {
27
21
  const iframeContainer = dom.querySelector('iframe');
28
22
  if (nodeType === 'embedCard') {
@@ -30,41 +24,96 @@ export const dragPreview = (container, dom, nodeType) => {
30
24
  } else if (nodeType === 'extension' && iframeContainer) {
31
25
  nodeContainer = iframeContainer;
32
26
  }
33
- shouldBeGenericPreview = nodeType === 'embedCard' || !!embedCard || nodeType === 'extension' && !!iframeContainer;
34
27
  }
35
28
  const nodeContainerRect = nodeContainer.getBoundingClientRect();
36
- container.style.width = `${nodeContainerRect.width}px`;
37
- container.style.height = `${nodeContainerRect.height}px`;
29
+ return {
30
+ width: nodeContainerRect.width,
31
+ height: nodeContainerRect.height
32
+ };
33
+ };
34
+ const getPreviewContainerDimensions = dragPreviewContent => {
35
+ let maxWidth = 0;
36
+ let heightSum = 0;
37
+ for (let index = 0; index < dragPreviewContent.length; index++) {
38
+ const element = dragPreviewContent[index];
39
+ const {
40
+ width,
41
+ height
42
+ } = getPreviewContainerDimensionsForSingle(element.dom, element.nodeType);
43
+ if (width > maxWidth) {
44
+ maxWidth = width;
45
+ }
46
+ heightSum += height;
47
+ }
48
+ return {
49
+ width: maxWidth,
50
+ height: heightSum
51
+ };
52
+ };
53
+ const createGenericPreview = () => {
54
+ const generalPreview = document.createElement('div');
55
+ // ProseMirror class is required to make sure the cloned dom is styled correctly
56
+ generalPreview.classList.add('ProseMirror', 'block-ctrl-drag-preview');
38
57
  const previewStyle = fg('platform_editor_elements_drag_and_drop_ed_23189') ? previewStyleNew : previewStyleOld;
58
+ generalPreview.style.border = `${previewStyle.borderWidth} ${previewStyle.borderStyle} ${previewStyle.borderColor}`;
59
+ generalPreview.style.borderRadius = previewStyle.borderRadius;
60
+ generalPreview.style.backgroundColor = previewStyle.backgroundColor;
61
+ generalPreview.style.height = '100%';
62
+ generalPreview.setAttribute('data-testid', 'block-ctrl-generic-drag-preview');
63
+ return generalPreview;
64
+ };
65
+ const createContentPreviewElement = (dom, nodeType, nodeSpacing) => {
66
+ const contentPreviewOneElement = document.createElement('div');
67
+ contentPreviewOneElement.classList.add('ProseMirror', 'block-ctrl-drag-preview');
68
+ const resizer = dom.querySelector('.resizer-item');
69
+ const clonedDom = resizer && ['mediaSingle', 'table'].includes(nodeType) ?
70
+ // Ignored via go/ees005
71
+ // eslint-disable-next-line @atlaskit/editor/no-as-casting
72
+ resizer.cloneNode(true) :
73
+ // Ignored via go/ees005
74
+ // eslint-disable-next-line @atlaskit/editor/no-as-casting
75
+ dom.cloneNode(true);
76
+ clonedDom.style.marginLeft = '0';
77
+ clonedDom.style.marginTop = nodeSpacing ? `${nodeSpacing.top}` : '0';
78
+ clonedDom.style.marginRight = '0';
79
+ clonedDom.style.marginBottom = nodeSpacing ? `${nodeSpacing.bottom}` : '0';
80
+ clonedDom.style.boxShadow = 'none';
81
+ clonedDom.style.opacity = browser.windows ? '1' : fg('platform_editor_elements_drag_and_drop_ed_23189') ? '0.31' : '0.4';
82
+ contentPreviewOneElement.appendChild(clonedDom);
83
+ return contentPreviewOneElement;
84
+ };
85
+ const isGenericPreview = (dom, nodeType) => {
86
+ const embedCard = dom.querySelector('.embedCardView-content-wrap');
87
+ return fg('platform_editor_elements_drag_and_drop_ed_23189') ? nodeType === 'embedCard' || !!embedCard || nodeType === 'extension' && !!dom.querySelector('iframe') : nodeType === 'embedCard' || !!embedCard || nodeType === 'extension';
88
+ };
89
+ const createPreviewForElement = (dom, nodeType, nodeSpacing) => {
90
+ const shouldBeGenericPreview = isGenericPreview(dom, nodeType);
39
91
  if (shouldBeGenericPreview) {
40
- parent.style.border = `${previewStyle.borderWidth} ${previewStyle.borderStyle} ${previewStyle.borderColor}`;
41
- parent.style.borderRadius = previewStyle.borderRadius;
42
- parent.style.backgroundColor = previewStyle.backgroundColor;
43
- parent.style.height = '100%';
44
- parent.setAttribute('data-testid', 'block-ctrl-generic-drag-preview');
92
+ return createGenericPreview();
45
93
  } else {
46
- const resizer = dom.querySelector('.resizer-item');
47
- const clonedDom = resizer && ['mediaSingle', 'table'].includes(nodeType) ?
48
- // Ignored via go/ees005
49
- // eslint-disable-next-line @atlaskit/editor/no-as-casting
50
- resizer.cloneNode(true) :
51
- // Ignored via go/ees005
52
- // eslint-disable-next-line @atlaskit/editor/no-as-casting
53
- dom.cloneNode(true);
54
-
55
- // Remove any margin from the cloned element to ensure is doesn't position incorrectly
56
- clonedDom.style.marginLeft = '0';
57
- clonedDom.style.marginTop = '0';
58
- clonedDom.style.marginRight = '0';
59
- clonedDom.style.marginBottom = '0';
60
- clonedDom.style.boxShadow = 'none';
61
- clonedDom.style.opacity = browser.windows ? '1' : fg('platform_editor_elements_drag_and_drop_ed_23189') ? '0.31' : '0.4';
62
- parent.appendChild(clonedDom);
94
+ return createContentPreviewElement(dom, nodeType, nodeSpacing);
95
+ }
96
+ };
97
+ export const dragPreview = (container, dragPreviewContent) => {
98
+ container.style.pointerEvents = 'none';
99
+ if (!Array.isArray(dragPreviewContent) && typeof dragPreviewContent === 'object') {
100
+ dragPreviewContent = [dragPreviewContent];
101
+ }
102
+ const {
103
+ width: maxWidth,
104
+ height: maxHeight
105
+ } = getPreviewContainerDimensions(dragPreviewContent);
106
+ container.style.width = `${maxWidth}px`;
107
+ container.style.height = `${maxHeight}px`;
108
+ const previewWrapperFragment = document.createDocumentFragment();
109
+ for (let index = 0; index < dragPreviewContent.length; index++) {
110
+ const element = dragPreviewContent[index];
111
+ const contentPreviewOneElement = createPreviewForElement(element.dom, element.nodeType, element.nodeSpacing);
112
+ previewWrapperFragment.appendChild(contentPreviewOneElement);
63
113
  }
64
- container.appendChild(parent);
114
+ container.appendChild(previewWrapperFragment);
65
115
  const scrollParent = document.querySelector('.fabric-editor-popup-scroll-parent');
66
116
  const scrollParentClassNames = scrollParent === null || scrollParent === void 0 ? void 0 : scrollParent.className;
67
-
68
117
  // Add the scroll parent class to the container to ensure the cloned element is styled correctly
69
118
  container.className = scrollParentClassNames || '';
70
119
  container.classList.remove('fabric-editor-popup-scroll-parent');