@atlaskit/editor-plugin-block-controls 1.4.28 → 1.4.30

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.
@@ -9,6 +9,7 @@ import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-sc
9
9
  import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
10
10
  import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
11
11
  import { dragHandleDecoration, dropTargetDecorations, mouseMoveWrapperDecorations, nodeDecorations } from './decorations';
12
+ import { handleMouseOver } from './handle-mouse-over';
12
13
  export const key = new PluginKey('blockControls');
13
14
  const destroyFn = api => {
14
15
  const scrollable = document.querySelector('.fabric-editor-popup-scroll-parent');
@@ -83,7 +84,7 @@ export const createPlugin = api => {
83
84
  return initialState;
84
85
  },
85
86
  apply(tr, currentState, oldState, newState) {
86
- var _meta$activeNode, _meta$isDragging, _meta$editorHeight;
87
+ var _meta$activeNode, _meta$activeNode2, _meta$isDragging, _meta$editorHeight;
87
88
  if (initialState.isDocSizeLimitEnabled && newState.doc.nodeSize > DRAG_AND_DROP_DOC_SIZE_LIMIT) {
88
89
  return initialState;
89
90
  }
@@ -113,21 +114,45 @@ export const createPlugin = api => {
113
114
  }) => spec.id === 'drag-handle');
114
115
  decorations = decorations.remove(oldHandle);
115
116
  }
117
+ let isDecsMissing = false;
118
+ let isHandleMissing = false;
119
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
120
+ // Ensure decorations stay in sync when nodes are added or removed from the doc
121
+ isHandleMissing = !(meta !== null && meta !== void 0 && meta.activeNode) && !decorations.find().some(({
122
+ spec
123
+ }) => spec.id === 'drag-handle');
124
+ const decsLength = decorations.find().filter(({
125
+ spec
126
+ }) => spec.id !== 'drag-handle').length;
127
+ isDecsMissing = !isDragging && decsLength !== newState.doc.childCount;
128
+ }
116
129
 
117
130
  // This is not targeted enough - it's trying to catch events like expand being set to breakout
118
131
  const maybeWidthUpdated = tr.docChanged && oldState.doc.nodeSize === newState.doc.nodeSize && !nodeCountChanged;
119
- const redrawDecorations = decorations === DecorationSet.empty || (meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== undefined && (meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== editorHeight || maybeWidthUpdated || nodeCountChanged || resizerMeta === false || !!(meta !== null && meta !== void 0 && meta.nodeMoved) && tr.docChanged;
132
+
133
+ // This addresses scenarios such as undoing table resizing,
134
+ // where a keyboard shortcut triggers a width change, and
135
+ // the node's actual width is then updated in a separate renderering cycle.
136
+ // The tr.meta.activeNode is triggered by the showDragHandleAt function during the mouse entry event
137
+ // (when the table node rerenders)
138
+ // The activeNode is from the previous rendering cycle, and verify if they share the same anchor.
139
+ const maybeNodeWidthUpdated = (meta === null || meta === void 0 ? void 0 : meta.activeNode) && (meta === null || meta === void 0 ? void 0 : (_meta$activeNode = meta.activeNode) === null || _meta$activeNode === void 0 ? void 0 : _meta$activeNode.nodeType) === 'table' && meta.activeNode.anchorName === (activeNode === null || activeNode === void 0 ? void 0 : activeNode.anchorName);
140
+ const redrawDecorations = decorations === DecorationSet.empty || (meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== undefined && (meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== editorHeight || maybeWidthUpdated || nodeCountChanged || maybeNodeWidthUpdated || resizerMeta === false || isDecsMissing || !!(meta !== null && meta !== void 0 && meta.nodeMoved) && tr.docChanged;
120
141
 
121
142
  // Draw node and mouseWrapper decorations at top level node if decorations is empty, editor height changes or node is moved
122
143
  if (redrawDecorations && !isResizerResizing && api) {
123
144
  decorations = DecorationSet.create(newState.doc, []);
124
145
  const nodeDecs = nodeDecorations(newState);
125
- const mouseWrapperDecs = mouseMoveWrapperDecorations(newState, api);
126
- decorations = decorations.add(newState.doc, [...nodeDecs, ...mouseWrapperDecs]);
146
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
147
+ decorations = decorations.add(newState.doc, [...nodeDecs]);
148
+ } else {
149
+ const mouseWrapperDecs = mouseMoveWrapperDecorations(newState, api);
150
+ decorations = decorations.add(newState.doc, [...nodeDecs, ...mouseWrapperDecs]);
151
+ }
127
152
 
128
153
  // Note: Quite often the handle is not in the right position after a node is moved
129
154
  // it is safer for now to not show it when a node is moved
130
- if (activeNode && !(meta !== null && meta !== void 0 && meta.nodeMoved)) {
155
+ if (activeNode && !(meta !== null && meta !== void 0 && meta.nodeMoved) && !isDecsMissing) {
131
156
  const newActiveNode = activeNode && tr.doc.nodeAt(tr.mapping.map(activeNode.pos));
132
157
  let nodeType = activeNode.nodeType;
133
158
  let anchorName = activeNode.anchorName;
@@ -197,7 +222,7 @@ export const createPlugin = api => {
197
222
  return {
198
223
  decorations,
199
224
  decorationState,
200
- activeNode: isEmptyDoc ? null : (_meta$activeNode = meta === null || meta === void 0 ? void 0 : meta.activeNode) !== null && _meta$activeNode !== void 0 ? _meta$activeNode : mappedActiveNodePos,
225
+ activeNode: isEmptyDoc || isHandleMissing ? null : (_meta$activeNode2 = meta === null || meta === void 0 ? void 0 : meta.activeNode) !== null && _meta$activeNode2 !== void 0 ? _meta$activeNode2 : mappedActiveNodePos,
201
226
  isDragging: (_meta$isDragging = meta === null || meta === void 0 ? void 0 : meta.isDragging) !== null && _meta$isDragging !== void 0 ? _meta$isDragging : isDragging,
202
227
  isMenuOpen: meta !== null && meta !== void 0 && meta.toggleMenu ? !isMenuOpen : isMenuOpen,
203
228
  editorHeight: (_meta$editorHeight = meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== null && _meta$editorHeight !== void 0 ? _meta$editorHeight : currentState.editorHeight,
@@ -236,41 +261,50 @@ export const createPlugin = api => {
236
261
  return true;
237
262
  }
238
263
  return false;
264
+ },
265
+ mouseover: (view, event) => {
266
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
267
+ handleMouseOver(view, event, api);
268
+ }
269
+ return false;
239
270
  }
240
271
  }
241
272
  },
242
273
  view: editorView => {
243
274
  const dom = editorView.dom;
275
+ let resizeObserver;
276
+ if (!getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
277
+ // Use ResizeObserver to observe height changes
278
+ resizeObserver = new ResizeObserver(rafSchedule(entries => {
279
+ const editorHeight = entries[0].contentBoxSize[0].blockSize;
244
280
 
245
- // Use ResizeObserver to observe height changes
246
- const resizeObserver = new ResizeObserver(rafSchedule(entries => {
247
- const editorHeight = entries[0].contentBoxSize[0].blockSize;
248
-
249
- // Update the plugin state when the height changes
250
- const pluginState = key.getState(editorView.state);
251
- if (!(pluginState !== null && pluginState !== void 0 && pluginState.isDragging)) {
252
- const isResizerResizing = !!dom.querySelector('.is-resizing');
253
- const transaction = editorView.state.tr;
254
- if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.isResizerResizing) !== isResizerResizing) {
255
- transaction.setMeta('is-resizer-resizing', isResizerResizing);
256
- }
257
- if (!isResizerResizing) {
258
- transaction.setMeta(key, {
259
- editorHeight
260
- });
281
+ // Update the plugin state when the height changes
282
+ const pluginState = key.getState(editorView.state);
283
+ if (!(pluginState !== null && pluginState !== void 0 && pluginState.isDragging)) {
284
+ const isResizerResizing = !!dom.querySelector('.is-resizing');
285
+ const transaction = editorView.state.tr;
286
+ if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.isResizerResizing) !== isResizerResizing) {
287
+ transaction.setMeta('is-resizer-resizing', isResizerResizing);
288
+ }
289
+ if (!isResizerResizing) {
290
+ transaction.setMeta(key, {
291
+ editorHeight
292
+ });
293
+ }
294
+ editorView.dispatch(transaction);
261
295
  }
262
- editorView.dispatch(transaction);
263
- }
264
- }));
265
-
266
- // Start observing the editor DOM element
267
- resizeObserver.observe(dom);
296
+ }));
297
+ // Start observing the editor DOM element
298
+ resizeObserver.observe(dom);
299
+ }
268
300
 
269
301
  // Start pragmatic monitors
270
302
  const pragmaticCleanup = destroyFn(api);
271
303
  return {
272
304
  destroy() {
273
- resizeObserver.unobserve(dom);
305
+ if (!getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
306
+ resizeObserver.unobserve(dom);
307
+ }
274
308
  pragmaticCleanup();
275
309
  }
276
310
  };
@@ -5,7 +5,9 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
5
  import { css, jsx } from '@emotion/react';
6
6
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
7
7
  import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
8
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
8
9
  import DragHandlerIcon from '@atlaskit/icon/glyph/drag-handler';
10
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
9
11
  import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
10
12
  import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
11
13
  import { key } from '../pm-plugins/main';
@@ -74,7 +76,7 @@ export const DragHandle = ({
74
76
  }
75
77
  }, [anchorName, nodeType, view.dom]);
76
78
  const handleOnClick = useCallback(() => {
77
- var _api$core, _api$core2;
79
+ var _api$core;
78
80
  setDragHandleSelected(!dragHandleSelected);
79
81
  api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
80
82
  tr
@@ -88,15 +90,15 @@ export const DragHandle = ({
88
90
  });
89
91
  return tr;
90
92
  });
91
- api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.focus();
92
- }, [start, api, dragHandleSelected, setDragHandleSelected, nodeType]);
93
+ view.focus();
94
+ }, [start, api, view, dragHandleSelected, setDragHandleSelected, nodeType]);
93
95
 
94
96
  // handleMouseDown required along with onClick to ensure the correct selection
95
97
  // is set immediately when the drag handle is clicked. Otherwise browser native
96
98
  // drag and drop can take over and cause unpredictable behaviour.
97
99
  const handleMouseDown = useCallback(() => {
98
- var _api$core3, _api$core4;
99
- api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
100
+ var _api$core2;
101
+ api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(({
100
102
  tr
101
103
  }) => {
102
104
  if (start === undefined) {
@@ -108,8 +110,33 @@ export const DragHandle = ({
108
110
  });
109
111
  return tr;
110
112
  });
111
- api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.focus();
112
- }, [start, api, nodeType]);
113
+ view.focus();
114
+ }, [start, api, view, nodeType]);
115
+
116
+ // TODO - This needs to be investigated further. Drag preview generation is not always working
117
+ // as expected with a node selection. This workaround sets the selection to the node on mouseDown,
118
+ // but ensures the preview is generated correctly.
119
+ const handleMouseDownWrapperRemoved = useCallback(() => {
120
+ var _api$core3;
121
+ api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
122
+ tr
123
+ }) => {
124
+ if (start === undefined) {
125
+ return tr;
126
+ }
127
+ const node = tr.doc.nodeAt(start);
128
+ if (!node) {
129
+ return tr;
130
+ }
131
+ const $startPos = tr.doc.resolve(start + node.nodeSize);
132
+ const selection = new TextSelection($startPos);
133
+ tr.setSelection(selection);
134
+ tr.setMeta(key, {
135
+ pos: start
136
+ });
137
+ return tr;
138
+ });
139
+ }, [start, api]);
113
140
  useEffect(() => {
114
141
  const element = buttonRef.current;
115
142
  if (!element) {
@@ -138,11 +165,11 @@ export const DragHandle = ({
138
165
  });
139
166
  },
140
167
  onDragStart() {
141
- var _api$core5, _api$core6;
168
+ var _api$core4;
142
169
  if (start === undefined) {
143
170
  return;
144
171
  }
145
- api === null || api === void 0 ? void 0 : (_api$core5 = api.core) === null || _api$core5 === void 0 ? void 0 : _api$core5.actions.execute(({
172
+ api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.execute(({
146
173
  tr
147
174
  }) => {
148
175
  var _api$blockControls, _api$analytics;
@@ -151,6 +178,7 @@ export const DragHandle = ({
151
178
  });
152
179
  const resolvedMovingNode = tr.doc.resolve(start);
153
180
  const maybeNode = resolvedMovingNode.nodeAfter;
181
+ tr.setMeta('scrollIntoView', false);
154
182
  api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.attachAnalyticsEvent({
155
183
  eventType: EVENT_TYPE.UI,
156
184
  action: ACTION.DRAGGED,
@@ -163,7 +191,7 @@ export const DragHandle = ({
163
191
  })(tr);
164
192
  return tr;
165
193
  });
166
- api === null || api === void 0 ? void 0 : (_api$core6 = api.core) === null || _api$core6 === void 0 ? void 0 : _api$core6.actions.focus();
194
+ view.focus();
167
195
  }
168
196
  });
169
197
  }, [anchorName, api, nodeType, view, start]);
@@ -190,7 +218,7 @@ export const DragHandle = ({
190
218
  }
191
219
  if (supportsAnchor) {
192
220
  return {
193
- left: hasResizer || isExtension || isBlockCard || isEmbedCard ? getLeftPosition(dom, nodeType, innerContainer, macroInteractionUpdates) : `calc(anchor(${anchorName} start) - ${DRAG_HANDLE_WIDTH}px - ${dragHandleGap(nodeType)}px)`,
221
+ left: hasResizer || isExtension || isBlockCard || isEmbedCard ? getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2') ? `calc(calc(anchor(${anchorName} start) + ${getLeftPosition(dom, nodeType, innerContainer, macroInteractionUpdates)}` : getLeftPosition(dom, nodeType, innerContainer, macroInteractionUpdates) : `calc(anchor(${anchorName} start) - ${DRAG_HANDLE_WIDTH}px - ${dragHandleGap(nodeType)}px)`,
194
222
  top: anchorName.includes('table') ? `calc(anchor(${anchorName} start) + ${DRAG_HANDLE_HEIGHT}px)` : `anchor(${anchorName} start)`
195
223
  };
196
224
  }
@@ -207,7 +235,7 @@ export const DragHandle = ({
207
235
  ,
208
236
  style: positionStyles,
209
237
  onClick: handleOnClick,
210
- onMouseDown: handleMouseDown,
238
+ onMouseDown: getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2') ? handleMouseDownWrapperRemoved : handleMouseDown,
211
239
  "data-testid": "block-ctrl-drag-handle"
212
240
  }, jsx(DragHandlerIcon, {
213
241
  label: "",
@@ -1,3 +1,4 @@
1
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
1
2
  import { B200 } from '@atlaskit/theme/colors';
2
3
  const previewStyle = {
3
4
  borderColor: `var(--ds-border-focused, ${B200})`,
@@ -14,6 +15,9 @@ export const dragPreview = (container, dom, nodeType) => {
14
15
  const parent = document.createElement('div');
15
16
  // ProseMirror class is required to make sure the cloned dom is styled correctly
16
17
  parent.classList.add('ProseMirror');
18
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
19
+ parent.classList.add('block-ctrl-drag-preview');
20
+ }
17
21
  const shouldBeGenericPreview = nodeType === 'embedCard' || nodeType === 'extension';
18
22
  const embedCard = dom.querySelector('.embedCardView-content-wrap');
19
23
  if (shouldBeGenericPreview || embedCard) {
@@ -13,7 +13,8 @@ const styleDropTarget = css({
13
13
  position: 'absolute',
14
14
  width: '100%',
15
15
  left: '0',
16
- display: 'block'
16
+ display: 'block',
17
+ zIndex: 1
17
18
  });
18
19
  const styleDropIndicator = css({
19
20
  height: '100%',
@@ -1,6 +1,30 @@
1
1
  /** @jsx jsx */
2
2
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles, @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
3
3
  import { css, Global, jsx } from '@emotion/react';
4
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
5
+ const extendedHoverZone = css({
6
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors
7
+ '.block-ctrl-drag-preview [data-drag-handler-anchor-name]::after': {
8
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-important-styles
9
+ display: 'none !important'
10
+ },
11
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors
12
+ '[data-drag-handler-anchor-name]::after': {
13
+ content: '""',
14
+ position: 'absolute',
15
+ top: 0,
16
+ left: '-100%',
17
+ width: '100%',
18
+ height: '100%',
19
+ background: 'transparent',
20
+ cursor: 'default',
21
+ zIndex: -1
22
+ },
23
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors
24
+ '[data-blocks-decoration-container="true"] + *::after': {
25
+ display: 'none'
26
+ }
27
+ });
4
28
  const globalStyles = css({
5
29
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors -- Ignored via go/DSP-18766
6
30
  '.ProseMirror-widget:first-child + *': {
@@ -10,6 +34,6 @@ const globalStyles = css({
10
34
  });
11
35
  export const GlobalStylesWrapper = () => {
12
36
  return jsx(Global, {
13
- styles: globalStyles
37
+ styles: [globalStyles, getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2') && extendedHoverZone]
14
38
  });
15
39
  };
@@ -26,7 +26,7 @@ export const getSelection = (tr, start) => {
26
26
  return true;
27
27
  });
28
28
  const inlineNodeDepth = inlineNodePos - start;
29
- return new TextSelection(tr.doc.resolve(inlineNodePos), tr.doc.resolve(start + nodeSize - inlineNodeDepth));
29
+ return new TextSelection(tr.doc.resolve(start + nodeSize - inlineNodeDepth), tr.doc.resolve(inlineNodePos));
30
30
  }
31
31
  };
32
32
  export const selectNode = (tr, start, nodeType) => {
@@ -2,6 +2,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { createElement } from 'react';
3
3
  import ReactDOM from 'react-dom';
4
4
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
5
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
5
6
  import { DragHandle } from '../ui/drag-handle';
6
7
  import { DropTarget } from '../ui/drop-target';
7
8
  import { MouseMoveWrapper } from '../ui/mouse-move-wrapper';
@@ -19,6 +20,9 @@ export var dropTargetDecorations = function dropTargetDecorations(oldState, newS
19
20
  decs.push(Decoration.widget(pos, function () {
20
21
  var element = document.createElement('div');
21
22
  element.setAttribute('data-blocks-drop-target-container', 'true');
23
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
24
+ element.style.clear = 'unset';
25
+ }
22
26
  ReactDOM.render( /*#__PURE__*/createElement(DropTarget, {
23
27
  api: api,
24
28
  index: index
@@ -61,11 +65,17 @@ export var dropTargetDecorations = function dropTargetDecorations(oldState, newS
61
65
  export var nodeDecorations = function nodeDecorations(newState) {
62
66
  var decs = [];
63
67
  newState.doc.descendants(function (node, pos, _parent, index) {
68
+ var _Decoration$node;
64
69
  var anchorName = "--node-anchor-".concat(node.type.name, "-").concat(index);
65
- var style = "anchor-name: ".concat(anchorName, "; ").concat(pos === 0 ? 'margin-top: 0px;' : '');
66
- decs.push(Decoration.node(pos, pos + node.nodeSize, _defineProperty({
70
+ var style;
71
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
72
+ style = "anchor-name: ".concat(anchorName, "; ").concat(pos === 0 ? 'margin-top: 0px;' : '', "; position: relative; z-index: 1;");
73
+ } else {
74
+ style = "anchor-name: ".concat(anchorName, "; ").concat(pos === 0 ? 'margin-top: 0px;' : '');
75
+ }
76
+ decs.push(Decoration.node(pos, pos + node.nodeSize, (_Decoration$node = {
67
77
  style: style
68
- }, 'data-drag-handler-anchor-name', anchorName)));
78
+ }, _defineProperty(_Decoration$node, 'data-drag-handler-anchor-name', anchorName), _defineProperty(_Decoration$node, 'data-drag-handler-node-type', node.type.name), _Decoration$node)));
69
79
  return false;
70
80
  });
71
81
  return decs;
@@ -110,6 +120,11 @@ export var dragHandleDecoration = function dragHandleDecoration(pos, anchorName,
110
120
  element.style.display = 'inline';
111
121
  element.setAttribute('data-testid', 'block-ctrl-decorator-widget');
112
122
  element.setAttribute('data-blocks-drag-handle-container', 'true');
123
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
124
+ // There are times when global clear: "both" styles are applied to this decoration causing jumpiness
125
+ // due to margins applied to other nodes eg. Headings
126
+ element.style.clear = 'unset';
127
+ }
113
128
  ReactDOM.render( /*#__PURE__*/createElement(DragHandle, {
114
129
  view: view,
115
130
  api: api,
@@ -0,0 +1,29 @@
1
+ export var handleMouseOver = function handleMouseOver(view, event, api) {
2
+ var _api$blockControls;
3
+ var _ref = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {},
4
+ isDragging = _ref.isDragging,
5
+ activeNode = _ref.activeNode;
6
+ // Most mouseover events don't fire during drag but some can slip through
7
+ // when the drag begins. This prevents those.
8
+ if (isDragging) {
9
+ return false;
10
+ }
11
+ var target = event.target;
12
+ if (target.classList.contains('ProseMirror')) {
13
+ return false;
14
+ }
15
+ var rootElement = target === null || target === void 0 ? void 0 : target.closest('[data-drag-handler-anchor-name]');
16
+ if (rootElement) {
17
+ var anchorName = rootElement.getAttribute('data-drag-handler-anchor-name');
18
+ if ((activeNode === null || activeNode === void 0 ? void 0 : activeNode.anchorName) === anchorName) {
19
+ return false;
20
+ }
21
+ var pos = view.posAtDOM(rootElement, 0, 0);
22
+ var rootPos = view.state.doc.resolve(pos).start(1) - 1;
23
+ var nodeType = rootElement.getAttribute('data-drag-handler-node-type');
24
+ if (nodeType) {
25
+ var _api$core, _api$blockControls2;
26
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.commands.showDragHandleAt(rootPos, anchorName, nodeType));
27
+ }
28
+ }
29
+ };
@@ -10,6 +10,7 @@ import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-sc
10
10
  import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
11
11
  import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
12
12
  import { dragHandleDecoration, dropTargetDecorations, mouseMoveWrapperDecorations, nodeDecorations } from './decorations';
13
+ import { handleMouseOver } from './handle-mouse-over';
13
14
  export var key = new PluginKey('blockControls');
14
15
  var destroyFn = function destroyFn(api) {
15
16
  var scrollable = document.querySelector('.fabric-editor-popup-scroll-parent');
@@ -82,7 +83,7 @@ export var createPlugin = function createPlugin(api) {
82
83
  return initialState;
83
84
  },
84
85
  apply: function apply(tr, currentState, oldState, newState) {
85
- var _meta$activeNode, _meta$isDragging, _meta$editorHeight;
86
+ var _meta$activeNode, _meta$activeNode2, _meta$isDragging, _meta$editorHeight;
86
87
  if (initialState.isDocSizeLimitEnabled && newState.doc.nodeSize > DRAG_AND_DROP_DOC_SIZE_LIMIT) {
87
88
  return initialState;
88
89
  }
@@ -113,21 +114,47 @@ export var createPlugin = function createPlugin(api) {
113
114
  });
114
115
  decorations = decorations.remove(oldHandle);
115
116
  }
117
+ var isDecsMissing = false;
118
+ var isHandleMissing = false;
119
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
120
+ // Ensure decorations stay in sync when nodes are added or removed from the doc
121
+ isHandleMissing = !(meta !== null && meta !== void 0 && meta.activeNode) && !decorations.find().some(function (_ref6) {
122
+ var spec = _ref6.spec;
123
+ return spec.id === 'drag-handle';
124
+ });
125
+ var decsLength = decorations.find().filter(function (_ref7) {
126
+ var spec = _ref7.spec;
127
+ return spec.id !== 'drag-handle';
128
+ }).length;
129
+ isDecsMissing = !isDragging && decsLength !== newState.doc.childCount;
130
+ }
116
131
 
117
132
  // This is not targeted enough - it's trying to catch events like expand being set to breakout
118
133
  var maybeWidthUpdated = tr.docChanged && oldState.doc.nodeSize === newState.doc.nodeSize && !nodeCountChanged;
119
- var redrawDecorations = decorations === DecorationSet.empty || (meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== undefined && (meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== editorHeight || maybeWidthUpdated || nodeCountChanged || resizerMeta === false || !!(meta !== null && meta !== void 0 && meta.nodeMoved) && tr.docChanged;
134
+
135
+ // This addresses scenarios such as undoing table resizing,
136
+ // where a keyboard shortcut triggers a width change, and
137
+ // the node's actual width is then updated in a separate renderering cycle.
138
+ // The tr.meta.activeNode is triggered by the showDragHandleAt function during the mouse entry event
139
+ // (when the table node rerenders)
140
+ // The activeNode is from the previous rendering cycle, and verify if they share the same anchor.
141
+ var maybeNodeWidthUpdated = (meta === null || meta === void 0 ? void 0 : meta.activeNode) && (meta === null || meta === void 0 || (_meta$activeNode = meta.activeNode) === null || _meta$activeNode === void 0 ? void 0 : _meta$activeNode.nodeType) === 'table' && meta.activeNode.anchorName === (activeNode === null || activeNode === void 0 ? void 0 : activeNode.anchorName);
142
+ var redrawDecorations = decorations === DecorationSet.empty || (meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== undefined && (meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== editorHeight || maybeWidthUpdated || nodeCountChanged || maybeNodeWidthUpdated || resizerMeta === false || isDecsMissing || !!(meta !== null && meta !== void 0 && meta.nodeMoved) && tr.docChanged;
120
143
 
121
144
  // Draw node and mouseWrapper decorations at top level node if decorations is empty, editor height changes or node is moved
122
145
  if (redrawDecorations && !isResizerResizing && api) {
123
146
  decorations = DecorationSet.create(newState.doc, []);
124
147
  var nodeDecs = nodeDecorations(newState);
125
- var mouseWrapperDecs = mouseMoveWrapperDecorations(newState, api);
126
- decorations = decorations.add(newState.doc, [].concat(_toConsumableArray(nodeDecs), _toConsumableArray(mouseWrapperDecs)));
148
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
149
+ decorations = decorations.add(newState.doc, _toConsumableArray(nodeDecs));
150
+ } else {
151
+ var mouseWrapperDecs = mouseMoveWrapperDecorations(newState, api);
152
+ decorations = decorations.add(newState.doc, [].concat(_toConsumableArray(nodeDecs), _toConsumableArray(mouseWrapperDecs)));
153
+ }
127
154
 
128
155
  // Note: Quite often the handle is not in the right position after a node is moved
129
156
  // it is safer for now to not show it when a node is moved
130
- if (activeNode && !(meta !== null && meta !== void 0 && meta.nodeMoved)) {
157
+ if (activeNode && !(meta !== null && meta !== void 0 && meta.nodeMoved) && !isDecsMissing) {
131
158
  var newActiveNode = activeNode && tr.doc.nodeAt(tr.mapping.map(activeNode.pos));
132
159
  var nodeType = activeNode.nodeType;
133
160
  var anchorName = activeNode.anchorName;
@@ -142,8 +169,8 @@ export var createPlugin = function createPlugin(api) {
142
169
 
143
170
  // Remove previous drag handle widget and draw new drag handle widget when activeNode changes
144
171
  if (meta !== null && meta !== void 0 && meta.activeNode && (meta === null || meta === void 0 ? void 0 : meta.activeNode.pos) !== (activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos) && (meta === null || meta === void 0 ? void 0 : meta.activeNode.anchorName) !== (activeNode === null || activeNode === void 0 ? void 0 : activeNode.anchorName) && api) {
145
- var _oldHandle = decorations.find().filter(function (_ref6) {
146
- var spec = _ref6.spec;
172
+ var _oldHandle = decorations.find().filter(function (_ref8) {
173
+ var spec = _ref8.spec;
147
174
  return spec.id === 'drag-handle';
148
175
  });
149
176
  decorations = decorations.remove(_oldHandle);
@@ -163,8 +190,8 @@ export var createPlugin = function createPlugin(api) {
163
190
 
164
191
  // Remove drop target decoration when dragging stops
165
192
  if ((meta === null || meta === void 0 ? void 0 : meta.isDragging) === false && !tr.docChanged) {
166
- var dropTargetDecs = decorations.find().filter(function (_ref7) {
167
- var spec = _ref7.spec;
193
+ var dropTargetDecs = decorations.find().filter(function (_ref9) {
194
+ var spec = _ref9.spec;
168
195
  return spec.type === 'drop-target-decoration';
169
196
  });
170
197
  decorations = decorations.remove(dropTargetDecs);
@@ -172,9 +199,9 @@ export var createPlugin = function createPlugin(api) {
172
199
 
173
200
  // Map drop target decoration positions when the document changes
174
201
  if (tr.docChanged && isDragging) {
175
- decorationState = decorationState.map(function (_ref8) {
176
- var index = _ref8.index,
177
- pos = _ref8.pos;
202
+ decorationState = decorationState.map(function (_ref10) {
203
+ var index = _ref10.index,
204
+ pos = _ref10.pos;
178
205
  return {
179
206
  index: index,
180
207
  pos: tr.mapping.map(pos)
@@ -197,7 +224,7 @@ export var createPlugin = function createPlugin(api) {
197
224
  return {
198
225
  decorations: decorations,
199
226
  decorationState: decorationState,
200
- activeNode: isEmptyDoc ? null : (_meta$activeNode = meta === null || meta === void 0 ? void 0 : meta.activeNode) !== null && _meta$activeNode !== void 0 ? _meta$activeNode : mappedActiveNodePos,
227
+ activeNode: isEmptyDoc || isHandleMissing ? null : (_meta$activeNode2 = meta === null || meta === void 0 ? void 0 : meta.activeNode) !== null && _meta$activeNode2 !== void 0 ? _meta$activeNode2 : mappedActiveNodePos,
201
228
  isDragging: (_meta$isDragging = meta === null || meta === void 0 ? void 0 : meta.isDragging) !== null && _meta$isDragging !== void 0 ? _meta$isDragging : isDragging,
202
229
  isMenuOpen: meta !== null && meta !== void 0 && meta.toggleMenu ? !isMenuOpen : isMenuOpen,
203
230
  editorHeight: (_meta$editorHeight = meta === null || meta === void 0 ? void 0 : meta.editorHeight) !== null && _meta$editorHeight !== void 0 ? _meta$editorHeight : currentState.editorHeight,
@@ -234,41 +261,50 @@ export var createPlugin = function createPlugin(api) {
234
261
  return true;
235
262
  }
236
263
  return false;
264
+ },
265
+ mouseover: function mouseover(view, event) {
266
+ if (getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
267
+ handleMouseOver(view, event, api);
268
+ }
269
+ return false;
237
270
  }
238
271
  }
239
272
  },
240
273
  view: function view(editorView) {
241
274
  var dom = editorView.dom;
275
+ var resizeObserver;
276
+ if (!getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
277
+ // Use ResizeObserver to observe height changes
278
+ resizeObserver = new ResizeObserver(rafSchedule(function (entries) {
279
+ var editorHeight = entries[0].contentBoxSize[0].blockSize;
242
280
 
243
- // Use ResizeObserver to observe height changes
244
- var resizeObserver = new ResizeObserver(rafSchedule(function (entries) {
245
- var editorHeight = entries[0].contentBoxSize[0].blockSize;
246
-
247
- // Update the plugin state when the height changes
248
- var pluginState = key.getState(editorView.state);
249
- if (!(pluginState !== null && pluginState !== void 0 && pluginState.isDragging)) {
250
- var isResizerResizing = !!dom.querySelector('.is-resizing');
251
- var transaction = editorView.state.tr;
252
- if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.isResizerResizing) !== isResizerResizing) {
253
- transaction.setMeta('is-resizer-resizing', isResizerResizing);
254
- }
255
- if (!isResizerResizing) {
256
- transaction.setMeta(key, {
257
- editorHeight: editorHeight
258
- });
281
+ // Update the plugin state when the height changes
282
+ var pluginState = key.getState(editorView.state);
283
+ if (!(pluginState !== null && pluginState !== void 0 && pluginState.isDragging)) {
284
+ var isResizerResizing = !!dom.querySelector('.is-resizing');
285
+ var transaction = editorView.state.tr;
286
+ if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.isResizerResizing) !== isResizerResizing) {
287
+ transaction.setMeta('is-resizer-resizing', isResizerResizing);
288
+ }
289
+ if (!isResizerResizing) {
290
+ transaction.setMeta(key, {
291
+ editorHeight: editorHeight
292
+ });
293
+ }
294
+ editorView.dispatch(transaction);
259
295
  }
260
- editorView.dispatch(transaction);
261
- }
262
- }));
263
-
264
- // Start observing the editor DOM element
265
- resizeObserver.observe(dom);
296
+ }));
297
+ // Start observing the editor DOM element
298
+ resizeObserver.observe(dom);
299
+ }
266
300
 
267
301
  // Start pragmatic monitors
268
302
  var pragmaticCleanup = destroyFn(api);
269
303
  return {
270
304
  destroy: function destroy() {
271
- resizeObserver.unobserve(dom);
305
+ if (!getBooleanFF('platform.editor.elements.drag-and-drop-remove-wrapper_fyqr2')) {
306
+ resizeObserver.unobserve(dom);
307
+ }
272
308
  pragmaticCleanup();
273
309
  }
274
310
  };